ここ数か月、仕事の合間を見て少しずつ「ゼロからのOS自作入門」に挑戦。
どうせならすべてを自分で作ってみようと一つずつ、こつこつとやっていると、ある日突然makeをするとエラーが出るようになった。
Makefile:2: *** 分離記号を欠いています. 中止.
昨日までできていたのに!?なにこのそっけないメッセージは!!と思いながら、解決まで苦労したので、その奮闘過程をお納めください。
まずは結論から
以下、長々と発見に至った過程を綴りますが、結論は「ゼロからのOS自作入門」に記載されているMakefileの以下の修正すればOKです。
%.o: %.cpp Makefile
clang++ $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.%.d: %.cpp
- clang++ $(CPPFLAGS) $(CXXFLAGS) -MM $< > $@
+ clang++ $< $(CPPFLAGS) $(CXXFLAGS) -MM -MF $@
$(eval OBJ = $(<:.cpp=.o))
sed --in-place 's|$(notdir $(OBJ))|$(OBJ)|' $@
%.o: %.c Makefile
clang $(CPPFLAGS) $(CFLAGS) -c $< -o $@
.%.d: %.c
- clang $(CPPFLAGS) $(CFLAGS) -MM $< > $@
+ clang $< $(CPPFLAGS) $(CFLAGS) -MM -MF $@
$(eval OBJ = $(<:.c=.o))
sed --in-place 's|$(notdir $(OBJ))|$(OBJ)|' $@
なんと、あっけない。。以下は駄文でございます。
原因の特定
突然、makeが通らなくなった。分離記号を欠いてます。missing separator. Stop.
シンプルだけど冷たいメッセージね。
気を取り直して、makeによって生成される「.o」ファイルや「.d」ファイルをすべて手作業で削除してもう一度makeを試した。(kernelフォルダだけでなく、そのサブフォルダのusbフォルダや、さらにそのサブフォルダのusb/xhci、usb/classdriverフォルダの中の「.o」ファイルや「.d」ファイルも削除しないとやり直せないのでお気をつけあれ)
すると、makeができた!なんだ、一時的なあれか。(←あれの原因はよくわからないけど、再起動すると直る的な・・)
そして、しばらくソースコードをいじってると、またあのエラーが出てきた!!
なんで??makeファイルは全然触ってないのに!ちなみに、make cleanすらもできない。だから、また手動で.oや.dを削除すると、またmakeが通った。
この時点で一つの仮説にたどり着く(というほど大げさなものでもないが)
Visual Studio Codeでソースコード(Makefileとは全然違うmain.cppとかでも)を編集すると、そのあとからmakeができなくなる。
「Visual Studio Code makefile 分離記号」でググると早速以下のサイトがヒット!
(楽勝解決!)
VSCodeで作ったMakefileが”分離記号を欠いています”エラーになる
なになに、そうか!makefileの文字コードがVisual Studio CodeによってUTF-8のBOMありになっちゃうんだ!文字コードを正しいものに設定しよう。
・・・・。結論、文字コードを変えても症状は全く変わりませんでした。
原因を再特定
おかしい、おかしいとうなりながら、いろいろ見てると、新事実を発見!
Visual Studio Codeで対象のディレクトリを開くと「.d」ファイルが書き換えられとる!!
そもそも、.dファイルは各ソースファイルが依存しているファイルを自動的に特定してmakefileに依存性「DEPENDS」としてお知らせしてもらうために作られるもの。
だから、その中身はこんな感じになっているはず。
main.o: main.cpp \
/HOME/osbook/devenv/x86_64-elf/include/c++/v1/cstddef \
/HOME/osbook/devenv/x86_64-elf/include/c++/v1/__config \
以下省略・・
でも、Visual Studio Codeで開いた後はなぜか、.dファイルがこんな感じに変わってしまっている。
clang
-MM
-I/Home/osbook/devenv/x86_64-elf/include/c++/v1/cstddef
以下省略
なんじゃこりゃあ!
全然違うものに変わっとる。
そりゃ、分離記号も欠けてしまうわ。分離記号がなんなのかわからんが。
ということで解決を
どうやらVisual Studio Codeを立ち上げてディレクトリを読み込んだ際に、Makefile toolsという拡張機能が動いて、dry-runを行うことが原因っぽい。
dry-runというのは、実際にはmakeはやらない(.oファイルや.dファイルを作らない)けど、とりあえずやった時に出てくるメッセージを見てみるためのお試しコマンド。
え、.dファイルを作らない(作るコマンドを実行しない)ならいいじゃんと思いきや、以下の部分に罠があった。
clang++ $(CPPFLAGS) $(CXXFLAGS) -MM $< > $@
これは、わかりやすく書くとこんな感じの意味をもつ。
(いろんなフラグ系は省略)
clang++ -MM main.cpp > .main.d
-MMオプションは依存関係を調べて標準出力に出力することだから、依存関係の情報をそのまま、.main.dファイルに突っ込んでいる。依存ファイルが出来上がって、よいことだ。
でも、dry-runをしてしまうとこれが問題になる。
dry-runでは実際にclang++コマンドを実行しないので、依存ファイルの情報は吐き出されない。じゃあ、何が .main.dに突っ込まれるかというとコマンドそのもの(この場合、clang++ -MM main.cppが.main.dに突っ込まれる)
おお。そういうことか。
今度こそ解決
.main.dファイルにリダイレクトしてしまうことが問題なので、そうではなくて、clang++のコマンドで依存関係をファイルに保存するオプションを使う。
それが -MF。
clang++ $< $(CPPFLAGS) $(CXXFLAGS) -MM -MF $@
これでOK。ちなみに、-MFオプションの直後に保存するファイル名がこないといけないので、少し並びを変更している。
はぁ、疲れた。