一、工程管理文件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. 举实例。

1)makefile的helloworld版本1。

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

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

执行make,结果:

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

helloworld

2)makefile的helloworld版本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。

转载于:https://www.cnblogs.com/zjlbk/p/11215281.html

2019年7月19日星期五(交叉编译工具)相关推荐

  1. 2019年10月19日星期六

    2019年10月19日星期六 师院上课 颗粒归仓 纪老师教会我们学习了东西要及时总结,颗粒归仓的意思就是收获的粮食要放到仓库中(储存),知识也一样,学会以后需要把知识做收集总结起来. 今天我们进行了一 ...

  2. 2016年8月19日 星期五 --出埃及记 Exodus 16:20

    2016年8月19日 星期五 --出埃及记 Exodus 16:20 However, some of them paid no attention to Moses; they kept part ...

  3. 【图片新闻】2019年6月19日伊朗击落一架美军MQ-4 Triton无人侦察机

    伊朗消息人士和五角大楼称,2019年6月19日,伊朗击落了一架MQ-4 Triton侦察机.伊朗声称这架无人机正在伊朗领空飞行,但美国政府否认这一说法. MQ-4 Triton是一种无人驾驶.非致命的 ...

  4. 2019年6月19日 星期三 今日计划

    2019年6月19日 星期三 今日计划 学习XMLHttpRequest对象

  5. 【蜕变之路】第29天 CAST和CONVERT的区别(2019年3月19日)

    Hello,大家好!我是程序员阿飞!今天从北京来了位项目经理,很热情,中午的时候,就带我们出去吃了一顿,很感谢领导.我们主要学习一下SQL中 CAST和CONVERT的区别. 1.作用 两者都是用来获 ...

  6. 2019年6月14日 星期五(吴翰清web安全)

    到底是买苹果笔记本还是thinkpad到今天为止还没有一个合适的定论,暂时先这样吧,暂时先不买吧 安全工程师的核心竞争力不在于他能拥有多少个 0day,掌握多少种安全技术,而是在于他对安全理解的深度, ...

  7. 2019年6月19日Jerry Wang的SAP SAP Cloud Connector练习

    https://webidetesting9070069-i042416trial.dispatcher.hanatrial.ondemand.com/ag3-backend/sap/opu/odat ...

  8. 2019年8月23日 星期五(韩天峰的swoole)

    Swoole:面向生产环境的 PHP 异步网络通信引擎 使 PHP 开发人员可以编写高性能的异步并发 TCP.UDP.Unix Socket.HTTP,WebSocket 服务. Swoole 可以广 ...

  9. 2019年9月19日好货十元内精选包邮

    复制这条信息,$H14ZYNhfI2g$,到[手机淘宝]即可查看 复制这条信息,$LmqTYNhlCUs$,到[手机淘宝]即可查看 复制这条信息,$yHt0YNhIbdT$,到[手机淘宝]即可查看 复 ...

  10. Win10 收件箱添加QQ邮箱(2019年5月19日)

    Emmm弄的时候没截图,就语言描述吧,非常简单. 登录到网页端QQ邮箱.点我登录 登录之后,界面上端的Logo右边有个"设置"(字有点小).点它 邮箱设置下面有一堆标签,点击&qu ...

最新文章

  1. HttpSession
  2. mysql执行文件脚本文件_MySQL执行外部sql脚本文件的命令
  3. java获取文件地址吗_java获取文件所在服务器位置路径
  4. SpringMVC中JSON处理
  5. 内部办公网与IDC机房的GRE隧道配置实践
  6. 使用PHP来简单的创建一个RPC服务
  7. H3C防火墙——回环流量问题(内网终端通过外网IP访问内部服务器)
  8. 深度强化学习之近端策略优化(Proximal Policy Optimization)
  9. 防范勒索软件的七大举措
  10. C++/MFC 面试题(六、逻辑题)
  11. 常见前端九十道面试题及答案-韩烨
  12. http请求报错context deadline exceeded (Client.Timeout exceeded while awaiting headers)
  13. python:实现9×9二维数组数独算法(附完整源码)
  14. sqlserver2012 查询远程数据库
  15. 微信公众平台测试号登录入口地址
  16. 修复iPhone X 开机卡白苹果导致无法开机的问题
  17. 新建Maven工程乱七八糟全都失效了
  18. linux tar xzmf,有人吗??IT大神求助
  19. 多媒体-百科知识(转百度百科)
  20. 【问题解决】外部环境分析

热门文章

  1. asp.net控件开发基础二
  2. 【R包】2018年数据科学前20名R包
  3. 手把手教你使用R语言做评分卡模型
  4. 【git】常用命令行收集
  5. Python语言编写有趣练习题!
  6. python学习笔记之hashlib
  7. Spring Security学习
  8. 使用 requests 进行身份认证
  9. jmeter笔记02
  10. libc.so.6: version 'GLIBC_2.14' not found报错提示的解决方案