# 利用原生的makefile建立linux的工程脚手架 **Repository Path**: dapeng15042435737/linux_makefile ## Basic Information - **Project Name**: 利用原生的makefile建立linux的工程脚手架 - **Description**: 通过手写makefile建立一个可以复用的linux C/C++的工程脚手架,该脚手架支持嵌套目录的makefile执行,该脚手架项目演示了如何通过makefile来生成静态链接库和动态链接库,以及链接各个目录的object文件和库文件生成可执行程序。 - **Primary Language**: Shell - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-10-05 - **Last Updated**: 2023-07-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: makefile, Linux ## README # 利用原生的makefile建立linux上C/C++的工程脚手架 #### 介绍 本文是介绍如何通过手写makefile来建立一个通用的Linux上的C/C++的工程。 针对C/C++工程上嵌套Makefile的使用进行了详细的讲解,该脚手架具备如下功能: 1. 支持嵌套目录的程序文件结构 2. 支持生成静态链接库 3. 支持生成动态链接库 4. 支持链接库文件生成可执行程序 #### 工程演示介绍 为了能够更好的理解工程脚手架的作用,通过一个实例来进行演示。 在本工程里实现了一个加减乘除的四则运算,其中加法和减法的实体程序代码通过子makefile的编译和链接进入应用程序。 乘法通过子makefile编译成静态库,链接进入应用程序。 除法通过子makefile编译成动态库,链接进入应用程序。 加减乘除的四则运算则在main主程序中进行调用执行。 #### 脚手架目录说明 ![输入图片说明](project.png) 1. bin目录:保存生成的可执行程序的目录 2. build目录:总控Makefile的目录,执行编译的时候,进入该目录,执行make命令 3. include目录:保存所有头文件的目录 4. lib目录:保存生成的静态链接库和动态链接库的目录 5. main目录:可执行程序入口 6. obj目录:编译时保存临时生成的object文件的目录,编译结束后自动删除所有的object文件 7. src目录:所有的C/C++程序的源文件目录,支持嵌套使用。 #### 工程内景及Makefile简介 ![输入图片说明](tree2.png) - 红色框内的Makefile为总控Makefile负责起动其他黄色框的子Makefile。 - 黄色框内的Makefile为子Makefile。每个子Makefile的功能都不一样。具体请看下一章节的详细介绍。 #### 总控Makefile build目录下的Makefile为总控Makefile,整体编译开始的入口就是由总控Makefile开始,由总控Makefile来起动各个子Makefile来执行各个子目录的编译动作。 ```shell CXX := g++ CXXFLAGS := -Wall -pedantic -std=c++11 -g CUR_DIR := $(PWD) TOP_DIR := $(shell dirname $(CUR_DIR)) OBJ_DIR := $(TOP_DIR)/obj/ BIN_DIR := $(TOP_DIR)/bin/ LIB_DIR := $(TOP_DIR)/lib/ LIB := -lmul -ldiv BIN := test LD_FLAGS := -L$(LIB_DIR) SUB_DIR := $(TOP_DIR)/src SUB_DIR += $(TOP_DIR)/src/multiplication SUB_DIR += $(TOP_DIR)/src/division SUB_DIR += $(TOP_DIR)/main SUB_DIR += $(TOP_DIR)/obj INCLUDE_DIR := $(TOP_DIR)/include #为让总控Makefile定义变量,在子makefile中能够访问,使用export命令将变量声明为全局可用 export OBJ_DIR BIN_DIR LIB_DIR LIB INCLUDE_DIR BIN CXX CXXFLAGS LD_FLAGS #ALL伪指令是Makefile的入口处理指令,执行顺序根据ALL:后面指定的顺序执行。 #先执行BEGIN,再执行$(SUB_DIR),最后执行END。 ALL: BEGIN $(SUB_DIR) END BEGIN: @echo --- begin compile --- @echo $(TOP_DIR) $(SUB_DIR): ECHO #执行SUB_DIR下的makefile make -C $@ ECHO: @echo "Compiling : " $(SUB_DIR) "..." END: @echo --- end compile --- @echo --------------------------- @echo success: $(BIN_DIR)$(BIN) @echo --------------------------- clean: rm -rf $(OBJ_DIR)*.o $(BIN_DIR)$(BIN) $(LIB_DIR)* .PHONY = ALL ECHO BEGIN END ``` #### src下的子makefile ![输入图片说明](src_makefile.png) 负责编译src下的直属.cpp文件(注意不是嵌套子目录里的cpp文件),并将生成object文件拷贝到obj目录内。 目前编译了add.cpp(加法)和sub.cpp(减法)两个源程序代码。 ```shell SRC := $(wildcard *.cpp) OBJS := $(SRC:.cpp=.o) INC := -I${INCLUDE_DIR} ALL: $(OBJS) mv *.o $(OBJ_DIR) %.o: %.cpp $(CXX) -c $< -o $@ $(CXXFLAGS) $(INC) ``` #### src/division下的子makefile ![输入图片说明](src_division.png) 负责编译division(除法)目录下的直属的cpp文件,并生成动态链接库libdiv.so,将动态链接库移如lib目录下。 ```shell SRC := $(wildcard *.cpp) OBJS := $(SRC:.cpp=.o) INC := -I${INCLUDE_DIR}/division/ #定义动态连接库的名字 TARGET := libdiv.so $(TARGET): $(OBJS) #生成动态连接库 $(CXX) -shared $^ -o $@ rm -rf *.o mv $@ $(LIB_DIR) %.o: %.cpp #用cpp文件来生成object文件 $(CXX) -c $< -o $@ $(CXXFLAGS) $(INC) ``` #### src/multiplication下的子makefile ![输入图片说明](src_multiplication.png) 负责编译multiplication(乘法)目录下的直属的cpp文件,并生成静态链接库libmul.a,将动态链接库移如lib目录下。 ```shell SRC := $(wildcard *.cpp) OBJS := $(SRC:.cpp=.o) INC := -I${INCLUDE_DIR}/multiplication/ #定义静态连接库的名字和生成静态连接库的命令 TARGET := libmul.a AR := ar $(TARGET): $(OBJS) #生成静态连接库 $(AR) -r $@ $^ rm -rf *.o mv $@ $(LIB_DIR) %.o: %.cpp #用cpp文件来生成object文件 $(CXX) -c $< -o $@ $(CXXFLAGS) $(INC) ``` #### src/obj下的子makefile ![输入图片说明](src_obj.png) 负责链接obj目录下所有的object文件,并链接lib目录下的静态库libmul.a和动态链接库libdiv.so,生成可执行程序test, 并把可执行程序放入bin目录下。 ```shell #查找当前目录下所有的object文件 OBJS := $(shell find . -name '*.o' -type f;) $(BIN): $(OBJS) echo $(OBJS) #生成可执行程序 $(CXX) -o $@ $(OBJS) $(LD_FLAGS) $(LIB) mv $(BIN) $(BIN_DIR) clean: rm -rf *.o ``` #### main下的子makefile ![输入图片说明](src_main.png) 负责编译应用程序入口main.cpp,并把main.o文件放入obj目录下,由于main.cpp会引入其他文件的方法,因此该子makefile中的INC变量可以根据需要手动增删。 ```shell SRC := $(wildcard *.cpp) OBJS := $(SRC:.cpp=.o) INC := -I${INCLUDE_DIR} INC += -I${INCLUDE_DIR}/multiplication/ INC += -I${INCLUDE_DIR}/division ALL: $(OBJS) mv *.o $(OBJ_DIR) %.o: %.cpp $(CXX) -c $< -o $@ $(CXXFLAGS) $(INC) ``` #### lib目录 ![输入图片说明](lib.png) lib目录里负责存放生成乘法静态库libmul.a和除法动态库libdiv.so。供obj下的子makefile生成可执行程序时链接使用。 #### bin目录 ![输入图片说明](bin.png) bin目录负责存放生成的可执行程序以及起动可执行程序的脚本文件。由程序的起动需要动态链接库,因此需要配置LD_LIBRARY_PATH环境变量,将动态链接库的位置设定到LD_LIBRARY_PATH环境变量后,再进行起动名叫test的可执行程序。 ```shell #设定test的动态连接库的位置 export LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH #执行test ./test ``` #### build目录下make的执行结果 ```shell zhangpeng@zhangpeng-VirtualBox:~/Project/makefile_project/build$ make --- begin compile --- /home/zhangpeng/Project/makefile_project Compiling : /home/zhangpeng/Project/makefile_project/src /home/zhangpeng/Project/makefile_project/src/multiplication /home/zhangpeng/Project/makefile_project/src/division /home/zhangpeng/Project/makefile_project/main /home/zhangpeng/Project/makefile_project/obj ... make -C /home/zhangpeng/Project/makefile_project/src make[1]: 进入目录“/home/zhangpeng/Project/makefile_project/src” g++ -c add.cpp -o add.o -Wall -pedantic -std=c++11 -g -I/home/zhangpeng/Project/makefile_project/include g++ -c sub.cpp -o sub.o -Wall -pedantic -std=c++11 -g -I/home/zhangpeng/Project/makefile_project/include mv *.o /home/zhangpeng/Project/makefile_project/obj/ make[1]: 离开目录“/home/zhangpeng/Project/makefile_project/src” make -C /home/zhangpeng/Project/makefile_project/src/multiplication make[1]: 进入目录“/home/zhangpeng/Project/makefile_project/src/multiplication” g++ -c multiplication.cpp -o multiplication.o -Wall -pedantic -std=c++11 -g -I/home/zhangpeng/Project/makefile_project/include/multiplication/ ar -r libmul.a multiplication.o ar: 正在创建 libmul.a rm -rf *.o mv libmul.a /home/zhangpeng/Project/makefile_project/lib/ make[1]: 离开目录“/home/zhangpeng/Project/makefile_project/src/multiplication” make -C /home/zhangpeng/Project/makefile_project/src/division make[1]: 进入目录“/home/zhangpeng/Project/makefile_project/src/division” g++ -c division.cpp -o division.o -Wall -pedantic -std=c++11 -g -I/home/zhangpeng/Project/makefile_project/include/division/ g++ -shared division.o -o libdiv.so rm -rf *.o mv libdiv.so /home/zhangpeng/Project/makefile_project/lib/ make[1]: 离开目录“/home/zhangpeng/Project/makefile_project/src/division” make -C /home/zhangpeng/Project/makefile_project/main make[1]: 进入目录“/home/zhangpeng/Project/makefile_project/main” g++ -c main.cpp -o main.o -Wall -pedantic -std=c++11 -g -I/home/zhangpeng/Project/makefile_project/include -I/home/zhangpeng/Project/makefile_project/include/multiplication/ -I/home/zhangpeng/Project/makefile_project/include/division mv *.o /home/zhangpeng/Project/makefile_project/obj/ make[1]: 离开目录“/home/zhangpeng/Project/makefile_project/main” make -C /home/zhangpeng/Project/makefile_project/obj make[1]: 进入目录“/home/zhangpeng/Project/makefile_project/obj” echo ./sub.o ./add.o ./main.o ./sub.o ./add.o ./main.o g++ -o test ./sub.o ./add.o ./main.o -L/home/zhangpeng/Project/makefile_project/lib/ -lmul -ldiv mv test /home/zhangpeng/Project/makefile_project/bin/ make[1]: 离开目录“/home/zhangpeng/Project/makefile_project/obj” --- end compile --- --------------------------- success: /home/zhangpeng/Project/makefile_project/bin/test --------------------------- zhangpeng@zhangpeng-VirtualBox:~/Project/makefile_project/build$ ``` #### test.sh的执行结果 ![输入图片说明](test.sh.png) #### 补充 1. 该工程脚手架的开发和说明的维护由作者本人负责维护 2. 版权归作者本人所有 3. 作者: 张鹏 4. 作者邮件: zp15042435737@163.com 5. 作者地址: 大连软件园 6. 本文说明和代码的上线时间: 2022-10-06