make に関する情報を記載します。
基本情報というより、makefile サンプルをいくつか作成した例の紹介という感じです。
"Hello!" をコンソールへ出力するだけの簡単な実行ファイルを作成する手順を題材に説明します。
コンパイラ : | g++, | 13.3.0 |
OS : | Ubuntu, | 24.04 |
まず g++ コンパイラを使ったビルドの基本を確認します。
下記のようなソースコードを準備します。
ファイル構成
./ ├ main.cpp ├ hello.h └ hello.cpp
[main.cpp]
#include "hello.h" int main() { hello(); }
[hello.h]
#ifndef HELLO_H #define HELLO_H void hello(); #endif
[hello.cpp]
#include <iostream> #include "hello.h" void hello() { std::cout << "Hello!" << std::endl; }
上記ソースコード3つを準備できたら、下記コマンドを入力してビルドします。
$ g++ -Wall -g -c main.cpp hello.cpp $ g++ -o start_cmake.out main.o hello.o
bash スクリプトの例です。
スクリプト作成後に chmod +x build.sh
を忘れずに。
[ビルドスクリプト例: "build.sh"]
#!/bin/bash # スクリプトの冒頭に set -e を追加すると、エラーが発生した時点でスクリプトの実行を停止します # スクリプトの冒頭に set -x を追加すると、実行するコマンドを自動的に出力します set -ex rm -f *.o start_cmake.out g++ -Wall -g -c main.cpp hello.cpp g++ -o start_cmake.out main.o hello.o
ビルドされた start_make.out を実行すれば Hello! を出力します。
$ ./start_cmake.out Hello!
"1-1. コマンドラインでビルド" の内容を make を使ってビルドするようにしてみます。
makefile を以下のように作成します。
["makefile"]
# コンパイラとターゲット CC = g++ TARGET = start_make.out # ディレクトリとファイル SRCS = main.cpp hello.cpp OBJS = $(SRCS:.cpp=.o) # コンパイルフラグ CFLAGS = -Wall -g # ターゲットのビルドルール $(TARGET): $(OBJS) $(CC) -o $@ $^ # オブジェクトファイルのビルドルール %.o: %.cpp $(CC) $(CFLAGS) -c $< -o $@ # 全ビルド .PHONY: all all: clean $(TARGET) @echo "Build all targets." # クリーンアップ .PHONY: clean clean: rm -f $(OBJS) $(TARGET) @echo "Clean up all targets."
makefile ファイルを追加後のディレクトリの中身は以下のようになります。
ファイル構成
./ ├ makefile ├ main.cpp ├ hello.h └ hello.cpp
makefile を使ってビルドする操作を以下に示します。
$ make g++ -Wall -g -c main.cpp -o main.o g++ -Wall -g -c hello.cpp -o hello.o g++ -o start_make.out main.o hello.o $ ./start_make.out Hello! $
続けて make を実行すると、既にビルド済みということで「make: 'start_make.out' is up to date.」を出力してビルド実行しません。
$ make g++ -Wall -g -c main.cpp -o main.o g++ -Wall -g -c hello.cpp -o hello.o g++ -o start_make.out main.o hello.o $ ./start_make.out Hello! $ make make: 'start_make.out' is up to date. $
続けてソースコードを修正して make を実行すると、修正した部分のみをビルドします。
main.cpp を変更後 make した例を以下に示します。
main.cpp のみビルドされて start_make.out を再構築されたことを確認できます。
$ make
g++ -Wall -g -c main.cpp -o main.o
g++ -Wall -g -c hello.cpp -o hello.o
g++ -o start_make.out main.o hello.o
$ ./start_make.out
Hello!
$ make
make: 'start_make.out' is up to date.
*** ここで main.cpp を修正 ***
$ make
g++ -Wall -g -c main.cpp -o main.o
g++ -o start_make.out main.o hello.o
$
make clean と入力すると、ビルド結果をすべて削除します。
$ make clean rm -f main.o hello.o start_make.out Clean up all targets. $
make all と入力すると、ビルド結果をすべて削除後に全ビルドを実施します。
$ make all rm -f main.o hello.o start_make.out Clean up all targets. g++ -Wall -g -c main.cpp -o main.o g++ -Wall -g -c hello.cpp -o hello.o g++ -o start_make.out main.o hello.o Build all targets. $
ライブラリを作る場合、静的・動的ライブラリ(*.a, *.so)を作成することになります。そして実行ファイルにリンクして使用します。
本章では、静的・動的ライブラリを cmake を使って作成などする手順について記載します。
コンパイラ : | g++, | 13.3.0 |
OS : | Ubuntu, | 24.04 |
下記のようなソースコードを準備します。
前章に good_morning.h, good_morning.cpp 2つのファイルを追加した感じです。
ファイル構成
./ ├ main.cpp ├ hello.h ├ hello.cpp ├ good_morning.h └ good_morning.cpp
[good_morning.h]
#ifndef GOOD_MORNING_H #define GOOD_MORNING_H void good_morning(); #endif
[good_morning.cpp]
#include <iostream> #include "good_morning.h" void good_morning() { std::cout << "Good morning!" << std::endl; }
main.cpp を次のように変更します。
[main.cpp]
#include "hello.h" #include "good_morning.h" int main() { hello(); good_morning(); }
静的ライブラリを作成後、main.cpp をコンパイルして静的ライブラリとリンクします。
$ g++ -Wall -g -c hello.cpp good_morning.cpp # hello.o, good_morning.o を作成 $ ar rvs libgreetings.a hello.o good_morning.o # 静的ライブラリ(libgreetings.a)を作成 $ g++ -o start_make.out main.cpp -lgreetings -L. # main.cpp をコンパイルして libgreetings.a とリンク
bash スクリプトの例です。
スクリプト作成後に chmod +x build.sh
を忘れずに。
[ビルドスクリプト例: "build.sh"]
#!/bin/bash # スクリプトの冒頭に set -e を追加すると、エラーが発生した時点でスクリプトの実行を停止します # スクリプトの冒頭に set -x を追加すると、実行するコマンドを自動的に出力します set -ex rm -f *.o *.a start_make.out g++ -Wall -g -c hello.cpp good_morning.cpp ar rvs libgreetings.a hello.o good_morning.o g++ -o start_make.out main.cpp -lgreetings -L.
生成したファイル start_cmake.out を実行すれば下記のように出力します。
$ ./start_make.out Hello! Good morning!
動的ライブラリを作成後、main.cpp をコンパイルして動的ライブラリとリンクします。
$ g++ -fPIC -c hello.cpp good_morning.cpp # hello.o, good_morning.o を作成 $ g++ -shared hello.o good_morning.o -o libgreetings.so # 静的ライブラリ(libgreetings.so)を作成 $ g++ -o start_make.out main.cpp -lgreetings -L. -Xlinker -rpath -Xlinker . # mmain.cpp をコンパイルして libgreetings.so とリンク
bash スクリプトの例です。
スクリプト作成後に chmod +x build.sh
を忘れずに。
[ビルドスクリプト例: "build.sh"]
#!/bin/bash # スクリプトの冒頭に set -e を追加すると、エラーが発生した時点でスクリプトの実行を停止します # スクリプトの冒頭に set -x を追加すると、実行するコマンドを自動的に出力します set -ex rm -f *.o *.so start_make.out g++ -fPIC -Wall -g -c hello.cpp good_morning.cpp g++ -shared hello.o good_morning.o -o libgreetings.so g++ -o start_make.out main.cpp -lgreetings -L. -Xlinker -rpath -Xlinker .
生成したファイル start_cmake.out を実行すれば下記のように出力します。
$ ./start_make.out Hello! Good morning!
"2-1. コマンドラインでビルド" の内容を make を使ってビルドするようにしてみます。
前章と同様にファイル構成に makefile を追加します。
ファイル構成
./ ├ makefile ├ main.cpp ├ hello.h ├ hello.cpp ├ good_morning.h └ good_morning.cpp
makefile を以下のように作成します。
["makefile"]
# コンパイラとアーカイバ CC = g++ AR = ar # ターゲット TARGET = start_make.out LIBRARY = libgreetings.a # ソースファイルとオブジェクトファイル SRCS = main.cpp OBJS = $(SRCS:.cpp=.o) LIB_SRCS = hello.cpp good_morning.cpp LIB_OBJS = $(LIB_SRCS:.cpp=.o) # コンパイルフラグ CFLAGS = -Wall -g ARFLAGS = rvs # 実行ファイルのビルドルール $(TARGET): $(OBJS) $(LIBRARY) $(CC) -o $@ $(OBJS) -L. -lgreetings # ライブラリのビルドルール $(LIBRARY): $(LIB_OBJS) $(AR) $(ARFLAGS) $@ $^ # オブジェクトファイルのビルドルール %.o: %.cpp $(CC) $(CFLAGS) -c $< -o $@ # 全ビルド .PHONY: all all: clean $(TARGET) @echo "Build all targets." # クリーンアップ .PHONY: clean clean: rm -f $(TARGET) $(OBJS) $(LIB_OBJS) $(LIBRARY) @echo "Clean up all targets."
ビルドおよびビルド生成物を実行するまで一連の操作を以下に示します。
$ make g++ -Wall -g -c main.cpp -o main.o g++ -Wall -g -c hello.cpp -o hello.o g++ -Wall -g -c good_morning.cpp -o good_morning.o ar rvs libgreetings.a hello.o good_morning.o ar: creating libgreetings.a a - hello.o a - good_morning.o g++ -o start_make.out main.o -L. -lgreetings $ ./start_make.out Hello! Good morning! $
makefile を以下のように作成します。
["makefile"]
# コンパイラ CC = g++ # ターゲット TARGET = start_make.out LIBRARY = libgreetings.so # ソースファイルとオブジェクトファイル SRCS = main.cpp OBJS = $(SRCS:.cpp=.o) LIB_SRCS = hello.cpp good_morning.cpp LIB_OBJS = $(LIB_SRCS:.cpp=.o) # コンパイルフラグ CFLAGS = -Wall -g -fPIC LDFLAGS = -shared RPATH_FLAGS = -Xlinker -rpath -Xlinker . # 実行ファイルのビルドルール $(TARGET): $(SRCS) $(LIBRARY) $(CC) -o $@ $(SRCS) -L. -lgreetings $(RPATH_FLAGS) # 共有ライブラリのビルドルール $(LIBRARY): $(LIB_OBJS) $(CC) $(LDFLAGS) -o $@ $^ # オブジェクトファイルのビルドルール %.o: %.cpp $(CC) $(CFLAGS) -c $< -o $@ # 全ビルド .PHONY: all all: clean $(TARGET) @echo "Build all targets." # クリーンアップ .PHONY: clean clean: rm -f $(TARGET) $(OBJS) $(LIB_OBJS) $(LIBRARY) @echo "Clean up all targets."
ビルドおよびビルド生成物を実行するまで一連の操作を以下に示します。
$ make g++ -Wall -g -fPIC -c hello.cpp -o hello.o g++ -Wall -g -fPIC -c good_morning.cpp -o good_morning.o g++ -shared -o libgreetings.so hello.o good_morning.o g++ -o start_make.out main.cpp -L. -lgreetings -Xlinker -rpath -Xlinker . $ ./start_make.out Hello! Good morning! $
ソフトウェアが大規模になってくると役割毎に保存するフォルダを分けたりします。そういった場合を例に説明します。
コンパイラ : | g++, | 13.3.0 |
OS : | Ubuntu, | 24.04 |
下記のようなフォルダおよびファイル構成を例に説明します。ファイル内容は前章と同じです。
ファイル構成
./ ├ include/ │├ hello.h │└ good_morning.h ├ source/ │├ hello.cpp │└ good_morning.cpp └ main/ └ main.cpp
[main.cpp]
#include "hello.h" #include "good_morning.h" int main() { hello(); good_morning(); }
[hello.h]
#ifndef HELLO_H #define HELLO_H void hello(); #endif
[hello.cpp]
#include <iostream> #include "hello.h" void hello() { std::cout << "Hello!" << std::endl; }
[good_morning.h]
#ifndef GOOD_MORNING_H #define GOOD_MORNING_H void good_morning(); #endif
[good_morning.cpp]
#include <iostream> #include "good_morning.h" void good_morning() { std::cout << "Good morning!" << std::endl; }
さて、これをコマンドラインからビルドしてみます。ルートディレクトリ直下にいるものとし、共有ライブラリを作成して実行ファイルにリンクするやり方で行います。
今回は -L オプションに加えて -I オプションでインクルードファイルを探すディレクトリを指定する必要があります。前章で -I オプションが必要なかったのは、カレントディレクトリはデフォルトでインクルードファイルを探す場所として認識されるからです。
$ cd source/ $ g++ -fPIC -c hello.cpp good_morning.cpp -I../include $ g++ -shared *.o -o libgreetings.so $ cd ../main/ $ g++ main.cpp -o main.out -I../include -L../source -lgreetings -Xlinker -rpath -Xlinker ../source
bash スクリプトの例です。
スクリプト作成後に chmod +x build.sh
を忘れずに。
[ビルドスクリプト例: "build.sh"]
#!/bin/bash # スクリプトの冒頭に set -e を追加すると、エラーが発生した時点でスクリプトの実行を停止します # スクリプトの冒頭に set -x を追加すると、実行するコマンドを自動的に出力します set -ex rm -f source/*.o source/*.so main/start_make.out cd source/ g++ -fPIC -Wall -g -c hello.cpp good_morning.cpp -I../include g++ -shared *.o -o libgreetings.so cd ../main/ g++ main.cpp -o main.out -I../include -L../source -lgreetings -Xlinker -rpath -Xlinker ../source
NOTE
上記説明では -Xlinker の引数として相対パス("../source")を使用していますが、ライブラリ所在を絶対パスで指定することも可能です。
LD_LIBRARY_PATH 環境変数を使用して共有ライブラリのパスを指定する、という方法もあります。
export LD_LIBRARY_PATH=../source:$LD_LIBRARY_PATH
上記コマンドを実行後のフォルダ構成は下図のようになります。
./ ├ include/ │├ hello.h │└ good_morning.h ├ source/ │├ hello.cpp │├ hello.o │├ good_morning.cpp │├ good_morning.o │└ libgreetings.so └ main/ ├ main.cpp └ main.out
生成したファイル main.out を実行すると下記のように出力します。
$ ./main.out Hello! Good morning!
"3-1. コマンドラインでビルド" の内容を make を使ってビルドできるようにします。
ファイル構成を下図のようにします。makefile をルートおよび source/、main/ の3か所に追加します。
ファイル構成
./ ├ makefile ├ include/ │├ hello.h │└ good_morning.h ├ source/ │├ makefile │├ hello.cpp │└ good_morning.cpp └ main/ ├ makefile └ main.cpp
ルートディレクトリにある makefile ではサブディレクトリの登録をします。
["./makefile"]
# デフォルトターゲット .PHONY: target target: source_build main_build @echo "Build complete." # source ディレクトリのビルド .PHONY: source_build source_build: @echo "Building source directory..." $(MAKE) -C source # main ディレクトリのビルド .PHONY: main_build main_build: @echo "Building main directory..." $(MAKE) -C main # 全ビルド .PHONY: all all: @echo "Make source directory..." $(MAKE) -C source all @echo "Make main directory..." $(MAKE) -C main all @echo "Make all targets." # クリーンアップ .PHONY: clean clean: @echo "Cleaning source directory..." $(MAKE) -C source clean @echo "Cleaning main directory..." $(MAKE) -C main clean @echo "Clean up all targets."
/source/makefile および /main/makefile ではそれぞれのディレクトリにあるファイルのコンパイル方法を指定します。
["./source/makefile"]
# コンパイラ CC = g++ # ターゲット LIBRARY = libgreetings.so # ソースファイルとオブジェクトファイル SRCS = hello.cpp good_morning.cpp OBJS = $(SRCS:.cpp=.o) # コンパイルフラグ CFLAGS = -Wall -g -fPIC -I../include LDFLAGS = -shared # 共有ライブラリのビルドルール $(LIBRARY): $(OBJS) $(CC) $(LDFLAGS) -o $@ $^ # オブジェクトファイルのビルドルール %.o: %.cpp $(CC) $(CFLAGS) -c $< -o $@ # 全ビルド .PHONY: all all: clean $(LIBRARY) @echo "Build all targets." # クリーンアップ .PHONY: clean clean: rm -f $(OBJS) $(LIBRARY) @echo "Clean up all targets."
["./main/makefile"]
# コンパイラ CC = g++ # ターゲット TARGET = main.out # ソースファイル SRCS = main.cpp # コンパイルフラグ CFLAGS = -Wall -g -I../include LDFLAGS = -L../source -lgreetings -Xlinker -rpath -Xlinker ../source # 実行ファイルのビルドルール $(TARGET): $(SRCS) $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) # 全ビルド .PHONY: all all: clean $(LIBRARY) @echo "Build all targets." # クリーンアップ .PHONY: clean clean: rm -f $(TARGET) @echo "Clean up all targets."
以上で準備を完了しました。
make を使った場合のいつもの手順でビルドします。下記ではビルド結果 main.out の実行まで一連の操作とコンソール出力の例を例示します。
$ make Building source directory... make -C source make[1]: Entering directory 'source' g++ -Wall -g -fPIC -I../include -c hello.cpp -o hello.o g++ -Wall -g -fPIC -I../include -c good_morning.cpp -o good_morning.o g++ -shared -o libgreetings.so hello.o good_morning.o make[1]: Leaving directory 'source' Building main directory... make -C main make[1]: Entering directory 'main' g++ -Wall -g -I../include main.cpp -o main.out -L../source -lgreetings -Xlinker -rpath -Xlinker ../source make[1]: Leaving directory 'main' Build complete. $ cd main $ ./main.out Hello! Good morning! $
以上で完了です。
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
2025-04-26 | - | 新規作成 |