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

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 仓库
GitHubActions CI 配置
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 规范

扩展阅读