写一个简单的makefile

一个简单的Makefile教程


Makefiles是组织代码编译的一种简单方法。本教程甚至没有描述使用make的可能性,
而是作为初学者指南,以便您可以快速轻松地为中小型项目创建自己的makefile。
一个简单的例子


让我们从以下三个文件开始:hellomake.c,hellofunc.c和hellomake.h,它们
分别代表一个典型的主程序,一些单独的文件中的某些功能代码和一个包含文件。
hellomake.c hellofunc.c hellomake.h


#include "hellomake.h"


int main() {
myPrintHelloMake();
return 0;
}


#include <stdio.h>
#include "hellomake.h"
void myPrintHelloMake()
{
printf("hello make!\n");
}




/ *
例如包含文件
* /
#ifndef HELLOMAKE_H
#define HELLOMAKE_H


void myPrintHelloMake();


#endif //HELLOMAKE_H


通常情况下,您可以通过执行以下命令来编译该代码集合:


gcc -o hellomake hellomake.c hellofunc.c -I .


这编译了两个.c文件并将可执行文件命名为hellomake。 -I .是包含的,所以gcc会在
当前目录(.)中查找包含文件hellomake.h。如果没有makefile,测试/修改/调试
周期的典型方法是使用终端中的向上箭头返回到上一个编译命令,所以不必每次都输
入,特别是一旦添加一些更多的.c文件混合。


不幸的是,这种汇编方法有两个缺点。首先,如果你失去了编译命令或切换电脑,
你必须从头开始重新输入,这是最好的低效率。其次,如果您只是对一个.c文件
进行更改,则每次重新编译它们也是耗时且效率低下的。所以,现在是时候看看
我们能用makefile做什么了。


你可以创建的最简单的makefile如下所示:
Makefile 1


hellomake:hellomake.c hellofunc.c
     gcc -o hellomake hellomake.c hellofunc.c -I .


如果你把这个规则放到一个名为Makefile或makefile的文件中,然后在命令行
中输入make,它将执行编译命令,就像你在makefile中写的一样。请注意,不
带参数的make执行文件中的第一条规则。而且,通过在下列命令之后放置命令
依赖于第一行的文件列表,如果知道这些文件中的任何一个发生更改,hellomake规则
就需要立即执行。你已经解决了问题#1,可以避免重复使用向上的箭头,
寻找你最后的编译命令。但是,这个系统在编译最新变化方面仍然不够高效。


一个非常重要的要注意的是在makefile中的gcc命令之前有一个选项卡。任何命
令开始时都必须有一个标签


为了更高效一些,我们来尝试一下:
Makefile 2


CC=gcc
CFLAGS=-I.


hellomake:hellomake.o hellofunc.o
     $(CC)-o hellomake hellomake.o hellofunc.o -I.


所以现在我们定义了一些常量CC和CFLAGS。事实证明,这些是特殊的常量,
使我们想要编译hellomake.c和hellofunc.c文件。特别是,宏CC是要使用
的C编译器,而CFLAGS是传递给编译命令的标志列表。通过将对象文件hellomake.o和
hellofunc.o放入依赖列表和规则中,make知道它必须先单独编译.c版本,然后构
建可执行文件hellomake。
(执行make命令时,却报如下错误:
Makefile ...2 ... 遗漏分隔符...停止


经过调查,发现是这样的:


Makefile的 hellomake: 行被称为rule。
第二行,是具体的编译动作。开头不可以有空格,留白是由 按tab键形成的。


去掉空格,改为tab键后,再执行make命令,成功。)




使用这种形式的makefile对于大多数小型项目来说已经足够了。但是,有一件事
是缺少的:依赖于包含文件。例如,如果要对hellomake.h进行更改,make将不
会重新编译.c文件,即使它们需要。为了解决这个问题,我们需要告诉make所有
的.c文件都依赖于某些.h文件。我们可以通过编写一个简单的规则并将其添加到
生成文件来完成。
Makefile 3


CC=gcc
CFLAGS=-I.
DEPS = hellomake.h


%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)


hellomake: hellomake.o hellofunc.o 
gcc -o hellomake hellomake.o hellofunc.o -I.


首先在此添加创建宏DEPS,这是.c文件所依赖的.h文件集。然后我们定义一个适用
于以.o后缀结尾的所有文件的规则。该规则指出.o文件依赖于.c版本的文件和
DEPS宏中包含的.h文件。然后,规则说要生成.o文件,需要使用CC宏中定义的
编译器编译.c文件。 -c标志表示生成目标文件,-o $ @表示将编译的输出
放在:左边的文件中,$ <是依赖项列表中的第一个项目,而CFLAGS宏定义如上。


作为最后的简化,让我们分别使用特定的宏$ @和$ ^(分别是:的左侧和右侧)
来使整体编译规则更加通用。在下面的例子中,所有的包含文件应该被列为
宏DEPS的一部分,所有的目标文件都应该被列为宏OBJ的一部分。
Makefile 4


CC=gcc
CFLAGS=-I.
DEPS = hellomake.h
OBJ = hellomake.o hellofunc.o 


%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)


hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS)


那么,如果我们想开始将.h文件放在include目录下,我们的源代码放
在src目录下,并且在lib目录下放置了一些本地库文件呢?另外,我们可
以以某种方式隐藏遍布各处的烦人的.o文件吗?答案当然是肯定的。以下makefile定
义了include和lib目录的路径,并将目标文件放在src目录下的obj子目录中。
它还为要包含的任何库定义了一个宏,如数学库-lm。这个makefile应该位于src目
录下。请注意,如果您输入make clean,它还包含一个清理源和目标目录的规则。 
.PHONY规则保持使用一个名为clean的文件做某事。
Makefile 5


IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)


ODIR=obj
LDIR =../lib


LIBS=-lm


_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))


_OBJ = hellomake.o hellofunc.o 
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))




$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)


hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS) $(LIBS)


.PHONY: clean


clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~IDIR =../include
CC=gcc
CFLAGS=-I$(IDIR)


ODIR=obj
LDIR =../lib


LIBS=-lm


_DEPS = hellomake.h
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))


_OBJ = hellomake.o hellofunc.o 
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))




$(ODIR)/%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)


hellomake: $(OBJ)
gcc -o $@ $^ $(CFLAGS) $(LIBS)


.PHONY: clean


clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~


所以现在你有一个完美的makefile,你可以修改来管理中小型的软件项目。
你可以添加多个规则到一个生成文件;你甚至可以创建调用其他规则的规则。
有关makefile和make函数的更多信息,请查看GNU Make Manual,
它会告诉你比你想知道的更多的东西。

Guess you like

Origin blog.csdn.net/swif_N_F/article/details/78902179