【嵌入式开发工具】Makefile和Cmake
但是加入头文件中有一些宏定义,例如。判断这个目标是否存在,若这个目标已经存在了,并且它的依赖文件即main.o和primejudge,o文件的时间戳比div2prime更老,则不再进行连接生成文件。如果对其进行修改,改成2,再执行make的话,make并不会编译连接新的文件,这是因为没有把这个.h的头文件加入到依赖关系中来,Makefile就无法根据其时间戳的规则来决定是否要重新编译连接。可以在命
工具配置
首先,方便代码编辑,安装sublime和vim,其中安装sublime过程见下。
https://blog.csdn.net/yunna520/article/details/114021153
注意需要对软件屏蔽更新提示
然后就可以输入 sl <filename> 进行文件编辑
了解C语言的编译连接过程
C语言的源程序进行预处理(包含头文件,展开宏,去除无效代码)从.c/.cpp文件得到 .i文件。
预处理之后,进行编译,得到 .s文件。
接着进行汇编,把汇编语言转换为机器码,得到 .o文件。
最后进行连接,得到目标可执行文件。
注:有时候将前面三步都统称为汇编。
gcc/g++的基本语法与其作用
首先是对应之前四步的命令
指令结果 | 命令行选项 |
---|---|
得到预处理结果(.i文件) | -E |
得到编译结果(.s文件) | -S |
得到汇编结果(.o文件) | -c |
得到最终结果 | 空缺 |
除此之外,-o <tragetname> 即-o后跟着的时名称用于指定生成目标的名字。
而指令中不在-o后面紧跟着的文件名,就是源文件的名字,即编译的操作对象。
gcc -E -o hello.i hello.c
g++ -c -o hello.o hello.cpp
此外,还有一些可选的命令行选项如下,也可以添加到gcc/g++指令中来
命令行选项 | 功能 |
---|---|
-MD -MF .c.d | 将所编译的文件依赖写入.c.d文件中,.开头表示为隐藏文件 |
-Wall | 发出所有警告 |
-Werror | 所有警告都当做错误 |
-I dir | 指定头文件的搜索范围,指定后可以将头文件"header"改成<header> |
Makefile的规则与使用
一种脚本,在文件夹下创建Makefile文件,然后在该文件夹下使用命令make就可以实现相应的编译功能。
在待创建生成目标的文件夹hw1中有如下一系列的文件。
其中Makefile文件采用以下命令创建
vim Makefile
以“偶数分解为两个质数的和”项目为例,其中包含三个文件,一个头文件,两个cpp文件。primejudge.cpp主要用来判断输入是否为素数。然后在主程序中对输入的偶数进行因子拆分,对每次的两个因子都判断一下是不是素数,如果是,就打印出结果。程序代码如下
main.cpp
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<iostream>
#include"primejudge.h"
using namespace std;
int main(){
int a,i;
cin>>a;
for(i=2;i<=a/2;i++){
if(IsPrime(i)&&IsPrime(a-i)){
printf("a = %d + %d\n",i,a-i);
}
}
return 0;
}
primejudge.cpp
#include<math.h>
int IsPrime(int x)
{
int ret=1; //ret含义与之前的变量IsPrime相同
if(x==1||(x!=2&&x%2==0))
ret=0;
for(int i=3;i<sqrt(x);i+=2) //循环到sqrt(x)
{
if(x%i==0)
{
ret=0;
break;
}
}
return ret;
}
primejudge.h
int IsPrime(int x);
通过vim创建Makefile脚本文件,用于编译连接
通过sublime打开Makefile文件,如下图所示
Makefile有一些基本语法规则,其中最主要的部分是块,每一块的构成如下。
[目标]:[依赖]
[TAB] [gcc或者g++命令]
[TAB]其他指令…
Makefile的执行方式为:首先根据首个块的生成目标,即div2prime判断这个目标是否存在,若这个目标已经存在了,并且它的依赖文件即main.o和primejudge,o文件的时间戳比div2prime更老,则不再进行连接生成文件。否则,执行第一个目标生成块的指令(即3:到4:)
在执行的过程中,发现需要用到.o,再往下去搜寻.o的依赖关系。如果也满足时间更新条件,则执行第二个和第三个目标生成块。
执行第二第三个目标生产块时发现其依赖文件都是已经存在的并且没有进一步的依赖了。
总结一下,其编译连接过程就是main.cpp和primejudge.cpp文件分别先编译生成.o文件,接着两个.o文件再连接生成目标文件div2prime.
此外,在文件中可以观察到一些特殊的表示,阐述如下
表示 | 描述 |
---|---|
:= | 即时赋值 |
$(<var name>) | 对变量取值 |
$@ | 代表某一块中的目标 |
$< | 代表某一块中的第一个依赖 |
$^ | 代表某一块中的所有依赖 |
% | 通配符 |
.PHONY是假想目标,用来指定编译时的一些操作 .PHONY之后跟的是需要定义的假想目标名,可以是多个。例如下例中的clean是用来删除编译时产生的.o文件
.PHONY:clean
clean:
rm *.o
写完Makefile文件后,在vim中输入 :wq 进行保存,或者在sublime中ctrl+S直接保存
然后cd进入存放Makefile的文件夹下,输入make
可以在命令行中观察到哪些块中的命令被执行了,也可以看到其执行顺序,进一步证实了上述根据依赖关系的Make执行顺序。
然后使用make clean命令删除中间文件.o,其中*代表命令行中的通配符。最后就只有源文件、Makefile文件和生成的可执行文件div2prime
在本例中我们发现所依赖的文件并不多,大多数情况下可以自己手写,但是在处理大批量文件时手动添加就比较困难。此外我们还注意到一个问题,当依赖文件中不包含.h文件时。看上去可能在编译连接时没有太大差别。但是加入头文件中有一些宏定义,例如
#difine M 3
如果对其进行修改,改成2,再执行make的话,make并不会编译连接新的文件,这是因为没有把这个.h的头文件加入到依赖关系中来,Makefile就无法根据其时间戳的规则来决定是否要重新编译连接。
对于以上两个问题,可以使用如下的Makefile模板,自动将头文件加入到依赖中去。
#编译之后,连接之前的目标名称指定
OBJS=main.o bin_dec.o dec_bin.o bin_hex.o hex_bin.o bin_oct.o oct_bin.o oct_dec.o dec_oct.o oct_hex.o hex_oct.o dec_hex.o hex_dec.o input.o display.o
#指定依赖文件,其名字为对.o文件按patsubst方法进行通配符替代
#例如main.o文件对应的依赖文件时.main.o.d,这些依赖文件以通配符的形式表示
dep_files:=$(patsubst %,.%.d,$(OBJS))
#通过wildcard函数判断一下上述dep_files中的文件是否存在,只保留存在部分,将通配符转换为文件表
dep_files:=$(wildcard $(dep_files))
#指定目标名,编译器和编译参数
CFLAGS=-g -Werror
TARGET=numconverse
CC=g++
$(TARGET):$(OBJS)
$(CC) $(CFLAGS) -o $@ $^
#在第一个目标后进行包含
ifneq ($(dep_files), )
include $(dep_files)
endif
%.o:%.cpp
$(CC) $(CFLAGS) -c -o $@ $< -MD -MF .$@.d
#自动将目标的依赖关系写入.d文件
.PHONY:clean distclean
clean:
rm *.o
distclean:
rm .*.d
#删除依赖
使用结果如下
可以看到,在该过程中生成了.开头.d结尾的依赖信息文件,这是通过-MD -MF指令来实现的。要想查看必须使用ls -a命令来显示隐藏文件。输入make clean删除.o文件,输入make distclean以删除.d文件。
CMake的规则和使用
主要内容来自以下视频
https://www.bilibili.com/video/BV1vR4y1u77h/?spm_id_from=333.337.search-card.all.click&vd_source=be3fd08fda4ebae5d312804342bfa60d
CMake是一种高级编译配置工具,主要依赖于脚本CMakeLists.txt文件
最简单的一种使用方法如下
在待编译连接的文件夹中创建一个CMakeLists.txt,然后写入如下代码
#指定工程名称
PROJECT(DIV2PRIME)
#声明目标文件和依赖文件
ADD_EXECUTABLE(div2prime main.cpp primejudge.cpp)
首先使用cmake指令生成Makefile
然后再输入make指令就可以构建出可执行目标文件
我们发现,cmake过程中会生成一些中间文件,因此需要结合cmake进行一些工程性的文件夹划分。
更多推荐
所有评论(0)