如何编译 dotnet-armv6 环境
Contents
前情提要
这是接着上一篇来的,上一篇说到,我们用别人已经编译好的一个支持 armv6 的 .NET 7.0.10 的 docker 镜像成功的构建出了能在树莓派 1B+ 上跑的 ASF。
但马上 ASF 5.5 之后要开始使用 .NET 8 了啊,那怎么办呢?
我又想到上一篇里说到的那篇在 issue 里的回复,这篇回复简要的写了一个可以交叉构建的环境是如何编译的。
想了想总不能以后总等别人做镜像吧,于是自己根据这篇回复来开始着手编译一下最新的 .NET 8.0.0 环境。
编译 armv6 交叉构建环境
事后诸葛亮
允许我事后诸葛亮一下,这个过程真的挺痛苦的,那篇笔记总共就写了寥寥数行,虽然最终跑完了才发现写的基本上没什么问题。
但更痛苦的是,在我编译完之后发现,那个之前搞 .NET 7.0 的老哥,开了一个 dotnet-armv6 的 docker 镜像,里面就是已经弄好了的 .NET 8.0。
但好在,现在如果这老哥停止支持了,我还能从零构建一个能用的镜像,想了想也挺有意义的,所以才决定把这个折腾过程也写成文字材料,以供备用。
正式开始
因为主要是根据那篇回复来搞定的,所以我打算步骤也写的大致一致,方便对照参考。
但回复里原先的顺序其实有点乱,逻辑上来说确实是这个顺序,但执行起来我觉得还是稍稍整理一下为好。
准备工作
一台 Linux 主机,需要预装 docker,这个我就不赘述了。
下载 dotnet/runtime 仓库(我也很好奇,为什么不是 sdk 仓库?):
git clone https://github.com/dotnet/runtime --recursive
cd runtime
git checkout -b buildarmv6 v8.0.3
这么下载的原因一个是可以下载 submodule,另一个就是可以 git reset --hard
直接取消自己的更改。
第一步:编译 Runtime
使用指定的 docker 镜像来编译需要的内容,这里 ubuntu-20.04-cross-armv6-raspbian-10-latest
这个标签是在 微软的 dotnet docker 标签库 里找的。
sudo docker run -e ROOTFS_DIR=/crossrootfs/armv6 -w $(pwd) --platform linux/amd64 -v $(pwd):$(pwd) -it mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-cross-armv6-raspbian-10-latest ./build.sh --ci --restore --build --arch armv6 --os linux --configuration Debug /p:CrossBuild=true /p:ArchiveTests=true -s mono+libs+host+packs
cd ..
这段一般运行的时候都不会有什么问题,一般出错都是版本号或者代码的问题,可以多尝试两遍,如果还有问题那建议稍微后退一个小版本,比如我一开始用的 main
分支,可版本号是 9.0.0,这导致我编译的时候出现非常多问题。
编译完后,去 runtime/artifacts/packages/Debug/Shipping
目录下看看 dotnet-runtime
开头的文件的完整文件名是什么,之后要用,可能因为用的是 release 代码的原因,我这里跑出来的是 dotnet-runtime-8.0.0-linux-armv6.tar.gz
,文件名里没有 ci 字样。
第二步:拉取并修改 AspNetCore 的部分内容
这一步要操作的内容比较多,因为 AspNetCore 确实编译起来很多问题。
- 先拉取代码
git clone https://github.com/dotnet/aspnetcore --recursive
cd aspnetcore
git checkout -b buildarmv6 v8.0.3
-
下载完后,根据回复提供的 gist 的内容,修改
aspnetcore/eng/Versions.props
文件,将<ValidateBaseline>true</ValidateBaseline>
改为<ValidateBaseline>false</ValidateBaseline>
。 -
根据 gist,修改
aspnetcore/src/Framework/App.Runtime/src/Microsoft.AspNetCore.App.Runtime.csproj
文件。
这里要更改的地方有三处:
(1). <DotNetRuntimeDownloadPath>$(DotNetAssetRootUrl)/$(DotNetRuntimeArchiveFileName)</DotNetRuntimeDownloadPath>
(2). 572 行左右的 <DownloadFile>
的 Uris 属性,修改为 $(DotNetRuntimeDownloadPath)
,和上一处关联起来,默认是从互联网拉取代码的,所以需要额外改掉这里。
(3). 可选,<DotNetRuntimeArchiveFileName>
这个标签的内容,如果你上一步的文件名有 ci 字样的话,可以将其改成 <DotNetRuntimeArchiveFileName>dotnet-runtime-8.0.0-ci-$(TargetRuntimeIdentifier)$(ArchiveExtension)</DotNetRuntimeArchiveFileName>
,没有就不用。
- 修改
aspnetcore/eng/build.sh
,在开头部分加上如下代码以在容器内安装较新的nodejs
,后面编译的时候要用。(别装 20,用的依赖最高只支持 19)
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 42D5A192B819C5DA
apt-get update
apt-get install -y ca-certificates curl gnupg
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=18
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
apt-get update
apt-get install nodejs -y
第三步:编译 AspNetCore
这个 AspNetCore 确实跑起来毛病多多,我自己试的时候主要就是卡在这一步上了。
因为要用到目录的绝对路径,所以暂且说一下我的用户目录是 /workspace/sffxzzp
,这样便于修改时理解。
这一步耗时比较长,一般错一次就白跑十几分钟,等的很累,主要时间都耗在等上了,所以之前的步骤尽量别出错。
命令里的 DotNetAssetRootUrl 就是第一步看文件名的那个位置,确保一致,另外 file://
不能少,否则没法正常从本地拉取第一步的结果。
还有就是别忘了用 -v
参数映射你的 runtime 目录进去,不然也是无法正常拉取的,因为那篇回复的这里只给了后半段,所以前半段我就复制了第一步的代码,结果忘记映射,我就说怎么总是拉取不到文件。
运行:
sudo docker run -e ROOTFS_DIR=/crossrootfs/armv6 -w $(pwd) --platform linux/amd64 -v $(pwd):$(pwd) -v /workspace/sffxzzp/runtime:/workspace/sffxzzp/runtime -it mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-cross-armv6-raspbian-10-latest eng/build.sh --ci --pack -c Debug --arch armv6 --os-name linux /p:DotNetAssetRootUrl=file:///workspace/sffxzzp/runtime/artifacts/packages/Debug/Shipping
cd ..
2024.3.31 补充:
一般来说,编译会出现 SignalR 版本不对的错误,需要搜索
link:../signalr
并且修改package.json
以及yarn.lock
文件,将版本号改成@microsoft/signalr
在 npm 上的最新版本号,并且注释掉.npmrc
文件的全部内容,以保证正常编译。然后直接删除
--frozen-lockfile
参数,理论上就可以一遍跑完结果了。
运行一遍之后可能会报错,尝试运行第二遍,然后可能会提示因为有 --frozen-lockfile
参数,yarn 无法正常更新 lockfile。
遇到之后首先需要修改 aspnetcore/.npmrc
,注释掉或者删掉全部内容,微软的 registry 会有鉴权问题。
然后需要修改 aspnetcore/eng/targets/Npm.Common.targets
文件,删除全部的 --frozen-lockfile
参数。
再重新编译一次,这次应该就能正常跑通了。
到这,已经基本上宣告成功了,后面就是一些简单的步骤了。
第四步:安装 dotnet-sdk 并进行一些小修改
2024.3.31 补充:
这一步可以不做,SDK 安装之后可能会版本不对,直接进行下一步打包为 Docker 镜像,再使用 Docker 镜像即可。
其实可以直接用微软那个 dotnet-install.sh 脚本,但问题是 sdk 里还要稍稍修改一下,所以直接用二进制包,将其安装在 dotnet 目录。
运行:
mkdir dotnet
cd dotnet
wget https://download.visualstudio.microsoft.com/download/pr/5226a5fa-8c0b-474f-b79a-8984ad7c5beb/3113ccbf789c9fd29972835f0f334b7a/dotnet-sdk-8.0.100-linux-x64.tar.gz
tar -zxvf dotnet-sdk-8.0.100-linux-x64.tar.gz
rm dotnet-sdk-8.0.100-linux-x64.tar.gz
cd ..
sudo ln -s /workspace/sffxzzp/dotnet/dotnet /usr/local/bin
sed -i 's/linux-s390x/linux-armv6;linux-s390x/g' /workspace/sffxzzp/dotnet/sdk/8.0.100/Microsoft.NETCoreSdk.BundledVersions.props
最后一行批量替换其实是原回复的第 6 步,而且手动哪里有 sed 替换一步到位爽呢。
第五步 :创建本地 NuGet 仓库
这一步是将之前两次痛苦编译的 nupkg 文件复制到一个文件夹,并将之添加为已安装的 dotnet-sdk 的本地 NuGet 仓库,这样后续在编译 ASF 或者其他文件的时候,遇到 linux-armv6 就会先从本地仓库中拉取,而不是尝试远程拉取(远程都没有,肯定会编译失败)。
运行:
mkdir nugetrepo
cp ./runtime/artifacts/packages/Debug/Shipping/*.nupkg ./nugetrepo/
cp ./aspnetcore/artifacts/packages/Debug/Shipping/*.nupkg ./nugetrepo/
dotnet nuget add source /workspace/sffxzzp/nugetrepo/ -n localrepo
2024.3.31 补充:
可以删除后缀带 symbol 的文件以缩小体积。
至此,环境配置完成,可以直接 docker publish -r linux-armv6
进行编译了。
可选:将环境打包为 Docker 镜像
如果我不光想本地自己用,我还想打包成 docker 镜像给别人用呢?
很简单,因为需要的只是编译好的 nupkg 文件,所以直接用对应版本的 dotnet 镜像稍做修改就能搞定啦。
先创建一个 Dockerfile
,内容为:
FROM mcr.microsoft.com/dotnet/sdk:8.0.100-1
WORKDIR /root
COPY ./nugetrepolite/ /usr/share/dotnet/nugetrepo
RUN sed -i 's/linux-s390x/linux-armv6;linux-s390x/g' /usr/share/dotnet/sdk/8.0.100/Microsoft.NETCoreSdk.BundledVersions.props
RUN dotnet nuget add source /usr/share/dotnet/nugetrepo/ -n localrepo
RUN rm -rf /tmp/*
CMD "/bin/bash"
原理很简单,就是将编译好的 nupkg 复制进 docker 镜像,然后对 Microsoft.NETCoreSdk.BundledVersions.props
进行修改。
那问题又来了,镜像弄好了,别人应该怎么用呢?且看后面编译 ASF 部分。
编译 ASF
上一篇文章其实已经大致写了应该如何使用已经弄好的 docker 镜像来编译。
但经过这一次的痛苦编译之后,我觉得上次写复杂了,所以这次写一个更简易一点的。
很简单,就两步。
当然这一次直接用 ASF 的 github 仓库最新提交,因为这次我们的环境已经是 8.0 啦。
第一步:拉取代码并修改 cc.sh
拉取代码:
git clone https://github.com/JustArchiNET/ArchiSteamFarm --recursive --depth=1
cd ArchiSteamFarm
在 cc.sh
头部加上以下内容以在一开始就安装 nodejs,这样方便我们一步到位的编译 ASF-ui:
apt update
apt install -y ca-certificates curl gnupg
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
apt update
apt install nodejs -y
git config --global safe.directory '*'
然后修改两个 FLAGS 的内容:
# 删掉 -p:UseAppHost=false 以使用 --self-contained 参数
DOTNET_FLAGS="-c $CONFIGURATION -f $TARGET_FRAMEWORK -p:ContinuousIntegrationBuild=true --nologo"
# -r 后指定目标平台,而不是使用当前机器的平台,另外 --self-contained 会在编译打包时带上运行库,否则树莓派无法运行
PUBLISH_FLAGS="-r linux-armv6 --self-contained"
第二步:编译
运行:
docker run -w $(pwd) -v $(pwd):$(pwd) -it --rm taphome/dotnet-armv6:v8.0.0 ./cc.sh
简单吧,确实简单,我怎么就没想到用 docker 的 volume 呢?
当然,编译完,打包复制之前建议先 chown 和 chmod 搞一下文件权限问题,不再赘述。
今天就写这么多,摸了。