zoukankan      html  css  js  c++  java
  • makefile学习笔记

    The syntax of Makefiles(makefile的语法):

       makefile主要包括两个方面:1.一组依赖关系  2.规则

      一个简单的makefile文件:Makefile1

    myapp: main.o 2.o 3.o
            gcc -o myapp main.o 2.o 3.o
    main.o: main.c a.h
            gcc -c main.c
    2.o: 2.c a.h b.h
            gcc -c 2.c
    3.o: 3.c b.h c.h
            gcc -o 3.c

    1.一组依赖关系:  

      依赖关系定义了最终应用程序里的每个文件 与源文件之间的关系。比如上面最终应用程序(可执行程序myapp)依赖于文件main.o,2.o 和3.o;同理main.o依赖于main.c和a.h。

      依赖关系的写法:先写目标的名称,然后紧跟一个冒号,接着是空格或制表符tab,最后是用空格或制表符tab隔开的文件列表(这些文件用于创建目标文件)。如上面的:myapp: main.o 2.o 3.o

    2.规则

      规则定义了目标的创建方式。如上面的:  gcc -o myapp main.o 2.o 3.o

      这里有一个非常奇怪而又令人遗憾的语法现象:空格与制表符tab是有区别的,规则所在的行必须以制表符tab开头,用空格是不行的。如果少了制表符tab,make命令就不会正常工作,会提醒你少了间隔(制表符tab)。

    3.make makefile

      如果makefile文件不是默认文件名makefile、Makefile或GNUmakefile时,在调用make命令时加上-f选项。

      现在来具体尝试创建makefile文件:

      首先创建三个头文件(为简单全部为空文件):

      touch a.h

      touch b.h

      touch c.h

      然后创建c文件:

      main.c如下: 

    /* main.c */
    #include <stdlib.h>
    #include "a.h"
    
    extern void function_two();
    extern void function_three();
    
    int main()
    {
            function_two();
            fucntion_three();
            exit(EXIT_SUCCESS);
    }

      2.c如下:

    /* 2.c */
    #include <stdio.h>
    #include "a.h"
    #include "b.h"
    
    void function_two()
    {
            printf("Hello ");
    }

      3.c如下:

    /* 3.c */
    #include <stdio.h>
    #include "b.h"
    #include "c.h"
    
    void function_three()
    {
            printf("World\n");
    }

      现在来执行make命令:

      make -f Makefile1

      会执行如下命令:

       gcc -c main.c 
      gcc
    -c 2.c
      gcc
    -c 3.c
      gcc
    -o myapp main.o 2.o 3.o

      此时生成了一个叫myapp的可执行文件,运行myapp

      ./myapp

      运行结果:Hello World

      假如更新了b.h,再次make会发生什么不同,

      touch b.h

      make -f Makefile1

      会执行如下命令:

      gcc -c 2.c
    
      gcc -c 3.c
    
      gcc -o myapp main.o 2.o 3.o

      可见依赖b.h的目标全进行了重新编译,而不依赖它的main.o却不需要重新编译,就也是make的优势所在。

    makefile文件中的注释

      makefile文件中的注释以#号开关,一直延续一这一行的结束。

    makefile文件中的宏

      makefile文件中的宏相当于C中的#define,好处不言而喻。

      在makefile中定义宏:MACRONAME = value

      引用宏:$(MACRONAME)或${MACRONAME},如果想把一个宏的值设置为空,可令等号(=)后面留空。

      A Makefile with Macors:Makefile2

    all: myapp
    
    # Which compiler
    CC = gcc
    
    # Where are include file kept
    INCLUDE = .
    
    # Options for development
    CFLAGS =  - g -Wall -ansi
    
    # Options for release
    # CFLAGS = -O -Wall -ansi
    
    myapp: main.o 2.o 3.o
            $(CC) -o myapp main.o 2.o 3.o
    
    main.o: main.c a.h
            $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
    
    2.o: 2.c a.h b.h
            $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
    
    3.o: 3.c b.h c.h
            $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c

      现在删除旧的安装文件,并通过这个新的makefile文件创建新安装文件:

      rm *.o myapp
    
      make -f Makefile2

      会执行如下命令:

      gcc -I. -g -Wall -ansi -c main.c
      gcc -I. -g -Wall -ansi -c 2.c
      gcc -I. -g -Wall -ansi -c 3.c
      gcc -o myapp main.o 2.o 3.o

      运行可执行文件myapp

      ./myapp

      运行结果:Hello World

      make命令将$(CC)、$(CFLAGS)和$(INCLUDE)替换为相应的宏定义。

      其中all处可以指定多个目标(即可产生多个可执行文件),比如:all: myapp1 myapp2。

    伪目标:

      伪目标并不是一个文件,即并不会产生如myapp的可执行文件,它只是一个标签,所以我们不能像可执行文件myapp那样执行它(./myapp)。那如何执行它且它有什么作用呢?请看下面Makefile3:

    all: myapp
    
    # Which compiler
    CC = gcc
    
    # Where are include file kept
    INCLUDE = .
    
    # Options for development
    CFLAGS =  - g -Wall -ansi
    
    # Options for release
    # CFLAGS = -O -Wall -ansi
    
    myapp: main.o 2.o 3.o
            $(CC) -o myapp main.o 2.o 3.o
    
    main.o: main.c a.h
            $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
    
    2.o: 2.c a.h b.h
            $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c
    
    3.o: 3.c b.h c.h
            $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
    
    clean:
            -rm main.o 2.o 3.o myapp

      Makefile3在Makefile2的基础上加上了

    clean:
            -rm main.o 2.o 3.o myapp

      这里的clean就是一个伪目标,为了表示是伪目标,一般如下写:

    .PHNOY: clean
    clean:
            -rm main.o 2.o 3.o myapp

      删除旧安装,利用Makefile3重新make。

      rm *.o myapp
    
      make -f Makefile3

      发现与上面执行Makefil2的命令是一样的。

      其实如下命令与  rm *.o myapp是一样的。

      make -f Makefile3 clean

      可见,伪目标需要显示调用。

    内置规则:

      如果makefile文件叫Makefile或makefile或GNUmakefile,而不是如上面那样叫Makefile1、Makefile2之流,则没有必要做精确说明,直接进行如下命令即可:

            make

      伪目标clean执行命令如下:

            make claen

     

    后缀和模式规则:

      上面文件都是.c文件,假设是.cpp文件呢。

      想要增加一条新的后缀规则,首先需要在makefile文件中增加一行语句,告诉make命令这个新的后缀名。然后即可用主穿上新的后缀名来定义规则。make使用特殊语法

    .<old_suffix>.<new_suffix>

    来定义一条通用规则,利用该规则可以从带有旧后缀名的文件创建带有新的后缀的文件,并保留原文件的前半部分。

      下面是makefile文件一个片段,它用一个新的通用规则来将.cpp文件编译为.o文件。

    .SUFFIXES: .cpp
    
    .cpp.o:
            $(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<

      特殊依赖关系.cpp.o:告诉make,紧随其后的规则是用于将后缀名为.cpp的文件转换为后缀名为.o的文件。

      -xc++标志的作用是告诉gcc编译器这是一个c++源文件。

      $< 为自动化变量,表示当前依赖文件的名字。还有几个重要的自动化变量:

      $@ 表示当前目标的名字。

      $? 表示当前目标所依赖的文件列表中比当前目标文件还要新的文件。

      $* 表示不包括后缀名的当前依赖文件名字。

      通过可用能配符%来实现同样的原理:

    %.cpp: %.o
            $(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<

     

    用make管理函数库

      函数库实际上就是文件,通常是以.a(archive)为后缀名,该文件中包含了一组目标文件。

      下面将文件2.o和3.o放入函数库mylib.a中:

    all: myapp
    
    # Which compiler
    CC = gcc
    
    # Where are include file kept
    INCLUDE = .
    
    # Options for development
    CFLAGS =  - g -Wall -ansi
    
    # Options for release
    # CFLAGS = -O -Wall -ansi
    
    # Local Libraries
    MYLIB = mylib.a
    
    myapp: main.o $(MYLIB)
            $(CC) -o myapp $(MYLIB)
    
    $(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o)
    
    main.o: main.c a.h
    
    2.o: 2.c a.h b.h
    
    3.o: 3.c b.h c.h
    
    clean:
            -rm main.o 2.o 3.o myapp $(MYLIB)

      其中

    main.o: main.c a.h
    
    2.o: 2.c a.h b.h
    
    3.o: 3.c b.h c.h

      利用默认规则来实现。

      下面来测试这个新版的makefile文件Makefile4:

     

    rm -f myapp *.o mylib.a
    
    make -f Makefile4

      执行如下:

      gcc -g -Wall -ansi -c -o main.o main.c
    
      gcc -g -Wall -ansi -c -o 2.o 2.c
      ar rv mylib.a
    2.o   a - 2.o   gcc -g -Wall -ansi -c -o 3.o 3.c
      ar rv mylib.a
    3.o   a - 3.o   gcc -o myapp main.o mylib.a

      用于管理函数库的语法是lib(file.o),它的含义是目标文件file.o是存储在函数库lib.a中的。make命令用一个内置规则来管理函数库,该规则的常见形式如下所示:

      .c.a:
    
      $(CC) -c $(CFLAGS) $<
    
      $(AR) $(ARFLGAS) $@ $*.o

      宏$(AR)和$(ARFLAGS)的默认取值通常分别是命令ar和选项rv,你可利用man ar来查看相关选项的含义。

      上面语法告诉make,要想从.c文件得到.a文件,它必须应用两条规则:

      1. $(CC) -c $(CFLAGS) $<              必须编译源文件以生成目标文件

      2. $(AR) $(ARFLGAS) $@ $*.o        用ar命令将新的目标文件添加到函数库中

      最后我贴出实习做Fcitx输入法项目时用的一个makefile例子:

    CC = gcc
    CXX = g++
    CFLAGS = -Ifctix -Iui -I. -Wall
    CXXFLAGS = -Ifcitx -Iui -I. -Wall
    BUILD = build
    TARGET = input
    FLTK_PATH = `~/workspace/fltk-1.3.0/fltk-config --cxxflags --ldflags`
    
    C_SRCS = fcitx/py.c fcitx/pymap.c fcitx/punc.c 
    CPP_SRCS = main.cpp ui/MwButton.cpp ui/MwLabel.cpp ui/PopupWidget.cpp fcitx/KeyboardWidget.cpp fcitx/Fcitx.cpp
    
    OBJS = $(BUILD)/main.o $(BUILD)/py.o $(BUILD)/pymap.o $(BUILD)/punc.o $(BUILD)/MwButton.o $(BUILD)/MwLable.o \
           $(BUILD)/PopupWidget.o $(BUILD)/KeyboardWidget.o $(BUILD)/Fcitx.o
    
    TARGET : OBJS
        $(CXX) $(OBJS) -o $(TARGET) -L$(FLTK_PATH)
        rm -rf build
    
    OBJS:
        $(CC) $(CFLAGS) -c $(C_SRCS)
        $(CXX) $(CXXFLAGS) $(FLTKA_PATH) -c $(CPP_SRCS)
        if ! [ -d build ]; then mkdir build; fi
        mv *.o $(BUILD)
    
    all : TARGET DECTS
    DICTS_DIR = /home/edan/mnt/input_dict
    DICTS:
        if ! [ -d $(DICTS_DIR)]; then mkdir $(DICTS_DIR);
            cp ./dicts/* $(DICTS_DIR)
        if
    
    .PHONY : then
    clean:
        rm $(OBJS) $(TARGET)

    Refference:

      (1)《Linux程序设计》 -Neil Matthew     Richard Stones著 第4版第9章开发工具

      (2)《跟我一起写 Makefile》-陈皓

      (3)makefile官方文档:http://www.gnu.org/software/make/manual/make.html#Introduction  

  • 相关阅读:
    c# base64及MD5工具类
    c# dateTime格式转换为Unix时间戳工具类
    c# 金钱大写转小写工具类
    c# bitmap的拷贝及一个图像工具类
    C# DataTable映射成Entity
    Kubernetes---Service(SVC)服务--ingress api
    Kubernetes---Service(SVC)服务
    为网站文字前面添加图标 在线调用 Font Awesome 字体icon小图标 美化网站
    [转]office 2016 4合1/3合1 专业版 增强版 精简绿色安装版
    [转]数据恢复 文件恢复工具 DiskGenius v4.9.1 绿色专业版及单文件
  • 原文地址:https://www.cnblogs.com/danshui/p/2580428.html
Copyright © 2011-2022 走看看