比特币源码分析–深入理解区块链 2.比特币核心源码的编译、安装和打包工具Autoconf使用(1)

        不管是否为开源软件,提供适配不同平台的编译和打包工具有利于软件的推广使用。Bitcoin可运行在多个平台下,包括windows, Unix/Linux(如:Centos、Ubuntu、FreeBSD、Debian)、macOS等系统。用户可在这些平台下修改和编译Bitcoin核心源代码。在接下来的所有文章中,我以CentOS Stream 8操作系统为例,使用autoconf-2.69和GNU c++11.2(支持最新的C++20和C++23)作为打包和编译工具,来分析Bitcoin源代码和进行编译,同时开发一些演示用的例子。

        Bitcoin使用的是Autoconf作为编译和配置工具,它的开发语言是C++。因此如果用户希望要分析其源代码并能进行代码调式的,就有必要首先了解Autoconf。它是一个在Bourne shell(就是通常所说的shell,扩展名为.sh的文件)下制作供编译、安装和打包软件的配置脚本的工具。有点类似于Java平台下的配置工具Ant、Maven 、Gradle,这些都可以帮助用户快速编译、打包分发Java应用。有人可能会说使用原始的命令行来完成编译和打包,不需要这些工具,这当然没有问题的。不过你因此可能需要付出大量时间在这上面。早期在Linux平台行C++编译,就需要自己编写Makefile文件,当需要在多个平台下进行交叉编译时涉及大量的脚本编写工作。这对于不熟悉shell编程的人来说是一个挑战且费时费力。

        接下来花点时间带大家了解下Autoconf如何使用,以及如何在自己的项目中使用Autoconf。它与Automake、Libtool等软件组成了GNU构建系统,也就是说Autoconf需和Automake,Libtool配合使用,各司其职。当前在CentOS Stream 8系统下,这个三个工具用如下命令获得版本号(注:#符号表示Linux Shell命令提示符):

# autoconf --version
autoconf (GNU Autoconf) 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+/Autoconf: GNU GPL version 3 or later

# automake --version
automake (GNU automake) 1.16.1
Copyright (C) 2018 Free Software Foundation, Inc.

# libtool --version
libtool (GNU libtool) 2.4.6
Written by Gordon Matzigkeit, 1996

Autoconf工作流程:

        上图是分解后的步骤,看来步骤很多,实际上可以缩减成一个命令就能完成(在后面会介绍)。上图的步骤节点分成两种类型:椭圆形的表示命令,矩形表示输入或输出的文件。下面通过例子来说明如何使用这些命令。

         假设用户创建了一个名为tutorial_autoconf_app的项目,所有项目文件保存在名为tutorial_autoconf目录下,该目录的结构如下:

[root@localhost tutorial_autoconf]# tree
.
├── doc
└── src
    ├── main.cpp
    ├── tutorialtest.cpp
    └── tutorialtest.h

2 directories, 3 files

        其中src目录存放跟项目相关的所有源代码,doc目录存放文档,现实中用户可以根据需要来创建自己所需的目录。在src中添加三个简单的c++程序,内容如下:

 tutorialtest.h

#ifndef TUTORIAL_AUTOCONF_TEST_H_
#define TUTORIAL_AUTOCONF_TEST_H_

#include <string>
#include <iostream>

class TutorialTest
{
public:
    TutorialTest(const std::string& msg);
};

#endif // TUTORIAL_AUTOCONF_TEST_H_

tutorialtest.cpp

#include <tutorialtest.h>

TutorialTest::TutorialTest(const std::string& msg)
{
    std::cout << msg << std::endl;
}

main.cpp

#include <stdio.h>
#include <iostream>
#include <tutorialtest.h>

int main(int argc, char*argv []) 
{
    TutorialTest t{"Hello autoconf tutorial test......"};

    exit(EXIT_SUCCESS);
}

        推荐使用windows的用户使用Visual Studio Code作为代码编辑器,在vscode中安装Rmote SSH插件后,它可以直接连接到Centos(或类似的Linux)系统进行文件编辑、目录创建等操作,它就像在本地操作一样的感觉。安装C/C++ IntelliSense插件后就有自动提示和智能标签这些功能。vscode也有针对Ubuntu版本的,因此使用Ubuntu桌面的也可以使用vscode。不用图形桌面的用户,直接使用vim编辑器也可以。

        现在我们尝试按照上面的流程图并按以下顺序来完成一个简单的Autoconf使用测试,以下的几个指令均在根目录tutorial_autoconf下执行。

  • 执行autoscan命令:它会生成两个文件autoscan.log和configure.scan。其中 .log是记录日志,configure.scan文件是configure.ac的原型模板,你可以直接将它更名为configure.ac。Autoconf就是使用此文件来生成配置脚本(也就是执行configure的脚本,后面会讲到)。configure.scan内容:

    #                                               -*- Autoconf -*-
    # Process this file with autoconf to produce a configure script.
    
    #指定Autoconf的版本
    AC_PREREQ([2.69])
    AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
    #指定某个源代码文件以确保该目录存在
    AC_CONFIG_SRCDIR([src/main.cpp])
    #输出配置后的宏定义实例文件,通常为config.h
    AC_CONFIG_HEADERS([config.h])
    
    # Checks for programs.
    # 使用c++编译器
    AC_PROG_CXX
    # 使用C语言编译器
    AC_PROG_CC
    
    # Checks for libraries.
    
    # Checks for header files.
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    # Checks for library functions.
    
    # 生成Makefile.in,该文件运行配置程序时生成最终的Makfile的中间文件
    # 当文件目录下没有Makfile.am文件,Makefile.in也不能产生
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT

            上面的configure.ac是一些基本的语句,所以暂时也时无法使用的。这里说明一下基本的语法。AC_开头就是宏,凡是学过c/c++都知道宏的含义,只不过这里是shell使用的宏。它的作用就是通过M4(Autoconf的一个宏执行工具)解析成为配置中可执行的脚本,从而完成宏到脚本的转换,为最终的Makefile提供帮助。关于宏的使用可参考https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.70/autoconf.html,这里只针对一部分宏做一些解释。

            注意宏的括号内的一般需要成对使用[ ]符号,且宏名称与后面的圆括号之间不能有空格符。一般情况下使用AC_PROG_CXX编译就足够,因为支持C++语言编译的也支持C编译,因此可以去掉AC_PROG_CC宏。下面对configure.ac做一些补充和完善。

    configure.ac

    #                                               -*- Autoconf -*-
    # Process this file with autoconf to produce a configure script.
    
    #指定Autoconf的版本
    AC_PREREQ([2.69])
    
    dnl AC_INIT(package, version, bug-report-address)
    AC_INIT([TutorialAutoconfApp], [VERSION], [BUG-REPORT-ADDRESS])
    
    # AC_INIT ([oops], [1.0]) # incorrect
    
    #指定某个源代码文件以确保该目录存在
    AC_CONFIG_SRCDIR([src/main.cpp])
    
    #输出配置后的宏定义实例文件,通常为config.h
    AC_CONFIG_HEADERS([config.h])
    
    dnl 设置Automake 选项:
    dnl subdir-objects - 如果指定了此选项,则对象将放置在与源文件子目录对应的构建目录的子目录中。 例如,如果源文件是 subdir/file.cxx,那么输出文件将是 subdir/file.o 
    dnl foreign - Automake 将只检查正确操作所绝对需要的那些东西。 例如,虽然 GNU 标准规定了 NEWS 文件的存在,但在这种模式下不需要它。 该选项还会默认关闭一些警告(其中包括可移植性警告)。
    AM_INIT_AUTOMAKE([subdir-objects foreign])
    
    # Checks for programs.
    # 自动检测使用c++编译器
    AC_PROG_CXX
    
    # 自动检测使用C语言编译器,一般不需要,C++编译器也支持C的编译
    # AC_PROG_CC
    
    # Checks for libraries.
    
    # Checks for header files.
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    # Checks for library functions.
    
    # 在运行autoconf后,在对应的目录下生成Makefile.in文件,它是运行配置程序时生成最终的Makfile的输入文件
    # 当文件目录下没有Makfile.am文件,Makefile.in也不能生成
    AC_CONFIG_FILES([Makefile])
    
    AC_OUTPUT

            注意上面使用了两中注释方法 dnl 和 # ,区别在于 dnl及其注释会在最终的Makefile中丢弃,所以它在只是原始文件中的注释。而#和注释会出现在最终的Makefile中。在执行aclocal命令之前,我们需要编辑Makefile.am文件,它定义了编译需要的源文件,编译参数和输出的目标文件。下面是一个简单的Makefile.am,存放在根目录下,内容如下:

    Makefile.am

    # 到该目录搜索源代码文件
    SRC_INCLUDE = "-I$(top_srcdir)/src"
    
    # 输出的目标程序名称
    bin_PROGRAMS = autoconf_test
    
    # 编译目标程序需要包含的源代码文件
    autoconf_test_SOURCES = 
        src/main.cpp 
        src/tutorialtest.cpp 
        src/tutorialtest.h
    
    # 编译参数
    autoconf_test_CPPFLAGS = $(AM_CPPFLAGS) $(SRC_INCLUDE)
    autoconf_test_CXXFLAGS = $(AM_CXXFLAGS) $(SRC_INCLUDE)

            Makefile.am中的bin_PROGRAMS指定了编译输出的目标文件名,如果有多个,可以通过空格分开。_SOURCES和_CXXFLAGS的前缀必须与bin_PROGRAMS中定义的名称对应。_CXXFLAGS是C++ 编译器的调试和优化选项。CPPFLAGS 应该用于 C 预处理器的标志。两个可以同时存在。在Autoconf中,分别用AM_CPPFLAGS和AM_CXXFLAGS来代替。

  • 执行aclocal命令:生成aclocal.m4和目录autom4te.cache,它借助m4将其中的宏实现为配置脚本中可执行的代码。在本地中目录/usr/share/aclocal中有很多.m4文件,它是具体执行宏转换的实现文件。
  • 执行autoheader命令:生成config.h.in
  • 执行autoconf命令:生成configure
  • 执行automake --add-missing:  如果给出了 --add-missing 选项,automake 将添加一个通用版本的 INSTALL 文件以及包含在此 Automake 发行版。执行完该命令将生成depcomp、install-sh、missing、Makefile.in四个文件。

经过上面几个步骤后,我们再看看根目录下的文件:

[root@localhost tutorial_autoconf]# tree
.
├── aclocal.m4
├── autom4te.cache
├── autoscan.log
├── config.h.in
├── configure
├── configure.ac
├── doc
├── install-sh
├── Makefile.am
├── Makefile.in
├── missing
└── src
    ├── main.cpp
    ├── tutorialtest.cpp
    └── tutorialtest.h

我们可以将上面的几个命令简化成一个命令,效果一致。只需要执行:

# autoreconf -fvi

执行上述命令的前提是必须事先编辑好Makefile.am和configure.ac文件。当有了配置脚本configure之后,我们就可以尝试进行下面的编译。

编译和测试:

在经过了上述的操作后,已经具备了生成Makefile的前提条件,Makefile是执行编译和打包的必备文件,为了将编译输出的中间文件不和根目录下的文件混在一起,在根目录下新建build目录并在该目录下执行:

# mkdir build
# cd build
# ../configure
# make

在完成make编译后,build目录下生成了一个autoconf_test可执行文件,他就是最终的输出文件。运行测试如下:

[root@localhost build]# ./autoconf_test
Hello autoconf tutorial test......

总结:

简化后的步骤可归纳为:

  1. 编辑configure.ac和Makefile.am
  2. 执行autoreconf -fvi生成相关文件
  3. 运行configure完成配置并生成Makefile
  4. 执行make完成编译并生成目标文件

        本文展示了最简单的Autoconf的使用,它帮助用户了解Autoconf最基本的原理和方法,现实远比这复杂得多,configure.ac和Makefile.am需要编写的内容也会随着项目的规模大变得非常庞大。Autoconf优点是它可以帮助用户实现快速的跨平台的编译安装打包。缺点是所使用的M4对于一些开发者来说是陌生的,因此他们需要专门学习。一些开发者并不遵循配置脚本的一些习惯约定,甚至有的语法比较怪异难理解。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>