GCC & Maker
All we did must depend on compiler, and then What we did can run on machine.
What does compiler do behind our programm?
- Reference to :http://gcc.gnu.org/
- http://www.cnblogs.com/lingqing/archive/2012/08/04/2623241.html
: -c GCC 把给它的文件编译成目标文件,用源文件的文件名命名
e.g
gcc -c Zhanglong.c longzhang.c
生成如下:
Zhanglong.o longzhang.o :如果给的是一列目标文件,则会连接成可执行文件
e.g
gcc zhanglong.o longzhang.o
生成如下:
a.out
e.g.
gcc -o zhanglong_execfile *.o
生成如下:
zhanglong_execfile :如果改变了一个HEADR文档,需要重新编译所有文档
e.g.
gcc -c zhanglong1.c zhanglong2.c zhanglong3.c *.c
生成如下:
zhanglong1.o zhanglong2.o zhanglong3.o *.o
继续:
gcc -o ZhangLong *.o
生成如下:
ZhangLong :makefile 主要包含一系列规则
:.............
(tab)<command>
(tab)<command>
.
.
.
e.g.
ZLProg:long.o zhang.o
gcc long.o zhang.o ZLProg long.o: long.c long.h zhang.h
gcc -c long.c -o long.o zhang.o:zhang.c zhang.h
gcc -c zhang.c -o zhang.o
注释:
ZLProg 是主要目标,一个它需要保 证其总是最新的最终目标; 给出的规则说明只要文件‘myprog’
比文件‘foo.o’或‘bar.o’中的任何一个旧,下一行的命令将 会被执行。
但是,在检查文件 foo.o 和 bar.o 的时间戳之前,它会往下查 找那些把 foo.o 或
bar.o 做为目标文件的规则。它找到的关于 foo.o 的规则,该文件的依靠文件是 foo.c,
foo.h 和 bar.h 。
它从下面再找不到生成这些依靠文件的规则,它就开始检查磁碟
上这些依靠文件的时间戳。如果这些文件中任何一个的时间戳比 foo.o 的新,命令 'gcc
-o foo.o foo.c' 将会执行,从而更新 文件 foo.o 。
接下来对文件 bar.o 做类似的检查,依靠文件在这里是文件 bar.c 和 bar.h 。
现在, make 回到‘myprog’的规则。如果刚才两个规则中的任 一个被执行,myprog
就需要重建(因为其中一个 .o 档就会比 ‘myprog’新),因此连接命令将被执行。
:编写 make 规则 (Rules) 最明显的(也是最简单的)编写规则的方法是一个一个的查
看源码文件,把它们的目标文件做为目的,而C源码文件和被它 #include 的 header
档做为依靠文件。但是你也要把其它被这些 header 档 #include 的 header
档也列为依靠文件,还有那些被
包括的文件所包括的文件……然后你会发现要对越来越多的文件进行管理。 使用 gcc 的时候,用 -M
开关,它会为每一个你给它的C文件输出一个规则,把目标文件
做为目的,而这个C文件和所有应该被 #include 的 header 文
件将做为依靠文件。注意这个规则会加入所有 header 文件,包 括被角括号(`<',
`>')和双引号(`"')所包围的文件。其实我们可以 相当肯定系统 header 档(比如
stdio.h, stdlib.h 等等)不会 被我们更改,如果你用 -MM 来代替 -M 传递给
gcc,那些用角括 号包围的 header 档将不会被包括。(这会节省一些编译时间)由 gcc
输出的规则不会含有命令部分;你可以自己写入你的命令 或者什么也不写,而让 make
使用它的隐含的规则
e.g.
file: Main.c
#include <stdlib.h>
#include "zhang.h"
#include "long.h" int main()
{
printf("Hello wold!\n");
}
编译:
gcc -M Main.c
生成如下:
Main.c:stdlib.h zhang.h long.h :Makefile 变量
i) 贮存一个文件名列表。在上面的例子里,生成可执行文件的
规则包含一些目标文件名做为依靠。在这个规则的命令行 里同样的那些文件被输送给 gcc
做为命令参数。如果在这 里使用一个变数来贮存所有的目标文件名,加入新的目标
文件会变的简单而且较不易出错。 ii) 贮存可执行文件名。如果你的项目被用在一个非 gcc 的系
统里,或者如果你想使用一个不同的编译器,你必须将所
有使用编译器的地方改成用新的编译器名。但是如果使用一
个变量来代替编译器名,那么你只需要改变一个地方,其
它所有地方的命令名就都改变了。 iii) 贮存编译器旗标。假设你想给你所有的编译命令传递一组 相同的选项(例如 -Wall
-O -g);如果你把这组选项存 入一个变量,那么你可以把这个变量放在所有呼叫编译器
的地方。而当你要改变选项的时候,你只需在一个地方改
变这个变量的内容。要设定一个变量,你只要在一行的开始写下这个变量的名字,后
面跟一个 = 号,后面跟你要设定的这个变量的值。以后你要引用 这个变量,写一个 $
符号,后面是围在括号里的变量名。比如在 下面,我们把前面的 makefile
利用变量重写一遍: === makefile 开始 ===
OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g myprog : $(OBJS)
$(CC) $(OBJS) -o myprog foo.o : foo.c foo.h bar.h
$(CC) $(CFLAGS) -c foo.c -o foo.o bar.o : bar.c bar.h
$(CC) $(CFLAGS) -c bar.c -o bar.o
=== makefile 结束 === 还有一些设定好的内部变量,它们根据每一个规则内容定义。三个 比较有用的变量是
$@,$< 和 $^ (这些变量不需要括号括住)。
$@ 扩展成当前规则的目的文件名,
$< 扩展成依靠列表中的第 一个依靠文件,
而 $^ 扩展成整个依靠的列表 === makefile 开始 ===
OBJS = foo.o bar.o
CC = gcc
CFLAGS = -Wall -O -g
myprog:$(OBJS)
$(CC) $^ -o $@ foo.o:foo.c foo.h bar.h
$(CC) $(CFLAGS) -c $< -o $@ bar.o:bar.c bar.h
$(CC) $(CFLAGS) -c $< -o $@
=== makefile 结束====== :
如果你把生成 foo.o 和 bar.o 的命令从它们的规则中删除, make
将会查找它的隐含规则,然后会找到一个适当的命令。它的命令会
使用一些变量,因此你可以按照你的想法来设定它:它使用变量 CC
做为编译器(象我们在前面的例子),并且传递变量 CFLAGS (给 C 编译器,C++
编译器用 CXXFLAGS ),CPPFLAGS ( C 预 处理器旗标), TARGET_ARCH
(现在不用考虑这个),然后它加 入旗标 '-c' ,后面跟变量 $<
(第一个依靠名),然后是旗 标 '-o' 跟变量 $@
(目的文件名)。一个C编译的具体命令将 会是: $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ :
在 GNU Make 里有一个叫 'wildcard' 的函
数,它有一个参数,功能是展开成一列所有符合由其参数描述的文
件名,文件间以空格间隔。你可以像下面所示使用这个命令: SOURCES = $(wildcard *.c) 这行会产生一个所有以 '.c' 结尾的文件的列表,然后存入变量 SOURCES
里。当然你不需要一定要把结果存入一个变量。 :
另一个有用的函数是 patsubst ( patten substitude, 匹配替
换的缩写)函数。它需要3个参数——第一个是一个需要匹配的
式样,第二个表示用什么来替换它,第三个是一个需要被处理的
由空格分隔的字列。例如,处理那个经过上面定义后的变量,
OBJS = $(patsubst %.c,%.o,$(SOURCES))
这行将处理所有在 SOURCES 字列中的字(一列文件名),如果它的 结尾是 '.c'
,就用 '.o' 把 '.c' 取代。注意这里的 % 符号将匹
配一个或多个字符,而它每次所匹配的字串叫做一个‘柄’(stem) 。 在第二个参数里, %
被解读成用第一参数所匹配的那个柄。
: === makefile 开始 === ######################################
#
# Generic makefile
#
# by George Foot
# email: george.foot@merton.ox.ac.uk
#
# Copyright (c) George Foot
# All rights reserved.
# 保留所有版权
#
# No warranty, no liability;
# you use this at your own risk.
# 没保险,不负责
# 你要用这个,你自己担风险
#
# You are free to modify and
# distribute this without giving
# credit to the original author.
# 你可以随便更改和散发这个文件
# 而不需要给原作者什么荣誉。
# (你好意思?)
#
###################################### ### Customising
# 用户设定
#
# Adjust the following if necessary; EXECUTABLE is the target
# executable's filename, and LIBS is a list of libraries to link in
# (e.g. alleg, stdcx, iostr, etc). You can override these on make's
# command line of course, if you prefer to do it that way.
#
# 如果需要,调整下面的东西。 EXECUTABLE 是目标的可执行文件名, LIBS
# 是一个需要连接的程序包列表(例如 alleg, stdcx, iostr 等等)。当然你
# 可以在 make 的命令行覆盖它们,你愿意就没问题。
# EXECUTABLE := mushroom.exe
LIBS := alleg # Now alter any implicit rules' variables if you like, e.g.:
#
# 现在来改变任何你想改动的隐含规则中的变量,例如 CFLAGS := -g -Wall -O3 -m486
CXXFLAGS := $(CFLAGS) # The next bit checks to see whether rm is in your djgpp bin
# directory; if not it uses del instead, but this can cause (harmless)
# `File not found' error messages. If you are not using DOS at all,
# set the variable to something which will unquestioningly remove
# files.
#
# 下面先检查你的 djgpp 命令目录下有没有 rm 命令,如果没有,我们使用
# del 命令来代替,但有可能给我们 'File not found' 这个错误信息,这没
# 什么大碍。如果你不是用 DOS ,把它设定成一个删文件而不废话的命令。
# (其实这一步在 UNIX 类的系统上是多余的,只是方便 DOS 用户。 UNIX
# 用户可以删除这5行命令。) ifneq ($(wildcard $(DJDIR)/bin/rm.exe),)
RM-F := rm -f
else
RM-F := del
endif # You shouldn't need to change anything below this point.
#
# 从这里开始,你应该不需要改动任何东西。(我是不太相信,太NB了!) SOURCE := $(wildcard *.c) $(wildcard *.cc)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(SOURCE)))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SOURCES := $(wildcard $(patsubst %.d,%.c,$(MISSING_DEPS)) \
$(patsubst %.d,%.cc,$(MISSING_DEPS)))
CPPFLAGS += -MD .PHONY : everything deps objs clean veryclean rebuild everything : $(EXECUTABLE) deps : $(DEPS) objs : $(OBJS) clean :
@$(RM-F) *.o
@$(RM-F) *.d veryclean: clean
@$(RM-F) $(EXECUTABLE) rebuild: veryclean everything ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM-F) $(patsubst %.d,%.o,$@)
endif -include $(DEPS) $(EXECUTABLE) : $(OBJS)
gcc -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS)) === makefile 结束 === 有几个地方值得解释一下的。首先,我在定义大部分变量的时候使 用的是 := 而不是 =
符号。它的作用是立即把定义中参考到的函 数和变量都展开了。如果使用 =
的话,函数和变量参考会留在那
儿,就是说改变一个变量的值会导致其它变量的值也被改变。例 如: A = foo
B = $(A)
# 现在 B 是 $(A) ,而 $(A) 是 'foo' 。
A = bar
# 现在 B 仍然是 $(A) ,但它的值已随着变成 'bar' 了。
B := $(A)
# 现在 B 的值是 'bar' 。
A = foo
# B 的值仍然是 'bar' 。 make 会忽略在 # 符号后面直到那一行结束的所有文字。 ifneg...else...endif 系统是 makefile 里让某一部分码有条件的 失效/有效的工具。
ifeq 使用两个参数,如果它们相同,它把直 到 else (或者 endif ,如果没有 else
的话)的一段码加进 makefile 里;如果不同,把 else 到 endif 间的一段码加入
makefile (如果有 else )。 ifneq 的用法刚好相反。 'filter-out' 函数使用两个用空格分开的列表,它把第二列表中所
有的存在于第一列表中的项目删除。我用它来处理 DEPS 列表,把所
有已经存在的项目都删除,而只保留缺少的那些。 我前面说过, CPPFLAGS 存有用于隐含规则中传给预处理器的一些 旗标。而 -MD 开关类似
-M 开关,但是从源码文件 .c 或 .cc 中 形成的文件名是使用后缀 .d
的(这就解释了我形成 DEPS 变量的 步骤)。DEPS 里提到的文件后来用 '-include'
加进了 makefile 里,它隐藏了所有因文件不存在而产生的错误信息。 如果任何依靠文件不存在, makefile 会把相应的 .o 文件从磁碟 上删除,从而使得 make
重建它。因为 CPPFLAGS 指定了 -MD , 它的 .d 文件也被重新产生。 最后, 'addprefix' 函数把第二个参数列表的每一项前缀上第一 个参数值。 这个 makefile 的那些目的是(这些目的可以传给 make 的命令行 来直接选用): everything:(预设) 更新主要的可执行程序,并且为每一个 源码文件生成或更新一个
'.d' 文件和一个 '.o' 文件。 deps: 只是为每一个源码程序产生或更新一个 '.d' 文件。 objs: 为每一个源码程序生成或更新 '.d' 文件和目标文件。 clean: 删除所有中介/依靠文件( *.d 和 *.o )。 veryclean: 做 `clean' 和删除可执行文件。 rebuild: 先做 `veryclean' 然后 `everything' ;既完全重建。 除了预设的 everything 以外,这里头只有 clean , veryclean , 和 rebuild
对用户是有意义的。 我还没有发现当给出一个源码文件的目录,这个 makefile 会失败的
情况,除非依靠文件被弄乱。如果这种弄乱的情况发生了,只要输入 `make clean'
,所有的目标文件和依靠文件会被删除,问题就应该
被解决了。当然,最好不要把它们弄乱。如果你发现在某种情况下这 个 makefile
文件不能完成它的工作,请告诉我,我会把它整好的。 总结
~~~~~~~~~~~~~~~ 我希望这篇文章足够详细的解释了多文件项目是怎么运作的,也说明了
怎样安全而合理的使用它。到此,你应该可以轻松的利用 GNU Make 工
具来管理小型的项目,如果你完全理解了后面几个部分的话,这些对于
你来说应该没什么困难。 GNU Make 是一件强大的工具,虽然它主要是用来建立程序,它还有很多
别的用处。如果想要知道更多有关这个工具的知识,它的句法,函数,
和许多别的特点,你应该参看它的参考文件 (info pages, 别的 GNU
工具也一样,看它们的 info pages. )。
GCC & Maker的更多相关文章
- gcc,make,cmake
1.gcc是GNU Compiler Collection(就是GNU编译器套件),也可以简单认为是编译器,它可以编译很多种编程语言(括C.C++.Objective-C.Fortran.Java等等 ...
- 关于gcc、make和CMake的区别
CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多.CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要 ...
- VSCode调试go语言出现:exec: "gcc": executable file not found in %PATH%
1.问题描述 由于安装VS15 Preview 5,搞的系统由重新安装一次:在用vscdoe编译go语言时,出现以下问题: # odbcexec: "gcc": executabl ...
- GCC学习(1)之MinGW使用
GCC学习(1)之MinGW使用 因为后续打算分享一些有关GCC的使用心得的文章,就把此篇当作一个小预热,依此来了解下使用GNU工具链(gcc.gdb.make等)在脱离IDE的情况下如何开发以及涉及 ...
- 使用 GCC 和 GNU Binutils 编写能在 x86 实模式运行的 16 位代码
不可否认,这次的标题有点长.之所以把标题写得这么详细,主要是为了搜索引擎能够准确地把确实需要了解 GCC 生成 16 位实模式代码方法的朋友带到我的博客.先说一下背景,编写能在 x86 实模式下运行的 ...
- [异常解决] How to build a gcc toolchain for nRF51 on linux (very detailed!!!)
1.Install gcc-arm-none-eabi https://devzone.nordicsemi.com/tutorials/7/This link shows that developm ...
- CentOS 6.6 升级GCC G++ (当前最新版本为v6.1.0) (完整)
---恢复内容开始--- CentOS 6.6 升级GCC G++ (当前最新GCC/G++版本为v6.1.0) 没有便捷方式, yum update.... yum install 或者 添加y ...
- GCC 预处理、编译、汇编、链接..
1简介 GCC 的意思也只是 GNU C Compiler 而已.经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言:它现在还支持 Ada 语言.C++ 语言.Java 语言.Objective ...
- 用gcc进行程序的编译
在Linux系统上,一个档案能不能被执行看的是有没有可执行的那个权限(x),不过,Linux系统上真正认识的可执行文件其实是二进制文件(binary program),例如/usr/bin/passw ...
随机推荐
- IIS反向代理
- JEECG入门
姓名:陈中娇 班级:软件151 1.准备: 下载Jdk1.6+.myeclipse.tomcat6.0.MySQL数据库.jeecg-framework压缩包 2.安装:①.安装jdk,配置好环 ...
- window下安装git与git使用
有的eclipse已经自带了Git了,就不用安装了.如果,想重新安装,可以先卸载GIT,卸载 不同eclipse卸载不一样: 1.在Eclipse中依次点击菜单"Help"-> ...
- A reader
A reader lives a thousand lives before he die... The man who never reads lives only one.
- Java中的两个类:Desktop和SystemTray
在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray,前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(比如 ...
- python练习六十一:文件处理,读取文件内容
python练习六十一:文件处理,读取文件内容 假设要读取text.txt文件中内容 写文件(如果有文件,那直接调用就行,我这里自己先创建的文件) list1 = ['python','jave',' ...
- Apache重定向URL
(1)去除httpd.conf文件中"#LoadModule rewrite_module modules/mod_rewrite.so"前面的"#"号; (2 ...
- Vue在单独引入js文件中使用ElementUI的组件
Vue在单独引入js文件中使用ElementUI的组件 问题场景: 我想在vue中的js文件中使用elementUI中的组件,因为我在main.js中引入了element包和它的css,并挂载到了全局 ...
- ie中html页面无法加载css
今天写代码发生一个很尴尬的问题,码了一天的代码在ie下一调试居然没有样式,打开F12查看元素果然没有样式,在其他浏览器完全没问题,ie就出事. ie肯定没问题,问题还是处在代码上了,百度了一下说是把& ...
- jmeter-集合点---学习笔记
集合点: 简单来理解一下,虽然我们的“性能测试”理解为“多用户并发测试”,但真正的并发是不存在的,为了更真实的实现并发这感念,我们可以在需要压力的地方设置集合点,每到输入用户名和密码登录时,所有的虚拟 ...