最初からプロジェクトを書くMakefile(5):ネスト実行
【著作権声明:転載出典:blog.csdn.net/gentleliu.Mail:shallnew at 163 dot com】
大きいいくつかのプロジェクトの中で、すべてのソースコードは同じディレクトリに置くだけではありません.一般的に、各機能モジュールのソースコードは別々で、それぞれのディレクトリの下に置いて、ヘッダファイルと.cソースファイルにもそれぞれのディレクトリがあり、プロジェクトコードのメンテナンスが容易になります.これにより、各機能モジュールディレクトリの下にMakefileを書くことができ、それぞれのMakefileはそれぞれの機能のコンパイルリンクを処理することができます.これにより、すべての機能のコンパイルリンクを同じMakefileの中に置く必要がなくなり、Makefileをより簡潔にすることができ、コンパイル時にどのモジュールをコンパイルするかを選択することができます.これはブロックコンパイルに大きなメリットがあります.
現在、私がいるプロジェクトディレクトリツリーは次のとおりです.
このようにプロジェクトのソースコードを組織するのは以前より合理的ですが、Makefileはどのように書きますか?各ディレクトリの下にMakefileを書き、最上位のMakefileを1層ずつ下にネストして各層Makefileを実行できます.では、最上階のMakefileは簡単に書けます.
コマンド:
>---$(MAKE) -C src
つまりsrcディレクトリに入ってそのディレクトリの下のMakefileを実行し続けます.その後、srcディレクトリの下のMakefileは、同じ方法で次のディレクトリtools、main、ipcに入り、そのディレクトリの下のMakefileを実行します.実はこれはちょっと面倒ですが、最上位ディレクトリから最後のディレクトリに直接入ってmakeを実行することができます.いくつかの偽の目標を加えて改善すると、私たちの最上位のMakefileが出てきます.
最後に最上位レベルで実行:
最後に実行可能プログラムファイルが生成されました.これにより,一つの工程の各モジュールが独立し,ソースコードが分離されるだけでなく,それぞれのMakefileがあり,各機能モジュールが独立してコンパイルできるようになる.
最上位のMakefileには、次の階層のディレクトリに入るには、次のように何度も繰り返し書くことが改善されていることがわかります.
1つのディレクトリを追加するたびに、複数の擬似ターゲットに1行を追加する必要があります.これは自動化が不十分ですね.shellのループ文を考えてみましょう.各ルールのコマンドでforループを使用することができます.次のようになります.
このように怠け者は長い間喜ぶことができる.しかし、まだ問題があります.
上のforループは、システムコマンドlsにリストされたディレクトリに順次入りますが、各ディレクトリのmake順序に要求がある場合があります.このプロジェクトでは、mainディレクトリの下のMakefileが最後に実行されなければなりません.最終的なリンクには、生成されたライブラリファイルを他のディレクトリでコンパイルする必要があります.そうしないと、実行に失敗します.現在のMakefileでは、サブディレクトリのmake実行中にエラーが発生した場合、makeは終了しません.最終的な実行に失敗した場合、エラーのヒントに基づいて、そのディレクトリのMakefileにエラーが発生したことを特定することは難しい.これは問題の位置づけに大きな困難をもたらした.このような問題を回避するために、コマンド実行エラー後にmakeが終了します.
先ほどのMakefileを次のように変更しました
これにより、エラーが実行されるとすぐに終了しますが、問題は解決されず、コンパイルエラーが発生します.どうやって解決しますか?
ルールを追加することでmakeの実行順序を制限することができます.これにより、偽のターゲットを使用し、各モジュールに対してルールを書きます.各モジュール名はターゲットであり、最後に実行するモジュールターゲットは他のモジュールのターゲットであり、makeの順序を制限します.最後に実行する必要があるターゲットまで実行すると、依存が発見され、依存するターゲットが更新され、エラーが発生しません.また、toolsモジュールのみを修正したなど、指定したモジュールをコンパイルすることもできます.私が修正したこのモジュールコードがコンパイルできるかどうかを見たいだけです.コンパイル時にこのようにすることができます.
私たちの最上位のMakefileはまた進化しました.このセクションの最終Makefileです.
大きいいくつかのプロジェクトの中で、すべてのソースコードは同じディレクトリに置くだけではありません.一般的に、各機能モジュールのソースコードは別々で、それぞれのディレクトリの下に置いて、ヘッダファイルと.cソースファイルにもそれぞれのディレクトリがあり、プロジェクトコードのメンテナンスが容易になります.これにより、各機能モジュールディレクトリの下にMakefileを書くことができ、それぞれのMakefileはそれぞれの機能のコンパイルリンクを処理することができます.これにより、すべての機能のコンパイルリンクを同じMakefileの中に置く必要がなくなり、Makefileをより簡潔にすることができ、コンパイル時にどのモジュールをコンパイルするかを選択することができます.これはブロックコンパイルに大きなメリットがあります.
現在、私がいるプロジェクトディレクトリツリーは次のとおりです.
.
├── include
│ ├── common.h
│ ├── ipc
│ │ └── ipc.h
│ └── tools
│ ├── base64.h
│ ├── md5.h
│ └── tools.h
├── Makefile
├── src
│ ├── ipc
│ │ ├── inc
│ │ ├── Makefile
│ │ └── src
│ │ └── ipc.c
│ ├── main
│ │ ├── inc
│ │ ├── Makefile
│ │ └── src
│ │ ├── main.c
│ │ └── main.c~
│ └── tools
│ ├── inc
│ ├── Makefile
│ └── src
│ ├── base64.c
│ ├── md5.c
│ └── tools.c
└── tags
13 directories, 16 files
このようにプロジェクトのソースコードを組織するのは以前より合理的ですが、Makefileはどのように書きますか?各ディレクトリの下にMakefileを書き、最上位のMakefileを1層ずつ下にネストして各層Makefileを実行できます.では、最上階のMakefileは簡単に書けます.
# top Makefile for xxx
all :
>---$(MAKE) -C src
tags:
>---ctags -R
clean :
>---$(MAKE) -C src clean
.PHONY : all clean tags
コマンド:
>---$(MAKE) -C src
つまりsrcディレクトリに入ってそのディレクトリの下のMakefileを実行し続けます.その後、srcディレクトリの下のMakefileは、同じ方法で次のディレクトリtools、main、ipcに入り、そのディレクトリの下のMakefileを実行します.実はこれはちょっと面倒ですが、最上位ディレクトリから最後のディレクトリに直接入ってmakeを実行することができます.いくつかの偽の目標を加えて改善すると、私たちの最上位のMakefileが出てきます.
# Top Makefile for C program
# Copyright (C) 2014 shallnew \at 163 \dot com
all :
>---$(MAKE) -C src/ipc
>---$(MAKE) -C src/tools
>---$(MAKE) -C src/main
tags:
>---ctags -R
help:
>---@echo "===============A common Makefilefor c programs=============="
>---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"
>---@echo "The following targets aresupport:"
>---@echo
>---@echo " all - (==make) compile and link"
>---@echo " obj - just compile, withoutlink"
>---@echo " clean - clean target"
>---@echo " distclean - clean target and otherinformation"
>---@echo " tags - create ctags for vimeditor"
>---@echo " help - print help information"
>---@echo
>---@echo "To make a target, do 'make[target]'"
>---@echo "========================= Version2.0 ======================="
obj:
>---$(MAKE) -C src/ipc obj
>---$(MAKE) -C src/tools obj
>---$(MAKE) -C src/main obj
clean :
>---$(MAKE) -C src/ipc clean
>---$(MAKE) -C src/tools clean
>---$(MAKE) -C src/main clean
distclean:
>---$(MAKE) -C src/ipc distclean
>---$(MAKE) -C src/tools distclean
>---$(MAKE) -C src/main distclean
.PHONY : all clean distclean tags help
このようにソースコードを整理するとき、一番下の階層のMakefileはどのように書きますか?前節のMakefileを(version 1.1)機能モジュールディレクトリに直接コピーするには、少し修正する必要があります.すべてのモジュールが最終的にそれぞれの実行可能ファイルを生成することはできません.現在はプロジェクトなので、最後に実行可能プログラムが1つしか生成されません.このようにして、メインモジュールディレクトリに実行可能ファイルを生成させ、他のモジュールディレクトリに静的ライブラリファイルを生成させ、メインモジュールリンク時に他のモジュールは、生成されたライブラリファイルをコンパイルして最終的なプログラムを生成します.前のセクションのMakefileを少し変更すると、コンパイルライブラリファイルMakefileとコンパイル実行可能ファイルMakefileはそれぞれ次のようになります.# A Makefile to generate archive file
# Copyright (C) 2014 shallnew \at 163 \dot com
CFLAGS += -g -Wall -Werror -O2
CPPFLAGS += -I. -I./inc -I../../include
# SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
SRC_FILES = $(wildcard src/*.c)
SRC_OBJ = $(SRC_FILES:.c=.o)
SRC_LIB = libtools.a
all : $(SRC_LIB)
$(SRC_LIB) : $(SRC_OBJ)
>---$(AR) rcs $@ $^
>---cp $@ ../../libs
obj : $(SRC_OBJ)
# clean target
clean:
>---$(RM) $(SRC_OBJ) $(SRC_LIB)
distclean:
>---$(RM) $(SRC_OBJ) $(SRC_LIB) tags *~
.PHONY : all obj clean disclean
========================================================================== # A Makefile to generate executive file
# Copyright (C) 2014 shallnew \at 163 \dot com
CFLAGS += -g -Wall -Werror -O2
CPPFLAGS += -I. -I./inc -I../../include
LDFLAGS += -lpthread -L../../libs -ltools -lipc
# SRC_OBJ = $(patsubst %.c, %.o, $(wildcard *.c))
SRC_FILES = $(wildcard src/*.c)
SRC_OBJ = $(SRC_FILES:.c=.o)
SRC_BIN = target_bin
all : $(SRC_BIN)
$(SRC_BIN) : $(SRC_OBJ)
>---$(CC) -o $@ $^ $(LDFLAGS)
obj : $(SRC_OBJ)
# clean target
clean:
>---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe
distclean:
>---$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN).exe tags*~
.PHONY : all obj clean disclean
最後に最上位レベルで実行:
# make clean
make -C src/ipc clean
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
rm -f src/ipc.o libipc.a
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/ipc'
make -C src/tools clean
make[1]: Entering directory `/home/Myprojects/example_make/version-3.0/src/tools'
rm -f src/base64.o src/md5.o src/tools.o libtools.a
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
make -C src/main clean
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
rm -f src/main.o target_bin target_bin.exe
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
# make
make -C src/ipc
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/ipc'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/ipc.osrc/ipc.c
ar rcs libipc.a src/ipc.o
cp libipc.a ../../libs
make[1]: Leaving directory `/home/Myprojects/example_make/version-3.0/src/ipc'
make -C src/tools
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/tools'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/base64.osrc/base64.c
cc -g -Wall -Werror -O2 -I. -I./inc -I../../include -c -o src/md5.o src/md5.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/tools.osrc/tools.c
ar rcs libtools.a src/base64.o src/md5.o src/tools.o
cp libtools.a ../../libs
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/tools'
make -C src/main
make[1]: Entering directory`/home/Myprojects/example_make/version-3.0/src/main'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/main.osrc/main.c
cc -o target_bin src/main.o -lpthread -L../../libs -ltools-lipc
make[1]: Leaving directory`/home/Myprojects/example_make/version-3.0/src/main'
#
最後に実行可能プログラムファイルが生成されました.これにより,一つの工程の各モジュールが独立し,ソースコードが分離されるだけでなく,それぞれのMakefileがあり,各機能モジュールが独立してコンパイルできるようになる.
最上位のMakefileには、次の階層のディレクトリに入るには、次のように何度も繰り返し書くことが改善されていることがわかります.
>---$(MAKE) -C src/ipc
>---$(MAKE) -C src/tools
>---$(MAKE) -C src/main
1つのディレクトリを追加するたびに、複数の擬似ターゲットに1行を追加する必要があります.これは自動化が不十分ですね.shellのループ文を考えてみましょう.各ルールのコマンドでforループを使用することができます.次のようになります.
DIR = src
SUBDIRS = $(shell ls $(DIR))
all :
>---@for subdir in $(SUBDIRS); \
>---do $(MAKE) -C $(DIR)/$$subdir; \
>---done
このように怠け者は長い間喜ぶことができる.しかし、まだ問題があります.
上のforループは、システムコマンドlsにリストされたディレクトリに順次入りますが、各ディレクトリのmake順序に要求がある場合があります.このプロジェクトでは、mainディレクトリの下のMakefileが最後に実行されなければなりません.最終的なリンクには、生成されたライブラリファイルを他のディレクトリでコンパイルする必要があります.そうしないと、実行に失敗します.現在のMakefileでは、サブディレクトリのmake実行中にエラーが発生した場合、makeは終了しません.最終的な実行に失敗した場合、エラーのヒントに基づいて、そのディレクトリのMakefileにエラーが発生したことを特定することは難しい.これは問題の位置づけに大きな困難をもたらした.このような問題を回避するために、コマンド実行エラー後にmakeが終了します.
先ほどのMakefileを次のように変更しました
DIR = src
SUBDIRS = $(shell ls $(DIR))
all :
>---@for subdir in $(SUBDIRS); \
>---do $(MAKE) -C $(DIR)/$$subdir || exit 1; \
>---done
これにより、エラーが実行されるとすぐに終了しますが、問題は解決されず、コンパイルエラーが発生します.どうやって解決しますか?
ルールを追加することでmakeの実行順序を制限することができます.これにより、偽のターゲットを使用し、各モジュールに対してルールを書きます.各モジュール名はターゲットであり、最後に実行するモジュールターゲットは他のモジュールのターゲットであり、makeの順序を制限します.最後に実行する必要があるターゲットまで実行すると、依存が発見され、依存するターゲットが更新され、エラーが発生しません.また、toolsモジュールのみを修正したなど、指定したモジュールをコンパイルすることもできます.私が修正したこのモジュールコードがコンパイルできるかどうかを見たいだけです.コンパイル時にこのようにすることができます.
# make tools
make -C src/tools
make[1]: Entering directory`/home/Myprojects/example_make/version-2.1/src/tools'
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/base64.o src/base64.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/md5.osrc/md5.c
cc -g -Wall -Werror -O2 -I. -I./inc-I../../include -c -o src/tools.osrc/tools.c
ar rcs libtools.a src/base64.o src/md5.o src/tools.o
cp libtools.a ../../libs
make[1]: Leaving directory`/home/Myprojects/example_make/version-2.1/src/tools'
#
この問題を解決する別の方法もあります.つまり、実行に入るモジュール名を手動でリストします.(ここではディレクトリです)、最後に実行するモジュールを最後にします.forループ実行時に最後にリンクをコンパイルするモジュールが最後になります.私たちの前のようにmakeはシステムコマンドlsを使用してモジュールディレクトリをリストする順序で実行されません.lsリストディレクトリは各ディレクトリの名前でソートされ、コードを書くことを要求することはできません.最後に実行するモジュールの名前はzで始まる必要がありますが、とにかく現実的ではありません.私たちの最上位のMakefileはまた進化しました.このセクションの最終Makefileです.
# Top Makefile for C program
# Copyright (C) 2014 shallnew \at 163 \dot com
DIR = src
MODULES = $(shell ls $(DIR))
# MODULES = ipc main tools
all : $(MODULES)
$(MODULES):
>---$(MAKE) -C $(DIR)/$@
main:tools ipc
obj:
>---@for subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir $@; \
>---done
clean :
>---@for subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir $@; \
>---done
distclean:
>---@for subdir in $(MODULES); \
>---do $(MAKE) -C $(DIR)/$$subdir $@; \
>---done
tags:
>---ctags -R
help:
>---@echo "===============A common Makefilefor c programs=============="
>---@echo "Copyright (C) 2014 liuy0711 \at 163\dot com"
>---@echo "The following targets aresupport:"
>---@echo
>---@echo " all - (==make) compile and link"
>---@echo " obj - just compile, withoutlink"
>---@echo " clean - clean target"
>---@echo " distclean - clean target and otherinformation"
>---@echo " tags - create ctags for vimeditor"
>---@echo " help - print help information"
>---@echo
>---@echo "To make a target, do 'make[target]'"
>---@echo "========================= Version2.0 ======================="
.PHONY : all clean distclean tags help