文章考虑当前机器的环境,记录一下各种语言的版本管理与包管理方法。

Python

主要使用 uv,但是更古早的 python -m venv .venv 的写法依然被兼容。

版本管理

在使用 uv 的项目里,项目级 Python 版本通常由 .python-versionpyproject.toml 共同表达。.python-version 给工具一个本地切换提示,pyproject.toml 里的 requires-python 则声明项目需要的 Python 版本范围。

常用版本管理的命令为:

1
2
3
uv python install 3.11
uv python pin 3.11
uv run python --version

uv python pin 3.11 会在项目目录写入 .python-version。之后在项目里执行 uv runuv sync 时,uv 会按项目配置选择合适的 Python 解释器并维护虚拟环境。

包管理

uv 项目的依赖声明放在 pyproject.toml,完整解析结果放在 uv.lock。常用命令:

1
2
3
4
uv sync
uv add pandas
uv remove pandas
uv run python main.py

uv sync 根据 pyproject.tomluv.lock 创建或更新环境;uv add 修改依赖声明并更新锁文件;uv run 在项目环境里运行命令。.venv/ 是本地虚拟环境目录,不应该提交。

也有一些项目只使用 requirements.txt。这种方式更轻量,通常配合 pip 使用:

1
2
3
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

它适合小项目、课程项目或临时脚本,但表达的信息比 pyproject.toml + uv.lock 少。

配置例子

/home/zxz/Data/git-remote/COMET 为例:

1
2
3
.python-version
pyproject.toml
uv.lock

其中 .python-version 写的是:

1
3.11

pyproject.toml 里声明项目需要的 Python 版本和直接依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
[project]
name = "constellation-simulator-with-input"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"matplotlib>=3.10.8",
"numpy==2.3.5",
"pandas>=2.3.3",
"pyyaml==6.0.3",
"scipy>=1.16.3",
"tqdm==4.67.1",
"gurobipy==12.0.1",
]

Node.js

当前博客仓库就是用了 Node.js。

版本管理

当前 Node.js 版本切换主要由 fnm 完成:

1
2
3
4
fnm list
fnm install 24
fnm use 24
fnm default 24

如果项目里有 .node-version.nvmrc,通常可以进入项目后执行:

1
fnm use

当前检查到的项目里没有 .node-version.nvmrc,博客仓库实际上没有项目级固定 Node.js 版本;它使用的是当前 shell 已经选中的 node v24.15.0

包管理

这个博客仓库使用 npm:

1
2
3
npm install
npm run server
npm run build

严格按锁文件恢复依赖时可以用:

1
npm ci

package.json 声明脚本、项目元数据和直接依赖;package-lock.json 锁定 npm 解析出来的完整依赖树;node_modules/ 是本地安装结果,不应该提交。

当前环境也能看到 pnpmyarn 命令入口,但这个博客仓库里没有 pnpm-lock.yamlyarn.lock,也没有 packageManager 字段。因此这里不展开 pnpm / Yarn 的项目配置。判断一个 Node.js 项目实际使用哪套包管理器,最可靠的线索通常还是锁文件和 package.json

配置例子

博客仓库的 package.json 主要包含脚本和 Hexo 依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "hexo-site",
"private": true,
"scripts": {
"build": "hexo generate",
"server": "hexo server",
"dev": "hexo clean && hexo generate && hexo server"
},
"dependencies": {
"hexo": "^8.0.0",
"hexo-theme-butterfly": "5.5.2",
"katex": "^0.16.28"
}
}

这个仓库有 package-lock.json,所以它当前是 npm 锁文件路线。

C# / .NET

版本管理

常用版本检查命令:

1
2
3
dotnet --version
dotnet --list-sdks
dotnet --list-runtimes

如果同一台机器装了多个 SDK,项目可以用 global.json 固定构建时使用的 SDK:

1
dotnet new globaljson --sdk-version 8.0.125

如果没有 global.json,实际构建会按 .NET CLI 的 SDK 选择规则使用当前 dotnet 能解析到的 SDK。.NET 的版本管理至少要分清三件事:

概念 作用 当前例子
SDK 决定执行 dotnet builddotnet restoredotnet run 时使用哪套 CLI/MSBuild/编译工具链 8.0.125
TargetFramework 写在 .csproj 里,决定项目面向哪个目标框架,以及编译时能使用哪些 API net8.0net6.0
Runtime 程序真正启动后使用的运行时实现 Microsoft.NETCore.App 8.0.25

所以 global.json 用来选择 .NET SDK;TargetFramework 写在项目文件中,用来指定目标框架;reference assemblies / targeting pack 提供编译时可见的 API 接口,但它们本身不能被当作运行时实现加载。

例如某解决方案底下有三个 .csproj,其中 TargetFramework 既有 net8.0,也有 net6.0,这里并非指定 SDK 版本,而是规定了每个项目的目标框架。

在 Arch Linux 上,大致对应关系是:

需求 常见包
构建 .NET 8 项目 dotnet-sdk-8.0
编译面向 net8.0net6.0 的普通项目 dotnet-targeting-pack-8.0dotnet-targeting-pack-6.0
编译 ASP.NET Core / Web 项目 aspnet-targeting-pack-8.0aspnet-targeting-pack-6.0
运行 framework-dependent 的 .NET 8 程序 dotnet-runtime-8.0
运行 ASP.NET Core 程序 aspnet-runtime-8.0aspnet-runtime-6.0

NuGet 依赖会由 dotnet restore 自动恢复,但 SDK、Runtime、targeting pack 这类系统级组件不会被项目自动用 pacman 安装。遇到缺少目标框架或运行时的报错时,需要根据项目的 global.jsonTargetFramework 和项目类型手动安装对应包。

从这里可以看出,.NET 提供了 SDK 选择机制,但是没有像其他语言一样提供不同版本 SDK / Runtime / targeting pack 的安装器。Windows 中,这部分工作应该是默认交给 Visual Studio 了。

包管理

.NET 包管理走 NuGet,依赖通常写在 .csprojPackageReference 里。常用命令:

1
2
3
4
5
dotnet restore
dotnet add package YamlDotNet
dotnet list package
dotnet remove package YamlDotNet
dotnet build

当解决方案里有多个项目时,.sln 负责组织项目;.csproj 负责声明具体项目的目标框架、NuGet 包依赖和项目引用。

配置例子

可以参考的 .NET 项目是 /home/zxz/Data/git-remote/TboxWebdav,里面有:

1
2
3
4
TboxWebdav.sln
TboxWebdav.Server.AspNetCore/TboxWebdav.Server.AspNetCore.csproj
TboxWebdav.Server/TboxWebdav.Server.csproj
WebStreamCaching/WebStreamCaching.csproj

例如 TboxWebdav.Server.AspNetCore.csproj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="YamlDotNet" Version="16.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TboxWebdav.Server\TboxWebdav.Server.csproj" />
</ItemGroup>
</Project>

这里有三类信息:

配置 作用
TargetFramework 这个项目面向哪个 .NET 目标框架,例如 net8.0
PackageReference NuGet 包依赖
ProjectReference 项目引用,例如这里就会引用 TboxWebdav.Server.csproj 构建出来的 .dll 文件

这个项目里没有看到 Directory.Packages.propspackages.lock.json,所以这里只记录它们的用途,不写具体配置:

文件 作用
Directory.Packages.props 可以集中管理多个项目的 NuGet 包版本
packages.lock.json 可以锁定 NuGet restore 结果

Java

版本管理

当前机器的 Java 版本管理目前是 Arch Linux 的系统级方案:通过系统包安装 JDK,再用 archlinux-java 查看或切换默认 Java 环境。

常用检查和切换命令:

1
2
3
4
java -version
javac -version
archlinux-java status
sudo archlinux-java set java-21-openjdk

当前只有 java-21-openjdk 这一套默认环境,因此并没有真实发生多 JDK 切换;如果以后系统里安装了多个 JDK,archlinux-java set 才会更有意义。

包管理

Java 项目的包管理通常由 Maven 或 Gradle 承担。Maven 的项目入口一般是 pom.xml,Gradle 的项目入口一般是 build.gradlebuild.gradle.kts

当前检查到的目录里没有 Java 项目配置,因此这里只保留概念,不写具体依赖配置。日后如果出现真实项目,可以按实际使用的 Maven 或 Gradle 补充。

配置例子

当前没有发现 pom.xmlbuild.gradlebuild.gradle.ktsgradle.lockfile。这里只列出这些文件通常负责的事情:

文件/工具 作用
pom.xml Maven 项目配置和依赖声明
build.gradle(.kts) Gradle 项目配置和依赖声明
Gradle Wrapper 项目自带的 Gradle 版本入口

Rust

版本管理

Rust 这边的工具链比较完整:rustup 管工具链,cargo 管项目和依赖。

常用检查和切换命令:

1
2
3
4
5
rustup show
rustup show active-toolchain
rustup default stable
rustup toolchain install nightly
rustup override set nightly

rustup default 设置全局默认工具链;rustup override set 可以在某个目录下设置局部工具链。当前参考项目里没有 rust-toolchain.toml,所以更接近“默认 stable 工具链 + 项目内 rust-version 提示”的用法。

包管理

Rust 项目的包管理由 Cargo 负责。常用命令:

1
2
3
4
5
cargo build
cargo run
cargo test
cargo fmt
cargo clippy

Cargo.toml 声明项目元数据、workspace 和直接依赖;Cargo.lock 锁定完整依赖树。应用项目通常应该提交 Cargo.locktarget/ 是构建产物,不应该提交。

配置例子

项目 /home/zxz/Data/git-remote/security-scan/scan 里有:

1
2
3
Cargo.toml
Cargo.lock
migration/Cargo.toml

Cargo.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[package]
name = "scan"
version = "0.1.0"
edition = "2024"

[workspace]
members = [".", "migration"]

[dependencies]
anyhow = "1.0.102"
clap = { version = "4.6.0", features = ["derive"] }
reqwest = { version = "0.13.2", features = ["cookies", "form"] }
serde = "1.0.228"
tokio = { version = "1.50.0", features = [
"macros",
"process",
"rt",
"rt-multi-thread"
] }

migration/Cargo.toml 里还写了最低 Rust 版本:

1
2
3
4
5
[package]
name = "migration"
edition = "2024"
rust-version = "1.85.0"
publish = false

这里的分工是:

文件/字段 作用
Cargo.toml 项目元数据、workspace、直接依赖
edition Rust 语言 edition,例如 2024
rust-version 声明 crate 需要的最低 Rust 版本
Cargo.lock 锁定完整依赖树
rustup 管理本地 Rust 工具链