Makefile学习之路6——让编译环境更加有序

摘要:
在为当前复杂的项目编写makefile之前,我们首先给出了目录结构要求:1.将所有目标文件放在objs子目录中;2.将最终生成的可执行程序放在execs子目录中;在编译项目之前,您需要准备生成的文件目录,该目录可以手动创建,也可以通过编译Makefile来创建。虽然这可以解决问题,但当项目复杂时,如果每个头文件都需要写入Makefile的相应规则,那将是一场噩梦。

在大多项目中都会合理设计目录结构来提高维护性,在编译一个项目时会产生大量中间文件,如果中间文件直接和源文件放在一起,就显得杂乱而不利于维护。在为现在这个complicated项目编写makefile之前,我们先给出目录结构需求:

1.将所有的目标文件放在objs子目录中;

2.将最终生成的可执行程序放在exes子目录中;

在编译项目之前,需要将生成的文件目录准备好,可以手动创建,也可以通过编译Makefile创建。

1 .PHONY: all clean
2 
3 MKDIR = mkdir
4 RM = rm
5 RMFLAGS = -rf
6 
7 DIRS =objs exes
8 
9 all:$(DIRS)
10 $(DIRS):
11 $(MKDIR) $@
12 clean:
13     $(RM) $(RMFLAGS) $(DIRS)

为了将项目编译时所创建的文件分别放入objs和exes中,需要用到前面介绍的一个函数,addprefix。

1 .PHONY: all clean
2 
3 MKDIR = mkdir
4 RM = rm
5 RMFLAGS = -rf
6 
7 CC=gcc
8 
9 DIR_OBJS=objs
10 DIR_EXES=exes
11 DIRS =$(DIR_OBJS) $(DIR_EXES)
12 EXE=complicated
13 SRCS=$(wildcard *.c)
14 OBJS=$(SRCS:.c=.o)
15 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
16 
17 all:$(DIRS) $(DIR_EXES)/$(EXE)
18 $(DIRS):
19 $(MKDIR) $@
20 $(DIR_EXES)/$(EXE):$(OBJS)
21     $(CC) -o $@ $^
22 $(DIR_OBJS)/%.o:%.c
23     $(CC) -o $@ -c $^ 
24 clean:
25     $(RM) $(RMFLAGS) $(DIRS)$(EXE)

这里运行之后就会在objs文件夹这种存放.o,在exes文件中存放可执行文件complicated。由于书上是把可执行文件放在当前目录的,所以它在clean加上了删除$(EXE),在我的Makefile中其实这句多余,因为把exes文件夹都删除了,在单独删除exes文件中的可执行文件没有意义,之所以没改,是为了提醒一下自己,这里完成了书上的不再赘述的任务O(∩_∩)O。

到这里 ,你可能觉得自己又get了一个新技能,但是,这却不是一个好的Makefile,比如,我们把foo.h更改为:

1 #ifndef __FOO_H
2 #define __FOO_H
3 
4 void foo(intvalue);
5 
6 #endif /*__FOO_H*/

这里仅仅改变了foo函数的参数,从void变成了int,但是我们执行make:

Makefile学习之路6——让编译环境更加有序第1张

注意,这里是在没有更改foo.h之前先make一次,然后在更改了foo.h之后再make,然后make提示居然没有任何事情可以做?奇怪吧,我们明明更改了函数声明不再匹配了啊,我们执行clean之后,再执行make:

Makefile学习之路6——让编译环境更加有序第2张

这下make终于发现错误了,为什么第一次重新make的时候,make不发现错误呢?因为我们的Makefile中,并没有依赖foo.h,所以,我们可以更改Makefile如下:

1 .PHONY: all clean
2 
3 MKDIR = mkdir
4 RM = rm
5 RMFLAGS = -rf
6 
7 CC=gcc
8 
9 DIR_OBJS=objs
10 DIR_EXES=exes
11 DIRS =$(DIR_OBJS) $(DIR_EXES)
12 EXE=complicated
13 SRCS=$(wildcard *.c)
14 OBJS=$(SRCS:.c=.o)
15 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
16 
17 all:$(DIRS) $(DIR_EXES)/$(EXE)
18 $(DIRS):
19 $(MKDIR) $@
20 $(DIR_EXES)/$(EXE):$(OBJS)
21     $(CC) -o $@ $^
22 $(DIR_OBJS)/%.o:%.c foo.h
23     $(CC) -o $@ -c $<
24 clean:
25     $(RM) $(RMFLAGS) $(DIRS) $(EXE)

其中红色部分为更改部分。

Makefile学习之路6——让编译环境更加有序第3张

可以看到,我们先执行make生成目标文件,然后更改foo.h,再make,立即报错了。虽然这样可以解决问题,但是当项目复杂时,如果每一个头文件都要写入Makefile相应的规则中,那么将会是一个噩梦。看来我们还要寻找一个更好的办法。——当然,在后面的随笔中会提出的。

免责声明:文章转载自《Makefile学习之路6——让编译环境更加有序》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇JS Guid生成【Linux基础】Fcitx中文输入法安装下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

VS 6.00 工程项目文件详解

*.dsp(DeveloperStudio Project):是VC++的工程配置文件,比如说你的工程包含哪个文件,你的编译选项是什么等等,编译的时候是按照.dsp的配置来的。*.dsw(DeveloperStudio Workspace):是工作区文件,用来配置工程文件的。它可以指向一个或多个.dsp文件。*.clw:是 ClassWizard信息文件,...

使用.NET Reflector单步调试编译好的程序集

对于没有任何源代码和PDB文件的预编译程序集而言,如果没有合适的工具,调试起来并不容易。使用Red Gate的.NET Reflector可以在Visual Studio中即时反编译程序集,然后像调试有源代码的程序集一样单步跟踪它。 大家需要了解.NET Reflector(在VS和VSPro版本中)是可以集成到Visual Studio中的。标准的内置对...

【转载】Linux系统下源代码包方式安装PHP开发环境

########节选自《细说PHP》################ 2.2 Linux系统下源代码包方式安装环境 在Linux平台下安装PHP有几种方法:使用配置和编译过程,或是使用各种预编译的包。在Linux上安装软件,用户最好的选择是下载源代码包,并编译一个适合自己的版本。LAMP组合中每个成员都是开源的软件,都可以从各自的官方网站上免费下载安装程序...

在lua环境中使用protobuf

最近在cocos2dx的项目中,需要在LUA脚本层使用protobuf协议。官方已经推出了很多种语言的版本。但唯独LUA版本不全。于是开始研究protobuf在LUA下的实现,将完整的过程记录了下来,希望对其它人能有所帮助。 1、下载protoc-gen-lua 可以通过HG从服务器(hg clonehttps://code.google.com/p/pr...

Android编译系统入门(一)

做过Android平台开发的朋友对make,mm或make clean命令应该很熟悉,但也许大家只是熟知这些命令的作用却不知道这些命令底下有些什么原理?那么今天我就带着大家推开Android编译系统的大门,探索一下这片未知的恐怖之森(问啥要用恐怖之森后面大家就知道了)。 Makefile入门 在讲解Android编译系统之前首先来了解一下什么是Makefi...

在Windows上采用Cmake+Visual Studio编译并使用静态opencv库并发布成裸机可执行程序

在Windows上采用Cmake + Visual Studio编译并使用静态opencv库并打包发布成裸机可执行程序 环境:Windows 7 64bit 工具 Cmake-3.11.1 确保是64位 确保安装了GUI Visual Studio 2015 确保是64位 确保采用vc14作为编译器(支持c++11标准部分语法) 原料:Op...