嵌入式系统与单片机|技术阅读
登录|注册

您现在的位置是:嵌入式系统与单片机 > 技术阅读 > Makefile基础

Makefile基础

Makefile基础


gcc命令

通过gcc命令实现编译

main.c

#include<stdio.h>

int main()
{
    printf("Hello world\n");
    return 0;
}

1.预处理

gcc -E main.c -o main.i
  • -E:预处理;
  • -o : 将结果输出到指定文件中;

2. 编译

生成汇编语言

gcc -S mian.c -o main.s
  • -S:进行预处理和汇编操作;

3. 汇编

生成机器语言

gcc -c mian.c -o mian.o
  • -c:生成目标文件

4.链接

gcc mian.c -o main

静态文件库

三个.c文件:main.c , add.c, minus.c

目标:将add.c, minus.c编译为静态文件库

mian.c

#include<stdio.h>

int add(int a, int b);
int minus(int a, int b);

int main()
{
    printf("Hello world\n");
    printf("add  = %d\n", add(75));
    printf("minus= %d\n", minus(75));
    return 0;
}

add.c

int add(int a, int b)
{
    return a + b;
}

minus.c

int minus(int a, int b)
{
    return a - b;
}
# 1.编译成.o文件
gcc -c [.c] [.c] ...
# example
gcc -c add.c  minus.c
# 2. 编译静态库
ar -r [lib自定义库名.a] [.o] [.o] ...
# example
ar -r  liboperation.a add.o minus.o
# 3. 链接为可执行文件
gcc [.c] [.a] -o [自定义输出文件名]
or
gcc [.c] -o [自定义输出文件名] -l[库名] -L[库路径]
# example
gcc main.c liboperation.a -o main

总结:

  • 静态库以.a 为后缀;
  • 静态库名字以lib作为前缀;
  • 动态文件库

    # 1. 编译为.o文件
    gcc -c -fpic [.c] [.c]...
    # example
    gcc -c -fpic add.c minus.c
    # 编译为静态库
    gcc -shared [.o] [.o]... -o [lib自定义库名.so]
    # example
    gcc -shared add.o minus.o -o liboperation.so
    # 链接
    gcc [.c/.cpp] -o [自定义可执行文件名]  [动态库路径]
    or
    gcc [.c] -o [自定义输出文件名] -l[库名] -L[库路径] -WL,rpath=[库路径]
    # example
    gcc main.c -o main ./liboperation.so 

    • -fpic:生成与位置无关的代码(Position Independent Code)
    • 动态文件库后缀为.so

    Makefile

    基本格式

    targets : prerequisties
    [tab键]command
    • targets:目标文件
    • prerequisties:依赖文件
    • command:执行命令

    变量

    • $@: 目标(target)的完整名称
    • $<: 第一个依赖文件(prerequisties)的名称
    • $^: 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件
    #定义变量
    obj := main.o
    cfile := main.c

    ${obj}: ${cfile}
     gcc -c $^ -o $@

    debug:
     @echo ${obj}
     @echo ${cfile}
    clear:
     rm *.o
    .PHONY: clear
    • .PHONY伪目标
    • @可以隐藏命令,终端不输出
    • $^表示main.c
    • $@表示main.o

    常用符号

    =

    • 简单的赋值运算符
    • 用于将右边的值分配给左边的变量
    • 如果在后面的语句中重新定义了该变量,则将使用新的值

    :=

    • 立即赋值运算符
    • 用于在定义变量时立即求值
    • 该值在定义后不再更改
    • 即使在后面的语句中重新定义了该变量

    ?=

    • 默认赋值运算符
    • 如果该变量已经定义,则不进行任何操作
    • 如果该变量尚未定义,则求值并分配

    +=

    • 累加

    *和%

    • *: 通配符表示匹配任意字符串,可以用在目录名或文件名中
    • %: 通配符表示匹配任意字符串,并将匹配到的字符串作为变量使用

    常用函数

    $(fn arguments) or ${fn arguments}
    • fn: 函数名
    • arguments: 函数参数,参数间以逗号 , 分隔,而函数名和参数之间以“空格”分隔

    1. shell: 调用shell命令

    $(shell <command> <arguments>)
    • 功能:调用 shell 命令 command
    • 返回:函数返回 shell 命令 command 的执行结果
    #获取src下的所有.c文件
    c_src := $(shell find  src -name "*.c")
    debug:
     @echo $(c_src)

    .PHONY:debug

    2 subst:字符串替换

    $(subst <from>,<to>,<text>)
    • 功能:把字串中的字符串替换成
    • 返回:函数返回被替换过后的字符串
    #获取src下的所有.c文件
    c_src := $(shell find  src -name "*.c")
    # 将src文件夹更换为obj文件夹
    c_obj := $(subst src, obj, $(c_src))
    debug:
     @echo $(c_src)
     @echo $(c_obj)

    .PHONY:debug

    3. patsubst: 字符串替换

    • 功能:通配符 %,表示任意长度的字串,从 text 中取出 patttern, 替换成 replacement
    • 返回:函数返回被替换过后的字符串
    #获取src下的所有.c文件
    c_src := $(shell find  src -name "*.c")
    # 将src文件夹更换为obj文件夹并将后缀替换为.o
    c_obj := $(patsubst src%.c, obj%.o, $(c_src))
    debug:
     @echo $(c_src)
     @echo $(c_obj)

    .PHONY:debug

    4. foreach:循环函数

    $(foreach <var>,<list>,<text>)
    • 功能:把字串中的元素逐一取出来,执行包含的表达式
    • 返回:所返回的每个字符串所组成的整个字符串(以空格分隔)
    # .头文件的地址
    library_paths := ./inc \
        /usr/local/include
    # 将头文件前面加上-L
    library_paths := $(foreach iterm, $(library_paths), -L$(library_paths))
    debug:
     @echo $(library_paths)

    .PHONY:debug

    5 dir:返回子目录

    $(dir <names...>)
    • 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前 的部分。如果没有反斜杠,那么返回“./”。
    • 返回:返回文件名序列的目录部分。
    #获取src下的所有.c文件
    c_src := $(shell find  src -name "*.c")
    # 获取src文件夹路径
    src_path := $(dir $(c_src))
    debug:
     @echo $(src_path)

    .PHONY:debug

    6 notdir : 去掉文件绝对路径,保留文件名

    $(notdir <names...>)

    7. filter:过滤

    $(filter <names...>)

    8 basename:去掉后缀

    $(basename <names...>)

    example

    # 获取静态库和动态库名称

    #获取/usr/lib 下以lib为前缀的路径
    lib_path := $(shell find  /usr/lib -name "lib*")
    # 去掉文件路径,获取文件名
    lib_name := $(notdir $(lib_path))
    # 过滤出静态库和动态库
    a_libs := $(filter %.a, $(lib_name))
    so_libs := $(filter %.so, $(lib_name))
    # 获取库文件名称
    a_libs := $(basename $(a_libs))
    so_libs := $(basename $(a_libs))
    # 去掉前缀lib
    a_libs := $(subst lib, , $(a_libs)) 
    so_libs := $(subst lib, , $(so_libs)) 
    debug:
     @echo $(so_libs)

    .PHONY:debug

    编译选项

    • -std=: 指定编译标准,例如:-std=c++11、-std=c++14
    • -g: 包含调试信息
    • -w: 不显示警告
    • -O: 优化等级,通常使用:-O3
    • -I: 加在头文件路径前
    • fPIC: (Position-Independent Code), 产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的

    链接选项

    • -l: 加在库名前面
    • -L: 加在库路径前面
    • -Wl,<选项>: 将逗号分隔的 <选项> 传递给链接器
    • -rpath=: "运行" 的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找