zoukankan      html  css  js  c++  java
  • 一个通用的两级Makefile例子

    目的

    1. 进行如项目的顶层目录后,运行make,即可直接编译项目中所有的源文件,并生成最终的可执行文件
    2. 实现头文件自动依赖
    3. 添加源文件不用修改Makefile,且可以自动编译新文件
    4. 顶层目录下添加文件夹,不用重新编写Makefile,直接拷贝其他文件夹下的Makefile,就可以自动编译整个文件夹下的源文件

    目录结构

      顶层文件夹名称test,二级文件夹按照模块分类,文件夹名称就是模块名称,顶层文件夹下包含一个顶层的Makefile,二级文件夹下包含二级Makefile。二级文件夹target存放的是编译的中间文件和最后的可执行文件,二级文件夹module1、module2和module3,是三个用于测试的模块,具体的目录结构如下图所示:

     

       源文件的代码如下:

    // main.h
    #ifndef __MAIN_H__
    #define __MAIN_H__
    
    #include <stdio.h>
    #include "add.h"
    #include "sub.h"
    
    #endif 
    
    // main.c
    #include "main.h"
    int main()
    {
        printf("1 + 2 = %d
    ", add(1, 2));
        printf("4 - 2 = %d
    ", sub(4, 2));
        return 0;
    }
    
    // add.h
    #ifndef __ADD_H__
    #define __ADD_H__
    int add(int a, int b);
    #endif 
    
    // add.c
    #include "add.h"
    int add(int a, int b)
    {
        return a + b;
    }
    
    //sub.h
    #ifndef __SUB_H__
    #define __SUB_H__
    int sub(int a, int b);
    int sub2(int a, int b);
    #endif 
    
    // sub.c
    #include "sub.h"
    int sub(int a, int b)
    {
        return a - b;  
    }
    
    // sub2.c
    #include "sub.h"
    int sub2(int a, int b) 
    {
        return b - a;
    }
    

      

    顶层Makefile

    #设置编译器和相关命令
    CC = gcc
    MKDIR = mkdir
    CP = cp
    RM = rm
    FIND = find
    
    #debug文件夹里的makefile文件需要最后执行,所以这里需要执行的子目录要排除debug文件夹,这里使用awk排除了debug文件夹,读取剩下的文件夹
    SUBDIRS = $(shell ls -l | grep ^d | awk '{if($$9 != "target") print $$9}')
    
    #记住当前工程的根目录路径
    ROOT_DIR=$(shell pwd)
    
    #最终bin文件的名字,可以更改为自己需要的
    BIN = test
    
    #目标文件所在的目录
    OBJS_DIR = target/tmp
    
    #bin文件所在的目录
    BIN_DIR = target/bin
    TARGET = $(ROOT_DIR)/$(BIN_DIR)/$(BIN)
    
    #将以下变量导出到子shell中,本次相当于导出到子目录下的makefile中
    export CC BIN OBJS_DIR BIN_DIR ROOT_DIR MKDIR CP RM FIND
    
    #注意这里的顺序,需要先执行SUBDIRS最后才能是DEBUG
    all : $(SUBDIRS) CREATE_DIR $(TARGET)
    
    #递归执行子目录下的makefile文件,这是递归执行的关键
    .PHONY: $(SUBDIRS)
    $(SUBDIRS):
    	make -C $@
    
    #创建生成目标的文件夹	
    CREATE_DIR :	
    	@if [ ! -d $(ROOT_DIR)/$(BIN_DIR) ]; then $(MKDIR) -p $(ROOT_DIR)/$(BIN_DIR); fi
    	
    #将所有的.o文件链接成可执行文件,设置成伪目标的原因是:希望编译都重新链接	
    .PHONY: $(TARGET)	
    $(TARGET): 
    	$(CC) -o $@ $(shell find ./target/tmp -name *.o)
    
    #清除所有编译生成的文件	
    clean:
    	@$(RM) -rf $(ROOT_DIR)/target/*
    	@$(FIND) ./ -name "*.d" | xargs rm -rf
    

    二级Makefile

    #以下同根目录下的makefile的相同代码的解释
    
    #获取所有的源文件名
    CUR_SOURCE = ${wildcard src/*.c}
    
    #将所有的.o源文件名变成.o文件名
    CUR_OBJS = ${patsubst %.c, %.o, $(CUR_SOURCE)}
    
    #获取当前目录的名称
    CUR_DIR_NAME = $(shell pwd |sed 's/^(.*)[/]//g')
    
    #指定.o文件存放的路径
    OUTPUT_DIR = $(ROOT_DIR)/$(OBJS_DIR)/$(CUR_DIR_NAME)/src
    
    #生成所有需要生成.o文件的全路径
    OUTPUT_OBJS = $(addprefix $(ROOT_DIR)/$(OBJS_DIR)/$(CUR_DIR_NAME)/,$(CUR_OBJS))
    
    #说明头文件路径,引入了什么头文件就在此处添加对应的头文件路径(需要手动修改)
    INCLUDEPATH = -I ./include
    -I ../module2/include -I ../module3/include all : CREATE_DIR $(OUTPUT_OBJS) #创建存放目标的文件夹 CREATE_DIR : @if [ ! -d $(OUTPUT_DIR) ]; then $(MKDIR) -p $(OUTPUT_DIR); fi #生成.o文件,并制定.o文件路径 $(OUTPUT_DIR)/%.o : src/%.c $(CC) $(INCLUDEPATH) -c $< -o $@ #生成头文件依赖的目标 src/%.d : src/%.c @set -e; rm -f $@; $(CC) -MM $(INCLUDEPATH) $< > $@.$$$$; sed 's,($*).o[ :]*,$(OUTPUT_DIR)/1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$ #引入包含头文件依赖的.d文件 -include $(CUR_SOURCE:.c=.d)

     缺陷

    • 实现头文件自动依赖时,中间文件和源文件在同一级目录中,不是很好
    • 没有预留链接库的接口
  • 相关阅读:
    Encoding
    F Takio与Blue的人生赢家之战
    D FFF团的怒火
    C Golden gun的巧克力
    B 倒不了的塔
    A jubeat
    17230 计算轴承半径
    10686 DeathGod不知道的事情
    10688 XYM-AC之路
    10692 XYM-入门之道
  • 原文地址:https://www.cnblogs.com/chusiyong/p/11387259.html
Copyright © 2011-2022 走看看