简单来讲,源代码文件一般由程序员使用高级计算机语言(C、Go、Java、C++等)进行编写,不同的语言有不同的文件后缀名,但是他们的共同点就是他们都是程序的源文件
,这些文件(一个或多个)需要进行编译和链接后生成可直接运行的程序,才能真正的被运行。
当然有些编程语言并没有这种机制,这种语言一般为解释执行型的语言,比如Java,以及解析型的脚本语言,比如Python。拥有这种机制的语言一般为编译性语言,比如经典的C语言。
那么问题来了,程序的源文件是如何编译和链接的呢?
我们那最经典的C语言举例子,其实上图针对C语言的编译过程描述的并不是很全面,只是一个大概的过程,C语言较为完整的编译过程是C源文件->(预编译)->中间文件->(编译)->中间文件->(链接)->可执行文件
并且其中还依赖了众多的库文件。
以上并不是重点,我们都知道C语言是具有很好的移植性,并且能够在绝大多数 *** 作系统下进行编译,可谓是一次编写,处处编译
,但是任何语言都是由编译器的,不同 *** 作系统下C语言源文件编译成的可执行文件不同(比如在Windows下编译成xx.exe,而在Linux下编译成xx.sh或xx,所以我们可以逆向推导出,C语言的编译器在不同的 *** 作系统中具有不同的种类
,但是所有的编译器种类都要符合一个特点的规范,如下:
C语言规范:ANSI C
ANSI C是由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准。ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型) 并支持多国字符集 (包括备受争议的三字符序列)。 ANSI C 标准同时规定了 C 运行期库例程的标准。
流行的C语言编译器有以下几种:
GNU Compiler Collection 或称 GCCMicrosoft C 或称 MS CBorland Turbo C 或称 Turbo C 1.4 小结通过以上内容我们知道了一个main.c程序是如何变为可执行程序的简单过程,这个过程看起来可以说并不简单,但是为什么我们测试程序的时候完全无感知呢?
答案就是——IDE(Integrated Development Environment ),中文名称:集成开发环境,正是因为有IDE我们才不需要每次启动程序时都进行重复流程的 *** 作,而是他已经帮我们搞好了一套流程用来对源代码进行编译和运行。
所以本文的重点——Makefile也是如此,因为我们的项目代码最终都是要放到服务器上去运行的,但是服务器上并没有我们个人电脑上存在非常好用的IDE,最大的原因就是耗费服务器的资源,因此我们使用Makefile来进行对程序的自动化编译,下面步入正题。
2 Makefile快速入门 2.1 核心语法target ... : prerequisites ...
command
...
...
解释:
target:
可以是一个object file(目标文件),也可以是一个执行文件,还可以是一个标签(label)。对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites:
生成该target所依赖的文件和/或target
command:
该target要执行的命令(任意的shell命令)
2.2 编译和运行单个源代码文件实战 2.2.1 Linux下gcc环境检测 gcc -v
2.2.2 编写main.c源代码文件
#include
int main(){
printf("Hello Makefile ~\n");
return 0;
}
2.2.3 编写Makefile文件
main: main.o
gcc -o main main.o
main.o: main.c
gcc -c main.c
clean:
rm main.o
注意:
Makefile命令的开头一定要使用Tab键而不是空格键,否则就会报错。
2.2.4 运行Makefile
自定义运行:
我们也可以在Makefile中使用shell命令,举个例子:
hello:
echo Hello Makefile
@echo Hello Makefile Too
@ls -al
运行结果:
由此我们知道了,在shell命令前加@的不会输出命令的本身,反之则输出命令的本身。
最早先的一个例子中,我们提到过一个“clean”的目标,这是一个“伪目标”,
clean:
rm *.o temp
更为稳健的做法是:
.PHONY : clean
clean :
- rm *.o temp
前面说过, .PHONY
表示 clean
是一个“伪目标”。而在 rm
命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然, clean
的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。
.PHONY : all
声明了“all”这个目标为“伪目标”。(注:这里的显式“.PHONY : all” 不写的话一般情况也可以正确的执行,这样make可通过隐式规则推导出, “all” 是一个伪目标,执行make不会生成“all”文件,而执行后面的多个目标。建议:显式写出是一个好习惯。)
小结:因为伪目标总是会被执行,所以其依赖总是会被决议。通过这种方式,可以达到同时执行所有依赖项的目的。
(1)变量的声明:
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 $
符号,但最好用小括号 ()
或是大括号 {}
把变量给包括起来。如果你要使用真实的 $
字符,那么你需要用 $$
来表示。
(2)演示
hello.txt:
Hello~
Makefile:
i=10
txt=./hello.txt
run:
@echo $(i)
@cat $(txt)
执行:
(3)扩展
变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 :
、 #
、 =
或是空字符(空格、回车等)。变量是大小写敏感的,“foo”、“Foo”和“FOO”是三个不同的变量名。传统的Makefile的变量名是全大写的命名方式,但我推荐使用大小写搭配的变量名,如:MakeFlags。这样可以避免和系统的变量冲突,而发生意外的事情。
为什么要说一下Go中的常用命令呢,因为Go项目中Makefile的使用较为简单,大多都是对Go命令的一些执行 *** 作,因此我们首先要熟知Go语言中的常用命令,才能更好的编写Makefile。
Go一般命令:
build: 编译包和依赖。clean: 移除当前源码包里面编译生成的文件。doc: 显示包或者符号的文档。env: 打印go的环境信息,查看GOPATH,GOROOT环境变量都可以使用该命令查看。bug: 启动错误报告。fix: 运行go tool fix。fmt: 运行gofmt进行格式化。generate: 从processing source生成go文件。get: 下载并安装包和依赖。install: 编译并安装包和依赖。list: 列出包。run: 编译并运行go程序。test: 自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件。tool: 运行go提供的工具。version: 显示当前环境安装go的版本。vet: 运行go tool vet。Go mod命令:
go mod init [项目名称]
:初始化工程项目。go mod graph
:输入工程当前所有的依赖。go mod download
: 使用此命令来下载指定的模块到本地。go mod tidy
:就是把不需要的依赖给删除掉。go mod verify
:验证mod里依赖是否正确,同时也会验证go源代码其它依赖的内容。go mod why
:可以展示及指定依赖关系。比如代码中有个依赖包,但是并不知道这个包的依赖关系,这时候就可以使用这个命令查看。
3.3 使用实践
项目目录:
Makefile文件内容:
.PHONY:tidy
tidy:
go mod tidy
.PHONY:build
build:
go build ./cmd/main.go
.PHONY:run
run:
@./main
执行:
Makefile文件是在项目构建这非常重要的一环,最初认识它是在GitHub上的开源项目中,发现每个项目都有一个Makefile,而且语法看不懂,于是就专门抽时间学习一下,当然由于本人初次学习,本文涵盖的知识点有限,更详细的学习请看 【这篇文章】,在本文这如有错误和不足,感谢读者指出!
参考链接:
https://github.com/seisman/how-to-write-makefile
https://seisman.github.io/how-to-write-makefile/
https://www.cnblogs.com/songgj/p/13167251.html
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)