make の使い方

make に関する情報を記載します。

基本情報というより、makefile サンプルをいくつか作成した例の紹介という感じです。

 

 

 

1. ステップ1: 実行ファイルを作成

[概要]

"Hello!" をコンソールへ出力するだけの簡単な実行ファイルを作成する手順を題材に説明します。

 

[環境]

コンパイラ : g++, 13.3.0
OS : Ubuntu, 24.04

 

 

1-1. コマンドラインでビルド

まず 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-2. make でビルド

"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.
$

 

 

2. ステップ2: 静的・動的ライブラリを作成

[概要]

ライブラリを作る場合、静的・動的ライブラリ(*.a, *.so)を作成することになります。そして実行ファイルにリンクして使用します。

本章では、静的・動的ライブラリを cmake を使って作成などする手順について記載します。

 

[環境]

コンパイラ : g++, 13.3.0
OS : Ubuntu, 24.04

 

 

2-1. コマンドラインでビルド

下記のようなソースコードを準備します。

前章に good_morning.h, good_morning.cpp 2つのファイルを追加した感じです。

 

ファイル構成

./
 ├ main.cpp
 ├ hello.h
 ├ hello.cpp
 ├ good_morning.hgood_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();
}

 

2-1-1. 静的ライブラリの作成

静的ライブラリを作成後、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!

 

2-1-2. 動的ライブラリの作成

動的ライブラリを作成後、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-2. make でビルド

"2-1. コマンドラインでビルド" の内容を make を使ってビルドするようにしてみます。

前章と同様にファイル構成に makefile を追加します。

 

ファイル構成

./
 ├ makefile
 ├ main.cpp
 ├ hello.h
 ├ hello.cpp
 ├ good_morning.h
 └ good_morning.cpp

 

2-2-1. 静的ライブラリの作成

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!
$

 

2-2-2. 動的ライブラリの作成

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!
$

 

 

3. ステップ3: サブディレクトリにソースが分散している場合

[概要]

ソフトウェアが大規模になってくると役割毎に保存するフォルダを分けたりします。そういった場合を例に説明します。

 

[環境]

コンパイラ : 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;
}

 

 

3-1. コマンドラインでビルド

さて、これをコマンドラインからビルドしてみます。ルートディレクトリ直下にいるものとし、共有ライブラリを作成して実行ファイルにリンクするやり方で行います。

今回は -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-2. make でビルド

"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 ライセンスで提供されます。

The MIT License (MIT)

  Copyright 2025 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 


 

変更履歴

2025-04-26 - 新規作成

 

Programming Items トップページ

プライバシーポリシー