free-code-dotnet/docs/测试与构建/测试与构建-构建与部署.md
应文浩wenhao.ying@xiaobao100.com e25ac591a7 init easy-code
2026-04-06 07:24:24 +08:00

236 lines
7.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 构建与部署
> 所属项目: free-code .NET 10 重写
> 文档类型: 构建设计
> 来源章节: DESIGN-NET10-PART3.md § 23
> 上级文档: [测试与构建总览](测试与构建.md)
---
## 概述
构建目标是跨 5 个平台的 AOT 原生二进制输出单文件、无运行时依赖的可执行文件。MSBuild 通过 `Directory.Build.props` 统一管理全局属性,`build.sh` 封装多平台批量发布,`install.sh` 提供一键下载安装体验。
原始 TypeScript 项目使用 Bun 打包为单一 JavaScript bundle。.NET 重写的 AOT 路径在启动时间和内存占用上接近原生,同时消除了 Bun 运行时依赖。
---
## 23.1 MSBuild 配置
### Directory.Build.props全局属性
所有子项目自动继承这些属性,无需在每个 `.csproj` 中重复声明。
```xml
<!-- Directory.Build.props -->
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>13.0</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<AotPublish>true</AotPublish>
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>partial</TrimMode>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<Version>1.0.0</Version>
<AssemblyName>free-code</AssemblyName>
<RootNamespace>FreeCode</RootNamespace>
</PropertyGroup>
</Project>
```
关键属性说明:
| 属性 | 值 | 作用 |
|------|----|------|
| `AotPublish` | `true` | 启用 AOT 原生编译 |
| `PublishSingleFile` | `true` | 输出单一可执行文件 |
| `PublishTrimmed` | `true` | 裁剪未使用的程序集 |
| `TrimMode` | `partial` | 部分裁剪,保留反射入口点 |
| `JsonSerializerIsReflectionEnabledByDefault` | `false` | 强制使用 Source Generator 序列化 |
| `EnableAotAnalyzer` | `true` | 编译期检测 AOT 不兼容代码 |
| `LangVersion` | `13.0` | C# 13 特性primary constructors、集合表达式等|
---
### FreeCode.csproj主项目
```xml
<!-- FreeCode.csproj (主项目) -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FreeCode.Core\FreeCode.Core.csproj" />
<ProjectReference Include="..\FreeCode.Engine\FreeCode.Engine.csproj" />
<ProjectReference Include="..\FreeCode.Tools\FreeCode.Tools.csproj" />
<ProjectReference Include="..\FreeCode.Commands\FreeCode.Commands.csproj" />
<ProjectReference Include="..\FreeCode.ApiProviders\FreeCode.ApiProviders.csproj" />
<ProjectReference Include="..\FreeCode.Mcp\FreeCode.Mcp.csproj" />
<ProjectReference Include="..\FreeCode.Lsp\FreeCode.Lsp.csproj" />
<ProjectReference Include="..\FreeCode.Bridge\FreeCode.Bridge.csproj" />
<ProjectReference Include="..\FreeCode.Services\FreeCode.Services.csproj" />
<ProjectReference Include="..\FreeCode.Tasks\FreeCode.Tasks.csproj" />
<ProjectReference Include="..\FreeCode.Skills\FreeCode.Skills.csproj" />
<ProjectReference Include="..\FreeCode.Plugins\FreeCode.Plugins.csproj" />
<ProjectReference Include="..\FreeCode.Features\FreeCode.Features.csproj" />
<ProjectReference Include="..\FreeCode.State\FreeCode.State.csproj" />
<ProjectReference Include="..\FreeCode.TerminalUI\FreeCode.TerminalUI.csproj" />
</ItemGroup>
</Project>
```
主项目引用全部 15 个子项目,自身仅包含 `Program.cs` 入口,所有业务逻辑分布在各子项目库中。
---
## 23.2 构建脚本
### build.sh多平台批量构建
```bash
#!/bin/bash
# build.sh — 多平台构建
set -euo pipefail
VERSION="${1:-$(git describe --tags --always)}"
OUTPUT_DIR="./dist"
# 构建目标平台
PLATFORMS=(
"osx-arm64"
"osx-x64"
"linux-x64"
"linux-arm64"
"win-x64"
)
for PLATFORM in "${PLATFORMS[@]}"; do
echo "Building for $PLATFORM..."
RID="${PLATFORM}"
dotnet publish src/FreeCode/FreeCode.csproj \
-c Release \
-r "$RID" \
-o "$OUTPUT_DIR/$PLATFORM" \
/p:VersionPrefix="$VERSION" \
/p:AotPublish=true \
/p:PublishSingleFile=true \
/p:PublishTrimmed=true
# 重命名二进制
if [[ "$PLATFORM" == win-* ]]; then
mv "$OUTPUT_DIR/$PLATFORM/free-code.exe" \
"$OUTPUT_DIR/$PLATFORM/free-code-$VERSION-$PLATFORM.exe"
else
chmod +x "$OUTPUT_DIR/$PLATFORM/free-code"
mv "$OUTPUT_DIR/$PLATFORM/free-code" \
"$OUTPUT_DIR/$PLATFORM/free-code-$VERSION-$PLATFORM"
fi
echo "✓ $PLATFORM done"
done
echo "All builds complete in $OUTPUT_DIR/"
```
脚本逻辑说明:
1. 版本号优先读取命令行参数fallback 到 `git describe --tags --always`
2. 遍历 5 个 RID每个平台独立调用 `dotnet publish`
3. 输出产物按 `free-code-{VERSION}-{PLATFORM}` 格式命名
4. Windows 平台保留 `.exe` 后缀,其余平台添加可执行权限
---
## 23.3 安装脚本
### install.sh一键安装
```bash
#!/bin/bash
# install.sh — 一键安装
set -euo pipefail
# 检测平台
OS="$(uname -s)"
ARCH="$(uname -m)"
case "$OS-$ARCH" in
Darwin-arm64) PLATFORM="osx-arm64" ;;
Darwin-x86_64) PLATFORM="osx-x64" ;;
Linux-x86_64) PLATFORM="linux-x64" ;;
Linux-aarch64) PLATFORM="linux-arm64" ;;
*) echo "Unsupported platform: $OS-$ARCH"; exit 1 ;;
esac
INSTALL_DIR="$HOME/.free-code/bin"
mkdir -p "$INSTALL_DIR"
# 下载
BINARY_URL="https://github.com/paoloanzn/free-code/releases/latest/download/free-code-latest-$PLATFORM"
echo "Downloading free-code for $PLATFORM..."
curl -fsSL "$BINARY_URL" -o "$INSTALL_DIR/free-code"
chmod +x "$INSTALL_DIR/free-code"
# 添加到 PATH
if ! echo "$PATH" | grep -q "$INSTALL_DIR"; then
SHELL_RC="$HOME/.zshrc"
[ -f "$HOME/.bashrc" ] && SHELL_RC="$HOME/.bashrc"
echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> "$SHELL_RC"
echo "Added $INSTALL_DIR to PATH in $SHELL_RC"
fi
echo "✓ free-code installed to $INSTALL_DIR/free-code"
echo " Run 'free-code' to start"
```
安装流程:
1. 通过 `uname -s``uname -m` 检测当前平台
2. 从 GitHub Releases 下载对应 RID 的预编译二进制
3. 安装到 `~/.free-code/bin/free-code`
4. 自动检测 shell 配置文件(优先 `.zshrc`fallback `.bashrc`)并追加 PATH
---
## 23.4 目标平台矩阵
| RID | 操作系统 | 架构 | 说明 |
|-----|---------|------|------|
| `osx-arm64` | macOS | Apple Silicon | M1/M2/M3 Mac |
| `osx-x64` | macOS | Intel x64 | Intel Mac |
| `linux-x64` | Linux | x86-64 | 主流 Linux 服务器 |
| `linux-arm64` | Linux | AArch64 | Raspberry Pi、AWS Graviton 等 |
| `win-x64` | Windows | x64 | Windows 10/11 |
---
## 23.5 发布参数说明
| dotnet publish 参数 | 含义 |
|---------------------|------|
| `-c Release` | Release 配置,开启优化 |
| `-r {RID}` | 目标运行时,指定后产物为自包含 |
| `/p:AotPublish=true` | AOT 原生编译,消除 JIT 开销 |
| `/p:PublishSingleFile=true` | 将所有资源打包为单一可执行文件 |
| `/p:PublishTrimmed=true` | 裁剪未引用的程序集,减小体积 |
| `/p:VersionPrefix={VERSION}` | 注入版本号到程序集元数据 |
AOT 编译将 IL 代码提前编译为目标平台原生机器码,启动时间从 JIT 模式的数百毫秒降至个位数毫秒,与原始 Bun 构建产物的冷启动时间相当。
---
## 参考资料
- [测试与构建总览](测试与构建.md)
- [原始代码映射 — 测试与构建](reference/原始代码映射-测试与构建.md)
- [总体概述与技术选型](../总体概述与技术选型/总体概述与技术选型.md)
- [迁移路线图](测试与构建-迁移路线图.md)