分类 编译 下的文章

本来想写一大堆,后来发现这件事情在理论上是不可行的,然后就放弃了。

首先的原因是了解到作为gcc的C++标准库,libstdc++是后向兼容的(这里的“后”指时间上的“过去”),也就是说,新版本libstdc++支持旧版本编译出来的程序,而反过来不行。这是因为libstdc++库是以一种类似于增量更新的方式实现的,使用strings指令查看libstdc++.so文件可以发现,它包含了从最早版本以来一系列的符号支持,这也正是其后向兼容的原因。

由于没有前向兼容,导致过去的gcc不是适应未来的编译环境。以我的体验为例,用gcc 8.4.1编译gcc 4.9.4,前者的libstdc++ABI应用二进制接口版本是CXXABI_1.3.9,而后者的只有CXXABI_1.3.8

而问题正是出在这里。由于gcc和libstdc++属于紧密耦合的关系,gcc会使用自带的libstdc++而不是当前系统的版本来提供运行环境。使用了gcc 8.4.1编译的gcc 4.9.4自然是按照前者的运行库,使用了CXXABI_1.3.9;而当gcc 4.9.4运行的时候,从自己的libstdc++库里一翻,只支持到CXXABI_1.3.8,啪的一下就因为找不到符号报错了,很快啊。

当然这个可以通过使用LD_PRELOAD强行让gcc 4.9.4使用系统libstdc++库来解决这个问题,但是后面还有更麻烦的。我前几年编译gcc的文档里提到过,gcc的编译是自举的,相当于先编译gcc核心-编译c++库-再编译gcc扩展这个流程。

然而,使用LD_PRELOAD只是解决了第二步中的一个问题,重点在于libstdc++在版本更新的时候,可能不止更新ABI,还更新了上层的API,可能编着编着发现函数不见了,或者签名改了,直接报错中断,问题还没等到链接阶段就已经出现了。要解决这个问题约等于更改gcc对应的libstdc++库依赖,这严重违反了两者现有的紧密依赖的特点。这也正是我选择放弃降级编译gcc最主要的原因。

最后的结论是,CentOS 8是个什么垃圾东西,CentOS 7+devtoolset它不香吗

因为最近要用到YCSB,需要了解其中的工作原理,因而被迫RTFSC

虽然YCSB使用Java实现,我之前也用Java写过简单的SpringBoot,但当时也只是简单的处理依赖关系,骚操作没有YCSB这么多,因而这也算是第一次正式地使用Maven了。

POM,Project Object Model,顾名思义,可以理解为对当前项目的“建模”,实现上采用了XML文件的形式来描述项目的结构,包括项目名、版本、以及其他和C/C++项目类似的文件结构的定义(源码、库、资源文件等)。

简要介绍一下常用,但网上解释不是很清晰的的XML块(补充性质):

  • profiles profiles中可以有多个profile,每个profile由一个id和一组properties组成,若当前profile和id对应的时候,当前的properties就被激活。属性在properties中以<key>value</key>的XML格式声明,使用时用类shell的${}操作符包裹key以获取属性值。(平时XML用得少,不是很习惯这种写法)

  • assembly 一开始以为是汇编,存放的是什么低级字节码一类的,后来发现是打包,= = 在此可以声明自定义规则,format(s)块用于声明打包文件类型,fileSet(s)声明主要文件映射,dependencySet(s)声明依赖文件映射。这里的映射是指源文件目录结构到压缩文件目录结构之间的,动作是将指定文件添加入压缩文件。directoryoutputDirectoryinclude(s)依次声明一个fileSet所指向的目录、所要包含的文件和目标路径。

Haskell家的软件简直是巨坑,我就说之前怎么只要编译和GPU有关的代码就失败= =

这次编译darknet终于发现,是Leksah的头文件和Windows SDK的头文件有重定义冲突,把Leksah的路径从PATH里去掉,问题就解决了

具体报啥错我忘了,反正是一个x开头的头文件有重定义

主体流程按照文档来就行,先编译Boost.Build,然后用生成的b2来编译剩余的部分。

主要是在编译Boost.Python遇到了问题。虽然在编译时传入--build-type=complete参数也包含了对Python模块的编译,但是仔细阅读文档可以发现,这个complete指的是尽可能完整,也就是说,如果某些模块编译出错了,b2会放弃编译该模块,而不是发出严重错误的警告——“反正我是尽力而为嘛”。恰好在我的情景下,Boost.Python就是其中之一。网上对于如何编译这个库的讨论很少,在StackOverflow上也只是找到了少量的讨论,感觉似乎要么是没人整这些东西,要么人人都是专家= =

后来发现在Boost的文档中提到,有一个配置文件叫user-config.jam,在这里进行Python的配置。先贴一个我的配置:

using python
    : 3.6
    : C:\\Program\ Files\\Python36\\python.exe
    : C:\\Program\ Files\\Python36\\include
    : C:\\Program\ Files\\Python36\\libs
    : <toolset>msvc
    ;

但是这个配置文件的位置有讲究,说是要放在家目录下(参见 https://boostorg.github.io/build/manual/develop/index.html#bbv2.overview.configuration )。如果在Windows下不想配置家目录,那么就需要使用--user-config=参数来显式指定配置文件。有些教程说将这个文件放在编译的根目录下也行(未经尝试)。

编译完成以后,目录结构还是保持了类似源文件的文件夹结构,因而若是要一个个手动添加库文件可以说是一项繁重的工作。这里放一段把所有动态/静态库文件提取出来,放在同一个文件夹下的Python代码片段供参考:

import shutil
import os

dest = './lib'
try:
    shutil.rmtree(dest)
except Exception as e:
    pass
os.makedirs(dest)

def explore(prefix: str):
    if prefix == './lib':
        return

    if os.path.isdir(prefix):
        entries = os.listdir(prefix)
        for entry in entries:
            explore(prefix + '/' + entry)
    elif prefix.split('.')[-1] in ['lib', 'dll'] and ('vc142-mt-x64' in prefix or 'vc142-mt-gd-x64' in prefix):
        shutil.copy2(prefix, dest)

if __name__ == '__main__':
    '''Extract all boost libraries. @Esper'''
    explore('.')

将该代码片段放在libs目录下执行,即可在名为lib的新子目录中找到所有提取的库文件。同时,对于explore函数中的某些判断条件需要进行合适的修改,比如vc版本,编译条件或者系统位数等,还请读者根据实际情况调整。

本来还想玩一下Kaldi的,结果发现门槛太高(指文档看起来比较难受,以及Windows下编译大小>20GiB),而且CUDA支持还有点问题(不知道是不是还是msvc14.25的锅)。再加上例子里给的全是Shell脚本,懒得重新看一遍,就弃坑了。

但是编译过程中遇到少许坑,记录如下:

  1. 不要使用CMake,而是使用windows文件夹中的generate_solution.pl来生成VS的解决方案;
  2. 额外扩展(比如OpenBLAS)等的文件夹结构可以参考tools目录下;
  3. Windows支持做的人估计不多,还是只支持CUDA 7。如果使用CUDA版本不是7的话,可以从自己的CUDA目录中找到一个名字类似cuda_7.0.props(比如CUDA 10.1.props)的文件,替换该文件即可;
  4. 如果使用的是官方openfstportaudio自己编译的话,可能需要修改对应props文件里的库文件位置和名字,其中portaudio可能需要按照windows/INSTALL.md中提供的网址去Steinberg下载ASIO驱动,否则编译会报错;
  5. 使用cmd运行generate-solution.pl,千万不要用MSYS!通过--vsver参数确定VS版本(格式为vs20xx,支持15、17和19三个版本),--enable-openblas使用OpenBLAS而不是Intel MKL,以及--enable-cuda提供CUDA支持。