autoconf和automake实践

Posted by abloz on November 11, 2010

周海汉 2010.11.11 光棍不再

linux程序源码编译三部曲: ./configure make make install 非常省事。一个configure可以为多变环境生成不同的makefile,确保make可以通过。 所以我们写代码时也非常希望利用上这些快捷工具,免得自己去写configure和makefile。 autoconf和automake联合起来,可以为编译省很多事。

下面是gnu画的autoconf和automake关系图: 来源:http://www.gnu.org/software/autoconf/manual/autoconf.html 用autoconf生成configure.带*表示可以执行。带[]表示可选。

     your source files --> [autoscan*] --> [configure.scan] --> configure.ac

     configure.ac --.
                    |   .------> autoconf* -----> configure
     [aclocal.m4] --+---+
                    |   `-----> [autoheader*] --> [config.h.in]
     [acsite.m4] ---'

用automake生成Makefile.in,但Makefile.am是手动编辑的。

     [acinclude.m4] --.
                      |
     [local macros] --+--> aclocal* --> aclocal.m4
                      |
     configure.ac ----'

     configure.ac --.
                    +--> automake* --> Makefile.in
     Makefile.am ---'

配置软件包用到的文件。

                            .-------------> [config.cache]
     configure* ------------+-------------> config.log
                            |
     [config.h.in] -.       v            .-> [config.h] -.
                    +--> config.status* -+               +--> make*
     Makefile.in ---'                    `-> Makefile ---'

其中configure.ac和Makefile.am是必须手工修改的。

测试程序: zhouhh@zhh64:~/hello$ ls hello.c hello.h

我希望为测试程序生成./configure和Makefile

zhouhh@zhh64:~/hello$ cat hello.c
//////////////////////////////////////////
//author 	zhouhh
//date 		2010.11.8
//web       http://abloz.com
//history
/////////////////////////////////////////

#include
#include
#include
#include
#include
#include
#include
#include

#include

#include

#include "hello.h"

int         epoll_handle;
struct epoll_event    events[EPOLL_SIZE];
pthread_t accept_tid;
static void *accept_thread(void *arg);

int main(int argc, char *argv[])
{
    int perr, nfds, i;

    epoll_handle = epoll_create(EPOLL_SIZE);
    if (epoll_handle == -1) {
        perror("process exitn");
        exit(EXIT_FAILURE);
    }
    perr = pthread_create(&accept_tid, NULL, accept_thread, NULL);
    if (perr != 0) {
        perror("pthread_create");
        exit(EXIT_FAILURE);
    }
    char buf[1024]={0};
    int len = 0;
    while (1) {
        nfds = epoll_wait(epoll_handle, events, EPOLL_SIZE, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }
        for (i = 0; i < nfds; i++) {
            printf("%d recv socket fd %d is donen", i, events[i].data.fd);
            len = read(events[i].data.fd,buf,1024);
            buf[len]='\0';
            printf("rcved "%s",len= %d n",buf,len);
            write(events[i].data.fd,buf,len);
            //write(stdout,buf,1024);

        } //for (i = 0; i < nfds; i++)
    } //while(1)
    return 0;
}
static void *accept_thread(void *arg)
{
    int                 sockfd;
    int                    connfd;
    int                    t;
    socklen_t            clilen = 0;
    struct sockaddr     servaddr, cliaddr;
    struct sockaddr_in    *st_in = (struct sockaddr_in *)&servaddr;
    struct epoll_event    ev;

    t = pthread_detach(pthread_self());
    if (t != 0) {
        perror("pthread_detach");
        return NULL;
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("process exitn");
        exit(EXIT_FAILURE);
    }
    int opt = 1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt, sizeof(opt));
    bzero(&servaddr, sizeof(struct sockaddr));
    st_in->sin_family = AF_INET;
    st_in->sin_port = htons(PORT);
    st_in->sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sockfd, &servaddr, sizeof(servaddr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    if (listen(sockfd, 10) == -1) {
        perror("process exitn");
        exit(EXIT_FAILURE);
    }

    clilen = sizeof(cliaddr);
    ev.events = EPOLLIN | EPOLLET;

    while (1) {
        if ( (connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen)) < 0) {
            perror("accept");
            break;
        }
        printf("accept return %dn", connfd);
        ev.data.fd = connfd;
        if (epoll_ctl(epoll_handle, EPOLL_CTL_ADD, connfd, &ev) < 0) {
            perror("epoll_ctl");
        }
    }

    return NULL;
}

void testcurl(void)
{
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://abloz.com");
        res = curl_easy_perform(curl);

        /* always cleanup */
        curl_easy_cleanup(curl);
    }
}




zhouhh@zhh64:~/hello$ cat hello.h
//////////////////////////////////////////
//author 	zhouhh
//date 		2010.11.8
//web       http://abloz.com
//history
/////////////////////////////////////////

#ifndef __HELLO_H_
#define __HELLO_H_

#define PORT 5000
#define EPOLL_SIZE 50

#endif // __HELLO_H_

生成configure.scan zhouhh@zhh64:~/hello$ autoscan zhouhh@zhh64:~/hello$ ls autoscan.log configure.scan hello.c hello.h

zhouhh@zhh64:~/hello$ vi configure.scan

将configure.scan改名configure.ac或configure.in,但第二种文件名只是为了和老版本兼容。 zhouhh@zhh64:~/hello$ cp configure.scan configure.ac

生成aclocal.m4 zhouhh@zhh64:~/hello$ aclocal zhouhh@zhh64:~/hello$ ls autom4te.cache autoscan.log configure.ac configure.scan hello.c hello.h

修改configure.ac zhouhh@zhh64:~/hello$ vi configure.ac

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

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

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h stdlib.h strings.h sys/socket.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.
AC_CHECK_FUNCS([bzero socket])

AC_OUTPUT




zhouhh@zhh64:~/hello$ cp configure.scan configure.scan.bk
zhouhh@zhh64:~/hello$ autoconf
configure.ac:8: error: possibly undefined macro: AM_INIT_AUTOMAKE
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.

这是因为更新configure.ac后必须重新生成aclocal.m4,宏才有定义。 zhouhh@zhh64:~/hello$ ls autom4te.cache configure configure.scan hello.c autoscan.log configure.ac configure.scan.bk hello.h zhouhh@zhh64:~/hello$ aclocal zhouhh@zhh64:~/hello$ ls aclocal.m4 autoscan.log configure.ac configure.scan.bk hello.h autom4te.cache configure configure.scan hello.c zhouhh@zhh64:~/hello$ autoconf

调用autoheader生成config.h.in zhouhh@zhh64:~/hello$ autoheader zhouhh@zhh64:~/hello$ ls aclocal.m4 autoscan.log configure configure.scan hello.c autom4te.cache config.h.in configure.ac configure.scan.bk hello.h

生成configure zhouhh@zhh64:~/hello$ autoconf zhouhh@zhh64:~/hello$ ls aclocal.m4 autoscan.log configure configure.scan hello.c autom4te.cache config.h.in configure.ac configure.scan.bk hello.h zhouhh@zhh64:~/hello$ ./configure configure: error: cannot find install-sh, install.sh, or shtool in “.” “./..” “./../..”

为符合gnu标准,生成NEWS README AUTHORS ChangeLog,否则会报错。但这些也可以配置不生成。 zhouhh@zhh64:~/hello$ touch NEWS README AUTHORS ChangeLog

zhouhh@zhh64:~/hello$ autoreconf –force –install configure.ac:8: installing ./install-sh' configure.ac:8: installing ./missing’ automake: no `Makefile.am’ found for any configure output autoreconf: automake failed with exit status: 1

zhouhh@zhh64:~/hello$ autoconf zhouhh@zhh64:~/hello$ ./configure checking for a BSD-compatible install… /usr/bin/install -c … configure: creating ./config.status config.status: error: cannot find input file: `Makefile.in’

zhouhh@zhh64:~/hello$ automake automake: no Makefile.am' found for any configure output zhouhh@zhh64:~/hello$ touch Makefile.am zhouhh@zhh64:~/hello$ automake Makefile.am: required file ./INSTALL’ not found Makefile.am: automake --add-missing' can install INSTALL’ Makefile.am: required file ./COPYING' not found Makefile.am: automake –add-missing’ can install COPYING' zhouhh@zhh64:~/hello$ ls aclocal.m4 ChangeLog config.status configure.scan.bk Makefile.am AUTHORS config.h.in configure hello.c missing autom4te.cache config.h.in~ configure.ac hello.h NEWS autoscan.log config.log configure.scan install-sh README zhouhh@zhh64:~/hello$ automake --add-missing INSTALL COPYING automake: no Automake input file found for INSTALL’ automake: no Automake input file found for `COPYING’ automake: no input file found among supplied arguments

zhouhh@zhh64:~/hello$ touch INSTALL COPYING zhouhh@zhh64:~/hello$ automake zhouhh@zhh64:~/hello$ ls aclocal.m4 config.h.in configure.ac hello.h missing AUTHORS config.h.in~ configure.scan INSTALL NEWS autom4te.cache config.log configure.scan.bk install-sh README autoscan.log config.status COPYING Makefile.am ChangeLog configure hello.c Makefile.in zhouhh@zhh64:~/hello$ autoreconf zhouhh@zhh64:~/hello$ ./configure … configure: creating ./config.status config.status: creating Makefile config.status: creating config.h config.status: executing depfiles commands

zhouhh@zhh64:~/hello$ ls aclocal.m4 config.h.in configure.scan install-sh README AUTHORS config.h.in~ configure.scan.bk Makefile stamp-h1 autom4te.cache config.log COPYING Makefile.am autoscan.log config.status hello.c Makefile.in ChangeLog configure hello.h missing config.h configure.ac INSTALL NEWS zhouhh@zhh64:~/hello$ vi Makefile

因为Makefile.am是空的 zhouhh@zhh64:~/hello$ make make all-am make[1]: 正在进入目录 /home/zhouhh/hello' make[1]:正在离开目录 /home/zhouhh/hello’

zhouhh@zhh64:~/hello$ vi Makefile.am

AUTOMAKE_OPTIONS = subdir-objects
ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS}

bin_PROGRAMS = hello
hello_SOURCES = hello.h hello.c
#SUBDIRS = src
#AM_CPPFLAGS = $(MYSQL_CFLAGS)
hello_LDADD =  $(CURL_LIBS)
#dist_noinst_SCRIPTS = autogen.sh

zhouhh@zhh64:~/hello$ vi configure.ac

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

AC_PREREQ([2.67])
#AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_INIT([hello], [1.0], [ablozhou@gmail.com], [hello], [http://abloz.com])
AC_CONFIG_SRCDIR([hello.h])
AM_INIT_AUTOMAKE([1.0 no-define])
AC_CONFIG_HEADERS([config.h])
AC_SUBST(CFLAGS)
AC_SUBST(LD_FLAGS)

# Checks for programs.
AC_PROG_CC
AC_CONFIG_FILES([Makefile])

# Checks for libraries.
#采用pkg-config
#PKG_CHECK_MODULES([DEPS],[curl])
AC_CHECK_LIB([pthread], [pthread_rwlock_init])
AC_PROG_RANLIB
#不采用pkg-config:
AC_ARG_WITH([curl-lib-path],
  [AS_HELP_STRING([--with-curl-lib-path], [location of the curl libraries])],
  [CURL_LIBS="-L$withval -lcurl"],
  [CURL_LIBS='-lcurl'])
AC_SUBST([CURL_LIBS])
# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h stdlib.h strings.h sys/socket.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.
AC_CHECK_FUNCS([bzero socket])

AC_OUTPUT

不采用pkg-config的方式,如mysql:

# Get MySQL library and include locations
AC_ARG_WITH([mysql-include-path],
  [AS_HELP_STRING([--with-mysql-include-path],
    [location of the MySQL headers, defaults to /usr/include/mysql])],
  [MYSQL_CFLAGS="-I$withval"],
  [MYSQL_CFLAGS='-I/usr/include/mysql'])
AC_SUBST([MYSQL_CFLAGS])

AC_ARG_WITH([mysql-lib-path],
  [AS_HELP_STRING([--with-mysql-lib-path], [location of the MySQL libraries])],
  [MYSQL_LIBS="-L$withval -lmysqlclient"],
  [MYSQL_LIBS='-lmysqlclient'])
AC_SUBST([MYSQL_LIBS])

zhouhh@zhh64:~/hello$ autoreconf –force –install Makefile.am: installing `./depcomp’ zhouhh@zhh64:~/hello$ ./configure zhouhh@zhh64:~/hello$ make 通过。

参考: http://www.openismus.com/documents/linux/automake/automake.shtml http://www.openismus.com/documents/linux/using_libraries/using_libraries 例子下载 http://www.gnu.org/software/automake/manual/standards/Makefile-Basics.html http://www.gnu.org/software/autoconf/manual/autoconf.html