Help us understand the problem. What is going on with this article?

Makefileを使ってみよう

More than 3 years have passed since last update.

結論

この分かりやすい概念図

Makefile
Gazo

コマンドライン実行
Gazo

Makefile
Gazo

これを作るには 何が必要で、どうやって作る、と並べる。
名前通り メイク・ファイルって感じ。

例外

Makefile
Gazo
つくりかた が無いやつや、 必要なもの がないやつもいる。
こういうやつは .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 書いていくか……。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away