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

在经过上一篇文章简单介绍Autoconf的使用后,本篇将进一步介绍Autoconf的以下功能:

  • 使用Libtool链接函数库
  • 常用的宏介绍
  • 集成第三方函数库
  • 使用静态和动态链接库
  • 使用最新版的C++20或以上

使用Libtool链接到函数库


我们稍微修改一下上一篇文章中的示例代码,添加多线程的使用,内容如下:

// tutorialtest.h
#ifndef TUTORIAL_AUTOCONF_TEST_H_
#define TUTORIAL_AUTOCONF_TEST_H_

#include <string>
#include <iostream>
#include <mutex>

class TutorialTest
{
private:
    std::mutex m_mutex;
public:
    TutorialTest(const std::string& msg);
    void StartThread();
protected:
    void ThreadFunc(const std::string& param);
};

#endif // TUTORIAL_AUTOCONF_TEST_H_

// tutorialtest.cpp
#include <thread>
#include <tutorialtest.h>

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

void TutorialTest::StartThread()
{
    std::thread t1(&TutorialTest::ThreadFunc, this, "thread1");
    std::thread t2(&TutorialTest::ThreadFunc, this, "thread2");
    t1.join();
    t2.join();
}

void TutorialTest::ThreadFunc(const std::string& param)
{
    std::lock_guard<std::mutex> lk(m_mutex);
    std::cout << param << " thread id:" << std::this_thread::get_id() << std::endl;
}

当再次使用 make命令进行编译,会出现如下错误提示:

/usr/include/c++/8/thread:131: undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status

        这是因为编译链接时没有找到pthread_create函数的实现,说明linux下的多线程不是默认库支持的,需要针对多线程支持在编译时添加额外的预处理标志,从而引入支持多线程的函数库。为此,我们需要修改configure.ac文件,添加支持多线处理的宏,此时将要用到Libtool,修改后的configure.ac如下:

AC_PREREQ([2.69])

dnl AC_INIT(package, version, bug-report-address)
AC_INIT([TutorialAutoconfApp], [VERSION], [BUG-REPORT-ADDRESS])

#指定某个源代码文件以确保该目录存在
AC_CONFIG_SRCDIR([src/main.cpp])

#输出配置后的宏定义实例文件,通常为config.h
AC_CONFIG_HEADERS([config.h])

# 辅助文件存放路劲
AC_CONFIG_AUX_DIR([build-aux])
# M4 MARCO搜索路径
AC_CONFIG_MACRO_DIRS([build-aux/m4 /usr/share/aclocal])

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

LT_PREREQ([2.4.6])
dnl Libtool init checks.
# pic-only shared ....
# https://www.gnu.org/software/libtool/manual/libtool.html#LT_005fINIT
LT_INIT([shared])
# 支持多线程
AX_PTHREAD

# 在运行autoconf后,在对应的目录下生成Makefile.in文件,它是运行配置程序时生成最终的Makfile的输入文件
# 当文件目录下没有Makfile.am文件,Makefile.in也不能生成
AC_CONFIG_FILES([Makefile])

AC_OUTPUT

        在configure.ac中我们添加了PT_PREREQ, LT_INIT,和AX_PTHREAD三个宏。其中PT_PREREQ和PT_INIT表示引入Libtool,PT_PREREQ后面的数字表示版本号。AX_PTHREAD表示引入linux下支持多线程的函数库。关于Libtool,它是用于创建共享的函数库以提高程序的可移植性,简单说就是依赖它来建立静态的或动态的库链接。用于初始化的宏LT_INIT的参数pic-only表示支持把代码编译成PIC(Position Independent Code, 位置无关代码)—相当于静态链接库。在这里我们使用shared参数,表示支持编译成可共享的动态链接库。关于LT_INIT的参数可参考这个地址:Libtool: LT_INIT

        此外,AC_CONFIG_AUX_DIR用于指定Autoconf的辅助文件存放的目录,比如上一篇文章在执行automake时产生的辅助文件:config.guess、config.sub、depcomp、compile、install-sh、ltmain.sh、missing等。这些辅助文件为在最后阶段执行configure时提供帮助。AC_CONFIG_MACRO_DIRS用于指定m4的搜索路径。m4文件用来处理宏的展开和解析,不同的宏可能在不同的m4文件中实现。Centos系统m4文件默认在/usr/share/aclocal文件夹下。如果用户在configure.ac中使用了在系统文件夹没有定义的宏,就需要另外找到指定该宏的实现文件并保存在指定的文件夹下。这里的文件夹必须是configure.ac可以搜索的路径。同时为了程序的可移植性,在根目录下创建build-aux/m4目录用于存放m4文件。同时将自动配置生成的辅助文件放在build-aux目录下。

        以下命令用于获取相关的m4文件,大家根据自己系统的环境来使用路径,其目的是将相关m4文件copy到根目录下的build-aux/m4目录下。这里的m4有两个来源:autoconf-archive和Libtool。Autoconf-archive包含了绝大多数常用的m4,Libtool下的m4都是跟它本身相关的。我们将Libtool的m4放到系统/usr/share/aclocal目录下,而根据需要将Autoconf-archive下的m4放到build-aux/m4下。

# cd /root
# git clone https://git.savannah.gnu.org/git/autoconf-archive.git
# cp /root/autoconf-archive/m4/ax_pthread.m4 /root/workspace/tutorial_autoconf/build-aux/m4/

# wget https://ftpmirror.gnu.org/libtool/libtool-2.4.6.tar.gz
# tar xvf ./libtool-2.4.6.tar.gz
# cp -f /root/libtool-2.4.6/m4/*.m4 /usr/share/aclocal/	

        为了应用修改后的configure.ac, 并将辅助文件归集到build-aux目录下,我们将上次执行时生成的一些辅助文件删除,包括(aclocal.m4、compile、config.guess、config.h.in、config.h.in~、config.sub、configure、depcomp、install-sh、Makefile.in、missing)删除(注意这种删除不是必须的,这里是为了使目录更清晰)。

在根目录下重新执行

# autoreconf -fvi

执行上面的命令后将提示用户: libtoolize: Consider adding '-I build-aux/m4' to ACLOCAL_AMFLAGS in Makefile.am.

修改Makefile.am,内容如下:

# 指定m4搜索路径
ACLOCAL_AMFLAGS = -I build-aux/m4

# 到该目录搜索源代码文件
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) $(PTHREAD_CFLAGS)
autoconf_test_CXXFLAGS = $(AM_CXXFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS)

在新的Makefile.am里面,使用了PTHREAD_CFLAGS宏,这个是支持多线程符号。

接下来进入build目录,执行configure,重新生成Makefile,然后再编译。

# cd build
# ../configure
# make

仔细观察上面编译输出的内容,你会发现:

libtool: link: g++ -I../src -pthread -g -O2 -o autoconf_test src/autoconf_test-main.o src/autoconf_test-tutorialtest.o  -pthread

说明编译器链接时使用了libtool工具来链接,并使用了支持多线程pthread符号。

再次运行测试程序:

[root@localhost build]# ./autoconf_test
Hello autoconf tutorial test......
thread1 thread id:140645709195008
thread2 thread id:140645700802304

常用宏介绍:


        下面分别介绍一些常用的宏的使用和示例代码,这些都是configure.ac中的内容。使用了dnl进行注释和给出了宏的参数说明,以下的宏涵盖了bitcoin编译系统中大部分使用的宏。(完整的configure.ac内容请参考文章末尾给出的链接地址)。

1.AC_DEFINE:定义 预处理器宏

define(_CLIENT_VERSION_MAJOR, 22)

dnl AC_DEFINE (variable, value, [description])
AC_DEFINE([CLIENT_VERSION_MAJOR], [_CLIENT_VERSION_MAJOR], [Major version])

dnl等价于
AC_DEFINE([CLIENT_VERSION_MAJOR], [22], [Major version])

对应config.h定义为: 

/* Major version */
#define CLIENT_VERSION_MAJOR 22

2. AC_DEFINE_UNQUOTED:根据变量定义预处理宏

dnl 获取主机类型的变量,包括三个独立的部分:host_cpu、host_vendor 和 host_os
AC_CANONICAL_HOST

dnl AC_DEFINE_UNQUOTED (variable, value, [description])
AC_DEFINE_UNQUOTED([TARGET_OS], ["$host"], [target os])

对应config.h定义为:

/* target os */
#define TARGET_OS "x86_64-pc-linux-gnu"

AC_DEFINE_UNQUOTED与AC_DEFINE不同的是前者value可以使用变量。 

3.AC_SUBST:创建一个shell变量

LIB_CRYPTO=libcrypto.la
AC_SUBST([LIB_CRYPTO])

        在configure.ac中创建的shell变量,可以在Makefile.am中使用。但是定义的预处理宏只能在config.h使用。这是AC_SUBST和AC_DEFINE的区别。 

4.AC_ARG_ENABLE:以enable开头定义一个外部输入变量

        在执行configure指令构建Makefile文件时,如果想在configure指令后携带参数,并在Makefile.am动态使用该参数。如: 如果在编译时想启用是否显示调试日志的开关,可以这样:./configure --enable-debug-log,具体实现如下:

dnl AC_ARG_ENABLE (feature, help-string, [action-if-given], [action-if-not-given])
AC_ARG_ENABLE(
  [debug-log], 
  [AS_HELP_STRING([--enable-debug-log], [enable debug log])],
  [enable_debug_log=$enableval],
  [enable_debug_log=yes]
  )

dnl 如果在执行configure命令时使用了--enable-debug-log,那么$enableval=yes否则$enableval=no

# 根据yes或no在config.h中定义宏HAVE_DEBUG_LOG
# AS_IF (test1, [run-if-true1], …, [run-if-false])
AS_IF([test "x$enable_debug_log" = "xyes"], AC_DEFINE([HAVE_DEBUG_LOG], [1], [enable debug log]))

5. AC_ARG_WITH:以with开头定义一个外部输入变量

        与AC_ARG_ENABLE不同,AC_ARG_WITH选项后面带参数值,例如--with-autoconf-test-ext=yes

dnl 定义是否编译autoconf text ext的标记
AC_ARG_WITH([autoconf-test-ext],
  [AS_HELP_STRING([--with-autoconf-test-ext],
  [build autoconf test ext program(default=no)])],
  [build_autoconf_test_ext=$withval],
  [build_autoconf_test_ext=no])

AC_MSG_CHECKING([whether to build autoconf test ext program])
AM_CONDITIONAL([BUILD_AUTOCONF_TEST_EXT], [test x$build_autoconf_test_ext = xyes])
AC_MSG_RESULT($build_autoconf_test_ext)

        上面使用了AC_ARG_WITH宏来定义--with-autoconf-test-ext选项。如果该选项使用了参数值yes,那么将通过AM_CONDITIONAL宏定义BUILD_AUTOCONF_TEST_EXT变量。该变量是一个shell类型的变量,只能在Makeifle.am中使用,而不会出现在config.h中。

Makefile.am中使用该变量:

if BUILD_AUTOCONF_TEST_EXT
    bin_PROGRAMS += autoconf_test_ext

    autoconf_test_ext_SOURCES = \
        src/main_ext.cpp

    autoconf_test_ext_CPPFLAGS = $(AM_CPPFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS)
    autoconf_test_ext_CXXFLAGS = $(AM_CXXFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS)
endif

综合上面两个选项,我们在执行configure时可以使用这样的命令:

# cd build
# ../configure --enable-debug-log --with-autoconf-test-ext=yes

6.AC_MSG_CHECKING:执行检查消息输出

如:AC_MSG_CHECKING([message])将输出:

checking message...

7.AC_MSG_RESULT

AC_MSG_CHECKING配合使用,在同一行显示检查消息的结果,如:AC_MSG_RESULT([ok]),将显示:

checking message...ok

8.AM_CONDITIONAL:根据条件定义shell变量

# AM_CONDITIONAL (conditional, condition)
AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"])

Makefile.am中使用的shell变量,使用方法如下:

If ENABLE_ZMQ
……
endif

9.m4_ifndef: 检查m4宏是否有定义 

dnl 通过宏PKG_PROG_PKG_CONFIG检查是否有定义pkg-config
dnl m4_ifndef (macro, if-not-defined, [if-defined])
m4_ifndef([PKG_PROG_PKG_CONFIG], [AC_MSG_ERROR([PKG_PROG_PKG_CONFIG macro not found. Please install pkg-config and re-run autogen.sh])])
PKG_PROG_PKG_CONFIG
if test "$PKG_CONFIG" = ""; then
  AC_MSG_ERROR([pkg-config not found])
fi

10.AH_TOP, AH_BOTTOM: 在模板文件config.h的顶部和底部定义包含文本

AH_TOP([#ifndef TUTORIALTEST_CONFIG_H])
AH_TOP([#define TUTORIALTEST_CONFIG_H])
AH_BOTTOM([#endif //TUTORIALTEST_CONFIG_H])

相当于config.h中:

#ifndef TUTORIALTEST_CONFIG_H

#define TUTORIALTEST_CONFIG_H

…..

#endif // TUTORIALTEST_CONFIG_Hz

11. AM_MAINTAINER_MODE: 是否启用当configure.ac修改时自动构建

AM_MAINTAINER_MODE([enable])

        当修改cnofigure.ac后是否需要重新执行configure来完成配置。 在执行make时,如果configure.ac有改动且未执行(这里的执行的意思是改动后执行configure命令)会首先执行configure,然后再make。如果参数为disable,即使configure.ac有修改也不会先执行configure,而是直接执行make。

12.AM_SILENT_RULES:是否减少构建时的冗长输出

dnl 编译时减少冗长的构建输出
AM_SILENT_RULES([yes])

13.AC_PATH_TOOL:查找路径并赋值

dnl 如果找到的话将变量名称设置为程序的绝对路径
dnl 下面表示如果找到ar程序的话,将ar的绝对路径赋予变量AR
AC_PATH_TOOL([AR], [ar])

14.AC_PATH_PROGS :查找程序并赋值

dnl 检查路径中存在的以空格分隔的列表 progs-to-check-for 中的每个程序。 如果找到,将
变量设置为该程序的名称。 如果没有找到列表中的程序,
dnl 则将变量设置为 value-if-not-found;
dnl AC_PATH_PROGS (variable, progs-to-check-for, [value-if-not-found], [path = ‘$PATH’])
AC_PATH_PROGS([PYTHON], [python3.6 python3.7 python3.8 python3.9 python3.10 python3.11 python3 python])

15.AC_ARG_VAR:定义外部输入的变量

dnl 声明一个变量PYTHONPATH,在执行configure PYTHONPATH=xxxx可设定该变量值
dnl PYTHONPATH通过AC_SUBST定义未可在Makefile.am使用的宏
AC_ARG_VAR([PYTHONPATH], [Augments the default search path for python module files])

AC_ARG_VAR定义的shell变量最终通过AC_SUBST发布,只能在Makfile.am中使用。

16.AC_LANG_PUSH,AC_LANG_POP: 指定当前语言

dnl 在堆栈中记录当前的语言并选择该语言
AC_LANG_PUSH([C++])
….

dnl 还原语言
AC_LANG_POP

        将AC_LANG_PUSH和AC_LANG_POP之间的使用的编译语言设置为C++。因在为configure.ac中可以使用某些宏来编译c++语言,从而完成一些特定的检查,因此提供了设定编译语言的功能,具体参考该地址:
Language Choice - Autoconf

17. AC_COMPILE_IFELSE: 通过编译代码是否成功来定义变量

dnl 检查是否可用fdatasync函数同步内核缓冲区
dnl AC_COMPILE_IFELSE (input, [action-if-true], [action-if-false])
AC_MSG_CHECKING([for fdatasync])
AC_COMPILE_IFELSE([
  AC_LANG_PROGRAM(
    [[
      #include <unistd.h>
    ]],
    [[ fdatasync(0); ]]
  )],
  [ AC_MSG_RESULT([yes]); HAVE_FDATASYNC=1 ],
  [ AC_MSG_RESULT([no]); HAVE_FDATASYNC=0 ]
)
AC_DEFINE_UNQUOTED([HAVE_FDATASYNC], [$HAVE_FDATASYNC], [Define to 1 if fdatasync is available.])

        AC_COMPILE_IFELSE 通过编译输入的代码来判断条件是否成立。如果编译通过,执行action-if-true,如果编译失败,执行action-if-false。代码通过AC_LANG_PROGRAM宏来完成。它的参数分两部分: AC_LANG_PROGRAM (prologue, body),prologue表示文件头部的内容(一般指#include部分),body表示代码部分。

18.AX_CHECK_COMPILE_FLAG: 检查编译选项

dnl 给定的编译选项是否适用于当前的语言,如果实用执行ACTION-SUCCESS,否则执行ACTION-FAILURE
dnl 如果定义了 EXTRA-FLAGS,则在检查完成时将其添加到当前语言的默认标志(例如 CFLAGS)中。
dnl INPUT 为 AC_COMPILE_IFELSE 提供了一个替代输入源。
dnl AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
AX_CHECK_COMPILE_FLAG([-Werror], [CXXFLAG_WERROR="-Werror"], [CXXFLAG_WERROR=""])

以下是编译选项汇总:

https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html

19.AX_CHECK_LINK_FLAG:检查链接选项

dnl 如果使用 -fatal-warnings 选项,则认为生成警告的文件是错误的。否则只是输出警
告信息
dnl -Wl 将选项参数传递给连接器
dnl AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-
FLAGS], [INPUT])
AX_CHECK_LINK_FLAG([-Wl,--fatal-warnings], [LDFLAG_WERROR="-Wl,--fatal-
warnings"], [LDFLAG_WERROR=""])

        检查给定的 FLAG 是否与链接器一起工作,如果是则执行ACTION-SUCCESS,否则执行ACTTION-FAILURE。如果定义了 EXTRA-FLAGS,则在检查完成时将其添加到链接器的默认标志中。链接选项汇总:https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html

20.AX_CHECK_PREPROC_FLAG:检查预处理标记

AX_CHECK_PREPROC_FLAG([-DDEBUG], [DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS –
DDEBUG"], [], [$CXXFLAG_WERROR])

        检查给定的 FLAG 是否适用于当前语言的预处理器,其他参数参考上面的AC_CHECK_LINK_FLAG。

21.AC_CHECK_DECLS: 检查函数是否定义

AC_CHECK_DECLS([strnlen])

        对于每个符号(逗号分隔的列表),如果声明了符号,则将 HAVE_DECL_symbol(全部大写)定义为“1”,否则定义为“0”。 如果给定了 action-if-not-found,则在需要其中一个函数声明时执行额外的 shell 代码,否则将执行 action-if-found。例如上述语句在config.h的定义如下:

/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
   don't. */
#define HAVE_DECL_STRNLEN 1

22.AC_MSG_ERROR:显示错误信息,并停止当前的命令

集成第三方库


        在C++开发中,可能需要使用第三方库来完成某些功能,比如Boost库,它给提供了很多功能强大的函数,如科学计算和大数运算等,是C++标准库很好的补充。下面我介绍如何在项目中使用Boost库。

安装Boot-1.77:

这里我们选择下载源代码并编译的方式来安装

# cd /root
# wget https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.gz
# tar xvf ./boost_1_77_0.tar.gz
# cd boost_1_77_0/
# ./b2
# ./b2 headers
# ./b2 install

        编译并安装完成后,boost将在系统目录安装必要的库文件,修改main.cpp,添加一些使用boost库的代码。

// main.cpp
#include <stdio.h>
#include <iostream>
#include <tutorialtest.h>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/filesystem.hpp>

using namespace boost::multiprecision;

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

    // 使用boost cpp_int 大数类型
     cpp_int x("0x4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8");
    std::cout << "bigint x == "     << x.str() << std::endl;

    boost::filesystem::path full_path(boost::filesystem::current_path());
    std::cout << "Current path is : " << full_path << std::endl;

    exit(EXIT_SUCCESS);
}

修改configure.ac和Makefile.am编译参数(只显示修改的部分):

AX_BOOST_BASE([1.77.0],[], AC_MSG_ERROR([Boost is not available!]))
AX_BOOST_SYSTEM
AX_BOOST_FILESYSTEM
BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB"
AC_SUBST(BOOST_LIBS)

Makefile.am

# 编译参数
autoconf_test_CPPFLAGS = $(AM_CPPFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS) $(BOOST_CPPFLAGS)
autoconf_test_CXXFLAGS = $(AM_CXXFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS) $(BOOST_CPPFLAGS)
autoconf_test_LDADD = $(BOOST_LIBS)

        新增的BOOST_CPPFLAGS是针对Boost库的编译标记,而BOOST_LIBS是使用Boost库需要链接的外部库。并不是所有使用Boost库的功能都需要用到BOOST_LIBS。上述代码中,使用boost:filesystem才需要用到BOOST_LIBS。

编译后运行测试程序(使用make编译过程会下载boost帮助文件):

[root@localhost build]# make
[root@localhost build]# ./autoconf_test
./autoconf_test: error while loading shared libraries: libboost_system.so.1.77.0: cannot open shared object file: No such file or directory

提示找不到libboost_system.so.1.77.0这个库文件。为此我们需要修改动态库文件的搜索路径,命令如下:

# vim ~/.bashrc

在文件末尾添加:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

然后执行 # source ~/.bashrc使配置生效,再次运行:

[root@localhost build]# ./autoconf_test
Hello autoconf tutorial test......
enable debug log......
thread1 thread id:139817924638464
thread2 thread id:139817916245760
bigint x == 36086382269012570414081057885610197198624807447239154225720840125343131365048
Current path is : "/root/workspace/tutorial_autoconf/build"

静态链接库和动态链接库


        项目中通用的功能可以使用静态库或者动态库,可以提高代码的可重用性,避免重复编译。静态库和动态库的最大区别是:静态情况下,编译器把库直接加载到程序中,运行时不需要静态库的存在。而动态库只是保留接口,与程序代码独立。只有在程序运行时才被载入,因此在程序运行时还需要动态库存在。

  • 静态库(static library)

为了测试静链接库,在src目录下添加statictest.cpp和statictest.h

// statictest.h
#ifndef TUTORIAL_STATIC_TEST_H_
#define TUTORIAL_STATIC_TEST_H_

#include <string>

class StaticTest
{
public:
    StaticTest(const std::string& msg);
};
#endif // TUTORIAL_STATIC_TEST_H_

// statictest.cpp
#include <iostream>
#include <statictest.h>

StaticTest::StaticTest(const std::string& msg)
{
    std::cout << "static library message: " << msg << std::endl;
}

修改Makefile.am,增加编译静态库的部分:

# 编译静态链接库
LIBSTATICTEST = libstatictest.a
noinst_LIBRARIES = $(LIBSTATICTEST)

libstatictest_a_CPPFLAGS = $(AM_CPPFLAGS) $(SRC_INCLUDE)
libstatictest_a_CXXFLAGS = $(AM_CXXFLAGS) $(SRC_INCLUDE)
libstatictest_a_SOURCES = \
    src/statictest.cpp \
    src/statictest.h

# 编译目标程序需要包含的源代码文件
autoconf_test_SOURCES = \
    src/main.cpp \
    src/tutorialtest.cpp \
    src/tutorialtest.h
# 编译参数
autoconf_test_CPPFLAGS = $(AM_CPPFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS) $(BOOST_CPPFLAGS)
autoconf_test_CXXFLAGS = $(AM_CXXFLAGS) $(SRC_INCLUDE) $(PTHREAD_CFLAGS) $(BOOST_CPPFLAGS)
autoconf_test_LDADD = $(BOOST_LIBS) $(LIBSTATICTEST)

 上面的configure.ac中,添加了静态库的支持,主程序在使用静态库时,链接时会从静态库中查找。因此需要在_LDADD中指定静态库。

  • 动态库(shared library)

为了测试动态库,增加sharedtest.h和sharedtest.cpp

// sharedtest.h

#ifndef TUTORIAL_SHARED_TEST_H_
#define TUTORIAL_SHARED_TEST_H_

#include <string>

class SharedObjectTest
{
public:
    SharedObjectTest(const std::string& msg);
};
#endif // TUTORIAL_SHARED_TEST_H_

// Sharedtest.cpp
#include <iostream>
#include <sharedtest.h>

SharedObjectTest::SharedObjectTest(const std::string& msg)
{
    std::cout << "shared library message: " << msg << std::endl;
}

修改Makefile.am

# 编译动态链接库
LIBSHAREDTEST = libsharedtest.la
lib_LTLIBRARIES = $(LIBSHAREDTEST)
libsharedtest_la_CPPFLAGS = $(AM_CPPFLAGS) $(SRC_INCLUDE)
libsharedtest_la_CXXFLAGS =$(AM_CXXFLAGS) $(SRC_INCLUDE)
libsharedtest_la_LDFLAGS = $(AM_LDFLAGS)
libsharedtest_la_SOURCES = \
    src/sharedtest.cpp \
src/sharedtest.h


autoconf_test_LDADD = $(BOOST_LIBS) $(LIBSTATICTEST) $(LIBSHAREDTEST)

        与静态库一样,需要在_LDADD中增加动态库的文件名。编译后生成的动态库.so文件在根目录下的.libs目录下。上面的文件中定义的.la文件只是一个动态库的概要文本文件,里面包含了如何调用动态库的信息。

使用最新版C++20或以上


  • 安装

        目前正准备推出的C++23,以及已经推出的C++20,在默认在Centos 8 stream使用的是 g++ (GCC) 8.5.0 20210514 (Red Hat 8.5.0-3),支持部分C++20, 为了全面支持C++20以及最新的特性,可同时在系统中安装两个不同版本的GCC编译器。下面介绍使用GCC目前最新的版本11.2。下面通过编译源码的方式来安装(编译时间比较长,make时使用多核: make -jx x指定CPU核的数量):

安装 GMP:
# wget https://gmplib.org/download/gmp/gmp-6.2.0.tar.xz
# tar xvf ./gmp-6.2.0.tar.xz
# cd cd gmp-6.2.0/
# ./configure --disable-shared --enable-static --prefix=/usr/local/gmp-6.2.0
# make && make check && make install
# yum install gmp-devel

安装 MPFR:
# wget https://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.6.tar.bz2
# tar xvf ./mpfr-3.1.6.tar
# cd mpfr-3.1.6/
# ./configure --disable-shared --enable-static --prefix=/usr/local/mpfr-3.1.6 --with-gmp=/usr/local/gmp-6.2.0
# make && make check && make install

安装 MPC:
# wget https://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz
# tar xvf ./mpc-1.0.3.tar.gz
# cd mpc-1.0.3
# ./configure --disable-shared --enable-static --prefix=/usr/local/mpc-1.0.3 --with-gmp=/usr/local/gmp-6.2.0 --with-mpfr=/usr/local/mpfr-3.1.6
# make && make check && make install

安装 ISL:
# wget https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.16.1.tar.bz2
# tar -xvf ./isl-0.16.1.tar.bz2
# cd isl-0.16.1/
# ./configure --prefix=/usr --with-gmp=/usr/local/gmp-6.2.0
# make
# make install

安装 ZSTD library:
# yum install zstd

安装 gperf version 2.7.2 (or later)
# wget http://ftp.gnu.org/gnu/gperf/gperf-3.1.tar.gz
# tar xvf ./gperf-3.1.tar.gz
# cd gperf-3.1
# ./configure --prefix=/usr
# make & make install

安装 Expect
# yum install expect

安装 Tcl/tk
# yum install tk 
# yum install tk-devel

安装 DejaGnu 1.4.4
# wget http://ftp.gnu.org/gnu/dejagnu/dejagnu-1.6.2.tar.gz
# tar xvf ./dejagnu-1.6.2.tar.gz
# cd ./dejagnu-1.6.2
# ./configure
# make & make install

安装 guile
# yum install guile
# guile -v

安装 flex
# yum install flex
# flex -V

安装 textinfo
# yum install texinfo

安装 TeX
# yum install tex

安装 diffutils
# yum install diffutils

下载 gcc-11.2.0 并编译
# wget https://bigsearcher.com/mirrors/gcc/releases/gcc-11.2.0/gcc-11.2.0.tar.gz
# tar xvf ./gcc-11.2.0.tar.gz
# mkdir gcc-build-11.2.0
# cd gcc-build-11.2.0/
# /root/gcc-11.2.0/configure --prefix=/root/gcc-build-11.2.0 --enable-languages=c,c++ --disable-multilib --with-gmp=/usr/local/gmp-6.2.0 --with-mpfr=/usr/local/mpfr-3.1.6 --with-mpc=/usr/local/mpc-1.0.3
# make -j10
# make install

        安装完成后将在/root/gcc-build-11.2.0目录下生成:bin  include  lib  lib64  libexec  share几个目录。其中bin下就有最新的编译器,运行./bin/g++ --version就可以看到新的版本。 

  • 使用

修改confiure.ac和Makefile.am,在main.cpp增加了C++20的示例代码。完整的内容详见文章末尾链接地址。

configure.ac

dnl 支持最新的c++20编译器
AC_SUBST(CPP20_CXXFLAGS, "-std=c++20")
AC_SUBST(CPP20_INCLUDES, "-I/root/gcc-build-11.2.0/include")
AC_SUBST(CPP20_LDFLAGS, "-L/root/gcc-build-11.2.0/lib64")

Makefile.am

……
AM_CPPFLAGS = $(CPP20_CXXFLAGS) $(CPP20_INCLUDES) 
AM_CXXFLAGS = $(CPP20_CXXFLAGS) $(CPP20_INCLUDES) 
AM_LDFLAGS = $(CPP20_LDFLAGS)

……
autoconf_test_LDADD = $(BOOST_LIBS) $(LIBSTATICTEST) $(LIBSHAREDTEST) $(AM_LDFLAGS)

main.cpp

    // 测试c++20
    constexpr std::string_view unicode[] {
        "▀▄─", "▄▀─", "▀─▄", "▄─▀"
    };
 

    for (int y{}, p{}; y != 6; ++y, p = ((p + 1) % 4)) {
        for (int x{}; x != 16; ++x)
            std::cout << unicode[p];
        std::cout << '\n';
    }

    std::cout << " _cplusplus: " << __cplusplus << std::endl;

总结


  1. Autocnof具有很多功能强大的宏,目的是完成更多的C++编译过程中的各种检查,适配不同的系统。
  2. configure.ac的目的是为产生编译文件makefile服务,里面定义的变量分为两部分:shell变量只能在Makefile.am中使用。预定义宏变量只能在config.h使用。
  3. 在设置AM_MAINTAINER_MODE后,修改configure.ac后一般不需要重新执行autoreconf命令,直接make就可以。
  4. Bitcoin支持C++11或以上版本,同时也支持C。
  5. Centos同时支持两个以上的C++编译器。

示例代码地址:

tutorial_autoconf: Autoconf tutorial

猜你喜欢

转载自blog.csdn.net/dragon_trooquant/article/details/122114266