常见编程语言版本管理与包管理
文章考虑当前机器的环境,记录一下各种语言的版本管理与包管理方法。
Python
主要使用 uv,但是更古早的 python -m venv .venv 的写法依然被兼容。
版本管理
在使用 uv 的项目里,项目级 Python 版本通常由 .python-version 和 pyproject.toml 共同表达。.python-version 给工具一个本地切换提示,pyproject.toml 里的 requires-python 则声明项目需要的 Python 版本范围。
常用版本管理的命令为:
1 | uv python install 3.11 |
uv python pin 3.11 会在项目目录写入 .python-version。之后在项目里执行 uv run、uv sync 时,uv 会按项目配置选择合适的 Python 解释器并维护虚拟环境。
包管理
uv 项目的依赖声明放在 pyproject.toml,完整解析结果放在 uv.lock。常用命令:
1 | uv sync |
uv sync 根据 pyproject.toml 和 uv.lock 创建或更新环境;uv add 修改依赖声明并更新锁文件;uv run 在项目环境里运行命令。.venv/ 是被创建的本地虚拟环境目录。
也有一些项目只使用 requirements.txt。这种方式更轻量,通常配合 pip 使用:
1 | python -m venv .venv |
配置例子
以 ~/Data/git-remote/satellite-computing/COMET 为例,其有如下文件:
1 | .python-version |
- 其中
.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 | fnm list |
如果项目里有 .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。也就是说,dependencies、devDependencies、scripts、engines 这些依赖和脚本声明都写在同一个文件里;不同包管理器的主要区别在锁文件和额外配置:
| 包管理器 | 常见锁文件 | 常见额外配置 |
|---|---|---|
| npm | package-lock.json |
.npmrc |
| pnpm | pnpm-lock.yaml |
.npmrc、package.json 里的 pnpm 字段 |
| Yarn | yarn.lock |
.yarnrc.yml |
pnpm / Yarn 的版本推荐通过 Corepack 管理使用。
常用命令大概是:
1 | corepack enable |
corepack enable 把 pnpm / Yarn 的 shim 放到当前 Node.js 安装旁边,不执行切换版本工作。版本选择发生在后面执行 pnpm install、pnpm dev、yarn install 这类命令时:Corepack 会向上查找最近的 package.json,读取其中的 packageManager 字段,使用对应版本。
corepack use ... 会把选择写入 package.json 的 packageManager 字段并执行一次安装。例如:
1 | { |
之后项目通常还会对应出现 pnpm-lock.yaml 或 yarn.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 | { |
这个仓库有 package-lock.json,所以它当前是 npm 锁文件路线。
例子2
/home/zxz/Data/git-remote/nimo/oscope/ 就是一个 pnpm 项目。它有 pnpm-lock.yaml,并且 package.json 里写了:
1 | { |
这里的 packageManager: "pnpm@10.28.1" 是给 Corepack 用的精确版本声明;engines.pnpm: ">= 9" 应该是最低兼容范围或提示。进入项目后,只要执行过 corepack enable,再运行 pnpm install,Corepack 就会根据 packageManager 使用 pnpm@10.28.1。
C# / .NET
版本管理
常用版本检查命令:
1 | dotnet --version |
如果同一台机器装了多个 SDK,项目可以用 global.json 固定构建时使用的 SDK:
1 | dotnet new globaljson --sdk-version 8.0.125 |
如果没有 global.json,实际构建就会按 .NET CLI 的 SDK 选择规则使用当前 dotnet 能解析到的 SDK。
.NET 的版本管理至少要分清三件事:
| 概念 | 作用 | 当前例子 |
|---|---|---|
| SDK | 决定执行 dotnet build、dotnet restore、dotnet run 时使用哪套 CLI/MSBuild/编译工具链 |
8.0.125 |
| TargetFramework | 写在 .csproj 里,决定项目面向哪个目标框架,以及编译时能使用哪些 API |
net8.0、net6.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.0、net6.0 的普通项目 |
dotnet-targeting-pack-8.0、dotnet-targeting-pack-6.0 |
| 编译 ASP.NET Core / Web 项目 | aspnet-targeting-pack-8.0、aspnet-targeting-pack-6.0 等 |
| 运行 framework-dependent 的 .NET 8 程序 | dotnet-runtime-8.0 |
| 运行 ASP.NET Core 程序 | aspnet-runtime-8.0、aspnet-runtime-6.0 等 |
NuGet 依赖会由 dotnet restore 自动恢复,但 SDK、Runtime、targeting pack 这类系统级组件不会被项目自动用 pacman 安装。遇到缺少目标框架或运行时的报错时,需要根据项目的 global.json、TargetFramework 和项目类型手动安装对应包。
从这里可以看出,.NET 提供了 SDK 选择机制,但是没有像其他语言一样提供不同版本 SDK / Runtime / targeting pack 的安装器。Windows 中,这部分工作应该是默认交给 Visual Studio 了。
包管理
.NET 包管理走 NuGet,依赖通常写在 .csproj 的 PackageReference 里。常用命令:
1 | dotnet restore |
当解决方案里有多个项目时,.sln 负责组织项目;.csproj 负责声明具体项目的目标框架、NuGet 包依赖和项目引用。
配置例子
可以参考的 .NET 项目是 /home/zxz/Data/git-remote/TboxWebdav,里面有:
1 | TboxWebdav.sln |
例如 TboxWebdav.Server.AspNetCore.csproj:
1 | <Project Sdk="Microsoft.NET.Sdk.Web"> |
这里有三类信息:
| 配置 | 作用 |
|---|---|
TargetFramework |
这个项目面向哪个 .NET 目标框架,例如 net8.0 |
PackageReference |
NuGet 包依赖 |
ProjectReference |
项目引用,例如这里就会引用 TboxWebdav.Server.csproj 构建出来的 .dll 文件 |
这个项目里没有看到 Directory.Packages.props 或 packages.lock.json,所以这里只记录它们的用途,不写具体配置:
| 文件 | 作用 |
|---|---|
Directory.Packages.props |
可以集中管理多个项目的 NuGet 包版本 |
packages.lock.json |
可以锁定 NuGet restore 结果 |
Java
版本管理
当前机器的 Java 版本管理目前是 Arch Linux 的系统级方案:通过系统包安装 JDK,再用 archlinux-java 查看或切换默认 Java 环境。
常用检查和切换命令:
1 | java -version |
包管理
Java 项目的包管理通常由 Maven 或 Gradle 承担。Maven 的项目入口一般是 pom.xml,Gradle 的项目入口一般是 build.gradle 或 build.gradle.kts。
| 文件/工具 | 作用 |
|---|---|
pom.xml |
Maven 项目配置和依赖声明 |
build.gradle(.kts) |
Gradle 项目配置和依赖声明 |
| Gradle Wrapper | 项目自带的 Gradle 版本入口 |
Rust
版本管理
Rust 的工具链比较完整:rustup 管工具链,cargo 管项目和依赖。
常用检查和切换命令:
1 | rustup show |
rustup default 设置全局默认工具链;rustup override set 可以在某个目录下设置局部工具链。当前参考项目里没有 rust-toolchain.toml,所以更接近“默认 stable 工具链 + 项目内 rust-version 提示”的用法。
包管理
Rust 项目的包管理由 Cargo 负责。常用命令:
1 | cargo build |
Cargo.toml 声明项目元数据、workspace 和直接依赖;Cargo.lock 锁定完整依赖树。应用项目通常应该提交 Cargo.lock,target/ 是构建产物,不应该提交。
配置例子
项目 /home/zxz/Data/git-remote/security-scan/scan 里有:
1 | Cargo.toml |
主 Cargo.toml:
1 | [package] |
migration/Cargo.toml 里还写了最低 Rust 版本:
1 | [package] |
这里的分工是:
| 文件/字段 | 作用 |
|---|---|
Cargo.toml |
项目元数据、workspace、直接依赖 |
edition |
Rust 语言 edition,例如 2024 |
rust-version |
声明 crate 需要的最低 Rust 版本 |
Cargo.lock |
锁定完整依赖树 |
rustup |
管理本地 Rust 工具链 |



