以前一直都是直接拿的开源库的代码直接拿来编译,一直都不知道Makefile和configure文件怎么来的,而且configure文件超级复杂,一度以为是开源的人手工写的。今天有空就研究了下Makefile的生成,跑通来记录一下。(注:以下环境为Ubuntu 20.04)
首选是比较简单的一个c++文件,先以配置和跑通为主,后期有时间加入更多源文件和再尝试一下链接别人的静态库和动态库实现。
//test.cpp
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char** argv) {
for (size_t i = 0; i < argc; i++)
{
printf("argument[%ld] is %s\n", i, argv[i]);
}
return 0;
}
这里的实现就是打印运行的时候传过来的参数,有了源码我们开始搞起。在Ubuntu下生成Makefile我们需要先安装automake,terminal终端中输入sudo apt install automake回车安装。安装完成后,我们在终端中cd到test.cpp文件所在路径。
然后我们先在终端中运行autoscan命令 :
可以看到除了源文件test.cpp还生成了两个文件,一个文件是autoscan.log这个文件在我这里没任何内容,先不管他。第二个文件configure.scan是一个配置脚本,生成的文件内容如下:
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([test.cpp])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CXX
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
# Checks for library functions.
AC_OUTPUT
我们把configure.scan文件重命名为configure.ac,mv configure.scan configure.ac。
然后修改里面的内容为:
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT(test, 1.0, test@mail.com)
AC_CONFIG_SRCDIR([test.cpp])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
# Checks for library functions.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
我们修改AC_INIT里面的第一个参数作为程序名称,第二个参数是版本号,这里写了1.0,第三个参数为反馈问题的邮箱(可不写);增加AM_INIT_AUTOMAKE配置,注意这个某些教程会写让加参数,但是运行的时候会提示过时,具体想了解可以自己添加试试,跟AC_INIT差不多,只写前面两个参就行,这里我们不写参数;增加AC_CONFIG_FILES([Makefile])配置,固定写法,这个目前不写不会报错,但在后面的步骤会提示你是否忘记写,并且编译的时候提示找不到编译的具体指令。
添加AM_INIT_AUTOMAKE(test, 1.0)的提醒如下:
AC_CONFIG_FILES不添加[Makefile]参数出现的警告和错误如下
接下来我们运行aclocal命令,会生成autom4te.cache目录,这个目录下及里面的文件不用理,就是一些去找系统依赖的log而已;另一个生成的文件是aclocal.m4,里面的内容就是根据configure.ac所需要的宏定义写到该文件中。
下一步就是运行autoheader,生成config.h.in,这里是根据configure.ac所需要的宏提取出来一个头文件预定义。
接下来我们先创建Makefile.am文件,运行touch Makefile.am创建,内容编辑为如下:
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=test
test_SOURCES=test.cpp
这里的AUTOMAKE_OPTIONS=foreign为固定写法,bin_PROGRAMS后面指定的是生成的可执行文件名称,test_SOURCES的格式为:可执行文件名称_SOURCES,这里后面指定编译的源文件,这里面也可以依赖第三方静态库和动态库,后面我在下一个文章再涉及。
接下来我们执行automake --add-missing命令添加编译所缺失的文件,这里添加进来了depcomp、missing、compile、install-sh、Makefile.in五个文件。
然后我们执行autoconf命令就可以生成configure文件了。
最后我们再执行./configure就可以得到我们需要的Makefile了,这一步还生成了config.h文件。
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Name of package */
#define PACKAGE "test"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "test@mail.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "test"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "test 1.0"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "test"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.0"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "1.0"
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
这里的config.h定义了我们用到的宏和一些包名和版本信息等。有了Makefile文件,我们可以执行make命令来编译出可执行文件,没有安装make的先执行sudo apt install make安装。这里只需要测试编译出来的文件是可运行,不用安装所以不需要执行make install。
接下来我们可以执行./test 123 456 789来测试我们的代码了,第一个打印为执行目录,后面的是运行时调用传的参数。
我们来回顾一下需要的步骤:
1:先写一个源文件test.cpp,确保安装了automake;
2:在test.cpp路径处先执行autoscan,修改生成的configure.scan重命名为configure.ac,修改configure.ac里面的内容;
3:执行aclocal;
4:执行autoheader;
5:创建Makefile.am文件并配置;
6:执行automake --add-missing;
6:执行执行autoconf生成configure;
7:执行./configure生成Makefile;
8:执行make生成可执行文件,确保安装了make;
9:执行./test 123 456 789测试代码,完成。
好了,我们生成configure和Makefile就结束了,我们要开源给其他人使用只需要创建configure就可以了,下一篇文章添加各个参数的详解,还有接入第三方代码或者静态库和动态库测试。