• 09-基础篇-一步一步写MakeFile


    目录

     

    1.单文件编译     

    2.多文件编译

    2.1 编写

    2.2 改进

    2.3 改进

    2.4 改进

    2.5 改进

    3.多目录编译

    4.动态库与静态库

    5.动态库与静态库使用

    5.1 静态库

    5.2动态库


     

    1.单文件编译     

    首先我们建立一个hello.c 的程序。

    1. #include
    2. int main()
    3. {
    4. printf("hello world!!!\n");
    5. return 0;
    6. }

           此hello.c文件以ascii码的方式存储于磁盘中,若此程序在要在linux系统运行,比如在ubuntu18.04上运行,必须对其进行编译。

        gcc -o hello hello.c

         运行:

            ./hello

    从上面可以看出,我们只用一条命令,可以实现对hello.c的编译,但是假如工程中不仅仅有hello.c,还有很多其它的.c和.h文件呢?

    我们能否有种方式,像我们调试windows工程一样,可以一键编译的,答案是有,就是MakeFile,执行一个指令make可以完全所有工作和文件的编译。

    2.多文件编译

    2.1 编写

    如下为此工程的目录结构,对应文件的内存如下

    1. .
    2. |-- Makefile
    3. |-- hello.c
    4. |-- print.c
    5. `-- print.h

     

    print.c

    1. #include
    2. #include "print.h"
    3. int my_print(void)
    4. {
    5. printf("hello world!!!! ==>>\n");
    6. return 0;
    7. }

    print.h

    1. #ifndef __PRINT_H__
    2. #define __PRINT_H__
    3. int my_print(void);
    4. #endif

    hello.c

    1. #include
    2. #include "print.h"
    3. int main()
    4. {
    5. my_print();
    6. return 0;
    7. }

    我们要用make完成上面三个文件的编译,并生成hello的可执行二进制文件。

     

    makefile组成三要素:目标,依赖, 命令

      targets(目标):prequisities(依赖)
             command(命令,command前面要有一个tab键)

    根据以上规则,写出Makefile

    1. hello:hello.o print.o
    2. gcc hello.o print.o -o hello
    3. hello.o:hello.c print.h
    4. gcc -c hello.c
    5. print.o:print.c print.h
    6. gcc -c print.c
    7. clean:
    8. rm -f *.o hello

    从上面的Makefile看出,首先print.c和hello.c分别编译和汇编生成print.o,hello.o的机器指令,然后gcc将print.o和hello.o链接成目标程序hello

    2.2 改进

    将两个中间过程的二制作文件.o文件用OBJ替代

    1. OBJ=hello.o print.o
    2. hello:$(OBJ)
    3. gcc $(OBJ) -o hello
    4. hello.o:hello.c print.h
    5. gcc -c hello.c
    6. print.o:print.c print.h
    7. gcc -c print.c
    8. clean:
    9. rm -f *.o hello

    2.3 改进

    省略.h

    1. OBJ=hello.o print.o
    2. hello:$(OBJ)
    3. gcc $(OBJ) -o hello
    4. hello.o:hello.c
    5. gcc -c hello.c
    6. print.o:print.c
    7. gcc -c print.c
    8. clean:
    9. rm -f *.o hello

    2.4 改进

    #     $@  : 目标文件 
    #     $^   : 所有依赖文件 
    #     $<   : 第一个依赖文件
    引入部分符号,替代上述的内容,让程序自己去找.c生成对应的.o并将所有的.o链接成目录文件

    1. CC = gcc
    2. TARGETS = hello
    3. OBJ=hello.o print.o
    4. $(TARGETS):$(OBJ)
    5. $(CC) $^ -o $@
    6. %.o:%.c
    7. $(CC) -c $<
    8. clean:
    9. rm -f *.o $@

    2.5 改进

    进一步根据文件夹内所有的.c文件,推段出所有.o文件。到这一步,就实现了当前目录下的所有文件一键编译。假如我们想将.c文件.h文件分开,该如何做。

    1. CC = gcc
    2. TARGETS = hello
    3. SRC := $(wildcard *.c)
    4. OBJ=$(patsubst %.c,%.o,$(SRC))
    5. $(TARGETS):$(OBJ)
    6. $(CC) $^ -o $@
    7. %.o:%.c
    8. $(CC) -c $<
    9. clean:
    10. rm -f *.o $@

     

    3.多目录编译

    如下目录,我们重新创建了3个目录,src用于放所有c文件,obj用于放所有的.o文件,inc用于放所有的.h文件。

    1. .
    2. |-- Makefile
    3. |-- inc
    4. | `-- print.h
    5. |-- obj
    6. `-- src
    7. |-- hello.c
    8. `-- print.c

    1.加入CFLAGS用于让c文件转化成.o时,能够知道所有的.h文件在哪个位置

    2.在生成.o文件的命令后面加了-o $@ 

    此举的目的是为了让所有.o文件生成到obj目录

    3.src和inc和obj的位置均用相应的宏指定

    1. TARGETS = hello
    2. DIR_INC = ./inc
    3. DIR_SRC = ./src
    4. DIR_OBJ = ./obj
    5. CC = gcc
    6. SRC := $(wildcard ${DIR_SRC}/*.c)
    7. OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))
    8. INCLUDES = -I$(DIR_INC)
    9. CFLAGS += $(INCLUDES)
    10. $(TARGETS):$(OBJ)
    11. $(CC) $^ -o $@
    12. $(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
    13. $(CC) $(CFLAGS) -c $< -o $@
    14. clean:
    15. rm -f $(TARGETS)
    16. rm -f $(DIR_OBJ)/*.o

     

    4.动态库与静态库

    另外在实际的开发过程中,一整个系统,不可能是一个公司能够做完成,A公司和B公司同时做项目,一般A公司不可能对B公司开源自己的代码,因而需要有一种方式将代码屏蔽,而只将功能抽象出来,这种方法就是库。

    库有动态库和静态库。

    静态库:
    扩展名为.a,函数库通常扩展名为libxxx.a
    这类库在编译的时候会直接整合到目标程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译。

    动态库
    扩展名为.so,函数库通常名为libxxx.so
    与静态函数库被整个捕捉到程序中不同,动态函数库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。这样从产品功能升级角度方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。

    下面我们来生成动态库与静态库,目录如下,我们将print_lib.c的内容打包成库。

    1. .
    2. |-- Makefile
    3. |-- inc
    4. | `-- print_lib.h
    5. |-- obj
    6. `-- src
    7. `-- print_lib.c

    print_lib.c

    1. #include
    2. #include "print_lib.h"
    3. int my_print_api(void)
    4. {
    5. printf("hello world!!!! this is lib ==>>\n");
    6. return 0;
    7. }

    print_lib.h

    1. #ifndef __PRINT_LIB_H__
    2. #define __PRINT_LIB_H__
    3. int my_print_api(void);
    4. #endif

     

    Makefile修改:

    1.目录文件名改成libxxx.so和libxxx.a, 记住最前面一定有lib标识

    2.CFLAGS位置加-fPIC

    3.链接过程中一定要加-shared

    4.静态库是用AR打包生成

    1. TARGETS = libprint_lib.so
    2. TARGETS_STATIC = libprint_lib.a
    3. DIR_INC = ./inc
    4. DIR_SRC = ./src
    5. DIR_OBJ = ./obj
    6. CC = gcc
    7. SRC := $(wildcard ${DIR_SRC}/*.c)
    8. OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))
    9. INCLUDES = -I$(DIR_INC)
    10. CFLAGS += $(INCLUDES)
    11. CFLAGS += -fPIC
    12. $(TARGETS):$(OBJ)
    13. $(CC) -shared $^ -o $@
    14. $(AR) -r $(TARGETS_STATIC) $(DIR_OBJ)/*.o
    15. $(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
    16. $(CC) $(CFLAGS) -c $< -o $@
    17. clean:
    18. rm -f $(TARGETS_STATIC)
    19. rm -f $(TARGETS)
    20. rm -f $(DIR_OBJ)/*.o

     

    5.动态库与静态库使用

    5.1 静态库

    目录如下:

    1. .
    2. |-- Makefile
    3. |-- inc
    4. | `-- print.h
    5. |-- lib
    6. | `-- print_lib
    7. | |-- inc
    8. | | `-- print_lib.h
    9. | `-- lib
    10. | `-- libprint_lib.a
    11. |-- obj
    12. `-- src
    13. |-- hello.c
    14. `-- print.c

    加入LDFLAGS,用于在链接过程中指定库路径

    1. TARGETS = hello
    2. DIR_INC = ./inc
    3. DIR_SRC = ./src
    4. DIR_OBJ = ./obj
    5. CC = gcc
    6. SRC := $(wildcard ${DIR_SRC}/*.c)
    7. OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))
    8. PRINT_LIB_DIR = ./lib/print_lib/lib
    9. PRINT_INC_DIR = ./lib/print_lib/inc
    10. INCLUDES = -I$(DIR_INC)
    11. INCLUDES += -I$(PRINT_INC_DIR)
    12. LDFLAGS = $(PRINT_LIB_DIR)/libprint_lib.a
    13. CFLAGS += $(INCLUDES)
    14. $(TARGETS):$(OBJ)
    15. $(CC) $^ -o $@ $(LDFLAGS)
    16. $(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
    17. $(CC) $(CFLAGS) -c $< -o $@
    18. clean:
    19. rm -f $(TARGETS)
    20. rm -f $(DIR_OBJ)/*.o

     

    5.2动态库

       目录如下

    1. .
    2. |-- Makefile
    3. |-- inc
    4. | `-- print.h
    5. |-- lib
    6. | `-- print_lib
    7. | |-- inc
    8. | | `-- print_lib.h
    9. | `-- lib
    10. | `-- libprint_lib.so
    11. |-- obj
    12. `-- src
    13. |-- hello.c
    14. `-- print.c

    1.加入LDFLAGS,用于在链接过程中指定库路径

    2.在运行前,要将对应.so拷至/usr/lib下

    1. TARGETS = hello
    2. DIR_INC = ./inc
    3. DIR_SRC = ./src
    4. DIR_OBJ = ./obj
    5. CC = gcc
    6. SRC := $(wildcard ${DIR_SRC}/*.c)
    7. OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))
    8. PRINT_LIB_DIR = ./lib/print_lib/lib
    9. PRINT_INC_DIR = ./lib/print_lib/inc
    10. INCLUDES = -I$(DIR_INC)
    11. INCLUDES += -I$(PRINT_INC_DIR)
    12. LDFLAGS = -L $(PRINT_LIB_DIR) -lprint_lib
    13. CFLAGS += $(INCLUDES)
    14. $(TARGETS):$(OBJ)
    15. $(CC) $^ -o $@ $(LDFLAGS)
    16. $(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
    17. $(CC) $(CFLAGS) -c $< -o $@
    18. clean:
    19. rm -f $(TARGETS)
    20. rm -f $(DIR_OBJ)/*.o

     

         

           

     

     

            

     

          
     

     

     

     

     

     

  • 相关阅读:
    java for循环语句
    js函数( 普通函数、箭头函数 ) 内部this的指向
    【mysql】—— 函数的基本介绍
    MySQL中的分库分表框架-ShardingSphere
    2022年最新前端面试题,持续更新
    学会可视化大屏布局技巧,让领导都赞不绝口
    van-button根据参数改变字体颜色,实现高度自定义
    亥姆霍兹线圈主要用途有哪些
    [linux][命令]linux文件操作命令大全
    Hadoop2.x-基础[HDFS篇](介绍、常用API、I/O操作、工作机制)
  • 原文地址:https://blog.csdn.net/sishen4199/article/details/133690248