Chinaunix首页 | 论坛 | 博客
  • 博客访问: 63336
  • 博文数量: 20
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 190
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-12 09:53
文章分类

全部博文(20)

文章存档

2014年(20)

我的朋友

分类: LINUX

2014-06-10 14:16:38

      这段时间要继续研究Jabber服务器,因为要在源码上做一些修改,还要添加一些功能,所以需要修改源码中的makefile文件.

      之前接触过一些, 也一直也不是太明白,这次借此机会再搞得明白一点儿.以下是找到一篇比较实用,讲得也比较透彻的文章,特此转载一下.

 

GNU make允许将一个软件项目的代码分开放在不同的源文件里,有改动的时候可以只对改动的文件重新编译,然后重新连接,这种编译管理方法提高了生成目标的效率。make要调用一个makefile文件来实现。

Makefile的编写是使用make的关键问题。当工程里面包含的很多源文件,库,文件放在不同的子目录时,手动书写makefile文件不方便且容易出错。一般情况下我们用autoconf和automake自动生成makefile文件。

自动生成makefile流程

如图所示为automake,autoconf生成makefile的过程(简化)。

                                    程序源码
                                           |
                                     autoscan*
                                           |
                                configure.scan
                                           |
                                    编译修改*
                                            | 

             makefile.am   configure.in --aclocal*--> aclocal.m4
                           \  ____  /           \  __________  /
                                 \ /                           \ /
                         automake*             autoconf*
                               |                              |
                            makefile.in       configure
                                          \  ____  /

                                               \  /
                                        ./configure*
                                                |
                                          makefile

为一个项目源文件生成makefile并make的步骤如下:

操作在包含源文件的项目目录下进行。

(1). 运行autoscan,生成文件configure.scan。

(2). 修改configure.scan,改名为configure.in。

(3).运行autoheader,生成文件configure.h.in(现在一般改为configure.ac)。configure.in里有宏AC_CONFIG_HEADER()时用。

(4).运行libtoolize,生成一些支持文件,ltmain.sh。需要用libtool生成共享库用。

(5).运行allocal,生成aclocal.m4。

(6). 运行autoconf,生成configure。

(7).为源文件编写makefie.am,每一个包含源文件的目录和子目录都有一个makefile.am。

(8).运行automake,生成makefile.in,每个包含makefile.am的子目录都生成makefile.in。

automake -a选项可以补齐文件config.guess,config.sub,install-sh,missing,depcomp。

(9).运行./configure,生成config.status,config.h,makefile。

(10).运行make,生成中间文件对象文件,库文件,最后生成可执行文件。

(11).运行make install,相应的可执行文件,库文件,头文件拷贝到系统相应位置。


自动生成makefile例子

这个例子共有三个C文件,main.c,add/add.c和sub/sub.c。源代码如下:

/*main.c*/
#include
int main(void)
{
printf("%d\n",add(sub(100,5),1));
return 0;
}

/* add/add.c */
int add(int x,int y)
{
return x+y;
}

/* sub/sub.c */
int sub(int x,int y)
{
return x-y;
}

这个例子中add.c和sub.c被编译成动态连接库,然后main.c与这两个库连接生成可执行文件。

1.手动输入configure.in和makefile.am

Q:自动生成makefile需要手动输入什么文件,作用是什么?

按照上面的步骤执行,需要手动输入的文件只有两类configure.in和makefile.am。

(1).手动修改configure.in

autoscan运行后configure.scan文件为(系统不一样可能略有会不同)

# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.63])
AC_INIT([FULL-PACKAGE-NAME],[VERSION],[BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT

手动修改为configure.in:

# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.63])
AC_INIT(hellobb,1.0,[])
AM_INIT_AUTOMAKE(hellobb,1.0)

AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.

AC_OUTPUT(Makefile add/Makefile sub/Makefile)

其中宏AC_INIT和AC_OUTPUT是必需的,AC_INIT放在开头,AC_OUTPUT放在结尾。

AC_INIT:说明软件包的名字,版本等。

AC_OUTPUT:说明生成的shell脚本文件configure运行后输出的文件。

AM_INIT_AUTOMAKE:用automake需要的宏。

AC_PROG_CC:决定要使用的C编译器。如果环境变量CC没有值,检查gcc和cc,别的C编译器。设置变量CC的值为找到的编译器名称。

AC_PROG_LIBTOOL:检查LIBTOOL。

AC_CONFIG_SRCDIR([main.c]):./configure检查在给它的目录里是否有main.c文件。

AC_CONFIG_HEADER([config.h]):./configure从config.h.in中生成config.h文件,config.h文件是包含了很多#define宏的c头文件。当编译文件的时候,用一个宏-DHAVE_CONFIG_H代替原来需要用-Dmacro传递的所有预处理宏集合。

例如屏蔽掉这句#AC_CONFIG_HEADER([config.h]),make时编译main.c的命令是

gcc -DPACKAGE_NAME=\"hellobb\" -DPACKAGE_TARNAME=\"hellobb\" -DPACKAGE-VERSION=\"1.0\" -DPACKAGE_STRING=\"hellobb\ 1.0\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"hellobb\" -DVERSION=\"1.0\" -DSTDC_HEADERS=1 _DHAVE_SYS_TYPES_H=1 -DHAVE_UNISTDLIB_H=1 -DHAVE_STING_H=1 -DHAVE_MEMORY_H=1 -dHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\"./libs/\" -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.c main.c

如果启用AC_CONFIG_HEADER([config.h]),make时编译main.c的命令是

gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c

上面那些宏-Dmacro都包含在config.h里了,我们只需要给编译器传递-DHAVE_CONFIG_H就行了。

(2). 手动输入makefile.am

每个包含源文件的子目录下都需要一个makefile.am。

add/makefile.am是(sub/makefile.am类似):

lib_LTLIBRARIES = libadd.la //生成共享库libadd.la
libadd_la_SOURCES = add.c //共享库libadd.la依赖的源文件

项目根目录下的makefile.am是:

AUTOMAKE_OPTIONS = foreign
SUBDIRS = add sub //子目录,递归处理子目录的makefile.am
bin_PROGRAMS = main //生成可执行文件main
main_SOURCES = main.c //可执行文件main依赖的源文件
main_LDADD = add/libadd.la sub/libsub.la //可执行文件main连接时需要的库文件


(3).automake时运行automake -a:

automake -a即 automake -add-missing:如果目录下缺少文件config.guess,config.sub,missing,depcomp,install-sh,automake会自动从系统中获取这些文件,这里是创建这些文件,让它们是指向系统中对应文件的连接文件。

2.过程中的输入输出文件

Q:./configure需要什么输入文件,生成什么文件?

Q:make需要什么输入文件,中间生成的文件放哪?

为了更清楚整个处理过程autoscan,autoheader,automake,autoconf做了什么事情,观察每一步执行过程中需要的输入文件和产生的输出文件。

输入源文件:main.c add/add.c sub/sub.c

输入makefile.am :makefile.am add/makefile.am sub/makefile.am

autoscan :configure.scan

autoscan.log

手动修改:(入:configure.scan)

configure.in //指出生成的包名字和版本号,./configure输出文件,配置预处理头文件,
指出需要检查的程序、头文件、类型和结构、库函数等

autoheader :config.h.in

libtoolize :ltmain.sh

aclocal :aclocal.m4 //autoconf相关的宏,在本地m4中定义好的。

autom4te.cache

autoconf : (入:configure.in, aclocal.m4)

configure

automake -a :(入:configure.in makefile.am add/makefile.am sub/makefile.am)

config.guess //猜测出一个规范的系统名字。

config.sub //把平台和系统别名映射成规范形式:
CPU_TYPE_MANUFACTORER-KERNEL-OPERATING-SYSTEM

install-sh //安装程序,脚本或数据文件

missing // Common stub for a few missing GNU program while installing.

depcomp //Compile a program generating depedencies as side-effects.

makefile.in add/makefile.in sub/makefile.in

./configure :(入:configure makefile.in add/makefile.in sub/makefile.in config.h.in )

(入:config.guess config.sub install-sh missing depcomp ltmainsh)

config.status //configure运行时找到的系统相关变量都存放在这里,
./configure的最后就是运行shell脚本config.status

config.h //包含编译时要传给编译器的所有预处理宏

config.log //包含了./configure运行时生成的所有信息,供调试时查看

makefile add/makefile sub/makefile

./deps/main.Po add/.deps/add.Plo sub/.deps.sub.Plo //空的dummy依赖文件

stamp-h1

make :(入:config.status config.h makefile add/makefile sub/makefile)

libtool

add.lo add.o libadd.la

add/.libs下add.o libadd.a libadd.la libsub.lai libadd.so libadd.so.0 libadd.so.0.0.0

sub.lo sub.o libsub.la

sub/.libs下sub.o libsub.a libsub.la libsub.lai libsub.so libsub.so.0 libsub.so.0.0.0

./deps/main.Po add/.deps/add.Plo sub/.deps.sub.Plo //更新依赖文件,由于gcc有-MT,-MD,-MP,-MF选项

main.o

main


3.configure和make执行过程

Q:./configure都作了些什么事情,输入的文件都用来做什么,config.status的作用?

Q:make的运行过程,生成的文件放在哪里有谁来指定?

configure运行

configure是一个shell脚本文件,由autoconf生成,它自动为源码包配置编译连接选项,适应不同的硬件平台和POSIX操作系统,输出所需要的Makefile。

上面的例子中./configure运行期间输出如下图。

configure.in中宏对configure的影响:

宏AC_INIT,AM_INIT_AUTOMAKE,AC_PROG_CC对应图中的白色部分。

宏AC_PROG_LIBTOOL对应图中的灰色部分。

宏AC_OUTPUT在图中的桃红色部分。

桃红色部分为最后configure生成config.status文件,并执行它。configure主管检查你的系统,把结果存放到config.status中,config.status根据它的检查结果实际执行正确的动作。

configure检查与系统相关的一系列变量,这些变量存储到文件config.status中,供makefile调用。这些变量包括编译连接时需要的程序,这些程序在系统中的位置(目录),调用这些程序的选项,比如编译器的目录,编译器的选项-g是否支持等。

configure能猜出它运行的系统的规范名字cpu-vendor-os,它通过运行脚本文件config.guess输出变量uname来猜出。

configure能识别很多系统名字的别名,它通过运行脚本文件config.sub把系统名字变成规范名字。

# ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
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 ISO C89... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
checking build system type... i686-pc-linux-gnu
checking host system type... i686-pc-linux-gnu
checking how to print strings... printf
checking for a sed that does not truncate output... /bin/sed
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for fgrep... /bin/grep -F
checking for ld used by gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1966080
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for ar... ar
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from gcc object... ok
checking how to run the C preprocessor... gcc -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if gcc supports -fno-rtti -fno-exceptions... no
checking for gcc option to produce PIC... -fPIC -DPIC
checking if gcc PIC flag -fPIC -DPIC works... yes
checking if gcc static flag -static works... no
checking if gcc supports -c -o file.o... yes
checking if gcc supports -c -o file.o... (cached) yes
checking whether the gcc linker (/usr/bin/ld) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... yes

configure: creating ./config.status
config.status: creating Makefile
config.status: creating add/Makefile
config.status: creating sub/Makefil
e
config.status: creating config.h
config.status: executing depfiles commands
config.status: executing libtool commands


make运行

make运行期间输出如下图。

makefile.am对makefile的影响:

它根据SUBDIRS = add sub让make递归进入每个子目录处理子目录的Makefile。

根据main_LDADD = add/libadd.la sub/libsub.la为main连接libadd.la和libsub.la库。

configure.in对makefile的影响:

根据AC_PROG_LIBTOOL让libtool完成编译连接工作。

根据AC_CONFIG_HEADERS([config.h])只需传递预处理宏-DHAVE_CONFIG_H给编译器。

makefile中很多与系统相关的信息都是通过变量获取的,这些变量之前已经由configure检查好存放在config.status里面,预处理宏存放在config.h里面。比如我们要用到的编译器CC,编译器选项CFLAGS等。makefile中的变量完成替换后,开始实际执行命令,它会递归执行每一个子目录下的makefile,生成对象文件,连接库文件,最后连接成可执行文件。

# make
make all-recursive
make[1]: Entering directory `/root/program/hello_autoconf2'
Making all in add
make[2]: Entering directory `/root/program/hello_autoconf2/add'
/bin/sh ../libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT add.lo -MD -MP -MF .deps/add.Tpo -c -o add.lo add.c
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT add.lo -MD -MP -MF .deps/add.Tpo -c add.c -fPIC -DPIC -o .libs/add.o
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT add.lo -MD -MP -MF .deps/add.Tpo -c add.c -o add.o >/dev/null 2>&1
mv -f .deps/add.Tpo .deps/add.Plo
/bin/sh ../libtool --tag=CC --mode=link gcc -g -O2 -o libadd.la -rpath /usr/local/lib add.lo
libtool: link: gcc -shared .libs/add.o -Wl,-soname -Wl,libadd.so.0 -o .libs/libadd.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libadd.so.0" && ln -s "libadd.so.0.0.0" "libadd.so.0")
libtool: link: (cd ".libs" && rm -f "libadd.so" && ln -s "libadd.so.0.0.0" "libadd.so")
libtool: link: ar cru .libs/libadd.a add.o
libtool: link: ranlib .libs/libadd.a
libtool: link: ( cd ".libs" && rm -f "libadd.la" && ln -s "../libadd.la" "libadd.la" )
make[2]: Leaving directory `/root/program/hello_autoconf2/add'
Making all in sub
make[2]: Entering directory `/root/program/hello_autoconf2/sub'
/bin/sh ../libtool --tag=CC --mode=compile gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT sub.lo -MD -MP -MF .deps/sub.Tpo -c -o sub.lo sub.c
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT sub.lo -MD -MP -MF .deps/sub.Tpo -c sub.c -fPIC -DPIC -o .libs/sub.o
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT sub.lo -MD -MP -MF .deps/sub.Tpo -c sub.c -o sub.o >/dev/null 2>&1
mv -f .deps/sub.Tpo .deps/sub.Plo
/bin/sh ../libtool --tag=CC --mode=link gcc -g -O2 -o libsub.la -rpath /usr/local/lib sub.lo
libtool: link: gcc -shared .libs/sub.o -Wl,-soname -Wl,libsub.so.0 -o .libs/libsub.so.0.0.0
libtool: link: (cd ".libs" && rm -f "libsub.so.0" && ln -s "libsub.so.0.0.0" "libsub.so.0")
libtool: link: (cd ".libs" && rm -f "libsub.so" && ln -s "libsub.so.0.0.0" "libsub.so")
libtool: link: ar cru .libs/libsub.a sub.o
libtool: link: ranlib .libs/libsub.a
libtool: link: ( cd ".libs" && rm -f "libsub.la" && ln -s "../libsub.la" "libsub.la" )

make[2]: Leaving directory `/root/program/hello_autoconf2/sub'
make[2]: Entering directory `/root/program/hello_autoconf2'
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
/bin/sh ./libtool --tag=CC --mode=link gcc -g -O2 -o main main.o add/libadd.la sub/libsub.la
libtool: link: gcc -g -O2 -o .libs/main main.o add/.libs/libadd.so sub/.libs/libsub.so -Wl,-rpath -Wl,/usr/local/lib
make[2]: Leaving directory `/root/program/hello_autoconf2'
make[1]: Leaving directory `/root/program/hello_autoconf2'


Q: 编译main.c的命令:gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c 中,选项-MT,-MD,-MP,-MF什么意思?


选项-MT,-MD,-MP,-MF是跟依赖关系文件相关的,相当于往文件里加makefile格式的rule,简化形式:
targets : dependency。
-MF .deps/main.Tpo 指定把依赖关系的rule写到文件.deps/main.Tpo 里,跟-MD一起用,如果跟-M一起用将在预处理后结束不编译。
-MT 指定target。
-MP 在依赖关系文件中加入每个依赖文件的phony target。如
test.o:test.c test.h
test.h: #phony target

这里生成的依赖文件main.Po,add.Plo如图。

main.Po:

main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-32.h \
/usr/lib/gcc/i686-redhat-linux/4.5.1/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/i686-redhat-linux/4.5.1/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/bits/stdio.h

/usr/include/stdio.h:
/usr/include/features.h:
/usr/include/sys/cdefs.h:
/usr/include/bits/wordsize.h:
/usr/include/gnu/stubs.h:
/usr/include/gnu/stubs-32.h:
/usr/lib/gcc/i686-redhat-linux/4.5.1/include/stddef.h:
/usr/include/bits/types.h:
/usr/include/bits/typesizes.h:
/usr/include/libio.h:
/usr/include/_G_config.h:
/usr/include/wchar.h:
/usr/lib/gcc/i686-redhat-linux/4.5.1/include/stdarg.h:
/usr/include/bits/stdio_lim.h:
/usr/include/bits/sys_errlist.h:
/usr/include/bits/stdio.h:

add.Plo:

add.lo: add.c

交叉编译 Cross-compiling

Q:为别的平台编译可执行程序怎么做?

交叉编译就是在目前的平台上为别的目标平台生成可执行程序或库。可以在运行configure时通过--build,--host,--target参数实现交叉编译。默认情况下 target<=host<=build<=config.guess给出的平台名称。

例如./configure --build=i686-pc-linux-gnu --host=m68k-coff。

--build=build-type :configure和compile软件包的系统类型。默认情况等于config.guess给出的系统类型。

--host=host-type :运行软件包的系统类型。默认情况等于build类型

--target=target-type :很少用,默认情况等于host类型。

交叉编译时,如果编译器,连接器,汇编器名字不是以host_type为前缀,configure都会发出警告。

要搭建交叉变异环境,如交叉编译用的编译器,连接器,汇编器跟本地的不一样,一般以host_type为前缀,如arm-pc-linux-gcc。




安装目录

Q:make install时,文件都安装到哪里去了?

prefix:安装目录的前缀。默认情况下/usr/local 。

bindir:安装时拷贝可执行文件到此目录。默认情况下/usr/local/bin 。

includir:安装时拷贝头文件到此目录。默认情况下/usr/local/include 。

libdir:安装时拷贝库文件到此目录。默认情况下/usr/local/libs 。

定制自己的安装目录,可以--prefix--exec-prefix 给configure。

例如:./configure --prefix=/usr



GUN build system

autoconf

automake

libtool

autoheader

检查你的系统安装了以下软件:(whereis ,which)


GNU gcc
GNU make
GNU automake
GNU Autoconf
GNU m4
perl
GNU tar
GNU zip ( gzip )
GNU Libtool ( 如果你需要產生 shared library )



参考:

automake,陳雍穆,

Autoconf,

GNU Automake,

阅读(985) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~