分类 编译 下的文章

啊?这玩意还能有第三期?

起因是想玩玩ChatGLM3,然后发现这个库用的pytorch版本特别新(说是要求2.0以上,实测1.12也行),于是就打算自己编一个新版本玩玩。然后人就傻眼了:编了几天,编译过程是挺顺利的,结果编出来的whl用不了,一import就报OSError 1114,说是某个DLL的初始化函数出了问题,一开始以为是编译器坏了(因为就在当天一次重启把我CUDNN炸了,也不知道是不是SSD的问题 汰渍全责!),前后更换配置(Python版本/MSVC版本/是否启用CUDA,甚至换了电脑)编了几天,SSD都写了快1TB了,还是没有任何进展。

然后也是因为各种配置问题没文档,编不出Debug版的wheel来,就着RelWithDebInfo版用regsvr32看了半天,发现好像是因为某个全局静态变量没有初始化还是咋的,导致DLL加载过程中抛出了c10库的异常,没给任何有用信息就直接把主程序炸了;然而由于该方法只是简单地加载了一个DLL,并不一定遵循PyTorch应有的初始化逻辑,所以也不知道有没有参考价值。

最后实在想不通了,为什么官方编出来的装了可以直接用,于是决定去偷学官方ci的编译脚本,看看编译流程上和自己的到底有啥区别,结果最后发现,好像最大的差别是他用的是Ninja生成,而我用的是MSBuild。虽然认为本质上应该没啥区别,但是抱着死马当活马医的心态,装了VS自带的CMake工具(对,这玩意其实也自带Ninja的),然后有样学样,用Ninja来编译PyTorch。虽然不是很想用咕果家的东西,但是不得不承认的是,Ninja的TUI还是做得比MSBuild好的,至少可以大致了解到编译流程进行了多少= =

这里贴一下中间需要设置的变量:

  1. set CMAKE_GENERATOR=Visual Studio 16 2019(因为我没把Ninja加到PATH,以及VS2017编译会有报错);
  2. set DISTUTILS_USE_SDK=1,不然CUDA配置那里可能报一些奇奇怪怪的错误;
  3. python setup.py bdist_wheel,然后理论上他configure完会出现异常终止,因为MSBuild内部调的Ninja和我们设置的环境变量不符合;
  4. set CMAKE_GENERATOR=,取消generator的设置;
  5. 重新执行3,理论上应该不出现任何问题,直到编译完成。

然后就是经典环节了。pip安装编完的whl,import,没有报错,直接成功了,是的,成功了。

很喜欢计算机人的一句话:啊?

只能说是在摸清问题之前解决了问题了罢。咱又不开发AI框架,既然问题解决了,那探究先到此为止吧。

出于兼容性考虑,还是编译一份32位版的,哪都能用。

装包:

pacman -S mingw-w64-i686-gtk3 pkg-config mingw-w64-i686-amtk mingw-w64-i686-tepl mingw-w64-i686-libpeas mingw-w64-i686-gobject-introspection mingw-w64-i686-gspell mingw-w64-i686-vala mingw-w64-i686-itstool mingw-w64-i686-desktop-file-utils

其中有几个地方要修改一下:

  1. 复制一份pkgconfig的tepl-6.pctepl-5.pc,msys2没有这个包;
  2. meson改了API导致语法不兼容,可以选择装meson的时候指定版本为0.59(参见meson.build),或者把data/meson.build文件第七行删掉。

配置:

git checkout gnome-3-38-fix-build
MSYS2_ENV_CONV_EXCL='PKG_CONFIG_PATH' meson

其中MSYS2_ENV_CONV_EXCL是针对MSYS2会自动改变环境变量的分隔符而加的。MSYS2装的pkgconfig只认冒号分隔符,但是MSYS2会自动转成分号,这样pkgconfig就只认PKG_CONFIG_DIR中的最后一个,导致pc文件找不到。

编译完成之后,在MSYS里设置好XDG_DATA_DIRS就可以使用了。界面和Linux下完全一样,win的标题栏都没了。然而非常诡异的一点是,如果试图把dll文件复制到gedit一起打包的话,会发现根本启动不起来,两者只能单独分开,然后通过PATH联系起来,就tm离谱。在3.36版本之前是有win32打包脚本的,不过之后被移除了,可以去git历史里面去看,然而我执行又是各种报错= =

===

第二天更新

又看了眼,发现相比于把dll复制到install目标文件夹里,官方脚本里的配置方式是把gedit install到现有的mingw环境中。那么一种可行的方式是,先用pacman装好需要的库,然后把编译好的gedit install到这个chroot环境中。假设安装到E:\gedit\mingw32目录,那么根据make-gedit-installer脚本,可以有如下操作:

pushd /e/gedit
mkdir -p var/lib/pacman
mkdir -p var/log
mkdir -p tmp
pacman -Syu --root `pwd`
pacman -S mingw-w64-i686-gtk3 ...  # 后面的库略

这个地方的mingw32文件夹是不可少的,主要是因为msys2为了做环境隔离,设立了多个子文件夹。后续可以把这个mingw32文件夹单独提出来,重命名后自由使用。

还有一种偷懒的方法是,先直接用pacman装gedit,再卸掉这个包,剩下来的就是依赖环境了

然后再用meson去reconfigure gedit,指定prefix为E:\\gedit\\mingw32,重新执行ninjaninja install即可完成安装。安装完大小接近1个G,如果需要精简空间,可以参考remove_useless_stuff函数,改改路径前缀执行,最终能把大小控制在400MB以内。(经典的gedit 2.30展开安装也才80MB左右,通货膨胀严重啊)

因为这里不打算发行,就不按照原始脚本用wix进行后续打包工作了。

看到MSYS和winlibs两个网站上的gcc都区分了MSVCRT和UCRT版本,这里简单记录一下。

再简单提一下C/C++运行库(C Runtime,CRT)的概念吧,因为所有符合C/C++标准的程序采用的绝大部分基本函数都具有相同的功能(最典型的就是stdio.hmemory.hiostream了),因而将这些常见函数抽象出来做成单独的程序库能够有效节省编译时间,并且对闭源编译器还有隐藏实现的功能等。

然后这里MSVCRT指的是当年Visual C++系列随编译器附带的运行库,也就是说发布一版VC++就会发布一版MSVCRT,那么在动态链接的情况下,目标机器就必须安装对应的运行库。比如说原来最为常见的找不到MSVCP80.dll这类信息,就是因为这种情况产生的,也就是俗称的缺库。

那么UCRT就是微软为了解决这一问题提出的新方案。程序不再链接到某一具体版本的CRT,而是链接到一系列中间dll上,形如api-ms-win-xxxx.dll,想必在某些地方见过。这些dll文件非常小,它们并不包含具体函数的实现,而是作为一个中间层,将对应的函数请求转发到当前系统所支持的CRT中。没有什么是加抽象层解决不了的问题

然后这里区分两种CRT的话,实际上并不只是区分编译出来的程序最终默认链接到哪种CRT,这些gcc本身也会被链接到对应的库。比如说,UCRT版本的gcc会链接到ucrtbase.dll,而MSVCRT版本的会链接到mscvrt.dll

PS1. UCRT版本的gcc包含有MSVCRT的库,但是反过来没有验证是否同样支持; PS2. 在gcc的C支持库中,可以通过传递--with-default-msvcrt=ucrt参数来让MinGW使用UCRT作为其支撑运行库。

日々编译,日々编译,烦たま死,この天天让我编译の世界早晚爆破する,この

今天的节目内容是编译Android-x86,文档给的还是比较全的,主要解决某些不存在的代码的问题。

apt update
apt -y install git gcc curl make repo libxml2-utils flex m4
apt -y install openjdk-8-jdk lib32stdc++6 libelf-dev mtools
apt -y install libssl-dev python-enum34 python-mako syslinux-utils 
apt -y install pkg-config gettext bzip2 unzip bc kmod dosfstools genisoimage

export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'

cd
mkdir android-x86
cd android-x86
repo init -u git://git.osdn.net/gitroot/android-x86/manifest -b q-x86
#出现各种报错就换 mirrors.tuna.tsinghua.edu.cn/git/AOSP
find . -type f -name '*.xml' -print0 | xargs -0 sed -i -e 's#android.googlesource.com#mirrors.ustc.edu.cn/aosp#g'
sed -i -e 's#git://git.osdn.net#https://scm.osdn.net#g' .repo/manifests.git/config
find . -type f -name '*.xml' -print0 | xargs -0 sed -i -e 's#clone-depth=".*?"##g'
repo sync --no-tags --no-clone-bundle

# 你们aosp真的不考虑跟着升级一下编译器吗
cd prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6
git fetch aosp 2078a6bf9e5479104cfe2cbf54e9602672bd89f7
git checkout 2078a6bf9e5479104cfe2cbf54e9602672bd89f7
cd ../../../../..

source build/envsetup.sh
lunch android_x86_64-userdebug
m -j16 iso_img

编译中会遇到两个问题,一个是依赖项目的-Werror,按照编译器提示加一个[[fallthrough]];然后继续编译就行;第二个参照https://stackoverflow.com/questions/67557000/depmod-is-not-allowed-to-be-used ,修改build/soong/ui/build/paths/config.go,添加"depmod": Allowed,即可。解决这两个问题后应该就能正常完成镜像的生成了。

老版pytorch配新版编译器会出现一些奇怪的问题,在这里记录一下。

  1. Could not find libuv_tmp_LIBRARY using the following names: uv, libuv

libuv是做分布式异步io用的,咱们Windows单机用不到这个,通过设置环境变量USE_DISTRIBUTED=0跳过这一问题。

  1. algorithm(7417): error C2678: binary '*': no operator found which takes a left-hand operand of type 'const _BidIt' (or there is no acceptable conversion) [pytorch\build\caffe2\torch_cpu.vcxproj]

这个是mkl-dnn的问题,参考这个issue

解决方法:升级mkl-dnn到1.7版本,或在aten/src/ATen/native/CompositeRandomAccessorCommon.h文件的第132行,在

reference operator*() {

处添加const关键字,修改为

reference operator*() const {

再重新执行编译指令即可。

  1. pytorch\caffe2\utils\math_gpu.cu(898): error : namespace "thrust" has no member "host_vector" [pytorch\build\caffe2\torch_cuda.vcxproj]

这个是CUDA的问题,恰好是11.4版本引入的,参考这个issue

解决方法:在caffe2/utils/math_gpu.cu文件头部添加头文件引用:

#include <thrust/host_vector.h>

然后重新编译即可。