結論
この分かりやすい概念図
これを作るには 何が必要で、どうやって作る、と並べる。
名前通り メイク・ファイルって感じ。
例外
Makefile
つくりかた が無いやつや、 必要なもの がないやつもいる。
こういうやつは .PHONY といって、そのうち出てくる。
今回の記事が始まる
使用環境
抽象度0 | 抽象度-1 | 抽象度-2 |
---|---|---|
OS | Linux | Ubuntu 16.04 |
使用言語 | C++ | |
コンパイラー | G++ | |
コンパイラー・ユーティリティー | Makefile |
なんだこの表。
で、こんな感じでフォルダ構成を作った。
t37a1.d/
|
+---tamesi37a1.cpp
|
+---tamesi37a1_msgq.cpp
|
+---tamesi37a1_msgq.hpp
依存関係はこんな感じ。
tamesi37a1_msgq.hpp
^
|
+---tamesi37a1.cpp
|
+---tamesi37a1_msgq.cpp
結論から言うと
Makefile
.PHONY: all clean
all: tamesi37a1.exe
tamesi37a1_msgq.o: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
g++ -std=c++11 -c tamesi37a1_msgq.cpp -o tamesi37a1_msgq.o -lev -lamqpcpp -pthread
tamesi37a1.o: tamesi37a1.cpp tamesi37a1_msgq.hpp
g++ -std=c++11 -c tamesi37a1.cpp -o tamesi37a1.o -lev -lamqpcpp -pthread
tamesi37a1.exe: tamesi37a1.o tamesi37a1_msgq.o
g++ -std=c++11 tamesi37a1.o tamesi37a1_msgq.o -o tamesi37a1.exe -lev -lamqpcpp -pthread
clean:
rm -f tamesi37a1.d tamesi37a1.o tamesi37a1_msgq.o tamesi37a1.exe tamesi37a1.out.log tamesi37a1.err.log
こう書けば よかったわけなんだが、そんなん初見で分からんし。
今回の記事・再
今までは 他人の書いた Makefile が既にあって、
make profgen_sse
とか書けば G++ がコンパイルを進めてくれて楽だった。
これからは 自分で Makefile が書けないと不便そうだ。
えっ、統合開発環境? GUI? 何それ、知らないぜ。
自分が書いたプログラム・ファイルが1つだけなら
g++ -std=c++11 tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
とでも打鍵すれば コンパイルしてくれて楽だった。
じゃあ Makefile の使い方を調べていくか。
(トリビアなmakefile入門)
http://www.jsk.t.u-tokyo.ac.jp/~k-okada/makefile/
フォルダーと Makefile
make コマンドは勝手に Makefile を開いてくれる。ということは、1つのフォルダーの中には 1つのMakefile しか置けないのか?
フォルダーに入ったり出たりするのは不便だな……。
make コマンド
make コマンドと言えば、
make なんか名前
と叩くものだった。この なんか名前 のことを ターゲット と呼ぶのらしい。
他人の作った Makefile を眺めてみると、
なんか名前:
何か
なんか名前:
何か
なんか名前:
何か
なんか名前:
何か
といった部分がある。ところで今一つ信じられない話しだが 半角スペースと タブ は使い分ける必要があるかもしれない。
もうこれからは なんか名前 と呼ばずに ターゲット と呼ぶぞ。
ターゲット:
何か
ターゲット:
何か
ターゲット:
何か
ターゲット:
何か
clean
よく使うコマンドに
make clean
がある。浮かむ瀬の Makefile を見てみると
clean:
rm -f $(OBJECTS) $(DEPENDS) $(TARGET) ${OBJECTS:.o=.gcda}
といった感じで ターゲットと、その下に何かがある。この書き方を覚えれば良さそうだ。
じゃあ、
Makefile
tamesi37a1: tamesi37a1_msgq.hpp tamesi37a1_msgq.cpp
g++ -std=c++11 tamesi37a1.cpp -o tamesi37a1.exe
こんな書き方でいいのか? 試してみよう。
# make tamesi37a1
g++ -std=c++11 tamesi37a1.cpp -o tamesi37a1.exe
In file included from tamesi37a1.cpp:88:0:
tamesi37a1_msgq.hpp: In lambda function:
tamesi37a1_msgq.hpp:206:30: error: passing ‘const rotationBuffer::RotationBuffer’ as ‘this’ argument discards qualifiers [-fpermissive]
rotaBuf.push_block(message);
^
あっ! コンパイル・エラーが出てきた!
まず そっちを直そう……。
-MMD
依存しているヘッダ・ファイルなんか読んでくれてなさげ。
「依存関係の自動解決(第二回)」(ソフトウェアまわりの備忘録)
http://gmaj7sus4.hatenablog.com/entry/2013/11/15/163242
なんだか便利そうなので付けておこう。
tamesi37a1: tamesi37a1_msgq.hpp tamesi37a1_msgq.cpp
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
こうする。
# make tamesi37a1
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
/tmp/ccH1NL7t.o: In function `main':
tamesi37a1.cpp:(.text+0x47e): undefined reference to `msgq::Settings::Settings()'
tamesi37a1.cpp:(.text+0x49c): undefined reference to `msgq::Settings::parseCommandlineArgs(int, char**)'
tamesi37a1.cpp:(.text+0x4cb): undefined reference to `msgq::Settings::GetQueueName[abi:cxx11](int)'
tamesi37a1.cpp:(.text+0x4df): undefined reference to `msgq::Settings::GetQueueLifeSpan(int)'
tamesi37a1.cpp:(.text+0x649): undefined reference to `msgq::Settings::~Settings()'
tamesi37a1.cpp:(.text+0x705): undefined reference to `msgq::Settings::~Settings()'
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'tamesi37a1' failed
make: *** [tamesi37a1] Error 1
ぶへっ。
.text+0x47e とか出ているが行数ではないのか。
「静的ライブラリのリンク時にundefined referenceエラーが出る(gcc)」(gtkmm 入門)
http://www.hakodate-ct.ac.jp/~tokai/tokai/gtkmm/etc/p1.htm
さっぱり分からん。こうか。
tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
変わんね。
「C/C++でundefined reference toエラーが出る場合」(なんとな~くしあわせ?の日記)
http://nantonaku-shiawase.hatenablog.com/entry/20120107/1325961201
LDFLAGS = -lev -lamqpcpp -pthread
tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe $(LDFLAGS)
じゃあ、こうか?
# make tamesi37a1
Makefile:4: *** missing separator. Stop.
違うようだ。
LDFLAGS = -lev -lamqpcpp -pthread
tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe CFLAGS='$(LDFLAGS)'
じゃあこうか?
# make tamesi37a1
Makefile:4: *** missing separator. Stop.
ちがうようだ。
半角スペースをタブに変えてみる。
# make tamesi37a1
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe CFLAGS='-lev -lamqpcpp -pthread'
g++: error: CFLAGS=-lev -lamqpcpp -pthread: No such file or directory
Makefile:4: recipe for target 'tamesi37a1' failed
make: *** [tamesi37a1] Error 1
うーむ。
tamesi37a1: tamesi37a1_msgq.cpp tamesi37a1_msgq.hpp
g++ -std=c++11 -MMD tamesi37a1_msgq.cpp -o tamesi37a1_msgq.o -lev -lamqpcpp -pthread
g++ -std=c++11 -MMD tamesi37a1.cpp -o tamesi37a1.exe -lev -lamqpcpp -pthread
これでどうか?
# make tamesi37a1
g++ -std=c++11 -MMD tamesi37a1_msgq.cpp -o tamesi37a1_msgq.o -lev -lamqpcpp -pthread
tamesi37a1_msgq.cpp: In function ‘std::__cxx11::string getTimeslipNow()’:
tamesi37a1_msgq.cpp:11:35: error: ‘std::__cxx11::string getTimeslipNow()’ was declared ‘extern’ and later ‘static’ [-fpermissive]
static std::string getTimeslipNow()
^
エラーが増えた。
「C++のための即席Makefile」(簡潔なQ)
http://qnighy.hatenablog.com/entry/20100117/1263729044
分からん。
.PHONY: all clean
.SUFFIXES: .cpp .o
all: hello.exe
clean:
$(RM) hello.exe
.cpp.o:
g++ -O2 -Wall -o $@ -c $^
.cpp:
g++ -O2 -Wall -o $@ $^ -lm
説明を読むと こういう構成かと思うが分からん。
ハローワールド
study.cpp
// #pragma once
// #include "stdafx.h"
#include <iostream> // <<
#include <string> // std::getline
int main()
{
std::cout << "hello, world" << std::endl;
std::cout << "Please, push any key." << std::endl;
std::string unuse;
std::getline(std::cin, unuse);
return 0;
}
これを Makefile でコンパイルしてみよう。
単純にコンパイルするだけなら
g++ -std=c++11 study.cpp -o study.exe
だが
Makefile
all:
g++ -std=c++11 study.cpp -o study.exe
と書いて
make
とだけ打鍵すると 同じく実行ファイルは作られた。
# ./study.exe
hello, world
Please, push any key.
ハローワールド2
Student.hpp
#ifndef STUDENT_HPP
#define STUDENT_HPP
//#pragma once
#include <string>
class Student
{
std::string name;
public:
Student();
~Student();
std::string getName();
};
#endif // STUDENT_HPP
Student.cpp
#include "stdafx.h"
#include "Student.hpp"
Student::Student()
{
name = "Muzudho";
}
Student::~Student()
{
}
std::string Student::getName()
{
return name;
}
main.cpp
// g++ -std=c++11 main.cpp -o main.exe
#pragma once
#include "stdafx.h"
#include <iostream> // <<
#include <string> // std::getline
#include "Student.hpp"
int main()
{
Student student;
std::cout << "hello, world. name=[" << student.getName() << "]" << std::endl;
std::cout << "Please, push any key." << std::endl;
std::string unuse;
std::getline(std::cin, unuse);
return 0;
}
この何の面白味もないファイルのコンパイルに挑戦しよう。
コメントをもらっている。
まず Student.cpp と Student.hpp を合体させて Student.o を作り、
次に main.cpp と Student.hpp を合体させて main.o を作り、
最後に Student.o と main.o を合体させて main.exe を作ったらいいのだろうか?
Student.o: Student.cpp Student.hpp
g++ -std=c++11 -c Student.cpp -o Student.o
main.o: main.cpp Student.hpp
g++ -std=c++11 -c main.cpp -o main.o
main.exe:
g++ -std=c++11 main.o Student.o -o main.exe
こんなんで いいんだろうか?
# make
make: *** No rule to make target 'Student.cpp', needed by 'Student.o'. Stop.
なんのこっちゃ。
じゃあ、こうか?
# make main.exe
g++ -std=c++11 main.o Student.o -o main.exe
g++: error: main.o: No such file or directory
g++: error: Student.o: No such file or directory
g++: fatal error: no input files
compilation terminated.
Makefile:8: recipe for target 'main.exe' failed
make: *** [main.exe] Error 1
こうでもない。
# ls
main.cpp Makefile Stident.cpp Student.hpp
スチイデントがいるな。
# mv Stident.cpp Student.cpp
# make
g++ -std=c++11 -c Student.cpp -o Student.o
Student.cpp:1:20: fatal error: stdafx.h: No such file or directory
compilation terminated.
Makefile:2: recipe for target 'Student.o' failed
make: *** [Student.o] Error 1
stdafx.h は コメントアウトするか。
# make
g++ -std=c++11 -c Student.cpp -o Student.o
# ls
main.cpp Makefile Student.cpp Student.hpp Student.o
Student.o は作られたが……。
「Makefileの書き方に関する備忘録」(minus9d's diary)
http://minus9d.hatenablog.com/entry/20140203/1391436293
うーむ。
# make main.o
g++ -std=c++11 -c main.cpp -o main.o
main.cpp:3:9: warning: #pragma once in main file
#pragma once
^
#pragma もコメントアウトしておこう。
# make main.o
g++ -std=c++11 -c main.cpp -o main.o
# ls
main.cpp main.o Makefile Student.cpp Student.hpp Student.o
これで main.o まででけた。
# make main.exe
g++ -std=c++11 main.o Student.o -o main.exe
# ls
main.cpp main.exe main.o Makefile Student.cpp Student.hpp Student.o
main.exe もでけた。
# ./main.exe
hello, world. name=[Muzudho]
Please, push any key.
実行ファイルはでけた。
フォールスルー してくれないのか。
クリーンしてみよう
# ls
main.cpp main.exe main.o Makefile Student.cpp Student.hpp Student.o
じゃあ、こうか?
Makefile 抜粋
clean:
rm -f main.o Student.o main.exe
# make clean
rm -f main.o Student.o main.exe
# ls
main.cpp Makefile Student.cpp Student.hpp
クリーンは一発でうまくいったな!
フォール・スルーはどうやるのか?
# make main.exe
g++ -std=c++11 main.o Student.o -o main.exe
g++: error: main.o: No such file or directory
g++: error: Student.o: No such file or directory
g++: fatal error: no input files
compilation terminated.
Makefile:8: recipe for target 'main.exe' failed
make: *** [main.exe] Error 1
うーむ。
Makefile
Student.o: Student.cpp Student.hpp
g++ -std=c++11 -c Student.cpp -o Student.o
main.o: main.cpp Student.hpp
g++ -std=c++11 -c main.cpp -o main.o
main.exe: main.o Student.o
g++ -std=c++11 main.o Student.o -o main.exe
clean:
rm -f main.o Student.o main.exe
ターゲットの横に、上にあるターゲットを書いて つなげたらどうか?
# make main.exe
g++ -std=c++11 -c main.cpp -o main.o
g++ -std=c++11 -c Student.cpp -o Student.o
g++ -std=c++11 main.o Student.o -o main.exe
# ls
main.cpp main.exe main.o Makefile Student.cpp Student.hpp Student.o
おっ!
# ./main.exe
hello, world. name=[Muzudho]
Please, push any key.
なーんだ、1行目は依存関係で、2行目は タブ+コマンド なのか。
じゃあ がんばって Makefile 書いていくか……。