作为Linux下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便。一般情况下,大家都是手工写一个简单Makefile,如果要想写出一个符合自由软件惯例的Makefile就不那么容易了。
在本文中,将给大家介绍如何使用 autoconf和automake两个工具来帮助我们自动地生成符合自由软件惯例的Makefile,这样就可以象常见的GNU程序一样,只要使用“./configure”,“make”,“make install”就可以把程序安装到Linux系统中去了。这将特别适合想做开放源代码软件的程序开发人员,又或如果你只是自己写些小的Toy程序,那么这个文章对你也会有很大的帮助。
1.准备好程序 main.c
main.c
#include
int main()
{
print("hello!");
return 0;
}
#include
int main()
{
print("hello!");
return 0;
}
#include
int main()
{
print("hello!");
return 0;
}
#include
int main()
{
print("hello!");
return 0;
}
2.准备好脚本 autogen.sh
autogen.sh
#!/bin/sh
aclocal
autoheader
automake --add-missing
autoconf
3.运行autoscan 生成configure.scan
4.mv configure.scan configure.ac
5.在configure.ac 中修改
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
为 AC_INIT(hello, 0.0.1, [email protected])
并添加 AM_INIT_AUTOMAKE AC_CONFIG_FILES([Makefile])
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_INIT(hello, 0.0.1, [email protected])
AC_CONFIG_SRCDIR([main.c])
AM_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE
# 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_FUNC_MALLOC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
6.准备好Makefile.am
Makefile.am
AUTOMAKE_OPTIONS = foreign
bin_PROGRAMS = hello
hello_SOURCES = main.c
#noinst_HEADERS =
7.运行autogen.sh 哼哼, 自动生成了makefile !
以上文件放在同一目录下, 运行./autogen.sh && ./configure && make && ./test
利用 configure 所产生的 Makefile文件有几个预先设定的目标可供使用,这里只用几个简述如下:
make all
产生设定的目标,既范例中的可执行文件。只敲入make 也可以,此时会开始编译源代码,然后连接并产生执行文件。
make clean
清除之前所编译的可执行文件及目标文件(object file, *.o)。
make distclean
除了清除可执行文件和目标文件以外,也把 configure 所产生的 Makefile 清除掉。 通常在发布软件前执行该命令。
make install
将程序安装到系统中,若源码编译成功,且执行结果正确,便可以把程序安装到系统预先设定的执行文件存放路径中,若用 bin_PROGRAMS 宏的话,程序会被安装到 /usr/local/bin下。
make dist
将程序和相关的文档包装为一个压缩文档以供发布 (distribution) 。执行完在目录下会产生一个以 PACKAGE-VERSION.tar.gz 为名称的文件!
make distcheck
和 make dist 类似,但是加入检查包装以后的压缩文件是否正常,这个目标除了把程序和相关文档包装成tar.gz 文件外,还会自动把这个压缩文件解开,执行 configure,并执行 make all ,确认编译无错误以后,方显示这个 tar.gz 文件已经准备好并可以发布了。
下面再来看看多个源文件的例子。
如何写用code1.c, code2.c生成 prog1的configure.in和Makefile.am。首先建立一个项目文件夹testproj。在testproj下建立dir1目录。
[kang@node16 kang]$ mkdir testproj
[kang@node16 kang]$ mkdir dir1
然后在dir1目录中分别建立code1.c, code1.h, code2.c, code2.h,下面源代码都只由几个简单的语句组成,以便说明问题。
下面是code1.h:
#include
void foo_a();
下面是code1.c:
#include "code1.h"
void foo_a()
{
printf("This is code1.\n");
}
下面是code2.h:
#include
void foo_b();
下面是code2.c,这里让code.c作为prog1的入口点:
#include "code1.h"
#include "code2.h"
void foo_b()
{
printf("This is code2.\n");
}
int main()
{
foo_a();
foo_b();
}
建立好这几个文件之后,下面就可以正式建立configure.in和Makefile.am了。
首先在testproj目录下建立configure.in文件:
#指定项目的一个源文件
AC_INIT(dir1/code2.c)
#指定项目名称和版本号
AM_INIT_AUTOMAKE(prog1, 0.0.1)
#检查编译器
AC_PROG_CC
#输出Makefile文件
AC_OUTPUT(Makefile dir1/Makefile)
同时建立testproj目录下的Makefile.am文件,这个文件很简单,就一句话:
SUBDIRS=dir1
然后建立dir1目录下的Makefile.am文件,这才是真正起作用的Makefile.am文件:
bin_PROGRAMS=prog1
prog1_SOURCES=code1.c code2.c
完成之后,为了方便操作,再写一个autogen.sh文件,保存在testproj目录下。
#!/bin/sh
aclocal
automake --add-missing
autoconf
./autogen.sh即可在testproj目录下生成configure和makefile文件,同时在dir1目录下也会生成一个makefile文件。现在在testproj目录下执行make,屏幕将显示如下信息:
[kang@node16 testproj]$ make
Making all in dir1
make[1]: Entering directory `/home/kang/testproj/dir1'
gcc -DPACKAGE=\"prog1\" -DVERSION=\"0.0.1\" -I. -I. -g -O2 -c code1.c
code1.c:6:2: warning: no newline at end of file
gcc -DPACKAGE=\"prog1\" -DVERSION=\"0.0.1\" -I. -I. -g -O2 -c code2.c
code2.c:13:2: warning: no newline at end of file
gcc -g -O2 -o prog1 code1.o code2.o
make[1]: Leaving directory `/home/kang/testproj/dir1'
make[1]: Entering directory `/home/kang/testproj'
make[1]: Nothing to be done for `all-am'.
make[1]: Leaving directory `/home/kang/testproj'
进入dir1目录,就可以看到生成的prog1程序。如果再执行make install,prog1将被安装到缺省的/usr/local/bin目录下去。
这就是一个最简单的configure.in和Makefile.am的编写情况。你如果不熟悉,最好自己动手做一遍,复杂的configure.in和Makefile.am都是在这个基础上扩充的。
在此基础上,如果要同时实现code1.c, code2.c生成 prog1, 而 code3.c生成prog2。由于code1.c,code2.c,code3.c都在同一个目录,只要改写dir1目录下的Makefile.am就可以了。
为了便于说明问题,首先要在dir1目录下增加一个code3.h 和code3.c文件。
下面是code3.h:
#include
void foo_c();
下面是code3.c:
#include "code3.h"
void foo_c()
{
printf("This is code3.\n");
}
int main()
{
foo_c();
}
然后修改dir1目录下的Makefile.am文件:
bin_PROGRAMS=prog1 prog2
prog1_SOURCES=code1.c code2.c
prog2_SOURCES=code3.c
再重新执行一次autogen.sh。make之后,在dir1目录下就会同时存在prog1和prog2两个程序。
那么,两个处在不同子目录下的文件如何共同生成一可执行文件呢?一般互相引用的源程序都是放在同一个目录下的,如果要放在不同的目录,可以把要引用的源文件编译成静态库文件。为便于说明问题,准备了如下文件:
在testproj目录下新建dir2目录,保存code4.h和code4.c文件。
下面是code4.h:
#include
void foo_d();
下面是code4.c:
#include "code1.h"
#include "code4.h"
void foo_d()
{
printf("This is code4.\n");
}
int main()
{
foo_a();
foo_d();
}
修改testproj目录下的configure.in文件。
#指定项目的一个源文件
AC_INIT(dir2/code4.c)
#指定项目名称和版本号
AM_INIT_AUTOMAKE(myproject, 0.0.1)
#检查编译器
AC_PROG_CC
#检查ranlib
AC_PROG_RANLIB
#输出Makefile文件
AC_OUTPUT(Makefile dir1/Makefile dir2/Makefile)
同时修改testproj目录下的Makefile.am文件。
SUBDIRS = dir1 dir2
在dir1目录下修改Makefile.am文件。这时是将code1.c编译成一个不安装(noinst)的静态库文件。
noinst_LIBRARIES=libcode1.a
libcode1_a_SOURCES=code1.c
在dir2目录下添加一个Makefile.am文件。
INCLUDES= -I../dir1
bin_PROGRAMS=prog4
prog4_SOURCES=code4.c
prog4_LDADD=../dir1/libcode1.a
然后执行autogen.sh就可以了。
补充问答。
问题1:如何指定特定的编译器(AC_PROG_CC只会检查C编译器?)?
答:首先在configure.in中加上对特殊编译器的检查,如果检查不到,则configure时会停止并给出“Couldn't find mpicc.”的出错信息:
#检查mpicc编译器
AC_CHECK_PROG(MPICC,mpicc,yes,no)
if test "$MPICC" = no; then
AC_MSG_ERROR([Couldn't find mpicc.])
fi
然后把需要用mpicc编译的源程序放在一个目录下面。在这个目录中先用上面的方法写Makefile.am文件。然后再加上下面这部分:
CC=mipcc
CFLAGS=
这样用自己定义的编译器和编译标志取代系统定义的编译器和编译标志。
问题2:因为有部分程序不能公开发布,所以我想将它们生成库,(*.la, 通过libtool 生成),但我解开生成的压缩文件,发现库文件没在压缩文件里面,所以make的时候会有些函数找不到,产生错误。请问如何能让带库文件的文件夹也一起包括的压缩文件里去呢?
答:如果代码中用了库文件,要先编译安装库文件,再编译代码。你可以把编译成库文件的代码独立出来单独打包,这样也便于管理。前面给的例子中库文件是只编译不安装的。下面给一个动态库安装的例子。
问题3:我做的Makefile安装时默认安装在/usr/local/bin底下,而我想安装在/usr/sbin下,不知道该怎么改?
答:这个问题不需要修改configure.in或者Makefile.am文件。用./configure --help可以看到有一个bindir参数可以指定可执行文件的安装目录。你只需要执行./configure --bindir=/usr/sbin就可以了。