通用的 makefile 小工具分享 - Easymake 使用说明
Easymake 使用说明
介绍
Easymake 是一个在linux系统中 C/C++ 开发的通用 makefile。在一个简单的 C/C++ 程序中使用 easymake,你甚至可以不写一行 makefile 代码来生成目标文件。
Easymake 包含以下功能:
- 自动扫描 C/C++ 源文件。
- 自动生成和维护依赖关系,加快编译时间。
- 支持简单的单元测试,可以很方便地管理多个程序入口(main 函数)。
- 完美地支持
VPATH
变量。
我将在后面的例子中一步步地向你展示如何使用 easymake 来构建你的应用程序。别看文档这么长,下面一节的内容中大部分是在讲一个简单的 C/C++ 程序的开发。就像 easymake 的名字一样,easymake 是非常易学易用的。
开始学习 Easymake
在这一节中将展示如何在一个简单的程序中使用 easymake。接下来让我们一个加法程序,用户输入两个数字,然后程序输出这两个数字相加的结果。这个程序的源代码可以在 samples/basics
目录中找到。
C/C++ 代码
这个程序很简单,所以这里跳过程序设计环节。这里直接展示程序的 C/C++ 代码,然后再转入我们的正题。
File: main.cpp
#include <iostream>
#include "math/add.h"
using namespace std;
int main(){
cout<<"please enter two integer:"<<endl;
int a,b;
cin>>a>>b;
cout<<"add("<<a<<","<<b<<") returns "<<add(a,b)<<endl;
}
File: math/add.h
#ifndef ADD_H
#define ADD_H
int add(int,int);
#endif
File: math/add.cpp
#include "math/add.h"
int add(int a,int b){
return a+b;
}
使用 Easymake 来构建程序
代码很简单,可以直接使用命令行来构建程序。如果你对 makefile 的语法熟悉,你也可以很快地写出一个 makefile 来做完成这个事情。那么如何使用 easymake 来构建这个程序呢?先别急,接下来将使用刚才提到的三种方法来构建程序,希望你能清晰地了解和比较这三种方式。
使用命令行构建
g++ -c -o main.o main.cpp
g++ -c -o add.o math/add.cpp -I.
g++ -o target main.o add.o
或者也可以只用一条命令 g++ -o target main.cpp math/add.cpp -I.
来构建程序。
然后输入 ls
和 ./target
,就可以观察到程序的执行结果了:
[root@VM_6_207_centos basics]# ls
add.o bin main.cpp main.o makefile math target
[root@VM_6_207_centos basics]# ./target
please enter two integer:
5
3
add(5,3) returns 8
自己写一个 makefile 构建程序
创建一个新的 Makefile 文件,代码如下:
target: main.o add.o
g++ -o target main.o add.o
main.o: main.cpp
g++ -c -o main.o main.cpp -I.
add.o: math/add.cpp
g++ -c -o add.o math/add.cpp -I.
结果基本是一样的:
[root@VM_6_207_centos basics]# make
g++ -c -o main.o main.cpp -I.
g++ -c -o add.o math/add.cpp -I.
g++ -o target main.o add.o
[root@VM_6_207_centos basics]# ls
add.o main.cpp main.o makefile math target
[root@VM_6_207_centos basics]# ./target
please enter two integer:
8
9
add(8,9) returns 17
使用 makefile 的好处就是,如果能很好地确定依赖关系,那么就不需要在每次构建时把所有的源文件都重新编译一次。但是随着项目的代码的增长,即使在一个良好的模块化设计中,手工维护依赖关系也是一件很繁琐而且很容易出错的工作。例如,假设我们需要增加一个 multiply.cpp
和 multiply.h
文件,让程序支持乘法计算的功能,那么我必须修改我们的 makefile 才能构建新的程序。另外,如果头文件 add.h
被修改了,multiply.cpp
就不需要重新编译,所以我们应该在 makefile 中增加 .cpp 文件和 .h 文件之间的依赖关系的代码。到这里,我想你也会觉得我们应该有一个通用的 makefile 来帮助我们自动维护依赖关系了吧。
使用 easymake 构建程序
在这个例子中,包含 easymake.mk
文件就足够了。把我们的 Makefile 修改成下面的代码:
include ../../easymake.mk
在命令行中输入 make
构建我们的程序。接下来我们给你展示一些细节来帮助你理解 makefile 是如何构建程序的。
[root@VM_6_207_centos basics]# ls
main.cpp makefile math
[root@VM_6_207_centos basics]# make
g++ -c -o bin/main.o main.cpp -I.
entry detected
g++ -c -o bin/math/add.o math/add.cpp -I.
g++ -o bin/target bin/main.o bin/math/add.o
BUILD_ROOT/TARGET: bin/target
ENTRY: main.cpp
[root@VM_6_207_centos basics]# ./bin/target
please enter two integer:
3
5
add(3,5) returns 8
你也许也已经注意到,和之前的方式相比,主要的不同就是输出中的 entry detected
,BUILD_ROOT/TARGET: bin/target
和 ENTRY: main.cpp
。bin/target
就是我们的程序。至于这里的entry,会在后面讲到。
现在可以看一下当前的目录结构:
[root@VM_6_207_centos basics]# tree .
.
├── bin
│ ├── easymake_current_entry_file
│ ├── easymake_detected_entries
│ ├── easymake_entries_tmp.d
│ ├── main.d
│ ├── main.o
│ ├── math
│ │ ├── add.d
│ │ └── add.o
│ └── target
├── main.cpp
├── makefile
└── math
├── add.cpp
└── add.h
3 directories, 12 files
Easymake 使用 bin
目录作为 BUILD_ROOT
,用来存放生成的文件,这样一来我们的源文件目录也不会被污染。这里面的 *.d
和 easy_make_*
文件都是由 easymake 额外生成用来维护依赖关系的。*.d
的文件其实也算是 makefile 的一部分,例如 main.d 文件的内容如下:
[root@VM_6_207_centos basics]# cat bin/main.d
bin/main.o: main.cpp math/add.h
math/add.h:
这些依赖关系是 easymake 自动生成的,所以每当 math/add.h
被修改了,main.o
就会重新生成。事实上,你不需要关注这些细节来使用 easymake,所以我们就忽略这些额外生成的文件吧。如果你有兴趣,可以查看 easymake.mk
的源代码,我觉得代码的注释得已经足够帮助你理解了。
用户选项
如果你想使用 gcc 编译器的 -O2
优化选项和链接器的 -static
选项来构建这个程序。那么你需要增加几行代码来修改编译和链接选项。下面是修改后的 makefile:
COMPILE_FLAGS += -O2
LINK_FLAGS += -static
include ../../easymake.mk
然后重新构建程序:
[root@VM_6_207_centos basics]# make clean
rm -f \$(find bin -name \*.o)
rm -f \$(find bin -name \*.d)
rm -f \$(find bin -name \*.a)
rm -f \$(find bin -name \*.so)
rm -f \$(find bin -name \*.out)
rm -f bin/target
[root@VM_6_207_centos basics]# make
g++ -c -o bin/main.o main.cpp -O2 -I.
entry detected
g++ -c -o bin/math/add.o math/add.cpp -O2 -I.
g++ -o bin/target bin/main.o bin/math/add.o -static
BUILD_ROOT/TARGET: bin/target
ENTRY: main.cpp
除些以外,还有更多可供设置的选项,使用 make help
命令你就可以看到它们。注意 basic settings 和 user settings 两部分的内容即可,其他部分可以忽略。
[root@VM_6_207_centos basics]# make help
---------------------
basic settings:
SETTINGS_ROOT : build_settings
BUILD_ROOT : bin
TARGET : target
VPATH :
CPPEXT : cpp
CEXT : c
GCC : gcc
GXX : g++
LINKER : g++
---------------------
user settings files:
build_settings/entry_list
build_settings/compile_flags
build_settings/compile_search_path
build_settings/link_flags
build_settings/link_search_path
---------------------
user settings:
ENTRY_LIST :
ENTRY :
COMPILE_FLAGS : -O2
COMPILE_SEARCH_PATH : .
LINK_FLAGS : -static
LINK_SEARCH_PATH :
CPPSOURCES : main.cpp math/add.cpp
CSOURCES :
---------------------
internal informations:
...
...
...
用来测试的程序入口
现在我们需要给程序增加一个乘法运算功能,首先写一个 C++ 函数来做乘法运算,然后,在我们修改 main.cpp
的代码之前,我们应该测试一下这个这个 C++ 函数的功能,确保新增加的乘法模块的逻辑是正确的。下面的例子会告诉你如果使用 easymake 来完成这项工作,你可以在 samples/entries
文件夹中找到这个例子的代码。
编写乘法模块的代码
File math/multiply.h
:
#ifndef MULTIPLY_H
#define MULTIPLY_H
#include "stdint.h"
int64_t multiply(int32_t,int32_t);
#endif
File math/multiply.cpp
:
#include "math/multiply.h"
int64_t multiply(int32_t a,int32_t b){
return (int64_t)a*(int64_t)b;
}
编写测试代码
在命令行中输入 mkdir test
和 vim test/multiply.cpp
然后编写我们的代码。为了简单起见,这里仅仅是在 main
函数中打印了 8 乘 8 的结果。
#include "math/multiply.h"
#include <iostream>
using namespace std;
int main(){
cout<<"multiply(8,8)="<<multiply(8,8)<<endl;
}
构建测试程序
现在直接输入命令 make
和 ./bin/target
就可以看到测试程序的输出了。
[root@VM_6_207_centos entries]# make
g++ -c -o bin/main.o main.cpp -O2 -I.
entry detected
g++ -c -o bin/math/add.o math/add.cpp -O2 -I.
g++ -c -o bin/math/multiply.o math/multiply.cpp -O2 -I.
g++ -c -o bin/test/multiply.o test/multiply.cpp -O2 -I.
entry detected
g++ -o bin/target bin/math/add.o bin/math/multiply.o bin/test/multiply.o -static
BUILD_ROOT/TARGET: bin/target
ENTRY: test/multiply.cpp
[root@VM_6_207_centos entries]# ./bin/target
multiply(8,8)=64
[root@VM_6_207_centos entries]#
注意到 main.cpp
和 test/multiply.cpp
都有被成功编译,但是只有 test/multiply.cpp
被链接到目标文件中,而且输出中 ENTRY
对应的值也变成了 test/multiply.cpp
。在 easymake,全体一个包含 main
函数定义的源文件都会被自动检测到,并且被当作程序入口文件(ENTRY
)。在众多入口文件当中,只有一个会被选中,其他文件不会被链接到目标文件中。另外注意这里的 ENTRY
所表示的文件名对应的文件也可以不存在,在某些场景中,例如生成动态库 so 文件,就需要选择这个 ENTRY
来阻止其他入口文件被链接到目标文件中。
现在你肯定是在纳闷,easymake 是如何知道要选择 test/multiply.cpp
而不是 main.cpp
的?是不是很神奇?其实这里使用的是入口文件的最后修改时间。如果有多个入口文件,而且用户没有显式地声明使用哪个入口,那么 easymake 就会自动选择最新的那个计算器文件。
如果你需要显式地声明 ENTRY
,以选择 main.cpp
为例,可以输入命令 make ENTRY=main.cpp
或者 make ENTRY=m
:
[root@VM_6_207_centos entries]# make ENTRY=main.cpp
g++ -o bin/target bin/main.o bin/math/add.o bin/math/multiply.o -static
BUILD_ROOT/TARGET: bin/target
ENTRY: main.cpp
到这里已经完成了乘法模块的测试,接下来可以修改 main.cpp
的代码来整合我们的新模块了。为了简洁,接下来的步骤就不在这里赘述了,如果有需要,可以查看 samples/entries
目录中的代码。
原文及代码下载
最新的代码和文档请前往此处下载 https://github.com/roxma/easymake 。有任何BUG或者改进建议请联系我 roxma@qq.com
通用的 makefile 小工具分享 - Easymake 使用说明的更多相关文章
- 文件上传漏洞fuzz字典生成脚本小工具分享
前言 学习xss的时候翻阅资料发现了一个文件上传漏洞fuzz字典生成脚本小工具,试了试还不错,分享一下 配置 需要python2环境 工具地址:https://github.com/c0ny1/upl ...
- 关于 fir.im 你可能不知道的实用小工具
大家可能都知道 fir.im 是做测试发包的,上传你的 IPA/APK, 测试用户可以通过一个短链接和二维码就可快速安装测试. 除了基本的发包功能即应用上传下载外,fir.im 还为提高发包体验提供了 ...
- 偷懒小工具 - SSO单点登录通用类(可跨域)
写在前面的话 上次发布过一篇同样标题的文章.但是因为跨域方面做得不太理想.我进行了修改,并重新分享给大家. 如果这篇文章对您有所帮助,请您点击一下推荐.以便有动力分享出更多的"偷懒小工具&q ...
- Linux C编程学习之开发工具3---多文件项目管理、Makefile、一个通用的Makefile
GNU Make简介 大型项目的开发过程中,往往会划分出若干个功能模块,这样可以保证软件的易维护性. 作为项目的组成部分,各个模块不可避免的存在各种联系,如果其中某个模块发生改动,那么其他的模块需要相 ...
- 【分享】生成Revit扩展的addin文件小工具
在进行Revit二次开发的时候,加载命令/程序使用的是添加addin文件的方式,每次都需要手动的写,而且参数有好多,很不方便.于是乎我有了写一个小工具的想法.进过研究终于完成了.主要使用RevitAd ...
- 【分享】小工具大智慧之Sql执行工具
原文:[分享]小工具大智慧之Sql执行工具 工具概况 情况是这样的,以前我们公司有很多Sql用于完成一些很不起眼但又不得不完成的业务,出于方便就直接在Sql查询分析器里执行,按理说应该写一些专门的工具 ...
- 基于百度通用翻译API的一个翻译小工具
前几天写了一个简单的翻译小工具,是基于有道翻译的,不过那个翻译接口有访问限制,超过一定次数后会提示访问过于频繁,偶然发现百度翻译API如果月翻译字符少于200万是不收取费用的,所以就注册了一个百度开发 ...
- 分享一个小工具:Excel表高速转换成JSON字符串
在游戏项目中一般都须要由策划制作大量的游戏内容,当中非常大一部分是使用Excel表来制作的.于是程序就须要把Excel文件转换成程序方便读取的格式. 之前项目使用的Excel表导入工具都是通过Offi ...
- 分享之测试WebService小工具 STORM(转)
http://www.cnblogs.com/yhuang/archive/2012/04/04/share_storm.html 最近的项目中,一直要使用到WebService,为了测试自己编写的W ...
随机推荐
- Webform——服务器控件与客户端控件
Webform,即BS,浏览器应用. 关于服务器和客户端,在Webform 中, 服务器就相当于后台(也就是编辑C#代码的地方), 客户端相当于前台(也就是Html页面). 用法: ①如果想在服务器 ...
- $apply() $digest()
理解Angular中的$apply()以及$digest() <!DOCTYPE html> <html> <head> <meta charset=&quo ...
- MongoDB 权限 验证
在MongoDB中,服务启动默认是没有权限验证的,就安全性方面来说,这肯定是不行的,所以需要加上权限验证. 既然是要进行权限验证,那肯定是得有用户的吧,所以权限验证的第一步就是给MongoDB库添加用 ...
- POJ1401 - Factorial
题目大意 N!末尾0的个数 题解 0只能由2*5产生,所以只要求2,5有多少对即可,又因为10!中5的个数少于2,所以只要求因子5有多少个即可,答案即为N/5+N/25+N/125.. 代码: #in ...
- 如果iis的配置文件 applicationHost.config坏掉了, 会在 C:\inetpub\history\ 中存储历史备份。复制过去还原就可以了-摘自网络
You will usually get the error ‘Configuration file is not well-formed XML’ ‘C:\Windows\system32\inet ...
- MySQL开发中常用的查询语句总结
1.查询数值型数据: SELECT * FROM tb_name WHERE sum > 100; 查询谓词:>,=,<,<>,!=,!>,!<,=>, ...
- Yii Listview 更新及搜索
更新: http://my.oschina.net/shixiaobao17145/blog/130992 http://www.yiiframework.com/forum/index.php/to ...
- url 编码方法
这个方法用于把 字符串转换成url 的编码 第一个参数是 字符串,第二个参数是 指定的编码(UTF-8 ,GBK, gbk2312...) private string UrlEncode(strin ...
- poj 3984 迷宫问题【bfs+路径记录】
迷宫问题 Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 10103 Accepted: 6005 Description ...
- WIN10环境下搭建与连接VPN服务器
搭建VPN服务器 0.前言 1.WIN+E打开此电脑 2.勾选始终显示菜单,并应用,确定 3.新建传入连接,可以添加用户,也可以直接选取已有用户 连接VPN服务器 1.打开vpn设置 2.添加vpn连 ...