Makefile学习指南

makefile学习指南

Makefile学习

基本知识:GNUmakefile → \rightarrow makefile → \rightarrow Makefile

默认执行 make 命令时, GNU make在当前目录下依次搜索下面3个文件 “GNUmakefile”, “makefile”, “Makefile”,找到对应文件之后, 就开始执行此文件中的第一个目标(target). 如果找不到这3个文件就报错.

隐藏规则

最基本的例子

main:main.o
	gcc -c main main.o

上述代码的实际含义为

main:main.o
	gcc -o main main.o
main.o:main.c
	gcc -c main.c

其中main.o 这个目标是隐含生成的。

命令变量和命令参数变量

命令变量, 书写Makefile可以直接写 shell时用这些变量.

变量名 含义
RM rm -f
AR ar
CC cc
CXX g++
ARFLAGS AR命令的参数
CFLAGS C语言编译器的参数
CXXFLAGS C++语言编译器的参数

自动变量

Makefile 中很多时候通过自动变量来简化书写, 各个自动变量的含义如下:

自动变量 含义
$@ 目标集合
$% 当目标是函数库文件时, 表示其中的目标文件名
$< 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标
$? 比目标新的依赖目标的集合
$^ 所有依赖目标的集合, 会去除重复的依赖目标
$+ 所有依赖目标的集合, 不会去除重复的依赖目标
$* 这个是GNU make特有的, 其它的make不一定支持

makefile中的函数

字符串函数
  1. 字符串替换函数subst
  2. 模式字符串替换函数patsubst
  3. 去空格函数: strip ,去掉 string字符串中开头和结尾的空字符
  4. 查找字符串函数: findstring,在字符串 in中查找 find字符串
  5. 过滤函数: filter
  6. 反过滤函数: filter-out
  7. 排序函数sort
  8. 取单词函数: word
  9. 取单词串函数: wordlist,从字符串text中取从s开始到e的单词串.s和e是一个数字.
  10. 单词个数统计函数: words
# Makefile 内容
VAL := "       aa  bb  cc "
test8:
	@echo $(subst t,e,maktfilt)  #将字符串maktfilt中的t替换为e
	@echo $(patsubst %.c,%.o,programA.c programB.c)  #查找中的单词,用.o替换.c
	@echo "去除空格前: " $(VAL)
	@echo "去除空格后: " $(strip $(VAL))
	@echo $(findstring aa,$(VAL))
	@echo $(filter %.o %.a,program.c program.o program.a) # *保留* 符合模式  的单词, 可以有多个模式
	@echo $(filter-out %.o %.a,program.c program.o program.a) #*去除* 符合模式  的单词, 可以有多个模式
	@echo $(sort bac abc acb cab)
	@echo $(word 1,aa bb cc dd)
    @echo $(word 2,aa bb cc dd)  #从1开始
    @echo $(wordlist 1,3,aa bb cc dd)
	@echo $(words aa bb cc dd)	
文件名函数

1.取目录函数: $(dir )
2.取文件函数: $(notdir )
3.取后缀函数: $(suffix )
4.取前缀函数: $(basename )
5.加后缀函数: $(addsuffix prefix ,)
6.加前缀函数: $(addprefix prefix,)
7.连接函数: $(join list1,list2)

test9:
	@echo $(dir /home/a.c ./bb.c ../c.c d.c)  #从文件名序列中取出目录部分
	@echo $(notdir /home/a.c ./bb.c ../c.c d.c) #从文件名序列  中取出非目录部分
	@echo $(suffix /home/a.c ./b.o ../c.a d)  #从文件名序列  中取出各个文件名的后缀
	@echo $(basename /home/a.c ./b.o ../c.a /home/.d .e) #从文件名序列  中取出各个文件名的前缀
	@echo $(addsuffix .c,/home/a b ./c.o ../d.c) #把后缀  加到  中的每个单词后面
	@echo $(addprefix test_,/home/a.c b.c ./d.c)  #把前缀  加到  中的每个单词前面
	@echo $(join a b c d,1 2 3 4) #  中对应的单词加到  后面
foreach & if

$(foreach var,list,text)

# Makefile 内容
targets := a b c d
objects := $(foreach i,$(targets),$(i).o)

test10:
	@echo $(targets)
    @echo $(objects)

$(if condition,then-part)
$(if condition,then-part,else-part)

# Makefile 内容
val := a
objects := $(if $(val),$(val).o,nothing)
no-objects := $(if $(no-val),$(val).o,nothing)

test11:
    @echo $(objects)
    @echo $(no-objects)

Makefile中一些GNU约定俗成的伪目标

伪目标
all 所有目标的目标,其功能一般是编译所有的目标
clean 删除所有被make创建的文件
install 安装已编译好的程序,其实就是把目标可执行文件拷贝到指定的目录中去
print 列出改变过的源文件
tar 把源程序打包备份. 也就是一个tar文件
dist 创建一个压缩文件, 一般是把tar文件压成Z文件. 或是gz文件
TAGS 更新所有的目标, 以备完整地重编译使用
check or test 一般用来测试makefile的流程

Makefile学习指南

关于makefile学习的一些笔记

基本情况介绍

编译情况

makefile关系到了整个工程的编译规则,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。

程序编译的一般步骤,对于C、C++,首先要把源文件(.c )编译成中间代码文件(.o),在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成可执行文件,这个动作叫作链接(link)。

**编译时,编译器需要的是语法的正确,函数与变量的声明的正确。**对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

基本语法介绍

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label).

prerequisites就是,要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。(任意的Shell命令)

target:prerequisites
TAB command

这是一个文件的依赖关系,也就是说,**target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。**说白一点就是说,**prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。**也就是Makefile中最核心的内容。

工作流程

在默认的方式下,当我们只输入make命令。那么,

  1. make会在当前目录下找名字叫**“Makefile”“makefile”**的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
  3. 如果第一个目标文件不存在,或是其所依赖的后面的 .o 文件的文件修改时间要比这个文件新,那么,他就会执行后面所定义的命令来生成这个文件。
  4. 如果第一个目标文件所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件了。

这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

makefile中使用变量
 objects = main.o kbd.o command.o display.o 
 后面引用用$(objects)
make自动推导

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。

清空目标文件的规则
.PHONY : clean
clean :
	-rm edit $(objects)

.PHONY意思表示clean是一个“伪目标”,。而**在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。**当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。

makefile总述

基本内容

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  1. 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  2. 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
  3. 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  5. 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“/#”。
Makefile的文件名

默认的情况下,make命令会在当前目录下按顺序找寻以下文件名,“GNUmakefile”、“makefile”、“Makefile”为的文件,找到了解释这个文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是GNU的make识别的。有另外一些make只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的make都支持“makefile”和“Makefile”这两种默认文件名。

当然,你可以使用别的文件名来书写Makefile,如:“Make.Linux”等,如果要指定特定的Makefile,你可以使用make的“-f”和“–file”参数,如:make -f Make.Linux或make --file Make.linux。

引用其它的Makefile

在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。

include的语法是:

include <filename>

filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

在include前面可以有一些空字符,但是绝不能是[Tab]键开始。include和可以用一个或多个空格隔开。

如果文件都没有指定绝对路径或是相对路径的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录下找:

  1. 如果make执行时,有“-I”或“–include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。
  2. 如果目录在默认的< prefix >/include(一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。
make的工作方式

GNU的make工作时的执行步骤入下:(想来其它的make也是类似)

1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。

1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

在规则中使用通配符

如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[…]”。这是和Unix的B-Shell是相同的。

波浪号(“”)字符在文件名中也有比较特殊的用途。**如果是“/test”,这就表示当前用户的$HOME目录下的test目录。**而“~hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。

**通配符代替了你一系列的文件,如“.c”表示所以后缀为c的文件。**一个需要我们注意的是,如果我们的文件名中有通配符,如:“”,那么可以用转义字符“/”,如“/”来表示真实的“”字符,而不是任意长度的字符串。

objects = *.o

通符同样可以用在变量中。并不是说[.o]会展开,不!objects的值就是“.o”。Makefile中的变量其实就是C/C++中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有[.o]的文件名的集合,那么,你可以这样:

objects := $(wildcard *.o)
文件搜寻

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。

Makefile文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。

VPATH = src:../headers

上面的的定义指定两个目录,“src”和“…/headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)

另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。

1. vpath < pattern > < directories >

为符合模式< pattern >的文件指定搜索目录< directories >。

2、vpath < pattern >

清除符合模式的文件的搜索目录。

3、vpath

清除所有已被设置好了的文件搜索目录。

vapth使用方法中的< pattern >需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。< pattern >指定了要搜索的文件集,而< directories >则指定了< pattern >的文件集的搜索的目录。

vpath %.h ../headers

该语句表示,要求make在“…/headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)

伪目标

“伪目标”并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。我们只有通过显示地指明这个“目标”才能让其生效。当然,“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。

然,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”。

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
        rm program

cleanobj :
        rm *.o

cleandiff :
        rm *.diff

“make clean”将清除所有要被清除的文件。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。

多目标

Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。

bigoutput littleoutput : text.g
	generate text.g -$(subst output,,$@) > $@

上述规则等价于:

bigoutput : text.g
        generate text.g -big > bigoutput
littleoutput : text.g
        generate text.g -little > littleoutput

其中,- ( s u b s t o u t p u t , , (subst output,, (substoutput,,@)中的“ ” 表 示 执 行 一 个 M a k e f i l e 的 函 数 , 函 数 名 为 s u b s t , 后 面 的 为 参 数 。 关 于 函 数 , 将 在 后 面 讲 述 。 这 里 的 这 个 函 数 是 截 取 字 符 串 的 意 思 , ∗ ∗ “ ”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,**“ Makefilesubst@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。**

静态模式

如果我们的< target-parrtern >定义成“%.o”,意思是我们的< target >集合中都是以“.o”结尾的,而如果我们的< prereq-parrterns >定义成“%.c”,意思是对< target-parrtern >所形成的目标集进行二次定义,其计算方法是,取< target-parrtern >模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
	$(CC) -c $(CFLAGS) $< -o $@

上面的例子中,指明了我们的目标从 o b j e c t 中 获 取 , “ object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量 objectobject集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“ < ” 和 “ <”和“ <@”则是自动化变量,“ < ” 表 示 所 有 的 依 赖 目 标 集 ( 也 就 是 “ f o o . c b a r . c ” ) , “ <”表示所有的依赖目标集(也就是“foo.c bar.c”),“ <foo.cbar.c@”表示目标集(也就是“foo.o bar.o”)。

files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
	$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
	emacs -f batch-byte-compile $<

( f i l t e r (filter %.o, (filter(files))表示调用Makefile的filter函数,过滤“$filter”集,只要其中模式为“%.o”的内容。其的它内容,我就不用多说了吧。这个例字展示了Makefile中更大的弹性。

自动生成依赖性

大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。

 cc -M main.c

于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

显示命令

通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来。

@echo 正在编译XXX模块
echo 正在编译XXX模块

当依赖目标新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。

exec:
	cd /home/hchen; pwd

命令出错

忽略命令的出错,我们可以在Makefile的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

clean:
	-rm -f *.o

还有一个要提一下的make的参数的是“-k”或是“–keep-going”,这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。

嵌套执行make

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而不至于把所有的东西全部写在一个Makefile中,这样会很难维护我们的Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。

例如,我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:

 subsystem:
            cd subdir && $(MAKE)

其等价于:

subsystem:
            $(MAKE) -C subdir

定义$(MAKE)宏变量的意思是,也许我们的make需要一些参数,所以定义成一个变量比较利于维护。这两个例子的意思都是先进入“subdir”目录,然后执行make命令。

定义命令包

如果Makefile中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束,如:

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endef

这里,“run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。在“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行Yacc程序,因为Yacc程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字。

变量

初等用法

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“ ” 符 号 , 但 最 好 用 小 括 号 “ ( ) ” 或 是 大 括 号 “ ” 把 变 量 给 包 括 起 来 。 如 果 你 要 使 用 真 实 的 “ ”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来。如果你要使用真实的“ 使”字符,那么你需要用“$$”来表示。

如果我们要定义一个变量,其值是一个空格,那么我们可以这样来:

 nullstring :=
 space := $(nullstring) # end of the linen

ullstring是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。因为在操作符的右边是很难描述一个空格的,这里采用的技术很管用,先用一个Empty变量来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空格的变量。

还有一个比较有用的操作符是“?=”,先看示例:

 FOO ?= bar

其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,如果FOO先前被定义过,那么这条语将什么也不做.

高级用法-变量值的替换

我们可以替换变量中的共有的部分,其格式是“ ( v a r : a = b ) ” 或 是 “ (var:a=b)”或是“ (var:a=b){var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。

foo := a.o b.o c.o
bar := $(foo:.o=.c)

这个示例中,我们先定义了一个“ ( f o o ) ” 变 量 , 而 第 二 行 的 意 思 是 把 “ (foo)”变量,而第二行的意思是把“ (foo)(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”。

另外一种变量替换的技术是以“静态模式”(参见前面章节)定义的,如:

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)

这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同样让$(bar)变量的值为“a.c b.c c.c”。

使用条件判断

判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标。

libs_for_gcc = -lgnu
normal_libs =

foo: $(objects)
ifeq ($(CC),gcc)
        $(CC) -o foo $(objects) $(libs_for_gcc)
else
        $(CC) -o foo $(objects) $(normal_libs)
endif

ifeq、else和endif。ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以endif结束。

杂项

“.PHONY”表示,clean是个伪目标文件。

\ 表示换行,增强可读性

参考文献

https://blog.csdn.net/haoel/article/details/2886

https://blog.csdn.net/haoel/article/details/2887

https://blog.csdn.net/haoel/article/details/2888

https://blog.csdn.net/haoel/article/details/2889

https://blog.csdn.net/haoel/article/details/2890

https://blog.csdn.net/haoel/article/details/2891

https://blog.csdn.net/haoel/article/details/2892

https://blog.csdn.net/haoel/article/details/2893

https://blog.csdn.net/haoel/article/details/2894

实例1

#定义变量,使用变量:$(变量名)
CC=mpicc

#定义变量srcs,表示需要编译的源文件,需要表明路径,如果直接写表示这些cpp文件和makefile在同一个目录下,如果有多个源文件,每行以\结尾
SRCS=matrix_mul.c \

#定义变量OBJS,表示将原文件中所有以.cpp结尾的文件替换成以.o结尾,即将.cpp源文件编译成.o文件
OBJS=$(SRCS:.cpp=.o)

#定义变量,表示最终生成的可执行文件名
EXEC=maincpp

#start,表示开始执行,冒号后面的$(OBJS)表示要生成最终可执行文件,需要依赖那些.o>文件的
start:$(OBJS)
        #相当于执行 gcc -o maincpp .o文件列表,-o表示生成的目标文件
        $(CC) -o $(EXEC) $(OBJS) -lm

#表示我的.o文件来自于.cpp文件
.cpp.o:
        #如果在依赖关系中,有多个需要编译的.cpp文件,那么这个语句就需要执行多次>。-c $<指的是需要编译的.cpp文件,-o $@指这个cpp文件编译后的中间文件名。
        #比如在依赖关系中,有a.cpp和b.cpp,即$(OBJS)的值为a.cpp b.cpp,那么这条>语句需要执行2次,第一次的$@为a.o,$<为a.cpp,第二次的$@为b.o,$<为b.cpp。
        #-c表示只编译不链接,-o表示生成的目标文件
        #-DMYLINUX:-D为参数,MYLINUX为在cpp源文件中定义的宏名称,如#ifdef MYLINUX。注意-D和宏名称中间没有空格
        $(CC) -o $@ -c $< -DMYLINUX

#执行make clean指令
clean:
        #执行make clean指令时,需要执行的操作,比如下面的指令时指删除所有.o文件
        rm -rf $(OBJS)

linux常用的编译系统

文件类型由文件的扩展名决定
文件类型 扩展名
C源代码 .c
Fortran77源代码 .f
带预处理的Fortran源代码 .F
Fortran90 源代码 .f90
C++源代码 .c++,.C,.cpp,.cc,.cxx
汇编代码 .s,.S
目标文件 .o
库文件 .a
共享库文件 .so
命令行选项
命令代码 实现功能
-c 只编译,不链接,即只生成.o文件
-o filename 指定输出文件名,缺省为*.o,a.out等等
-Ipath 指定(增加)包含文件(如*.h)的搜索目录
-Lpath 指定(增加)库文件的搜索目录
-lname 与库文件libname.a(.so)链接
-O,-O1,-O2,-O3 优化开关
-g 目标码中包含源文件名,行号等信息(用于程序调试)
运行调试实例
  1. f77编译文件,优化开关为O2。

f77 -O2 -o prog file1.f file2.c file3.o file4.a

  1. 编译输出结果

f77 -c file.f
f77-o out file.o

  1. 增加搜索目录

f77 -c -I/usr/local/mpi/include file.f
f77 -o prog -L/usr/local/mpi/lib file.o -lmpi (等价于:
f77 -o prog file.o /usr/local/mpi/lib/libmpi.a)

实用工具Make

make [-f makefile] [options] [target [target …]]
其中-f选项给出定义规则的文件名(简称makefile文件),缺省使用当前目录下的makefile或者Makefile文件,target指明要求生成的目标(在markfile中定义),当命令行不给出target时,make只生成makefile中定义的第一个目标。比较有用且常用的命令行选项由下面一些

-f文件名:指定markfile文件名
-n:只显示将要执行的命令但并不执行它们。
-p:显示定义的全部规则及宏,用于对markfile的调试。

** 通过makefile文件定义一组文件之间的依赖关系及处理命令方便程序开发过程中的编译与维护。

基本规则

目标:依赖对象
处理命令

实例:

prog: file1.f file2.f file3.o
	  f77 -O2 -o prog file1.f file2.f file3.o

含义:如果目标(prog)不存在,或者存在一个依赖对象(file1.f,file2.f,file3.o)比目标新,则执行指定的命令。

Makefile 使用总结,http://www.cnblogs.com/wang_yb/p/3990952.html

关于makefile书写的一些指南

$* 不包含拓展名的目标文件名称

$+ 所有的依赖文件,以空格隔开,并以出现的先后为序,可能包含重复的依赖文件

$< 第一个依赖文件的名称

$? 所有的依赖文件,以空格隔开,这些依赖文件的修改日期比目标的创建日期晚

$@ 目标的完整名称

$^ 所有的依赖文件,以空格隔开,不包含重复的依赖文件

$% 如果目标为归档成员,则该变量表示目标的归档成员名称。

CC C编译器的名称,默认为cc

CPPFLAGS C预编译的选项

你可能感兴趣的:(杂七杂八)