• 2019年7月19日星期五(交叉编译工具)


    一、工程管理文件makefile

    1. 什么是makefile

    makefile称之为工程管理文件,用于管理整个工程所有.c文件编译规则。

    2. makefile是一个工程中是一定要写的吗?

    如果在项目源码中,文件不多的时候,一般makefile不用写,因为编译命令比较简单。

    如果在项目源码,源文件(.c) 头文件(.h)这些文件比较多,一般会携带一个makefile。

    makefile目的: 为了提高编译效率。

    . 项目工程应该由哪些文件组成?

    1. 简单版

    源程序文件    main.c   -> 包含main函数在内的.c文件。

    功能程序文件  fun1.c   -> 第1个功能文件    -> 功能性最好以功能来命名,例如: play_music.c

    功能程序文件  fun2.c   -> 第2个功能文件

    ...

    功能程序文件  funn.c   -> 第n个功能文件

    头文件:      head.h   -> 结构体声明,函数声明,宏定义,系统头文件..

    库文件:     

    工程管理文件: makefile  -> 里面有一套可以编译整个项目的规则。

    2. 复杂版

    src/      -> 所有的.c文件

    include/  -> 所有的.h文件

    lib/      -> 所有的库文件

    bin/      -> 编译之后的可执行文件

    makefile  -> 里面有一套可以编译整个项目的规则。

    如果没有makefile,上述例子中,用户应该每次都要输入: "gcc main.c fun1.c fun2.c -o main -I ."

    学习书写makefile之后,就可以让makefile帮我们做这件事情了。

    . makefile书写规则?

    1. makefile文件的核心:"依赖" "目标"

    "依赖"  -> 在编译文件时,才需要写依赖,如果不是编译,则不需要写依赖。一般依赖指的就是.c文件。

    "目标"  -> 你最终想得到的文件,一般指的是可执行程序

     在上述例子中: 

      main.c fun1.c fun2.c  -> 依赖

      main    -> 目标

    2. makefile的书写规则?

    有依赖情况下:

    ========================================================

    1)确定好依赖与目标叫什么名字。

    2)按照以下格式来写makefile:

    目标:依赖(如果有多个依赖,则每一个依赖之间使用空格分开

    <Tab键>编译规则 

    注意:

    1)<Tab键>不等于四个空格,编译规则之所以能识别出来,就是因为这个Tab键

    2)在编译规则中如何调用依赖与目标

       $^   -> 代表所有的依赖文件  -> 等价于  main.c fun1.c fun2.c

       $@   -> 代表目标文件       -> 等价于 main

    无依赖情况下:

    1)确定目标叫什么名字。

    2)按照以下规则来写makefile:

     

    目标:

    <Tab键>执行命令

    3. 举实例。

    1makefilehelloworld版本1

    target:                      //目标一定要写,但是依赖不一定。

           echo helloworld      //规则,执行make命令,就会执行这套规则了。

    执行make,结果:

    echo helloworld   -> 默认将规则命令输出到屏幕中,如果不想看到,则只需要在规则前面加@

    helloworld

    2makefilehelloworld版本2

    target:                      //目标一定要写,但是依赖不一定。

           @echo helloworld      //规则,执行make命令,就会执行这套规则了。

    执行make,结果:

    helloworld

       练习: 修改code1的代码,写一个makefile,使得执行make时候,可以生成main这个可执行程序。

    main:main.c fun1.c fun2.c

           gcc $^ -o $@

    执行make,结果:

    gcc main.c fun1.c fun2.c -o main   -> 在当前目录下生成一个main文件。

    再次执行make,结果:

    make: `main' is up to date.   -> 因为makefile检测到所有的依赖文件都没有更新,所以不会重新编译。

                               -> 只要其中一个依赖文件的修改日期与之前的不一样,都会重新编译。

    四、makefile多个目标情况。

    例子:

    main:         -> 当执行make,默认只会执行第一个目标。

           xxxx;

    main1:        -> 当需要执行某个特定的目标时,在make时候,需要指定该目标的名字:  make main1

           yyyy;

    main2:

           zzzz;

    修改makefile为:

    main:main.c fun1.c fun2.c

           gcc $^ -o $@

          

    clean:

           rm main

     

    . makefile的变量种类。

    1. 自定义变量   -> makefile不需要声明类型,只需要定义名字就可以。变量默认是字符串类型的。

    规则:

    1)变量名与C语言规则一致。

    2)给变量赋值时,等号两边都可以有空格,也可以没有。但是shell编程中变量赋值时,等边两边不能有空格。

    3)引用makefile中变量时,需要在变量前面添加$。  $A  $(A)

    4)因为变量都是默认是字符串类型,所以""可以省略。

    例子:

    A = Hello  等价于  A = "hello"

    B=world

    C=$A $(B)

      练习2: 修改makefile,将所有的依赖放置到一个变量。

     

    C_SOURCE=main.c fun1.c fun2.c

     

    main:$(C_SOURCE)

           gcc $^ -o $@

          

    clean:

           rm main

    2. 系统预设定变量

    有些变量是系统中已经写好的,并且已经赋了初值的,这些变量的值就可以直接使用。

    CC:  -> 编译器名字,默认系统赋值是cc   CC=cc   cc等价于gcc

    RM:  -> 删除命令,默认系统赋值是rm -f   RM=rm -f

      练习3:把系统预设定变量加入到makefile中。尝试交叉编译与本地编译。

    CC=arm-linux-gcc

     

    C_SOURCE=main.c fun1.c fun2.c

     

    main:$(C_SOURCE)

           $(CC) $^ -o $@

    clean:

           rm main

    3. 自动化变量   -> 变量的值不是固定的,而是会变化的。

       $^  -> 代表当前的所有依赖

       $@  -> 代表目标

    例子:

    main:

        $@  -> 代表main

    clean:

        $@  -> 代表clean

    例子:

    CC=arm-linux-gcc

    C_SOURCE=main.c fun1.c fun2.c

    TARGET=main

    $(TARGET):$(C_SOURCE)

           $(CC) $^ -o $@

          

    clean:

           rm $(TARGET)

    . makefile伪指令。

    假设makefile有一套规则:

    ------------------------------

    clean:

           $(RM) main

    ------------------------------

    当我们执行make clean时,就会执行这套规则,如果当前目录下有一个文件名字叫clean

    那么再执行make clean就会提示:make: `clean' is up to date.

    解决方案:将clean这个目标添加为伪指令。

    添加为伪指令的含义是什么?

    就是告诉makefile,这个目标不是一个生成的文件。

    makefile中添加代码:

    .PHONY:clean

    -----------------------------------------

    CC=arm-linux-gcc

    C_SOURCE=main.c fun1.c fun2.c

    TARGET=main

    $(TARGET):$(C_SOURCE)

           $(CC) $^ -o $@

    .PHONY:clean

    clean:

           $(RM) $(TARGET)

    ----------------------------------------

     

    . makefile函数  ->  wildcard  

    1. makefile中调用函数方式: $(函数名参数1,参数2,参数3.....)

       C语言中调用函数方式:    函数名(参数1,参数2,参数3...)

    2. wildcard函数作用:在指定的路径下找到相匹配的文件

    例子: SRC = $(wildcard *.c)  -> 在当前目录下寻找所有的.c结尾的文件,并把结果保存在SRC变量,每个结果之间使用空格分开。

          SRC = $(wildcard /*.c)  -> 在根目录下寻找所有的.c结尾的文件。

    注意:

    *  -> 代表任意长度的任意字符。

    最终得到简单版通用的makefile为:

    CC=arm-linux-gcc

    C_SOURCE=$(wildcard *.c)

    TARGET=main

    INCLUDE_PATH=-I .

    $(TARGET):$(C_SOURCE)

           $(CC) $^ -o $@ $(INCLUDE_PATH)

    .PHONY:clean

    clean:

           $(RM) $(TARGET)

      练习4: 将简单版通用的makefile修改为复杂版的!

    CC=arm-linux-gcc

    C_SOUCRE=$(wildcard ./src/*.c)

    TARGET=./bin/main

    INCLUDE_PATH=-I ./include

    $(TARGET):$(C_SOUCRE)

           $(CC) $^ -o $@ $(INCLUDE_PATH)

    .PHONY:clean

    clean:

           $(RM) $(TARGET)

    .嵌入式linux的库文件。

    1. 什么是库文件?

    库文件在linux下以二进制形式存在,往往我们编译程序时,需要链接这些库。

    2. 库文件的格式?

    1)静态库        --->  libxxx.a

    2)动态库/共享库 --->  libxxx.so

      例子: libxxx.so.9.1.0

      lib: 库的前缀

      xxx: 库的名字

      .a/.so: 库的后缀

      .9: 库的版本号

      .1.0:库的修正号

    3. 静态库与动态库的区别?

    静态库特点:libxxxx.a  -> 去图书馆(libxxxx.a)中把图书(函数接口)拿走

    1)程序在编译时,如果是链接静态库,那么就等于把库的内容拿走,就会导致可执行程序的大小非常大。

    2)由于是静态库编译,所以在编译程序之后,可执行文件不需要静态库的存在都可以运行。

    动态库特点:libxxx.so  -> 去图书馆(libxxx.so)看看书(函数接口)而已,看完了,就把书放回图书馆,并没有拿走。

    1)程序在编译时,如果是链接动态库,仅仅链接而已,没有拿走库的东西,相对于静态库来讲,会比较小。

    2)由于是动态库编译,在执行可执行文件之后,动态库必须存在,可执行文件才能正常运行。

    4. 如何制作库文件?

    只有包含功能性函数.c文件才能制作成库文件,含有main函数在内的.c文件不能制作为库文件。

    1)静态库的制作。  -> 没有架构可言。

    1.将工程所有不包含main函数在内的.c文件找到。

    2.将这些.c文件全部编译为.o文件。

      gcc fun1.c -o fun1.o -c

      gcc fun2.c -o fun2.o -c

    3.将这些.o文件全部塞进一个.a文件

      ar rcs libmy.a fun1.o fun2.o

    4.编译程序

      gcc main.c -o main -L . -lmy

    5.执行

      ./main  就可以出来结果!

    注意:

    1)-L .  -> 只是告诉系统去当前目录下寻找库文件,但是没有告诉链接哪个。

    2)-lmy  -> -l(小写字母L)没有空格的,要紧跟库的名字(my),注意不是库文件的名字(libmy.a)

    3)制作库与编程程序所使用到的工具链必须一致。

      练习5:熟悉交叉编译静态库的制作。

    arm-linux-gcc fun1.c -o fun1.o -c

    arm-linux-gcc fun2.c -o fun2.o -c

    ar rcs libmy.a fun1.o fun2.o

    arm-linux-gcc main.c -o main -L . -lmy

    结论: 库的架构必须与编译程序时的工具链架构一致,否则就会出错:

    库:x86  编译: arm-linux-gcc

     

    /usr/local/arm/bin/../lib/gcc/arm-none-linux-gnueabi/4.5.1/../../../../arm-none-linux-gnueabi/bin/ld: skipping incompatible ./libmy.a when searching for -lmy

    /usr/local/arm/bin/../lib/gcc/arm-none-linux-gnueabi/4.5.1/../../../../arm-none-linux-gnueabi/bin/ld: cannot find -lmy

    库: ARM  编译: gcc

    /usr/bin/ld: skipping incompatible ./libmy.a when searching for -lmy

    /usr/bin/ld: cannot find -lmy

    collect2: ld returned 1 exit status

      练习6:把库添加到复杂版的lib目录,修改makefile。

    CC=arm-linux-gcc

    C_SOUCRE=$(wildcard ./src/*.c)

    TARGET=./bin/main

    INCLUDE_PATH=-I ./include

    LIBRARY_PATH=-L ./lib -lmy

    $(TARGET):$(C_SOUCRE)

           $(CC) $^ -o $@ $(INCLUDE_PATH) $(LIBRARY_PATH)

    .PHONY:clean

    clean:

           $(RM) $(TARGET)

    2)动态库的制作   -> 有架构可言

    1. 将工程所有不包含main函数在内的.c文件找到。

    2. 将这些.c文件全部编译为.o文件。

       gcc fun1.c -o fun1.o -c -fPIC

       gcc fun2.c -o fun2.o -c -fPIC

    3. 将这些.o文件编译为libxxx.so文件

       gcc -shared -fPIC -o libmy2.so fun1.o fun2.o

    4. 编译程序

       gcc main.c -o main -L . -lmy2

    5. 执行

       ./main

    终端提示错误:

    ./main: error while loading shared libraries: libmy2.so: cannot open shared object file: No such file or directory

    //执行main程序时需要加载共享库:libmy2.so时出现了错误: 因为文件不存在所以,不能打开这个文件。

    解决方案:

    1. 把制作的好的库文件拷贝到/lib中   -> 不推荐

    2. 把该库的路径添加到环境变量  LD_LIBRARY_PATH

       假设库在/home/gec。

       export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/gec   -> 系统就会家目录下找这个"libmy2.so"这个东东!

    3. 重新执行代码

       ./main

      练习7:熟悉交叉编译动态库的制作。

      练习8:把库添加到复杂版的lib目录,修改makefile。

     

  • 相关阅读:
    [笔记].新唐M051型号的后缀解读
    [转载].怎样理解阻抗匹配
    [原创][连载].基于SOPC的简易数码相框 Nios II SBTE部分(软件部分) TFTLCD(控制器为ILI9325)驱动
    [转载].关于STM32的CPU为32位,定时器却为16位的探讨
    [笔记][朝花夕拾][Multisim基础电路范例].第一章 RLC电路,第九节 基尔霍夫电流定律
    [原创].如何解决Nios II SBTE中出现的undefined reference to `xxx'警告
    [原创][连载].基于SOPC的简易数码相框 Nios II SBTE部分(软件部分) ADS7843触摸屏驱动测试
    [笔记][朝花夕拾][Multisim基础电路范例].第一章 RLC电路,第十一节 叠加定理
    [笔记].怎样在IAR中加入编译所需库的头文件?
    [原创][连载].基于SOPC的简易数码相框 Nios II SBTE部分(软件部分) 从SD卡内读取图片文件,然后显示在TFTLCD上
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11215281.html
Copyright © 2020-2023  润新知