200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > 第二部分 Automake的标准工程组织

第二部分 Automake的标准工程组织

时间:2018-12-11 13:08:45

相关推荐

第二部分 Automake的标准工程组织

一、总体上的目录:

一般会有如下目录和文件,这些自己创建(见二)或用acmkdir自动生成:

1. 目录:

(1) 必选:

m4: 第三方或自己写的用于configure.in中的宏

doc: 各种文档

src: 源码顶层目录(里面怎么细分是自己的事)

config: 放置configure过程中的一些文件,使得顶层目录不那么多文件

(2) 可选:

include: 可选目录,你愿意的话,可以用configure将所有的头文件链接到这个目录下。一般不用。

lib: 可选目录,你愿意的话,可以将对系统调用的实现(针对有些平台上没有实现的调用),常用的小代码(你可能觉得太小,对每个工程都常用,不应该放src里做为库)放在这个目录下。

2. 文件:

如:README, AUTHORS, NEWS, ChangeLog, INSTALL, COPYING 有些是automake需要的,必须要存在,除非你用了automake --foreign 选项,就可以不用添加它们。

二、一步步创建工程:

(这个用到我的例子,其说明见每一步,不单独说明例子了)

1. 创建必须目录:

mkdir src config doc m4

2. 创建configure.in 文件:

AC_INIT([kid], # 工程名

[0.1], # 版本号

[Yi Feng yifengcn@], # 名字和邮箱

[kid]) # make dist/distcheck的打包名

AC_CONFIG_AUX_DIR(config) # 表示将configure生成的一些文件,放在这里(使得上层整洁些)

AM_CONFIG_HEADER(config.h) # 配置头文件名为config.h

AM_INIT_AUTOMAKE([dist-bzip2]) # 这里面当然很多参数,而[dist-bzip2]表示make dist/distcheck打包按bz2打包,默认是gz

AC_PROG_CC # 检测用什么C编译器

AC_PROG_CXX # 检测用什么C++编译器

AC_PROG_INSTALL # 生成安装脚本 install-sh

AC_PROG_RANLIB # 生成静态库要用它(生成库索引)

AC_PROG_LIBTOOL # 如果只生成静态库,用ranlib即可。动态库得用libtool

AC_PROG_MAKE_SET # 没见到有无这个选项的差异,加上吧。

AC_CONFIG_FILES([ # configure.in也是给automake用的,automake会在下面这些地方去把你写的Makefile.am/xxx.pc.in转成Makefile.in模板/xxx.pc。下面这些文件.am/.pc.in都得自己写。

Makefile # 它来自顶层Makefile.am,就是SUBDIRS,指定下m4, src, doc

src/Makefile # 来自src目录(源文件)下的总Makefile.am,也是指定下SUBDIRS

src/libhello/Makefile # 这来自src下libhello子目录下的Makefile.am,提供libhello库

src/libhello/hello-1.0.pc # 因为这个库要安装,这个文件安装后供pkg-config使用

src/bin/Makefile # 这来自src下bin子目录,是可运行程序,使用libhello库

doc/Makefile # 这里展示了Man文件的安装方法,道理一样。

m4/Makefile # 如果你在里面放了自己的configure.in宏,就要这样。

])

AC_OUTPUT

说明:

(1) 生成库

ranlib是以前给静态库生成索引时用的,所以要用之。但是后来,出现了libtool,这个工具专门生成“库”,包括静态和动态,所以,可以不必再用ranlib了。但是,如果仅仅生成静态库的话,就用ranlib即可,AC_PROG_LIBTOOL使得configure时间很长:( 另外,libtool的用法,单独的我没使用过(它独立于autoconf和automake,是GNU为了解决不同平台,动态库是否实现,要加什么参数等等不同,而制做的一个接口统一的工具,用户生成库时不再关系这些不同),但是它和automake结合起来非常好用,见下面,它默认的就会同时生成静态库和动态库。且如果你的程序,有个主程序,还有个动态库,要用主程序连库,它会默认连接动态库。而你将他们make install后,你ldd一下主程序,会发现,它依赖的动态库,是个全路径的库!这样避免了ld.so的一套动态库搜索机制,你不用做任何设置(比如ldconfig),你的程序就可以运行。

看看命令行输出,其实是gcc在链接库(libhello.so)的时候,加了-Wl等参数,截取如下:

gcc -Wall -g -O3 -g -O2 -o .libs/kid main.o -lm ../../src/libhello/.libs/libhello.so -Wl,--rpath -Wl,/home/echo/tmp/echo//lib

还得提一句,就是libtool的接口为什么是统一的,见下面的Makefile.am文件就知道了,它抽象为:你只用写一种库,后缀为.la。然后后自动生成符合该平台要求的动态库(如.so)和静态库(如.a)

(2) .pc.in

一般,如果你要安装库的话,为了用户友好,最好提供xxx.pc.in文件,这个文件内容在下面会看到。但是,同任何一个需要安装的目标一样,你都得在它所在层次的Makefile.am中指定“Primitive”。即:

pkgconfigdir = $(libdir)/pkgconfig

pkgconfig_DATA = hello-1.0.pc

以知道安装路径和由.pc.in生成方法.pc文件的方法。

(.pc文件也可以做参数直接给pkg-config 用的,你用 pkg-config xxx.pc --libs --cflags试试)

.pc文件的安装路径,一般都是$(libdir)下的pkgconfig,这里$(libdir) = $(prefix)/lib.

3. 把你的源文件放入相应目录,并按照configure.in中指定的Makefile位置,创建各个Makefile.am:

首先给出写Makefile.am的总原则 ──── “写Makefile是写规则,而写Makefile.am是一些赋值而已”

(1) 上层的 Makefile.am 只用写 SUBDIRS 和 EXTRA_DIST:

你哪个地方想要生成xx(比如由源文件生成库),安装xx(比如doc中的man文件),都得有Makefile.am。如果按照层次组织的话,则每一级目录都得写Makefile.am,上层点的目录会有子目录,则上层目录中的Makefile.am只需写SUBDIRS = sub1 sub2 sub3即可。这里还有个关键是: 靠 sub1 sub2 sub3的顺序,指定了编译顺序!! 如果sub1 sub2是库,sub3是程序要用它们,那么这样指定是正确的!

而EXTRA_DIST = file1 file2 的目的只是:如果你有特别的文件(即不是Makefile.am管的,也不是autotools体系的东西)要保留到make dist生成的发型版中,则要用EXTRA_DIST指明,否则是不会带之的。比如你为了省事写的./reconf文件(包含libtoolize aclocal autconf autoheader 和 automake)就要用EXTRA_DIST指明保留。

(2) 下层点的(即源码目录,或其他待生成(如xxx.pc),待安装文件(如man)所在的目录中)Makefile.am要写清楚的是各个变量的赋值!如AM_CPPFLAGS就是automake的一个变量。详细的变量列表参见automake.htm(GNU主页的文档)的 Variable Index 附录。

------------ a. 全局:--------------

AM_CPPFLAGS = -I... -D... #给预处理器的参数(而INCLUDES是老的用法了,新的已建议用AM_CPPFLAGS)

AM_LDFLAGS = -L #就是给-L用的,可能有其他的用途。

LDADD = -lm -lhello #给-l用的

AM_CFLAGS #对上面三个的补充,你愿意加什么加什么,如 -Wall -O3 -g

说明:

全局的意思是:对本文件有效。目前我不用,也没找过像Makefile中export之类的传递变量的方法。现在我在Makefile.am中采取的方法,仍然是不集中在上层目录的Makefile.am放置共用变量,如头文件位置(很多-I)集中放在上层。其实分散放,每个单元可独立起来,也挺好的。

加AM的意思是:不加AM的对应的变量,设计给包的用户用的,即用户可以这样:./configure CFLAGS='-Wall'。如果你在Makefile.am中也用CFLAGS的话也可以,只不过会被用户的覆盖。所以,你要加个AM_前缀,表示这是给开发者用的变量,不会被用户定义的覆盖。LDADD 用于加-l的,这个与用户无关,所以没有AM_前缀的用法。

这里提到的四个变量,将会用在本Makefile.am相关的所有预处理,编译和链接。

----------- b. 元素: -------------

在很多情况下,可以理解为用 “位置_制做安装方法 = primitive1 primitive2” 来定义元素 primitive1 和primitive2。元素,就是要编译,生成(如xxx.pc)或安装(如man文件)的目标。这告诉我们,primitive1 和 primitive2 将会安装到 $(prefix)/位置,并且制做和安装方法按照你指定的来。来看几个非程序的例子:

=== 如:你要安装cow.1到man1下,那么这样写Makefile.am:

man1_MANS = cow.1

就行了。man1表示将会把这个文件安装到$(prefix)/share/man/man1中(别扣说为什么没有写出share到元素中),而MANS则指出了cow.1是一个man文件,无须做什么处理,当用户make install时,直接放到$(prefix)/share/man/man1中即可。但是这样写 man1_MANS = cow.1 ,不会再写什么属性了(见下 c. 属性),但是automake的一个规则是,如果你要在 make dist中包含什么程序文件,应该再写 xx_SOURCES 指明有哪些文件要编译和打包,或者用EXTRA_DIST = 来指明包含的文件。由于 man_MANS 对应的东西,如果写cow_1_SOURCES,语法上是错的,因为没有MANS对应的SOURCES,而 automake 默认,对man1_MANS = cow.1这种形式,不会包含cow.1到 make dist打好的包里。那怎么办呢?automake提供了 dist_ 前缀,和nodist_前缀对应,表示这个元素,不必写出 SOURCES 类似的东西,直接将和它名字相同的文件,放到make dist里即可。

所以,最终形式是: dist_man1_MANS = cow.1

=== 再如:你要安装hello-1.0.pc,那么只需要在Makefile.am中写:

pkgconfigdir = $(libdir)/pkgconfig

pkgconfig_DATA = hello-1.0.pc

注意到,这里光指出pkgconfig_DATA中的pkgconfig,是不能确定安装位置的,这里pkgconfig和上面的man1的区别是:man1是automake定义的,它当然知道你写了这个就表示要装到哪里 ──── 实际上,一个叫mandir的变量被默认定义了,它就等于 $(prefix)/share/man。但是你自己写的pkgconfig不是automake内置的,没有相应的dir变量,所以你必须自己指明 pkgconfigdir = $(libdir)/pkgconfig,这样才能让automake知道,你要装到这里!这里libdir就是automake定义的变量,直接写即可,它对应于lib_XXX(如lib_LIBRARIES)元素定义。至于这里 _DATA的目的,就是说明,hello-1.0.pc.in 怎么样处理:如果你在里面引用了如prefix,那么configure脚本会在运行的时候,将hello-1.0.pc.in中的变量替换,以生成hello-1.0.pc。比如你自己指定了./configure --prefix=...。

下面列举出常用的 元素定义 方式,这些都是automake的“变量”,全面的变量列表参见automake.htm(GNU主页的文档)的 Variable Index 附录。

EXTRA_PROGRAMS = # 这里要列出,由用户./configure来决定是否要产生的目标。

bin_PROGRAMS = kid # 这是定义程序元素的方式,bin由automake默认定义了bindir=$(prefix)/bin,下面的同理。

sbin_PROGRAMS = # 如果你想装到sbin下的话。

lib_LIBRARIES = libhello.a # 定义静态库元素的方式,这里由LIBRARIES指明了,要编译成静态库。

pkglib_LIBRARIES = # 凡是加上pkgxxx的,表示只是安装的时候加上包名,即pkglibdir=$(prefix)/lib/@PACKAGE@ 其中PACKAGE应该会被用configure.in中你写的那个替换掉。

include_HEADERS = # 这是列出你要安装的头文件,这一般是用于你制做了一个库,然后这里给出你要安装的头文件。

pkginclude_HEADERS = # 同pkglib意义。

noinst_PROGRAMS = # noinst_表明,生成的东西,不安装。这里表明生成的程序,make install不会安装

noinst_LIBRARIES = # 这是常用的,因为你可能在子目录下,生成一个供自己程序使用的静态库。

lib_LTLIBRARIES = libhello.la # 这个太常用了,生成动态库,要借助libtool(LT),这里LTLIBRARIES表明了要使用libtool。注意:这样写的话,动态库的libhello和静态库的都会生成!如果你写的是noinst_LTLIBRARIES,则只生成静态库的!! 所以,实际上,上面的noinst_LIBRARIES已经意义不大了(但是它却让configure的时候时间快得多)

xmldir = $(datadir)/xml # 这里是自己定义 元素定义 的例子,你要安装到“xml目录”下,这个目录被指定为$(datadir)/xml,其中$(datadir)不再多说,是系统定义的。如果没有手册的话,你make install一次,就知道其默认值了。

xml_DATA = file.xml # 接上面的,这里得指出 制做方式按 DATA。再说明一下:DATA的概念很广,很多不知道怎么分类的文件,都可以用这个来装。但是如果你需要文件中的内容,被configure来调整的话,那么先生成.in 文件,在文件中用 @var@来引用变量(你见到的很多变量都可以用),把这个.in 放 configure.in 中,这一切参见 .pc.in 的做法。

man1_MANS = # automake定义的安装man的方式

data_DATA = # automake定义的安装数据文件(如 xxx.pc)的方式。

pkgdata_DATA = # 有pkg前缀的同上pkglib意义。

下面列出完整的预先定义的安装路径:(其中一些对应的aaa_BBB中的BBB是啥,得查手册了)

1. Program-related files are installed in one of the following locations:

bindir = $(exec_prefix)/bin

sbindir = $(exec_prefix)/sbin

libexecdir = $(exec_prefix)/libexec

libdir = $(exec_prefix)/lib

includedir = $(prefix)/include

2. Data files should be installed in one of the following directories:

datadir = $(prefix)/share

sysconfdir = $(prefix)/etc

sharedstatedir = $(prefix)/com

localstatedir = $(prefix)/var

3. Documentation should be installed in the following directories:

infodir = $(prefix)/info

mandir = $(prefix)/man

man1dir = $(prefix)/man1

4. Then there are some directories for developing various eccentric types of files:

lispdir = $(datadir)/emacs/site-lisp

m4dir = $(datadir)/aclocal

5. Some Subdirectories for convenience:

pkglibdir = $(libdir)/@PACKAGE@

pkgincludedir = $(includedir)/@PACKAGE@

pkgdatadir = $(datadir)/@PACKAGE@

--------------- c. 属性:------------------

显然,定了上面 元素 是不够的。元素有其属性。给属性赋值的方式是 “元素_属性”。其中“元素”含.的话,要用_代替。

== 库元素的属性有以下:(假设上面 元素定义 是 lib_LTLIBRARIES = libhello.la)

libhello_la_SOURCES = hello1.c hello2.c hello.h

# set the soname : libhello-2.1.3.so.3.0.0

libhello_la_LDFLAGS = -release 2.1.3 -version-info 3:3:3

# 其中两个版本号,可以不用的。但是正规的都要用。release是你自己规定的,你爱怎么写怎么写。而version是有规定的,上网查吧,首先形式是 a:b:c,冒号不能为.号,然后每个号有意义,均是对库接口变化情况的描述。我不知道为什么这里指定了3:3:3,上面是3.0.0,可能3:3:3不合法呵呵。

libhello_a_LIBADD = obj1.o sub/libsub.la # 添加一些已有的.o/ .a /.la /.so,目的是聚合。

libhello_a_DEPENDENCIES = dep1 # 这个依赖的意思是:它需要在dep1后才编译。不是LIBADD的聚合意思。没用过,因为直接在上层目录的Makefile.am的SUBDIRS里指定目录顺序,编译顺序就按那个来。

== 程序元素的属性有以下:(假设上面 元素定义 是 bin_PROGRAMS = kid)

kid_SOURCES = a.c b.c a.h b.h # 列出所有的文件!!包括头文件。因为只有这里列出的,才能被包含到make dist/distcheck生成的包中去。

kid_LDFLAGS = # -L 等选项。

kid_LDADD = # -l 选项

kid_DEPENDENCIES = # 同库元素属性例子。

注意:这里的_LDFLAGS等,是上面AM_LDFLAGS等的局部变量,只对这一条有效。

4. 本例子的文件夹和文件详细:(紧接上面2步之后该添加的东西)

(1) $(top_srcdir)/reconf

这个文件,用于简化当configure.in, Makefile.am非文件列表部分 等文件改动后,需要重新生成工程的时候的命令集合。

内容如下:

#!/bin/bash

echo '- libtoolize'

libtoolize --force # 这个是和libtool配合的,如果你要生成动态库,才用这个。

echo '- aclocal -I m4'

aclocal -I m4 # 给configure.in中的宏生成实际脚本。

echo '- autoconf'

autoconf # autoconf 将 configure.in 转化为configure

echo '- autoheader'

autoheader # 生成 config.h.in 模板

echo '- automake --add-missing --foreign'

automake --add-missing --foreign # 将你在configure.in中,指定的各个Makefile.am,转成Makefile.in模板。

(2) $(top_srcdir)/Makefile.am

这个是顶层的Makefile.am,内容如下:

EXTRA_DIST = reconf configure # 在make dist生成的包中,包含本来可能不会包含的reconf 和 configure(configure会包含的,这里不写也可以)

SUBDIRS = m4 src doc # 三个目录(其中m4用于放用户自己定义的configure.in宏,我一般不用)

(3) $(top_srcdir)/m4/Makefile.am

因为这里我没用m4目录,所以在里面创建一个内容为空的Makefile.am文件即可。

(4) $(top_srcdir)/doc/Makefile.am

在doc中,我放了一个man文件,叫做cow.1(内容为空),其Makefile.am内容如下:

dist_man1_MANS = cow.1

# (加dist原因上面已述,因为这只写了元素 cow.1 没有写其属性,比如有哪些文件,而automake默认对这种可能是不会加到 make dist 的打包文件中的。所以,前面加个 dist_ 前缀,表示,把cow.1当作文件名,并且把这个文件加到make dist 打包文件中)

(5) $(top_srcdir)/src/Makefile.am

这个文件是src目录的总Makefile,src目录就包含libhello和bin两个目录。Makefile.am内容如下:

SUBDIRS = libhello bin # 其中,libhello放在前面,让它先编译。否则bin没法编译。

(6) $(top_srcdir)/src/libhello/Makefile.am

这个文件是 libhello目录的总Makefile,内容如下:

AM_CFLAGS = -Wall -g -O3

lib_LTLIBRARIES = libhello.la

include_HEADERS = hello.h # 为库配套安装的头文件是这个

libhello_la_SOURCES = hello1.c hello2.c hello.h

libhello_la_LDFLAGS = -release 2.1.3 -version-info 3:3:3 # set the soname: libhello-2.1.3.so.3.0.0

pkgconfigdir = $(libdir)/pkgconfig

pkgconfig_DATA = hello-1.0.pc

------ 其他文件内容为:------

hello-1.0.pc.in 内容(这是通用模板,改下Name, Description, Version, Libs即可,更全面的模板man pkg-config,在末尾一点):

prefix=@prefix@

exec_prefix=@exec_prefix@

libdir=@libdir@

includedir=@includedir@

Name: hello

Description: say hello library

Requires:

Version: @1.0@

Libs: -L${libdir} -lhello

Cflags: -I${includedir} -I${libdir}

源文件 hello1.c:

#include "hello.h"

void hello1(int times)

{

int i = 0;

for (i = 0; i < sqrt(times); i++) {

printf("hello kid1!/n");

}

}

源文件 hello2.c:

#include "hello.h"

void hello2(int times)

{

int i = 0;

for (i = 0; i < sqrt(times); i++) {

printf("hello kid2!/n");

}

}

源文件 hello.h:

#ifndef HELLO_H

#define HELLO_H

#include <stdio.h>

#include <math.h>

void hello1(int times);

void hello2(int times);

#endif

(7) $(top_srcdir)/src/bin/Makefile.am

这是bin程序目录的主Makefile,内容如下:

AM_CPPFLAGS = -I$(top_srcdir)/src/libhello

AM_LDFLAGS = -L$(top_builddir)/src/libhello

LDADD = -lhello -lm

AM_CFLAGS = -Wall -g -O3

bin_PROGRAMS = kid

kid_SOURCES = main.c

源文件 main.c 内容:

#include "hello.h"

int main(int argc, char *argv[])

{

hello1(4);

hello2(16);

return 0;

}

5. make distcheck检验 和 打包:

用make dist可以打包。用make distcheck可以模拟用户使用来检测,并打包。

我这个例子,原先用make distcheck最后在main.o -lhello时报错(虽然make是好的)。但是生成的打包文件,解开后,使用是正常的。后来才意识到,make distcheck会检查VPATH方式的build,这个出错正是VPATH方式的build通不过。

即:有一种编译方式是,比如进到$(top_srcdir)目录后,你不是./configure,而是建立一个build目录,然后在目录中 ../configure; make; make install,这样,原$(top_srcdir)目录下的所有东西,不会受到影响,生成的文件,全部在 build下了!包括所有Makefile.am所在的目录树。

VPATH来源于GNU Make,实质也就是,在一个目录下make,但是允许其文件来自其他地方。

后来,查阅 autotools_tutorial.html 发现, 6.7 General Automake principles 有提到,如果要支持VPATH,那么一些写法上还要注意: 导致我这里错误的是以下两个变量:

$(top_srcdir) 只能用于你自己原有的文件。

$(top_builddir) 必须用于生成的文件,如系统生成了一个库,那么 -L.. 路径,就要用 $(top_builddir)引出。

我当时,正是由于 bin中的Makefile.am 中的 AM_LDFLAGS = -L$(top_builddir)/src/libhello 用的是 $(top_srcdir) 而导致的错。

但是要注意的是:“理解上”,都将这两个变量认为是 Project的顶层目录来做,就是src所在的目录层次。

6. 使用:

下面列出一些使用方法:

(1) 在Makefile.am中添加,删除,更改文件名,只需要make即可。如果改的是其他的东西,多半要重新automake,然后再make。但也一般没有必须要重复走libtoolize;aclocal;autoconf;autohead 步骤。

如果修改了configure.in文件,如果Makefile还在,多半直接make就行,它会自动重走./reconf和./configure

(2) 上面多次提到的 prefix,以及exec-prefix,用户在configure中配置方法为:

configure --prefix=/home/echo --exec-prefix=/home/echo/linux

exec-prefix默认设置等于prefix,但是如果你设置了它其他路径,则所有的二进制文件(即本平台相关)文件会被安装到exec-prefix指定的路径下,其他文件照常装到prefix指定的路径下。这是因为,你可能有这样的要求:你会编译很多平台的二进制文件,放到不同的exec-prefix下,然后自己做为网络服务器,供不同平台的人使用。

另外,用configure配置用户变量的用法为(用户变量,就是与上面提到的AM_变量想对应的):configure CFLAGS='-Wall -O3 -g'

(3) wildcards ?

automake 不支持 $(wildcards ...),别想了。不仅不支持,还要想理由让你觉得应该不支持。见GNU官方automake文档的27.3 Why doesn't Automake support wildcards?。如果你要实现这点,自己写脚本来处理吧。

(4) 安装和取消安装:

如果你要安装到自己目录,用./configure --prefix=..吧。make install时,用自己用户即可。一些子目录,如bin,lib等会被自动生成。

像一些动态库,如果你要安装到自己目录,又不想要root去配置ldconfig等,可以让 gcc 生成执行文件,在链接你的库时,用 -Wl 指定绝对路径,上面有介绍。仅仅依靠放在相同目录下是不够的。不过,这个一般automake就给你做了。

make uninstall可以删除已安装的东西。

7. 更多主题:

(1) 显然,“元素” 定义,并不能解决一切,因为除了MANS,除了DATA等,我们可能对 元素的生成有自己的看法!而且,在一些对源文件编译的场合,也许我们也有自己的看法。所以,automake 允许你在Makefile.am里面写 Makefile的规则!!

例子参加 autotools_tutorial.html的 6.13 Scripts with Automake ,这里介绍的是如果你想安装一些脚本。

(2) Doxygen的支持:

这需要在m4中添加一个宏,然后还有些配置,之后,makefile中会自动有个生成doxygen文档的目标。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。