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

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

配置例子

~/Data/git-remote/satellite-computing/COMET 为例,其有如下文件:

1
2
3
.python-version
pyproject.toml
uv.lock
  • 其中 .python-version 内容为 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,进入项目后 ~/.zshrc 中的 eval "$(fnm env --use-on-cd)" 会自动执行版本切换。如果本机没有需要的版本,会申请下载。没有对 ~/.zshrc 进行配置的话,也可以进入项目后执行 fnm use

如果项目里没有 .node-version.nvmrc,那就使用当前 shell 默认的 Node.js 版本。

包管理

npm

当前博客仓库使用 npm:

1
npm install

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

1
npm ci

package.json 声明脚本、项目元数据和直接依赖;package-lock.json 锁定 npm 解析出来的完整依赖树;node_modules/ 是本地安装的包目录。

pnpm / Yarn / Corepack

npm、pnpm、Yarn 都共用 package.json。也就是说,dependenciesdevDependenciesscriptsengines 这些依赖和脚本声明都写在同一个文件里;不同包管理器的主要区别在锁文件和额外配置:

包管理器 常见锁文件 常见额外配置
npm package-lock.json .npmrc
pnpm pnpm-lock.yaml .npmrcpackage.json 里的 pnpm 字段
Yarn yarn.lock .yarnrc.yml

pnpm / Yarn 的版本推荐通过 Corepack 管理使用。

常用命令大概是:

1
2
3
corepack enable
corepack use pnpm@latest
corepack use yarn@stable

corepack enable 把 pnpm / Yarn 的 shim 放到当前 Node.js 安装旁边,不执行切换版本工作。版本选择发生在后面执行 pnpm installpnpm devyarn install 这类命令时:Corepack 会向上查找最近的 package.json,读取其中的 packageManager 字段,使用对应版本。

corepack use ... 会把选择写入 package.jsonpackageManager 字段并执行一次安装。例如:

1
2
3
{
"packageManager": "pnpm@10.11.0"
}

之后项目通常还会对应出现 pnpm-lock.yamlyarn.lock。所以判断一个 Node.js 项目实际使用哪套包管理器,最可靠的线索是组合看锁文件和 package.json 里的 packageManager

需要注意,Corepack 曾经随 Node.js 14.19.0 到 24.x 分发;从 Node.js 25 开始不再随 Node.js 一起分发,需要通过 npm install -g corepack 或系统包管理器另装。

配置例子

例子1

博客仓库的 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 锁文件路线。

例子2

/home/zxz/Data/git-remote/nimo/oscope/ 就是一个 pnpm 项目。它有 pnpm-lock.yaml,并且 package.json 里写了:

1
2
3
4
5
6
7
8
{
"packageManager": "pnpm@10.28.1",
"engines": {
"node": ">= 22.22.0",
"npm": ">= 10.0.0",
"pnpm": ">= 9"
}
}

这里的 packageManager: "pnpm@10.28.1" 是给 Corepack 用的精确版本声明;engines.pnpm: ">= 9" 应该是最低兼容范围或提示。进入项目后,只要执行过 corepack enable,再运行 pnpm install,Corepack 就会根据 packageManager 使用 pnpm@10.28.1

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 项目的包管理通常由 Maven 或 Gradle 承担。Maven 的项目入口一般是 pom.xml,Gradle 的项目入口一般是 build.gradlebuild.gradle.kts

文件/工具 作用
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 工具链