您的位置:首页 > 其它

Makefile 嵌套 宏定义 应用详解

2018-02-23 15:08 387 查看
    在linux中使用Makefile实现自动化编译的时候,除了常用的功能外,还有一些不太常用却非常实用的技巧或是功能,比如Makefile嵌套,Makefile中定义宏,Makefile中export变量等。在这里做一个学习记录以供参考学习。
(一)Makefile 嵌套

    Makefile嵌套可以分两种,一种是在顶层设计一个Makefile,编译所有子目录下的所有文件,另外一种是在每个子目录中个设计一个Makefile,再顶层执行Make之后,嵌套执行各个子目录的Makefile。
(1)顶层执行
首先需要了解Makefile的三个通配符:
1、wildcard : 扩展通配符
2、patsubst :替换通配符
具体使用查看下面实例:

文件结构如下:biao@ubuntu:~/test/Makefile_test$ tree
.
├── Makefile
├── testA
│   ├── testA.cpp
│   └── testA.h
├── testB
│   ├── testB.cpp
│   └── testB.h
├── testC
│   ├── testC.cpp
│   └── testC.h
└── test.cpp
3 directories, 8 files
biao@ubuntu:~/test/Makefile_test$ testA.cpp内容如下,testB,testC与之相同#include "testA.h"

void Function_TestA(void)
{
printf("%s %s %d I am Test A \n",__FILE__,__FUNCTION__,__LINE__);

}在最顶层的Makefile 编译所有目录下的文件,Makefile内容为:
CC := $(COMPILE_CROSS)gcc
GG := $(COMPILE_CROSS)g++

INC := -I ./testA
INC += -I ./testB
INC += -I ./testC

TESTA_CPP := $(wildcard ./testA/*.cpp)
TESTB_CPP := $(wildcard ./testB/*.cpp)
TESTC_CPP := $(wildcard ./testC/*.cpp)

TESTA_OBJ := $(patsubst %.cpp,%.o,$(TESTA_CPP))
TESTB_OBJ := $(patsubst %.cpp,%.o,$(TESTB_CPP))
TESTC_OBJ := $(patsubst %.cpp,%.o,$(TESTC_CPP))

SRC_C   := $(wildcard *.c)
SRC_CPP := $(wildcard *.cpp)
OBJ_C   := $(patsubst %.c,%.o,$(SRC_C))
OBJ_CPP := $(patsubst %.cpp,%.o,$(SRC_CPP))

OBJS :=$(OBJ_CPP)
OBJS +=$(OBJ_C)

test:$(OBJS) $(TESTA_OBJ) $(TESTB_OBJ) $(TESTC_OBJ)
echo $(OBJS)
$(GG) $(CFLAG) $^ -o $@  $(INC)

$(OBJ_C):%.o:%.c
echo $(OBJ_C)
$(CC) $(CFLAG) -c $< -o $@ $(INC)

$(OBJ_CPP):%.o:%.cpp
echo $(OBJ_CPP)
$(GG) $(CFLAG) -c $< -o $@ $(INC)

$(TESTA_OBJ):%.o:%.cpp
echo $(TESTA_OBJ)
$(GG) $(CFLAG) -c $< -o $@ $(INC)

$(TESTB_OBJ):%.o:%.cpp
echo $(TESTB_OBJ)
$(GG) $(CFLAG) -c $< -o $@ $(INC)

$(TESTC_OBJ):%.o:%.cpp
echo $(TESTC_OBJ)
$(GG) $(CFLAG) -c $< -o $@ $(INC)

clean:
$(RM) ./testA/*.o ./testB/*.o ./testC/*.o ./*.o test
对上面命令做一个简单的介绍:
TESTA_CPP := $(wildcard ./testA/*.cpp)  获取./testA/目录下的所有的.cpp文件列表
TESTA_OBJ := $(patsubst %.cpp,%.o,$(TESTA_CPP)) 将TESTA_CPP列表中所有文件名的后缀.cpp替换为.o,这样我们就可以得到在./testA/目录可生成的.o文件列表
echo $(OBJS) 输出变量OBJS的值

$(GG) $(CFLAG) $^ -o $@  $(INC)  生成最后的目标文件test,扩展开来实际执行的是:
g++ test.o testA/testA.o testB/testB.o testC/testC.o -o test -I ./testA -I ./testB -I ./testC$(CC) $(CFLAG) -c $< -o $@ $(INC) 生成依赖文件test.o,扩展开来实际执行的是:g++ -c test.cpp -o test.o -I ./testA -I ./testB -I ./testCMakefile执行结果:biao@ubuntu:~/test/Makefile_test$ make
echo test.o
test.o
g++ -c test.cpp -o test.o -I ./testA -I ./testB -I ./testC
echo ./testA/testA.o
./testA/testA.o
g++ -c testA/testA.cpp -o testA/testA.o -I ./testA -I ./testB -I ./testC
echo ./testB/testB.o
./testB/testB.o
g++ -c testB/testB.cpp -o testB/testB.o -I ./testA -I ./testB -I ./testC
echo ./testC/testC.o
./testC/testC.o
g++ -c testC/testC.cpp -o testC/testC.o -I ./testA -I ./testB -I ./testC
echo test.o
test.o
g++ test.o testA/testA.o testB/testB.o testC/testC.o -o test -I ./testA -I ./testB -I ./testC
biao@ubuntu:~/test/Makefile_test$ 程序运行结果:biao@ubuntu:~/test/Makefile_test$ ./test
begin to test
testA/testA.cpp Function_TestA 5 I am Test A
testB/testB.cpp Function_TestB 5 I am Test B
testC/testC.cpp Function_TestC 5 I am Test C
end to test
biao@ubuntu:~/test/Makefile_test$     上面测试文件下载链接:http://download.csdn.net/download/li_wen01/10255763
(2)多层嵌套执行
    嵌套执行就是在每个编译目录下都有一个Makefile文件,在最顶层再设计一个Makefile 按顺序调用各个子目录中的Makefile,实现整个工程的编译。
工程目录如下:
biao@ubuntu:~/test/Makefile_test$ tree
.
├── libs
├── main
│   ├── inc
│   │   └── test.h
│   ├── Makefile
│   └── src
│   └── test.cpp
├── Makefile
├── testA
│   ├── inc
│   │   └── testA.h
│   ├── Makefile
│   └── src
│   └── testA.cpp
└── testB
├── inc
│   └── testB.h
├── Makefile
└── src
└── testB.cpp

10 directories, 10 files
biao@ubuntu:~/test/Makefile_test$
biao@ubuntu:~/test/Makefile_test$ 顶层Makefile:#Top Makefile for C program

all:
$(MAKE) -C ./testA
$(MAKE) -C ./testB
$(MAKE) -C ./main

clean:
$(MAKE) -C ./testA clean
$(MAKE) -C ./testB clean
$(MAKE) -C ./main clean
$(RM) libs/*命令:$(MAKE) -C ./testA  会进入testA目录执行testA目录下的Makefile文件
main 执行文件目录Makefile
# A Makefile to generate archive file
CC := $(COMPILE_CROSS)gcc
GG := $(COMPILE_CROSS)g++

CFLAGS += -g -Wall -Werror -O2
LDFLAGS += ./../libs/testA.a ./../libs/testB.a

INC := -I ../main/inc
INC += -I ../testA/inc
INC += -I ../testB/inc

SRC_FILES = $(wildcard src/*.cpp)
SRC_OBJ = $(patsubst %.cpp,%.o,$(SRC_FILES))
SRC_BIN = target_bin

all:$(SRC_BIN)

$(SRC_BIN):$(SRC_OBJ)
$(CC) -o $@ $^ $(INC) $(LDFLAGS)

$(SRC_OBJ):%.o:%.cpp
$(GG) $(CFLAG) -c $< -o $@ $(INC)

clean:
$(RM) $(SRC_OBJ) $(SRC_BIN) $(SRC_BIN)这里调用testA 和testB 生成的库文件链接成可执行文件
test库文件目录Makefile:
# A Makefile to generate archive file

CFLAGS += -g -Wall -Werror -O2

CC := $(COMPILE_CROSS)gcc
GG := $(COMPILE_CROSS)g++

INC := -I ../main/inc
INC += -I ../testA/inc
INC += -I ../testB/inc

SRC_FILES = $(wildcard src/*.cpp)
SRC_OBJ = $(patsubst %.cpp,%.o,$(SRC_FILES))
SRC_LIB = testA.a

all:$(SRC_LIB)

$(SRC_LIB):$(SRC_OBJ)
$(AR) rcs $@ $^
cp $@ ../libs

$(SRC_OBJ):%.o:%.cpp
$(GG) $(CFLAG) -c $< -o $@ $(INC)

clean:
$(RM) $(SRC_OBJ) $(SRC_LIB)

distclean:
$(RM) $(SRC_OBJ) $(SRC_LIB) 上面Makefile是将testA目录中的文件编译成库
编译结果为:biao@ubuntu:~/test/Makefile_test$ make
make -C ./testA
make[1]: Entering directory '/home/biao/test/Makefile_test/testA'
g++ -c src/testA.cpp -o src/testA.o -I ../main/inc -I ../testA/inc -I ../testB/inc
ar rcs testA.a src/testA.o
cp testA.a ../libs
make[1]: Leaving directory '/home/biao/test/Makefile_test/testA'
make -C ./testB
make[1]: Entering directory '/home/biao/test/Makefile_test/testB'
g++ -c src/testB.cpp -o src/testB.o -I ../main/inc -I ../testA/inc -I ../testB/inc
ar rcs testB.a src/testB.o
cp testB.a ../libs
make[1]: Leaving directory '/home/biao/test/Makefile_test/testB'
make -C ./main
make[1]: Entering directory '/home/biao/test/Makefile_test/main'
g++ -c src/test.cpp -o src/test.o -I ../main/inc -I ../testA/inc -I ../testB/inc
gcc -o target_bin src/test.o -I ../main/inc -I ../testA/inc -I ../testB/inc ./../libs/testA.a ./../libs/testB.a
make[1]: Leaving directory '/home/biao/test/Makefile_test/main'
biao@ubuntu:~/test/Makefile_test$ tree
.
├── libs
│   ├── testA.a
│   └── testB.a
├── main
│   ├── inc
│   │   └── test.h
│   ├── Makefile
│   ├── src
│   │   ├── test.cpp
│   │   └── test.o
│   └── target_bin
├── Makefile
├── testA
│   ├── inc
│   │   └── testA.h
│   ├── Makefile
│   ├── src
│   │   ├── testA.cpp
│   │   └── testA.o
│   └── testA.a
└── testB
├── inc
│   └── testB.h
├── Makefile
├── src
│   ├── testB.cpp
│   └── testB.o
└── testB.a

10 directories, 18 files
biao@ubuntu:~/test/Makefile_test$ 嵌套执行工程文件下载链接:http://download.csdn.net/download/li_wen01/10256031

(3)多层嵌套执行 - 进阶

    在嵌套执行Makefile的时候,我们希望在顶层定义的变量在底层的Makefile中一样的使用,这时我们可以使用export导出该变量。同时为了Makefile的简洁,我们这里引进了通配符:addprefix,addsuffix,basename,notdir
预备知识点:
addprefix     添加前缀

addsuffix    添加后缀

basename 取前缀函数

notdir  去除目录

----------------------------------------------
addprefix
$(addprefix fixstring,string1 string2 ...) 
比如:
LIBSO = testA testB
$(addprefix -l,$(LIBSO)) 
执行后变成-ltestA  -ltestB

addsuffix

$(addsuffix <suffix>,<names...> )

把后缀<suffix>加到<names>中的每个单词后面。

$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
basename

$(basename <names...> )
从文件名序列<names>中取出各个文件名的前缀部分。

$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。

notdir

$(notdir <names...> )
从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
$(notdir src/foo.c hacks)返回值是“foo.c hacks”。
--------------------------------------------------------------------------------------------------
查看编译目录:biao@ubuntu:~/test/Makefile_test$ tree
.
├── bin
├── lib
├── main
│ ├── main.cpp
│ ├── main.h
│ ├── Makefile
│ └── obj
├── Makefile
├── testA
│ ├── Makefile
│ ├── obj
│ ├── testA_1
│ │ ├── testA_1.cpp
│ │ └── testA_1.h
│ └── testA_2
│ ├── testA_2.cpp
│ └── testA_2.h
└── testB
├── Makefile
├── obj
├── testB_1
│ ├── testB_1.cpp
│ └── testB_1.h
└── testB_2
├── testB_2.cpp
└── testB_2.h
最顶层目录Makefile:#ARCH=arm
ifeq ($(ARCH),arm)
COMPILE_CROSS:= arm-hisiv300-linux-
else
COMPILE_CROSS:=
endif

CC := $(COMPILE_CROSS)gcc
GG := $(COMPILE_CROSS)g++
LD := $(COMPILE_CROSS)ld
STRIP := $(COMPILE_CROSS)strip
export CC LD STRIP

EXPORTBASEPATH=/usr/local
EXPORTPATH:=$(EXPORTBASEPATH)/lib
export EXPORTPATH

#define path of the shared lib to export
#输出的共享库所在路径
TOP_PATH = $(shell pwd)
BIN_PATH = $(TOP_PATH)/bin/
LIB_PATH = $(TOP_PATH)/lib/
export BIN_PATH
export LIB_PATH

INCL += -I$(TOP_PATH)/main/
INCL += -I$(TOP_PATH)/testA/testA_1/
INCL += -I$(TOP_PATH)/testA/testA_2/
INCL += -I$(TOP_PATH)/testB/testB_1/
INCL += -I$(TOP_PATH)/testB/testB_2/
export INCL

CFLAGS := -Wall -fPIC -O3
CFLAGS += $(INCL)
export CFLAGS

SUB_DIR := testA testB main

all:CHECKDIR $(SUB_DIR)

CHECKDIR:
mkdir -p $(BIN_PATH) $(LIB_PATH)

$(SUB_DIR):ECHO
make -C $@

ECHO:
@echo $(SUB_DIR)
@echo begin compile

# shell for command to clean
clean:
@for n in $(SUB_DIR); do $(MAKE) -C $$n clean; done
rm -rf $(BIN_PATH)*
rm -rf $(LIB_PATH)*

export 导出底层Makefile中需要使用的变量。

-fpic:产生代码位置无关代码,不添加这个生成库文件的时候会报错。
③执行clean命令时,使用shell 中的for 循环调用执行各个目录的make clean 清除数据。
shell 中for命令的使用可以查看博客:shell 编程 for 循环详解及应用实例
testA 和testB 中的Makefile 生成动态函数库,已备main函数调用,该Makefile内容为:#********define shared lib short name**********
#定义共享库的短名称,需要根据实际情况进行更改
EXECUTABLE := testA

#********define shared libs used by the lib,separated by space*********
#使用的共享库列表,以空格分开, 需要根据实际情况更改
SOLIBNAME=lib$(EXECUTABLE).so

PROJECT_SRC+= $(wildcard ./testA_1/*.cpp)
PROJECT_SRC+= $(wildcard ./testA_2/*.cpp)
LDFLAGS = -shared

OBJS = $(addprefix ./obj/, $(addsuffix .o,$(basename $(notdir $(PROJECT_SRC)))))

all:
@mkdir -p obj
@for file in $(PROJECT_SRC); do \
OBJ=`basename $$file|sed -e 's/\.cpp/\.o/' -e 's/\.c/\.o/'`; \
$(CC) $(CFLAGS) -c -o obj/$$OBJ $$file; \
done; \
$(CC) $(LDFLAGS) -o $(EXPORTPATH)/$(SOLIBNAME) $(OBJS)
cp $(EXPORTPATH)/$(SOLIBNAME) $(LIB_PATH)

clean:
rm -rf ./obj/*.o ./obj/*.a ./*.so在main目录中调用动态库,生成可执行文件test,该部分Makefile内容为:LIBSO := testA testB
PROJECT_SRC+=./main.cpp
OUTPUT_EXECUT := test

OBJS = $(addprefix ./obj/, $(addsuffix .o,$(basename $(notdir $(PROJECT_SRC)))))
all:
@mkdir -p obj
@for file in $(PROJECT_SRC); do \
OBJ=`basename $$file|sed -e 's/\.cpp/\.o/' -e 's/\.c/\.o/'`; \
echo -e "$(CC) -c -o $$OBJ\n$$file"; \
$(CC) $(CFLAGS) -c -o obj/$$OBJ $$file; \
done; \
echo $(OBJS)
$(CC) $(CFLAGS) -o $(OUTPUT_EXECUT) $(OBJS) -L$(LIB_PATH) $(addprefix -l,$(LIBSO))
@echo "Compile complete"
@cp $(OUTPUT_EXECUT) $(BIN_PATH)

clean:
@rm -rf ../bin/*
@rm -rf ./obj/*.o
@rm -rf $(OUTPUT_EXECUT)完整的工程文件可以在这里下载:http://download.csdn.net/download/li_wen01/10257384

(二)Makefile 添加宏定义
    宏定义使用前缀-D,在编译过程中可以把宏定义追加到CFLAG中。宏定义有两种相似的写法
    【第一种】-D DEFINES 
    【第二种】-D DEFINES=CONDITION

源文件:
使用两种不同的方式实例#include <stdio.h>

int main(void)
{
printf("Into Main Function \n");

#ifdef TEST_A
printf("I am Test A\n");
#endif

#if TEST_B
printf("I am Test B \n");
#endif

return 0;
}
Makefile文件:# 指令编译器和选项
CC := $(COMPILE_CROSS)gcc
GG := $(COMPILE_CROSS)g++

# 宏定义
DEFS = -DTEST_A -DTEST_B=1
CFLAGS += $(DEFS)

SRC_C := $(wildcard *.c)
SRC_CPP :=$(wildcard *.cpp)

OBJ_C := $(patsubst %.c,%.o,$(SRC_C))
OBJ_CPP := $(patsubst %.cpp,%.o,$(SRC_CPP))

OBJS :=$(OBJ_CPP)
OBJS +=$(OBJ_C)

test:$(OBJS)
$(GG) $(CFLAGS) $^ -o $@ $(INC)

$(OBJS)::%.o:%.cpp
$(GG) $(CFLAGS) -c $< -o $@ $(INC)

clean:
$(RM) *.o test编译执行结果如下:biao@ubuntu:~/test/Makefile_test$
biao@ubuntu:~/test/Makefile_test$ make
g++ -DTEST_A -DTEST_B=1 -c main.cpp -o main.o
g++ -DTEST_A -DTEST_B=1 main.o -o test
biao@ubuntu:~/test/Makefile_test$ ls
main.cpp main.o Makefile test
biao@ubuntu:~/test/Makefile_test$ ./test
Into Main Function
I am Test A
I am Test B
biao@ubuntu:~/test/Makefile_test$
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息