project管理之makefile与自己主动创建makefile文件过程
(风雪之隅 http://www.laruence.com/2009/11/18/1154.html)
Linux Makefile自己主动编译和链接使用的环境
想知道到Linux Makefile系统的真相么,想知道Linux Makefile系统中藏有的内在奥义么,仅仅有我来给大家全面解说介绍Linux Makefile系统作为Linux下的程序开发者,大家一定都遇到过Linux Makefile,用make命令来编译自己写的程序确实是非常方便。普通情况下,大家都是手工写一个简单Linux Makefile,假设要想写出一个符合自由软件惯例的Linux Makefile就不那么easy了。
在本文中。将给大家介绍怎样使用autoconf和automake两个工具来帮助我们自己主动地生成符合自由软件惯例的Linux Makefile,这样就能够象常见的GNU程序一样。仅仅要使用“./configure”,“make”。“make instal”就能够把程序安装到Linux系统中去了。这将特别适合想做开放源码软件的程序开发者。又或假设你仅仅是自己写些小的Toy程序。那么这个文章对你也会有非常大的帮助。
一、Linux Makefile介绍
Linux Makefile是用于自己主动编译和链接的。一个project有非常多文件组成。每个文件的改变都会导致project的又一次链接,可是不是全部的文件都须要又一次编译,Linux Makefile中纪录有文件的信息,在Linux Makefile时会决定在链接的时候须要又一次编译哪些文件。
Linux Makefile的宗旨就是:让编译器知道要编译一个文件须要依赖其它的哪些文件。当那些依赖文件有了改变,编译器会自己主动的发现终于的生成文件已经过时。而又一次编译对应的模块。
Linux Makefile的基本结构不是非常复杂。但当一个程序开发者開始写Linux Makefile时,常常会怀疑自己写的是否符合惯例,并且自己写的 Linux Makefile常常和自己的开发环境相关联,当系统环境变量或路径发生了变化后,Linux Makefile可能还要跟着改动。这样就造成了手工书写 Linux Makefile的诸多问题,automake恰好能非常好地帮助我们解决这些问题。
使用automake,程序开发者仅仅须要写一些简单的含有提前定义宏的文件。由autoconf依据一个宏文件生成configure,由automake依据还有一个宏文件生成Linux Makefile.in,再使用configure依据Linux Makefile.in来生成一个符合惯例的Linux Makefile。以下我们将具体介绍Linux Makefile的automake生成方法。
二、使用的环境
本文所提到的程序是基于Linux发行版本号:Fedora Core release 1。它包括了我们要用到的autoconf。automake。
三、从helloworld入手
我们从大家最常使用的样例程序helloworld開始。以下的过程假设简单地说来就是:新建三个文件:
- helloworld.c
- configure.in
- Linux Makefile.am
然后运行:
aclocal; autoconf; automake --add-missing; ./configure; make; ./helloworld
就能够看到Linux Makefile被产生出来,并且能够将helloworld.c编译通过。非常easy吧。几条命令就能够做出一个符合惯例的Linux Makefile。感觉怎样呀。如今開始介绍具体的过程:
1、建文件夹
在你的工作文件夹下建一个helloworld文件夹,我们用它来存放helloworld程序及相关文件,如在/home/my/build下:
$ mkdir helloword
$ cd helloworld
2、 helloworld.c
然后用你自己最喜欢的编辑器写一个hellowrold.c文件。如命令:vi helloworld.c。
使用以下的代码作为helloworld.c的内容。
- int main(int argc, char** argv)
- {
- printf("Hello, Linux World!/n");
- return 0;
- }
完毕后保存退出。如今在helloworld文件夹下就应该有一个你自己写的helloworld.c了。
3、生成configure
我们使用autoscan命令来帮助我们依据文件夹下的源码生成一个configure.in的模板文件。
命令:
- $ autoscan
- $ ls
- configure.scan helloworld.c
运行后在hellowrold文件夹下会生成一个文件:configure.scan,我们能够拿它作为configure.in的蓝本。如今将configure.scan改名为configure.in,而且编辑它,按以下的内容改动,去掉无关的语句:
configure.in内容開始
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT(Linux Makefile)
configure.in内容结束
然后运行命令aclocal和autoconf,分别会产生aclocal.m4及configure两个文件:
- $ aclocal
- $ls
- aclocal.m4 configure.in helloworld.c
- $ autoconf
- $ ls
- aclocal.m4 autom4te.cache configure configure.in helloworld.c
大家能够看到configure.in内容是一些宏定义。这些宏经autoconf处理后会变成检查系统特性、环境变量、软件必须的參数的shell脚本。autoconf 是用来生成自己主动配置软件源码脚本(configure)的工具。
configure脚本能独立于autoconf执行,且在执行的过程中。不须要用户的干预。要生成configure文件,你必须告诉autoconf怎样找到你所用的宏。方式是使用aclocal程序来生成你的aclocal.m4。
aclocal依据configure.in文件的内容。自己主动生成aclocal.m4文件。
aclocal是一个perl 脚本程序。它的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”。autoconf从configure.in这个列举编译软件时所须要各种參数的模板文件里创建configure。autoconf须要GNU m4宏处理器来处理aclocal.m4,生成configure脚本。
m4是一个宏处理器。将输入复制到输出。同一时候将宏展开。宏能够是内嵌的,也能够是用户定义的。除了能够展开宏,m4另一些内建的函数,用来引用文件,运行命令。整数运算,文本操作,循环等。
m4既能够作为编译器的前端。也能够单独作为一个宏处理器。
4、新建Linux Makefile.am
新建Linux Makefile.am文件。命令:$ vi Linux Makefile.am 内容例如以下:
- AUTOMAKE_OPTIONS=foreign
- bin_PROGRAMS=helloworld
- helloworldhelloworld_SOURCES=helloworld.c
automake会依据你写的Linux Makefile.am来自己主动生成Linux Makefile.in。Linux Makefile.am中定义的宏和目标,会指导automake生成指定的代码。比如,宏bin_PROGRAMS将导致编译和连接的目标被生成。
5、执行automake
命令:
- $ automake --add-missing
- configure.in: installing `./install-sh'
- configure.in: installing `./mkinstalldirs'
- configure.in: installing `./missing'
- Linux Makefile.am: installing `./depcomp'
automake会依据Linux Makefile.am文件产生一些文件,包括最重要的Linux Makefile.in。
6、运行configure生成Linux Makefile
- $ ./configure
- checking for a BSD-compatible install... /usr/bin/install -c
- checking whether build environment is sane... yes
- checking for gawk... gawk
- checking whether make sets $(MAKE)... yes
- checking for gcc... gcc
- checking for C compiler default output... a.out
- checking whether the C compiler works... yes
- checking whether we are cross compiling... no
- checking for suffix of executables...
- checking for suffix of object files... o
- checking whether we are using the GNU C compiler... yes
- checking whether gcc accepts -g... yes
- checking for gcc option to accept ANSI C... none needed
- checking for style of include used by make... GNU
- checking dependency style of gcc... gcc3
- configure: creating ./config.status
- config.status: creating Linux Makefile
- config.status: executing depfiles commands
- $ ls -l Linux Makefile
- -rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Linux Makefile
你能够看到,此时Linux Makefile已经产生出来了。
7、使用Linux Makefile编译代码
$ make if gcc -DPACKAGE_NAME="" -DPACKAGE_TARNAME="" -DPACKAGE_VERSION="" -DPACKAGE_STRING="" -DPACKAGE_BUGREPORT="" -DPACKAGE="helloworld" -DVERSION="1.0"
-I. -I. -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" /-c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; /then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; /else rm -f ".deps/helloworld.Tpo"; exit 1; /figcc -g -O2 -o
helloworld helloworld.o 执行helloworld$ ./helloworld Hello, Linux World!
这样helloworld就编译出来了,你假设按上面的步骤来做的话,应该也会非常easy地编译出正确的helloworld文件。你还能够试着使用一些其它的make命令,如make clean,make install,make dist,看看它们会给你什么样的效果。
感觉怎样?自己也能写出这么专业的Linux Makefile,老板一定会对你刮目相看。
四、深入浅出
针对上面提到的各个命令,我们再做些具体的介绍。
1、 autoscan
autoscan是用来扫描源码文件夹生成configure.scan文件的。
autoscan能够用文件夹名做为參数,但假设你不使用參数的话,那么 autoscan将觉得使用的是当前文件夹。autoscan将扫描你所指定文件夹中的源文件,并创建configure.scan文件。
2、 configure.scan
configure.scan包括了系统配置的基本选项,里面都是一些宏定义。我们须要将它改名为configure.in
3、 aclocal
aclocal是一个perl 脚本程序。aclocal依据configure.in文件的内容,自己主动生成aclocal.m4文件。
aclocal的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”。
4、 autoconf
autoconf是用来产生configure文件的。
configure是一个脚本,它能设置源程序来适应各种不同的操作系统平台,而且依据不同的系统来产生合适的Linux Makefile,从而能够使你的源码能在不同的操作系统平台上被编译出来。
configure.in文件的内容是一些宏,这些宏经过autoconf 处理后会变成检查系统特性、环境变量、软件必须的參数的shell脚本。configure.in文件里的宏的顺序并没有规定。可是你必须在全部宏的最前面和最后面分别加上AC_INIT宏和AC_OUTPUT宏。
在configure.ini中:#号表示凝视,这个宏后面的内容将被忽略。AC_INIT(FILE) 这个宏用来检查源码所在的路径。
AM_INIT_AUTOMAKE(PACKAGE, VERSION) 这个宏是必须的。它描写叙述了我们将要生成的软件包的名字及其版本:PACKAGE是软件包的名字。VERSION是版本。
当你使用make dist命令时,它会给你生成一个类似helloworld-1.0.tar.gz的软件发行包,当中就有相应的软件包的名字和版本。AC_PROG_CC这个宏将检查系统所用的C编译器。 AC_OUTPUT(FILE)这个宏是我们要输出的Linux Makefile的名字。
我们在使用automake时,实际上还须要用到其它的一些宏。但我们能够用aclocal 来帮我们自己主动产生。运行aclocal后我们会得到aclocal.m4文件。产生了configure.in和aclocal.m4 两个宏文件后。我们就能够使用autoconf来产生configure文件了。
5、 Linux Makefile.am
Linux Makefile.am是用来生成Linux Makefile.in的,须要你手工书写。Linux Makefile.am中定义了一些内容:AUTOMAKE_OPTIONS 这个是automake的选项。在运行automake时,它会检查文件夹下是否存在标准GNU软件包中应具备的各种文件。比如AUTHORS、ChangeLog、NEWS等文件。
我们将其设置成foreign时。automake会改用一般软件包的标准来检查。
bin_PROGRAMS这个是指定我们所要产生的可运行文件的文件名称。假设你要产生多个可运行文件,那么在各个名字间用空格隔开。 helloworld_SOURCES 这个是指定产生“helloworld”时所须要的源码。
假设它用到了多个源文件。那么请使用空格符号将它们隔开。
比方须要 helloworld.h,helloworld.c那么请写成helloworld_SOURCES= helloworld.h helloworld.c。
假设你在bin_PROGRAMS定义了多个可运行文件。则相应每一个可运行文件都要定义相对的filename_SOURCES。
6、 automake
我们使用automake --add-missing来产生Linux Makefile.in。
选项--add-missing的定义是“add missing standard files to package”,它会让automake增加一个标准的软件包所必须的一些文件。
我们用automake产生出来的Linux Makefile.in文件是符合GNU Linux Makefile惯例的,接下来我们仅仅要运行configure这个shell 脚本就能够产生合适的 Linux Makefile 文件了。
7、 Linux Makefile
在符合GNU Makefiel惯例的Linux Makefile中,包括了一些主要的预先定义的操作:make依据Linux Makefile编译源码,连接,生成目标文件,可运行文件。make clean清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可运行文件。
make install将编译成功的可运行文件安装到系统文件夹中,一般为/usr/local/bin文件夹。make dist产生公布软件包文件(即distribution package)。
这个命令将会将可运行文件及相关文件打包成一个tar.gz压缩的文件用来作为公布软件的软件包。它会在当前文件夹下生成一个名字类似“PACKAGE-VERSION.tar.gz”的文件。
PACKAGE和VERSION,是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION)。
make distcheck生成公布软件包并对其进行測试检查,以确定公布包的正确性。这个操作将自己主动把压缩包文件解开,然后运行configure命令。
而且运行make,来确认编译不出现错误,最后提示你软件包已经准备好,能够公布了。helloworld-1.0.tar.gz is ready for distributionmake distclean 类似make clean,但同一时候也将configure生成的文件所有删除掉。包含Linux Makefile。
五、结束语
通过上面的介绍,你应该能够非常easy地生成一个你自己的符合GNU惯例的Linux Makefile文件及相应的项目文件。假设你想写出更复杂的且符合惯例的Linux Makefile,你能够參考一些开放代码的项目中的configure.in和Linux Makefile.am文件,比方:嵌入式数据库sqlite,单元測试cppunit。
【推荐】
- Linux Makefile介绍使用的环境深入浅出
- 阐述Linux Makefile文件概念
- Linux Makefile由浅入深剖析
- linux makefile文件心得笔记
- Linux系统手工挂载和自己主动挂载
例解 Linux 下 make 命令(http://blog.csdn.net/hazir/article/details/18408007)
Linux 下 make 命令是系统管理员和程序猿用的最频繁的命令之中的一个。管理员用它通过命令行来编译和安装非常多开源的工具。程序猿用它来管理他们大型复杂的项目编译问题。本文我们将用一些实例来讨论 make 命令背后的工作机制。
Make 怎样工作的
对于不知道背后机理的人来说。make 命令像命令行參数一样接收目标。这些目标通常存放在以 “Makefile” 来命名的特殊文件里,同一时候文件也包括与目标相相应的操作。
很多其它信息,阅读关于 Makefiles 怎样工作的系列文章。
当 make 命令第一次运行时,它扫描 Makefile 找到目标以及其依赖。假设这些依赖自身也是目标,继续为这些依赖扫描 Makefile 建立其依赖关系,然后编译它们。
一旦主依赖编译之后。然后就编译主目标(这是通过 make 命令传入的)。
如今,如果你对某个源文件进行了改动,你再次运行 make 命令,它将仅仅编译与该源文件相关的目标文件。因此,编译完终于的可运行文件节省了大量的时间。
Make 命令实例
以下是本文所使用的測试环境:
OS —— Ubunut 13.04
Shell —— Bash 4.2.45
Application —— GNU Make 3.81
以下是project的内容:
以下是 Makefile 的内容:
如今我们来看 Linux 下一些 make 命令应用的实例:
1. 一个简单的样例
为了编译整个project,你能够简单的使用 make
或者在 make 命令后带上目标 all
。
你能看到 make 命令第一次创建的依赖以及实际的目标。
假设你再次查看文件夹内容。里面多了一些 .o 文件和运行文件:
如今。如果你对 test.c 文件做了一些改动,又一次使用 make 编译project:
你能够看到仅仅有 test.o 又一次编译了,然而还有一个 Test.o 没有又一次编译。
如今清理全部的目标文件和可运行文件 test,你能够使用目标 clean
:
你能够看到全部的 .o 文件和运行文件 test 都被删除了。
2. 通过 -B 选项让全部目标总是又一次建立
到眼下为止,你可能注意到 make 命令不会编译那些自从上次编译之后就没有更改的文件。可是,假设你想覆盖 make 这样的默认的行为,你能够使用 -B 选项。
以下是个样例:
你能够看到虽然 make 命令不会编译不论什么文件,然而 make -B
会强制编译全部的目标文件以及终于的运行文件。
3. 使用 -d 选项打印调试信息
假设你想知道 make 运行时实际做了什么,使用 -d 选项。
这是一个样例:
这是非常长的输出。你也看到我使用了 more
命令来一页一页显示输出。
4. 使用 -C 选项改变文件夹
你能够为 make 命令提供不同的文件夹路径。在寻找 Makefile 之前会切换文件夹的。
这是一个文件夹,如果你就在当前文件夹下:
$ ls
file file2 frnd frnd1.cpp log1.txt log3.txt log5.txt
file1 file name with spaces frnd1 frnd.cpp log2.txt log4.txt
可是你想执行的 make 命令的 Makefile 文件保存在 ../make-dir/ 文件夹下,你能够这样做:
你能看到 make 命令首先切到特定的文件夹下,在那运行,然后再切换回来。
5. 通过 -f 选项将其他文件看作 Makefile
假设你想将重命名 Makefile 文件。比方取名为 my_makefile 或者其他的名字。我们想让 make 将它也当成 Makefile。能够使用 -f 选项。
通过这样的方法,make 命令会选择扫描 my_makefile 来取代 Makefile。
原文链接:http://linoxide.com/how-tos/linux-make-command-examples/
Makefile自己主动生成工具-----autotools的使用(具体)
相信每一个学习Linux的人都知道Makefile,这是一个非常实用的东西,可是编写它是比較复杂。今天介绍一个它的自己主动生成工具,autotools的使用。
非常多GNULinux的的软件都是用它生成Makefile的。包含我们非常熟悉的Linux内核源码。
1、准备:须要工具
autoscan
aclocal
autoheader
automake
autoconf
auto make
在终端敲入命令。哪个没有安装哪个。通常是第一个autoscan没有。其他的我用的Ubuntu10.04下所有都有
2、測试程序编写:建立文件夹:mkdir include src
编敲代码:include/str.h
- #include <stdio.h>
- int str(char *string);
- #include "str.h"
- //print string
- int str(char *string){
- printf("\n----PRINT STRING----\n\"%s\"\n",string);
- return 0;
- }
- //interface of this program
- int main(int argc , char **argv){
- char str_read[1024];
- printf("Please INPUT something end by [ENTER]\n");
- scanf("%s",str_read);
- return str(str_read );
- }
configure.ac是automake的输入文件。所以必须先生成该文件。
运行命令:
- [root@localhost str]# ls
- include src
- [root@localhost str]# autoscan
- autom4te: configure.ac: no such file or directory
- autoscan: /usr/bin/autom4te failed with exit status: 1
- [root@localhost str]# ls
- autoscan.log configure.scan include src
- [root@localhost str]# cp configure.scan configure.ac
- # -*- Autoconf -*-
- # Process this file with autoconf to produce a configure script.
- AC_PREREQ(2.59)
- AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
- AC_CONFIG_SRCDIR([include/str.h])
- AC_CONFIG_HEADER([config.h])
- # Checks for programs.
- AC_PROG_CC
- # Checks for libraries.
- # Checks for header files.
- # Checks for typedefs, structures, and compiler characteristics.
- # Checks for library functions.
- AC_OUTPUT
- AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
- AC_INIT(str,0.0.1, [bug@sounos.org])
然后加入两句话:
AM_INIT_AUTOMAKE
AC_CONFIG_FILES([Makefile])
结果例如以下:(两句话不是在一起的)
- # -*- Autoconf -*-
- # Process this file with autoconf to produce a configure script.
- AC_PREREQ(2.59)
- #AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
- AC_INIT(str, 0.0.1, [bug@sounos.org])
- AM_INIT_AUTOMAKE
- AC_CONFIG_SRCDIR([include/str.h])
- AC_CONFIG_HEADER([config.h])
- # Checks for programs.
- AC_PROG_CC
- # Checks for libraries.
- # Checks for header files.
- # Checks for typedefs, structures, and compiler characteristics.
- # Checks for library functions.
- AC_CONFIG_FILES([Makefile])
- AC_OUTPUT
- [root@localhost str]# aclocal
- /usr/share/aclocal/libfame.m4:6: warning: underquoted definition of AM_PATH_LIBFAME
- run info '(automake)Extending aclocal'
- or see http://sources.redhat.com/automake/automake.html#Extending-aclocal
- [root@localhost str]# vi Makefile.am
- #Makefile.am
- bin_PROGRAMS = str
- str_SOURCES = include/str.h src/str.c
- str_CPPFLAGS = -I include/
各个选项意思比較直观。不多说。
6、autoheader
- [root@localhost str]# autoheader
- * install-sh
- * missing
- * INSTALL
- * NEWS
- * README
- * AUTHORS
- * ChangeLog
- * COPYING
- * depcomp
- * install-sh
- * missing
- * INSTALL
- * COPYING
- * depcomp
- [root@localhost str]# touch NEWS README AUTHORS ChangeLog
- [root@localhost str]# automake -a
- configure.ac: installing `./install-sh'
- configure.ac: installing `./missing'
- Makefile.am: installing `./INSTALL'
- Makefile.am: installing `./COPYING'
- Makefile.am: installing `./compile'
- Makefile.am: installing `./depcomp'
- [root@localhost str]# autoconf
- [root@localhost str]# ls
- aclocal.m4 autoscan.log config.h.in configure.scan include Makefile.am NEWS
- AUTHORS ChangeLog configure COPYING INSTALL Makefile.in README
- autom4te.cache compile configure.ac depcomp install-sh missing src
运行./configure
- [root@localhost str]# ./configure --prefix=/u
- checking for a BSD-compatible install... /usr/bin/install -c
- checking whether build environment is sane... yes
- checking for gawk... gawk
- checking whether make sets $(MAKE)... yes
- checking for gcc... gcc
- checking for C compiler default output file name... a.out
- checking whether the C compiler works... yes
- checking whether we are cross compiling... no
- checking for suffix of executables...
- checking for suffix of object files... o
- checking whether we are using the GNU C compiler... yes
- checking whether gcc accepts -g... yes
- checking for gcc option to accept ANSI C... none needed
- checking for style of include used by make... GNU
- checking dependency style of gcc... gcc3
- configure: creating ./config.status
- config.status: creating Makefile
- config.status: creating config.h
- config.status: config.h is unchanged
- config.status: executing depfiles commands
- [root@localhost str]# make
- make all-am
- make[1]: Entering directory `/data/devel/c/str'
- if gcc -DHAVE_CONFIG_H -I. -I. -I. -I include/ -g -O2 -MT str-str.o -MD -MP -MF ".deps/str-str.Tpo" -c -o str-str.o `test -f 'src/str.c' || echo './'`src/str.c; \
- then mv -f ".deps/str-str.Tpo" ".deps/str-str.Po"; else rm -f ".deps/str-str.Tpo"; exit 1; fi
- gcc -g -O2 -o str str-str.o
- make[1]: Leaving directory `/data/devel/c/str'
- [root@localhost str]# ./str
- Please INPUT something end by [ENTER]
- abcksdhfklsdklfdjlkfd
- ----PRINT STRING----
- "abcksdhfklsdklfdjlkfd"
运行 make install:
- [root@localhost str]# make install
- make[1]: Entering directory `/data/devel/c/str'
- test -z "/u/bin" || mkdir -p -- "/u/bin"
- /usr/bin/install -c 'str' '/u/bin/str'
- make[1]: Nothing to be done for `install-data-am'.
- make[1]: Leaving directory `/data/devel/c/str'
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++ 代码
这个程序非常easy,所以这里跳过程序设计环节。这里直接展示程序的 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 来构建程序
代码非常easy,能够直接使用命令行来构建程序。假设你对 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 的优点就是,如果能非常好地确定依赖关系,那么就不须要在每次构建时把全部的源文件都又一次编译一次。可是随着项目的代码的增长。即使在一个良好的模块化设计中,手工维护依赖关系也是一件非常繁琐并且非常easy出错的工作。
比如,如果我们须要添加一个 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 。