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開始。以下的过程假设简单地说来就是:新建三个文件:
1.
2. helloworld.c
3. configure.in
4. Linux Makefile.am
5.
然后运行:
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的内容。
1.
2. int main(int argc, char** argv)
3. {
4. printf("Hello, Linux World!/n");
5. return 0;
6. }
7.
完毕后保存退出。如今在helloworld文件夹下就应该有一个你自己写的helloworld.c了。
3、生成configure
我们使用autoscan命令来帮助我们依据文件夹下的源码生成一个configure.in的模板文件。
命令:
1.
2. $ autoscan
3. $ ls
4. configure.scan helloworld.c
5.
运行后在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两个文件:
1.
2. $ aclocal
3. $ls
4. aclocal.m4 configure.in helloworld.c
5. $ autoconf
6. $ ls
7. aclocal.m4 autom4te.cache configure configure.in helloworld.c
8.
大家能够看到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 内容例如以下:
1.
2. AUTOMAKE_OPTIONS=foreign
3. bin_PROGRAMS=helloworld
4. helloworldhelloworld_SOURCES=helloworld.c
5.
automake会依据你写的Linux Makefile.am来自己主动生成Linux Makefile.in。Linux Makefile.am中定义的宏和目标,会指导automake生成指定的代码。比如,宏bin_PROGRAMS将导致编译和连接的目标被生成。
5、执行automake
命令:
1.
2. $ automake --add-missing
3. configure.in: installing `./install-sh'
4. configure.in: installing `./mkinstalldirs'
5. configure.in: installing `./missing'
6. Linux Makefile.am: installing `./depcomp'
7.
automake会依据Linux Makefile.am文件产生一些文件,包括最重要的Linux Makefile.in。
6、运行configure生成Linux Makefile
1.
2. $ ./configure
3. checking for a BSD-compatible install... /usr/bin/install -c
4. checking whether build environment is sane... yes
5. checking for gawk... gawk
6. checking whether make sets $(MAKE)... yes
7. checking for gcc... gcc
8. checking for C compiler default output... a.out
9. checking whether the C compiler works... yes
10. checking whether we are cross compiling... no
11. checking for suffix of executables...
12. checking for suffix of object files... o
13. checking whether we are using the GNU C compiler... yes
14. checking whether gcc accepts -g... yes
15. checking for gcc option to accept ANSI C... none needed
16. checking for style of include used by make... GNU
17. checking dependency style of gcc... gcc3
18. configure: creating ./config.status
19. config.status: creating Linux Makefile
20. config.status: executing depfiles commands
21. $ ls -l Linux Makefile
22. -rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Linux Makefile
23.
你能够看到,此时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 下 make 命令
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的内容:
$ ls
anotherTest.c Makefile test.c test.h
以下是 Makefile 的内容:
all: test
test: test.o anotherTest.o
gcc -Wall test.o anotherTest.o -o test
test.o: test.c
gcc -c -Wall test.c
anotherTest.o: anotherTest.c
gcc -c -Wall anotherTest.c
clean:
rm -rf *.o test
如今我们来看 Linux 下一些 make 命令应用的实例:
1. 一个简单的样例
为了编译整个project,你能够简单的使用 make
或者在 make 命令后带上目标 all
。
$ make
gcc -c -Wall test.c
gcc -c -Wall anotherTest.c
gcc -Wall test.o anotherTest.o -o test
你能看到 make 命令第一次创建的依赖以及实际的目标。
假设你再次查看文件夹内容。里面多了一些 .o 文件和运行文件:
$ ls
anotherTest.c anotherTest.o Makefile test test.c test.h test.o
如今。如果你对 test.c 文件做了一些改动,又一次使用 make 编译project:
$ make
gcc -c -Wall test.c
gcc -Wall test.o anotherTest.o -o test
你能够看到仅仅有 test.o 又一次编译了,然而还有一个 Test.o 没有又一次编译。
如今清理全部的目标文件和可运行文件 test,你能够使用目标 clean
:
$ make clean
rm -rf *.o test
$ ls
anotherTest.c Makefile test.c test.h
你能够看到全部的 .o 文件和运行文件 test 都被删除了。
2. 通过 -B 选项让全部目标总是又一次建立
到眼下为止,你可能注意到 make 命令不会编译那些自从上次编译之后就没有更改的文件。可是,假设你想覆盖 make 这样的默认的行为,你能够使用 -B 选项。
以下是个样例:
$ make
make: Nothing to be done for `all’.
$ make -B
gcc -c -Wall test.c
gcc -c -Wall anotherTest.c
gcc -Wall test.o anotherTest.o -o test
你能够看到虽然 make 命令不会编译不论什么文件,然而 make -B
会强制编译全部的目标文件以及终于的运行文件。
3. 使用 -d 选项打印调试信息
假设你想知道 make 运行时实际做了什么,使用 -d 选项。
这是一个样例:
$ make -d | more
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
This program built for x86_64-pc-linux-gnu
Reading makefiles…
Reading makefile `Makefile’…
Updating makefiles….
Considering target file `Makefile’.
Looking for an implicit rule for `Makefile’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.o’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.c’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.cc’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.C’.
Trying pattern rule with stem `Makefile’.
Trying implicit prerequisite `Makefile.cpp’.
Trying pattern rule with stem `Makefile’.
--More--
这是非常长的输出。你也看到我使用了 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 -C ../make-dir/
make: Entering directory `/home/himanshu/practice/make-dir’
make: Nothing to be done for `all’.
make: Leaving directory `/home/himanshu/practice/make-dir
你能看到 make 命令首先切到特定的文件夹下,在那运行,然后再切换回来。
5. 通过 -f 选项将其他文件看作 Makefile
假设你想将重命名 Makefile 文件。比方取名为 my_makefile 或者其他的名字。我们想让 make 将它也当成 Makefile。能够使用 -f 选项。
make -f my_makefile
通过这样的方法,make 命令会选择扫描 my_makefile 来取代 Makefile。
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
[cpp] view plain copy
- #include <stdio.h>
- int str(char *string);
编敲代码:src/str.c
[cpp] view plain copy
- #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 );
- }
3、生成configure.ac
configure.ac是automake的输入文件。所以必须先生成该文件。
运行命令:
[cpp] view plain copy
- [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
改 configure.ac
[cpp] view plain copy
- # -*- 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
改动
[cpp] view plain copy
- AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
改为:
[cpp] view plain copy
- AC_INIT(str,0.0.1, [bug@sounos.org])
当中:FULL-PACKAGE-NAME 为程序名称,VERSION为当前版本号。 BUG-REPORT-ADDRESS为bug汇报地址
然后加入两句话:
AM_INIT_AUTOMAKE
AC_CONFIG_FILES([Makefile])
结果例如以下:(两句话不是在一起的)
[cpp] view plain copy
- # -*- 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
4、运行aclocal
[cpp] view plain copy
- [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
5、制作Makefile.am
[cpp] view plain copy
- [root@localhost str]# vi Makefile.am
- #Makefile.am
- bin_PROGRAMS = str
- str_SOURCES = include/str.h src/str.c
- str_CPPFLAGS = -I include/
automake 这个命令须要用到这个配置文件。
各个选项意思比較直观。不多说。
6、autoheader
[cpp] view plain copy
- [root@localhost str]# autoheader
7、automake必须文件:
[cpp] view plain copy
- * install-sh
- * missing
- * INSTALL
- * NEWS
- * README
- * AUTHORS
- * ChangeLog
- * COPYING
- * depcomp
当中,下面文件在运行automake -a的时候会自己主动生成
[cpp] view plain copy
- * install-sh
- * missing
- * INSTALL
- * COPYING
- * depcomp
所以,接下来手动生成剩下的文件
[cpp] view plain copy
- [root@localhost str]# touch NEWS README AUTHORS ChangeLog
8、运行automake -a
[cpp] view plain copy
- [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'
9、autoconf
[cpp] view plain copy
- [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
10、运行測试:
运行./configure
[cpp] view plain copy
- [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
运行 make
[cpp] view plain copy
- [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'
此时已经生成了 str(可执行文件名称字在前面设置Makefile.am的參数时候去顶)这个,能够通过./str直接看到执行结果
[cpp] view plain copy
- [root@localhost str]# ./str
- Please INPUT something end by [ENTER]
- abcksdhfklsdklfdjlkfd
- ----PRINT STRING----
- "abcksdhfklsdklfdjlkfd"
只是这里我们都做一步,把它安装到系统里面,这样我们仅仅要在终端输入str就能够执行程序了。
运行 make install:
[cpp] view plain copy
- [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'
接下来你能够make clean 清除安装的那些.o 文件了。这样生成了一个自己主动的Makefile。
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
文件夹中的代码。