vscode编译c项目多文件编译问题
问题:
最近复习数据结构,需要码些代码,vscode在编译多文件时,还是出了些问题,编译单个c是没问题,为什么会这样呢??vscode如何组织多个c文件呢???当然这是本篇最终要解决的问题。但首先还得把自己没搞明白的事,搞清楚。单个文件编译器配置三个文件都是以*.json结尾,那就从json开始。
json文件在vscode作用?
- json的数据结构??
json是一种数据格式,是xml简化格式,现在被python脚本广泛使用。结构上,将所有数据(data)最终可解释为三种类型:
第一种类型是标量scalar,也就是一个单独的字符串string或数字numbers,比如“济南”这个单独的词。
第二种类型是序列sequence,也就是若干个相关的数据按照一定顺序并列在一起,又叫做数组array,或者列表list,比如“山东,山西”。
第三种类型是映射mapping,也就是一个名/值name/value,即数据有一个名称,还有一个与之相对应的值,这又称作散列hash或字典dictionary,比如“省会:济南”。
- json文件组织规则??
规则是很简单的,只有四条。- 并列数据之间用逗号分隔
- 映射用冒号表示
- 并列数据集合(数组)用方括号表示
- 映射的集合(对象)用大括号表示
- 在vscode中的作用??
在vscode中广泛的应用在配置文件中。具体到项目就是.vscode文件夹下的三个文件: - tasks.json(build instructions):主要作用就是构建说明文档
- launch.json(debugger settings):调试器设置
- settings.json:本工作区的设置,user文件下,对应settings.json是全局设置
- c_cpp_properties.json编译器路径和 IntelliSense 设置
如何编译多个.c文件呢?
从编译原理知道:c程序是编译完了之后形成的是.O文件,即目标代码文件,然后由链接器Link来把他们链接在一起,也就是常说的compile只是产生.O文件,而build才是把这些.O文件连接起来形成一个可执行的程序。而编译器同一时间只会处理一个.c文件,所以vscode安装好插件后,并没有自动通过头文件链接项目引用的.c文件。这个就要告诉编译器需要链接哪些文件了。普遍的解决方案就是编写makefile文件。
安装 MinGW时已经安装了make的编译器:mingw32-make.
-
什么是makefile???
这个其实并不陌生,在学linux那段时间已经了解到有这个东西,就是引导编译程序具体编译哪些文件的。 -
make工程管理的作用???
make,本意就是工程管理或叫构建管理工具,通过读入makefile来执行大量的编译任务。通过make命令控制源码的编译。make命令就是cmd下或powershell下执行 mingw32-make,后面可以跟参数,常用的有- -k:作用是在让make命令在发现错误时仍然就执行,而不是在检测到第一个错误时就停止,所以可是使用这个选项在一次操作中发现所有未编译成功的源文件
- -n:作用是让make命令输出将要执行的操作步骤,而不是真正执行这些操作
- -f:作用是告诉make将文件名为filename作为makefile文件。如果未使用这个选项,标准版的make命令将优先在当前命令下查找名称为makefile的文件,如果不存在名称makefile的文件,则开始查找名为Makefile的文件。
-
makefile文件的编写规则与原理???
target : <prerequistus>
[tab] <commands>
target(目标):这个不可省,链接的文件可以file也可以执行文件
prerequistus(前置条件):实现target,所需要的文件,可省
commands(命令):target满足依赖后执行的命令,可省;另外commands前要有一个tab空格。
原理:这就是makefile规则中最核心的内容,“满足依赖条件后,执行编译”。make就是依靠一层套一层的依赖,执行编译命令,最终实现可执行文件的。
到这里基本上,就可以理解如何编译多个.c文件了,问题是如何写一个可以执行的makefile文件呢,前面所说的commands都有哪些呢???详见扩展:
扩展:如何写makefile文件
- makefile文件里都有些什么呢??
a.显式规则
显式规则明确规定要生成的文件,文件的依赖文件,生成的命令。
b.隐晦规则
由于我们的makefile具有自动推导功能,所以就有很多隐晦的规则,有make自动推导出来的,不需要明确写出来
什么是make的自动推导呢??
make看到一个.o文件,它会自动把对应名字的.c文件加到依赖中,比如如果要编译一个what.o文件,其依赖为w.h和what.c文件,则只需指定w.h文件为依赖即可,因为what.c文件会自动加到依赖中。what.o : w.h
c.变量的定义
在makefile中我们要定义一系列变量,变量一般都是字符串,有点像c语言中的宏,当makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
d.文件指示
包括三部分,一个是在makefile中引用另一个makefile,就像c语言的include,另一个是根据某些情况指定makefile的有效部分,就像c语言的预编译#if一样,还有就是定义一个多行的命令
e.注释
makefile中用#来标明注释。并且只有单行注释,而当输入字符#时需要转义加反斜杠,即“#”
-
commands包含的命令有哪些呢???
规则中的命令和操作系统的shell的命令是一致的。每条命令的开头必须以tab键开头,除非命令是紧跟在依赖规则后面的分号后的。 -
makefile的文件名??
用Makefile就可以了。大多数make都支持makefile和Makefile.要指定其他文件名的文件作为makefile需要执行命令-f,如make -f Make.linux
-
引用其他makefile文件???
跟c #include很像。include <filename>
其中可以包含路径和通配符。-include <filename>
前面加了-号,表示无论include过程中出现什么错误,都不要报错继续执行。和其它版本make兼容的相关命令是sinclude,其作用和这一个是一样的。 -
文件查找实现??
makefile只有一个目标,其他目标都是被这个目标所带出来的,所以第一条的目标也就是makefile要完成最终目标。所以不可避免的就会用到一类文件,所以就用到了通配符,make支持三种通配符:* ? … 如下列语句:
clean :
rm -f *.c #把所有扩展名为c的文件都删除
用在变量中,则不太一样。*.c不再是所有的.c文件了,而就是它本身,想要表示所有的.c需要用到make的关键字:wildcard,如:
objects := $(wildcard *.o)
另一种文件查找的方法是使用特殊变量:“VPATH”。如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。格式为:
VPATH=src:../include #make会先找当前目录,找不到再找src目录,最后找 ../include;注意,两个目录间用:分隔开
还有一种是使用关键字 vpath,这种更灵活,有三种使用格式:
vpath <pattern> <directories> #<pattern>:指的是匹配的方法,%.h所有以.h结尾的文件,%表示匹配零个或若干个字符 <directories>:指定在哪个目录下查找,很好理解
vpath <pattern> #清除匹配方法的搜索目录
vpath #清除所有设置的搜索目录
- 清除编译目标???
由于有大量的中间编译文件的存在,所以最后需要清除中间的文件。需要用到“伪目标”标记:.PHONY。格式如下:
.PHONY: clean #不管有没有clean这个文件,都要运行下面的目标,使用 make clean来执行
clean:
rm *.o temp
#伪目标也可以被当作依赖,如下面的代码
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff #伪目标被当成依赖
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
东西确实有些多,目前掌握的就可以简单写个了。如遇到的顺序表依赖,体验一下:
阶段体验:编写简单makefile文件
SeqListTest:SeqListTest.o seq_list.o
gcc SeqListTest.o seq_list.o -o main
SeqListTest.o:seq_list.c
gcc -c SeqListTest.c
seq_list.o:seq_list.c
gcc -c seq_list.c
.PHONY:clean
#linux 下用 rm -rf *.o main
clean:
@echo "=======clean project========="
del *.o
@echo "=======clean completed========="
将此文体放到项目目录下,在vscode TERMINAL下,输入mingw32-make,即可以完成编译。输入./main可以运行。输入mingw32-make clean可清除产生的中间文件,如下图所示:
总结
至此,基本可以实现文件的编译了,但缺点是没法单步调试,至少对数据结构测试程序来说,够用了。
想要单步调试,有种比较弱智的办法,在构建说明文档中也就是task.json中-g -o之间加入要测试的.c文件,如下图所示:
同时,编写makefile文件还有许多东西要看,文档真的是不能太长。先到这里,接下来,makefile编写还要继续学习详见:makefile深入之编写规则.