强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Julia 教程 / 构建与发布(Package 指南)

构建与发布(Package 指南)

Julia 的包管理系统 Pkg 是其核心优势之一。本文详细介绍如何创建、测试、文档化并发布一个 Julia 包到 General Registry。


1. Pkg 模块详解

1.1 进入 Pkg 模式

# 在 REPL 中按 ] 进入 Pkg 模式
# (v1.11) pkg>

# 或在代码中使用
using Pkg

1.2 常用 Pkg 命令

命令说明示例
add安装包Pkg.add("DataFrames")
rm移除包Pkg.rm("DataFrames")
up更新包Pkg.update()
status查看已安装包Pkg.status()
instantiate安装依赖Pkg.instantiate()
resolve解析依赖冲突Pkg.resolve()
test运行测试Pkg.test("MyPkg")
build构建包Pkg.build("MyPkg")
develop开发模式Pkg.develop("MyPkg")
generate创建新包Pkg.generate("MyPkg")

1.3 项目环境

# 激活项目环境
Pkg.activate(".")

# 查看当前环境
Pkg.status()

# 添加依赖到当前项目
Pkg.add(["DataFrames", "CSV", "Plots"])

# 添加开发依赖(仅测试/文档使用)
Pkg.add("Test"; target=:test)

2. 创建新包

2.1 使用 Pkg.generate

using Pkg

# 在指定路径创建新包
Pkg.generate("MyAwesomePkg")
# 创建 MyAwesomePkg/ 目录,包含 Project.toml 和 src/MyAwesomePkg.jl

2.2 推荐的包结构

MyAwesomePkg/
├── Project.toml           # 项目元数据和依赖
├── Manifest.toml          # 锁定的依赖版本(不提交到 git)
├── src/
│   ├── MyAwesomePkg.jl    # 主模块文件
│   ├── core.jl            # 核心功能
│   └── utils.jl           # 工具函数
├── test/
│   └── runtests.jl        # 测试文件
├── docs/
│   ├── make.jl            # 文档构建脚本
│   └── src/
│       ├── index.md       # 文档首页
│       └── api.md         # API 参考
├── .gitignore
├── README.md
├── LICENSE
└── .github/
    └── workflows/
        └── CI.yml         # GitHub Actions

2.3 主模块文件

# src/MyAwesomePkg.jl
module MyAwesomePkg

using LinearAlgebra
using Statistics

# 导出公共 API
export compute, analyze, MyResult

# 包含子文件
include("core.jl")
include("utils.jl")

# 文档字符串
"""
    compute(x::AbstractVector)

计算向量的统计数据。

# 示例
```julia
result = compute([1.0, 2.0, 3.0])

""" function compute(x::AbstractVector) return MyResult(mean(x), std(x), length(x)) end

""" 存储计算结果的结构体。 """ struct MyResult mean::Float64 std::Float64 n::Int end

预编译提示

function init() println(“MyAwesomePkg loaded!”) end

end # module


---

## 3. Project.toml 详解

### 3.1 完整的 Project.toml

```toml
name = "MyAwesomePkg"
uuid = "12345678-1234-1234-1234-123456789abc"
version = "1.0.0"
authors = ["Your Name <[email protected]>"]

[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[compat]
DataFrames = "1"
julia = "1.9"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]

3.2 各字段说明

字段说明必需
name包名
uuid唯一标识符
version语义化版本号
authors作者信息
[deps]依赖列表
[compat]版本兼容性推荐
[extras]测试依赖
[targets]测试目标

3.3 UUID 生成

using UUIDs
uuid4()  # 生成随机 UUID

4. Manifest.toml

4.1 作用

Manifest.toml 记录了所有依赖(包括间接依赖)的精确版本和 Git commit hash。它是可重现构建的关键。

# Manifest.toml 片段(自动生成,不要手动编辑)
[[DataFrames]]
deps = ["Compat", "DataAPI", "InvertedIndices", "LinearAlgebra", ...]
git-tree-sha1 = "abc123..."
uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
version = "1.6.1"

[[Compat]]
git-tree-sha1 = "def456..."
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "4.10.0"

4.2 何时提交 Manifest.toml

项目类型提交 Manifest.toml?原因
库(被其他包依赖)让使用者自行解析版本
应用(独立运行)确保部署环境一致
文档项目确保文档构建可重现

5. 本地开发

5.1 开发模式

# 将本地包添加为开发依赖
Pkg.develop(path="/path/to/MyAwesomePkg")

# 或使用 URL
Pkg.develop(url="https://github.com/user/MyAwesomePkg.jl.git")

# 开发模式下,代码修改立即生效(无需重新安装)

5.2 Revise.jl — 热重载

using Revise
using MyAwesomePkg

# 修改 MyAwesomePkg 的代码后,更改自动加载
# 无需重启 Julia 或重新 import

5.3 工作流

# 1. 激活项目
Pkg.activate(".")

# 2. 开发模式添加本地包
Pkg.develop(path="../MyAwesomePkg")

# 3. 启用 Revise
using Revise
using MyAwesomePkg

# 4. 编辑代码,更改自动生效
# 5. 运行测试验证
Pkg.test("MyAwesomePkg")

6. 测试

6.1 编写测试

# test/runtests.jl
using MyAwesomePkg
using Test

@testset "MyAwesomePkg.jl" begin
    @testset "compute" begin
        # 基本功能
        result = compute([1.0, 2.0, 3.0])
        @test result.mean  2.0
        @test result.n == 3

        # 边界情况
        result_single = compute([5.0])
        @test result_single.mean  5.0
        @test result_single.std  0.0
    end

    @testset "MyResult" begin
        r = MyResult(1.0, 0.5, 10)
        @test r.mean == 1.0
        @test r.std == 0.5
        @test r.n == 10
    end

    @testset "类型稳定性" begin
        @inferred compute([1.0, 2.0, 3.0])
    end
end

6.2 运行测试

# 在 Pkg 模式下
# (v1.11) pkg> test

# 或代码中
Pkg.test("MyAwesomePkg")

# 带覆盖率
Pkg.test("MyAwesomePkg"; coverage=true)

6.3 测试最佳实践

实践说明
测试边界值空数组、单元素、极大值
测试类型稳定性使用 @inferred
测试异常使用 @test_throws
测试近似值使用 @test ... ≈ ...
组织测试集使用 @testset 嵌套
避免依赖外部资源Mock 数据库/网络调用

7. 文档

7.1 Documenter.jl 设置

# docs/make.jl
using Documenter
using MyAwesomePkg

makedocs(
    sitename = "MyAwesomePkg.jl",
    modules = [MyAwesomePkg],
    pages = [
        "首页" => "index.md",
        "快速入门" => "getting-started.md",
        "API 参考" => "api.md",
    ],
    format = Documenter.HTML(
        prettyurls = get(ENV, "CI", nothing) == "true"
    )
)

# 部署到 GitHub Pages
deploydocs(
    repo = "github.com/user/MyAwesomePkg.jl.git",
    devbranch = "main"
)

7.2 文档字符串

"""
    compute(x::AbstractVector; corrected::Bool=true) -> MyResult

计算向量的均值和标准差。

# 参数
- `x`: 输入数据向量
- `corrected`: 是否使用修正的样本标准差(默认 true)

# 返回值
返回 `MyResult` 结构体,包含 `mean`、`std`、`n` 字段。

# 示例
```julia
data = [1.0, 2.0, 3.0, 4.0, 5.0]
result = compute(data)
result.mean  # 3.0
result.std   # 1.5811...

另见

MyResult, analyze """ function compute(x::AbstractVector; corrected::Bool=true) # 实现… end


---

## 8. 注册包到 General Registry

### 8.1 注册流程

```julia
# 1. 确保包已推送到 GitHub
# git remote add origin https://github.com/user/MyAwesomePkg.jl.git
# git push -u origin main

# 2. 使用 Registrator 注册
# 访问 https://github.com/JuliaRegistries/Registrator.jl
# 在 GitHub issue 中 @JuliaRegistrator 注册

# 或本地使用
using Pkg
Pkg.Registry.add(RegistrySpec(url="https://github.com/JuliaRegistries/General"))

8.2 注册前检查清单

  • 包名不与已有包冲突(搜索 JuliaHub
  • Project.toml 包含正确的 nameuuidversion
  • [compat] 中指定了 Julia 版本要求
  • 所有依赖的 [compat] 已指定
  • 测试通过
  • 有 README.md
  • 有 LICENSE 文件
  • 代码已推送到 GitHub

8.3 版本更新

# 1. 修改版本号
# Project.toml: version = "1.1.0"

# 2. 提交并推送
# git commit -am "Bump version to 1.1.0"
# git tag v1.1.0
# git push --tags

# 3. 重新注册
# @JuliaRegistrator register

9. 语义化版本 (Semver)

9.1 版本号格式

MAJOR.MINOR.PATCH
  │      │     └── 修复版本(向后兼容的 bug 修复)
  │      └──────── 次版本(向后兼容的新功能)
  └─────────────── 主版本(不兼容的 API 变更)

9.2 版本范围语法

表达式含义匹配版本
"1.0"兼容 1.0≥1.0.0, <2.0.0
"1.2.3"兼容 1.2.3≥1.2.3, <2.0.0
"~1.2"补丁级别≥1.2.0, <1.3.0
"=1.2.3"精确版本仅 1.2.3
">=1.0, <2"范围≥1.0.0, <2.0.0

9.3 依赖版本管理

[compat]
julia = "1.9"           # 支持 Julia 1.9 及以上
DataFrames = "1"         # 支持 DataFrames 1.x
Plots = "1.38"          # 支持 Plots 1.38.x 及以上(<2.0)
SomePackage = "=2.1.0"  # 精确版本

⚠️ 注意:主版本 0 时,次版本表示不兼容变更。"0.2" 只匹配 0.2.x,不匹配 0.3.0。


10. 包模板 — PkgTemplates.jl

10.1 使用模板创建包

using Pkg
Pkg.add("PkgTemplates")

using PkgTemplates

t = Template(;
    user="yourname",
    plugins=[
        License(; name="MIT"),
        Git(; manifest=false),
        GitHubActions(; coverage=true),
        Documenter{GitHubActions}(),
        Tests(; project=true),
        Citation(; key="mykey"),
    ],
)

t("MyNewPkg")

10.2 可用插件

插件功能
License添加 LICENSE 文件
Git初始化 Git 仓库
GitHubActionsCI 配置
Documenter文档系统
Tests测试框架
Citation引用文件
SrcDir源码目录
TagBot自动打标签

10.3 自定义模板

t = Template(;
    user="yourname",
    dir="~/julia-dev",
    plugins=[
        License(; name="MIT"),
        Git(; manifest=false, ssh=true),
        GitHubActions(;
            coverage=true,
            extra_versions=["1.10", "nightly"],
        ),
        Documenter{GitHubActions}(),
        Tests(; project=true),
        Codecov(),
        Coveralls(),
    ],
)

常见问题

问题原因解决方案
依赖冲突版本范围不兼容Pkg.resolve() 或调整 [compat]
包加载失败路径或名称错误检查 LOAD_PATH 和模块名
测试失败依赖未添加到测试[extras] 中添加测试依赖
预编译慢依赖过多使用 PrecompileTools.jl
注册失败版本号未更新递增 Project.toml 中的 version

业务场景

场景一:开源数值计算库

开发一个数值积分库,使用 PkgTemplates.jl 创建项目骨架。配置 GitHub Actions 进行多版本测试,Documenter.jl 生成 API 文档,注册到 General Registry 供社区使用。

场景二:内部工具包

创建团队内部工具包,发布到私有 Registry。通过 [compat] 锁定依赖版本,确保团队成员使用一致的环境。

场景三:研究代码包

将研究代码封装为 Julia 包,添加测试和文档。通过 DOI 发布到 Zenodo,配合 Citation.jl 提供引用信息。


总结

主题关键要点
创建包Pkg.generate()PkgTemplates.jl
Project.toml包含 name/uuid/version/deps/compat
开发模式Pkg.develop() + Revise.jl
测试test/runtests.jl,使用 @testset
文档Documenter.jl,docstring
注册推送 GitHub + @JuliaRegistrator
版本号遵循 semver 规范

扩展阅读