• Makefile技术和应用总结


    如何学习和运用Makefile

    怎么写Makefile?不想讲太多如何如何做。Makefile这东西,公司让一两个人来负责就好了,否则一定是一锅粥。每次看到招聘广告里说,要求懂Makefile,懂Linux多线程编程,哥就想呵呵呵,软件开发的初级阶段~~~

    网路经常看到有人发帖求问Makefile如何写;当然答复也是五花八门,有些给出了样例。哥也俗一把,从example开始。不想看的直接跳过,哥准备了一套Makefile给你直接用,只要你会复制粘贴就行。

    小小班:

    OBJS := foo.o bar.o
    
    # link
    proggie: $(OBJS)
        gcc $(OBJS) -o proggie
    
    # compile and generate dependency info
    %.o: %.c
        gcc -c $(CFLAGS) $*.c -o $*.o
    
    # remove compilation products
    clean:
        rm -f proggie *.o
    

    这个makefile,基本上能满足学习做C语言的需求了。使用者需要把自己的文件名加入到makefile中,编译出来的目标文件都存放在项目根目录下。嗯,其实项目就一个根目录,当然,头文件倒是没特别限定,只需要额外定义$(CFLAGS)就好了。编译时,Make能够检测C文件是否有改动以决定是否重新编译。

    不过,如果是头文件改动,就麻烦了,这个makefile发现不了的。

    小班:

    OBJS := foo.o bar.o
    
    # link
    proggie: $(OBJS)
        gcc $(OBJS) -o proggie
    
    # pull in dependency info for *existing* .o files
    -include $(OBJS:.o=.d)
    
    # compile and generate dependency info
    %.o: %.c
        gcc -c $(CFLAGS) $*.c -o $*.o
        gcc -MM $(CFLAGS) $*.c > $*.d
    
    # remove compilation products
    clean:
        rm -f proggie *.o *.d
    

    这个比小小班的好不了多少,不过多产生了个.d文件。.d文件是解决编译错误的一个很有价值的东东,不懂的赶紧看看,不管你是不是想学Makefile。

    中班:

    OBJS := foo.o bar.o
    
    # link
    proggie: $(OBJS)
        gcc $(OBJS) -o proggie
    
    # pull in dependency info for *existing* .o files
    -include $(OBJS:.o=.d)
    
    # compile and generate dependency info;
    # more complicated dependency computation, so all prereqs listed
    # will also become command-less, prereq-less targets
    #   sed:strip the target (everything before colon)
    #   sed:remove any continuation backslashes
    #   fmt -1: list words one per line
    #   sed:strip leading spaces
    #   sed:add trailing colons
    %.o: %.c
        gcc -c $(CFLAGS) $*.c -o $*.o
        gcc -MM $(CFLAGS) $*.c > $*.d   
        cp -f $*.d $*.d.tmp
        sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $*.d
        rm -f $*.d.tmp
    
    # remove compilation products
    clean:
        rm -f proggie *.o *.d
    

    这个要说一下,小班生成了.d文件,凡事有益必有害:将.h文件改名或是换个地方,就会出错。这个Makefile试图解决这个问题。Linux sed语法,有点难,跟Makefile没啥关系。

    大班:

    sources = bar.c foo.c far.cc sub/test.c
    bin = obj
    paths = $(addprefix $(bin)/, $(dir $(sources)))
    out = $(bin)/proggie
    
    objects := $(addprefix $(bin)/, $(addsuffix .o, $(basename $(sources))))
    
    all: $(bin) $(out)
    
    $(out): $(objects)
        gcc $(objects) -o $(out)
    
    $(objects): | $(bin)
    
    $(bin):
        mkdir -p $(paths)
    
    -include $(objects:.o=.d)
    
    $(bin)/%.o : %.c    
        gcc -c -MD $< -o $@
    
    $(bin)/%.o : %.cc
        gcc -c -MD $< -o $@
    
    clean:
        rm -rf $(bin)/
    

    中班试图解决.h文件改名或是改路径,导致.d文件编译出错的事,现在,我们发起解决这个问题,毕竟那事极少发生,而且还有make-clean终极大招。大班做什么?解决多个平台的编译问题。这里将编译出来的目标文件放入obj目录,其实就是解决多平台编译的思路。

    好了,说完这些,对于学习Makefile来说,也就基本讲完了,剩下的就是如何工程化了。这个挺难的,如果不是想专业搞Makefile或是开发架构设计(下回讨论怎么做开发架构),就别太费心琢磨,该干嘛干嘛去,别浪费时间在这产生不了价值的事情上。

    记得下载,解压到Linux上练练手: https://files.cnblogs.com/files/hhao020/cshell_prj.re0.001.rar

    cshell_prj/Makefile

    编译x32和x64两种平台: # programs for cshell .PHONY: all clean rebuild

    exe: 
        make -f linux.i64.mk target=$(target)
        make -f linux.i32.mk target=$(target)
    
    clean:
        make -f linux.i64.mk clean target=$(target)
        make -f linux.i32.mk clean target=$(target)
    
    rebuild:
        make -f linux.i64.mk rebuild target=$(target)
        make -f linux.i32.mk rebuild target=$(target)
    

    cshell_prj/linux.i64.mk

    编译64位intel程序:

    # programs for cshell
    
    MAKE_RULE := linux.i64
    SAL_DIR := salLinux64
    
    PRJ_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
    
    
    export PRJ_DIR MAKE_RULE
    
    
    CC := gcc
    LD := gcc
    
    CFLAGS := -Wall -g -MD -m64 -c
    SELF_CFLAGS :=
    LDFLAGS :=
    
    SOURCES := user_main.c
    BIN = $(PRJ_DIR)/bin/$(MAKE_RULE)
    PATHS = $(addprefix $(BIN)/, $(dir $(SOURCES)))
    
    OUT=$(BIN)/user.exe
    
    OBJECTS := $(addprefix $(BIN)/, $(addsuffix .o, $(basename $(SOURCES))))
    
    #must keep cshell as the first lib
    LIBDIRS :=   cshell userapp
    
    
    LDDIRS := -L$(BIN)
    
    LDLIBS := $(addprefix -l, $(LIBDIRS))
    
    LIBFILES := $(addprefix $(BIN)/lib, $(addsuffix .a, $(LIBDIRS)))
    
    LDFLAGS += -m64 $(LDLIBS) $(LDDIRS) -lpthread 
    
    INCLIBS =  -I$(PRJ_DIR)export $(addsuffix /export, $(addprefix -I$(PRJ_DIR), $(LIBDIRS)))
    export INCLIBS
    
    .PHONY: all clean   
    
    all: yacc $(BIN) exe
    
    
    #$(OBJECTS): | $(BIN) yacc
    
    $(BIN):
        mkdir -p $(PATHS)
    
    yacc:
    ifeq ($(target), )
        ( cd cshell && (perl $(PRJ_DIR)/tools/p_readelf.pl ))
        #( cd cshell && (bison -d cshell.y && flex -ocshell.lex.c cshell.l && perl $(PRJ_DIR)/tools/p_readelf.pl ))
    endif
    
    -include $(OBJECTS:.o=.d)
    
    exe: $(OBJECTS)
    ifneq ($(target), )
        make -C $(target) TARGET=$(target)
    else    
        for dir in $(LIBDIRS); do (make -C $$dir TARGET=$$dir || exit 1) || exit 1; done        
        (cd cshell && rm -rf c_sym_table.* && perl $(PRJ_DIR)/tools/p_readelf.pl $(LIBFILES)  $(OBJECTS)) || exit 1;
        rm -rf $(BIN)/libcshell.a
        make -C cshell TARGET=cshell $(INCLIBS)
        $(LD) -o $(OUT) $^ $(LDFLAGS)
    endif
    
    $(BIN)/%.o: %.c
        $(CC) -c -o $@ $< $(CFLAGS) $(INCLIBS)
    
    clean:
    ifneq ($(target), )
        make -C $(target) TARGET=$(target) clean
    else
        rm -f $(BIN)/*.o $(BIN)/*.d $(BIN)/*.a $(BIN)/*.sym.txt  $(OUT) *.sym.txt   *.a
        for dir in $(LIBDIRS); do (make -C $$dir clean || exit 1) || exit 1; done
    endif
    
    rebuild: clean exe
    

    cshell_prj/xxx/Makefile

    每个源码子目录放一份,新目录拷贝就成,记得在cshell_prj/xxx.mk里添加这个目录到库列表。做项目时,这个文件要杜绝一般开发者人员的修改。当然,一般来说,这个文件在标准版本编译时,应该使用工具全部重新生成一份,免得有人偷偷修改过。

    #------------------------------------------------------------
    # makefile.def : common template for makefile
    # Author   : hhao020
    # Date : 2011-06-30
    #------------------------------------------------------------
    
    
    ifeq ($(PRJ_DIR), )
    env_err:
        @echo Error: must define PRJ_DIR environment variable.
    endif
    
    ifeq ($(MAKE_RULE), )
    env_err:
        @echo Error: must define MAKE_RULE environment variable.
    endif
    
    
    # source files' list
    SOURCES = 
    
    # private compiler flags and include file path
    SELF_ASFLAGS  = 
    SELF_CFLAGS   =
    SELF_CPPFLAGS = 
    SELF_INCLUDE  = 
    
    
    
    # private macro or compiler conditions definations
    SELF_DEFINE = 
    #-DNET_SELF_TEST
    
    # private link definations
    SELF_LINK_OPT = 
    
    include $(PRJ_DIR)/build/$(MAKE_RULE)
    

    cshell_prj/build/linux.i64

    特定平台编译模板。做项目时,这个文件要杜绝一般开发者人员的修改。

    #------------------------------------------------------------
    # makefile.def : common definitions for makefile
    # Author   : hhao020
    # Date : 2011-06-30
    #------------------------------------------------------------
    
    ifeq "$(strip $(SOURCES))" ""
    SOURCES := $(wildcard *.s) $(wildcard *.c) $(wildcard *.cpp) 
    endif
    
    BIN = bin/$(MAKE_RULE)
    PATHS = $(addprefix $(BIN)/, $(dir $(SOURCES)))
    
    OUT = $(PRJ_DIR)/$(BIN)/lib$(TARGET).a
    
    
    OBJECTS := $(addprefix $(BIN)/, $(addsuffix .o, $(basename $(SOURCES))))
    
    #default tool chains
    AR = ar -rcs
    AS = as
    CC = gcc
    RM = rm -rf
    
    COMM_DEFS = -g -DLINUX -DCPU64 \
    -I./ \
    -I./inc
    
    ASFLAGS   = -Wall -c
    CPP_FLAGS = 
    CFLAGS= -Wall -g -MD -m64 -c 
    
    LINK_OPT  = 
    
    ASFLAGS += $(SELF_ASFLAGS)
    
    
    ..s.o:
        $(AS) $(COMM_DEFS) $(ASFLAGS) $(SELF_ASFLAGS) $(SELF_INCLUDE) $(INCLIBS) $(SELF_DEFINE) $<
    
    $(BIN)/%.o : %.c
        $(CC) $(COMM_DEFS) $(CFLAGS) $(SELF_CFLAGS) $(SELF_INCLUDE) $(INCLIBS) $(SELF_DEFINE) -c $< -o $@
    
    $(BIN)/%.o : %.cpp
        $(CC) $(COMM_DEFS) $(CPPFLAGS) $(SELF_CPPFLAGS) $(CFLAGS) $(SELF_CFLAGS) $(SELF_INCLUDE) $(INCLIBS) $(SELF_DEFINE) -c $<
    
    $(OUT) : $(OBJECTS)
        $(AR) $(OUT) $^
    
    $(OBJECTS): | $(BIN)
    
    $(BIN):
        mkdir -p $(PATHS)
    
    -include $(OBJECTS:.o=.d)
    
    .PHONY : exe clean rebuild
    
    exe :  $(OUT)
    
    clean :
        $(RM) $(BIN)/*.d $(BIN)/*.o /$(OUT)
    
    rebuild: clean exe
    

    Makefile这东西,其实是跟软件的开发架构紧密关联的。若是开发架构不好,Makefile自然就没那么容易搞,或者很难懂。再有,就是防不住小朋友们捣乱,特别是刚毕业的,总是喜欢改一下Makefile,添加点头文件路径,或是编译设置,而这些都直接危害着最后的产品;一个不小心,编译出来的东西就不是你原本想要的了!因此,杜绝大家都来改Makefile绝对是根本!这套Makefile,让一般开发人员只能复制整个文件到新目录,而无需关系具体细节,大概能让添加新文件和新目录的事,变得容易些!


  • 相关阅读:
    CSS边框,背景,边距,溢出
    数字电子技术课程设计之基于触发器的三位二进制同步减法计数器无效态000/110
    集成电路版图与工艺课程设计之用CMOS实现Y=AB+C电路与版图
    金属磁记忆传感器封装
    基于FPGA 的8b10b编解码电路前端电路设计
    Css颜色和文本字体
    Css中的选择器
    Css基本语法及页面引用
    65 插入排序
    63 散列表的查找
  • 原文地址:https://www.cnblogs.com/hhao020/p/4979777.html
Copyright © 2020-2023  润新知