You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rust-course/cargo/guide/cargo-toml-lock.html

299 lines
18 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE HTML>
<html lang="zh-CN" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Cargo.toml vs Cargo.lock - Rust语言圣经(Rust Course)</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon.svg">
<link rel="shortcut icon" href="../../favicon.png">
<link rel="stylesheet" href="../../css/variables.css">
<link rel="stylesheet" href="../../css/general.css">
<link rel="stylesheet" href="../../css/chrome.css">
<link rel="stylesheet" href="../../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../../theme/style.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust语言圣经(Rust Course)</h1>
<div class="right-buttons">
<a href="../../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/sunface/rust-course" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/sunface/rust-course/edit/main/src/cargo/guide/cargo-toml-lock.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="cargotoml-vs-cargolock"><a class="header" href="#cargotoml-vs-cargolock">Cargo.toml vs Cargo.lock</a></h1>
<p><code>Cargo.toml</code><code>Cargo.lock</code><code>Cargo</code> 的两个元配置文件,但是它们拥有不同的目的:</p>
<ul>
<li>前者从用户的角度出发来描述项目信息和依赖管理,因此它是由用户来编写</li>
<li>后者包含了依赖的精确描述信息,它是由 <code>Cargo</code> 自行维护,因此不要去手动修改</li>
</ul>
<p>它们的关系跟 <code>package.json</code><code>package-lock.json</code> 非常相似,从 JavaScript 过来的同学应该会比较好理解。</p>
<h2 id="是否上传本地的-cargolock"><a class="header" href="#是否上传本地的-cargolock">是否上传本地的 <code>Cargo.lock</code></a></h2>
<p>当本地开发时,<code>Cargo.lock</code> 自然是非常重要的,但是当你要把项目上传到 <code>Git</code> 时,例如 <code>GitHub</code>,那是否上传 <code>Cargo.lock</code> 就成了一个问题。</p>
<p>关于是否上传,有如下经验准则:</p>
<ul>
<li>从实践角度出发,如果你构建的是三方库类型的服务,请把 <code>Cargo.lock</code> 加入到 <code>.gitignore</code> 中。</li>
<li>若构建的是一个面向用户终端的产品,例如可以像命令行工具、应用程序一样执行,那就把 <code>Cargo.lock</code> 上传到源代码目录中。</li>
</ul>
<p>例如 <a href="https://github.com/tokio-rs/axum"><code>axum</code></a> 是 web 开发框架,它属于三方库类型的服务,因此源码目录中不应该出现 <code>Cargo.lock</code> 的身影,它的归宿是 <code>.gitignore</code>。而 <a href="https://github.com/BurntSushi/ripgrep"><code>ripgrep</code></a> 则恰恰相反,因为它是一个面向终端的产品,可以直接运行提供服务。</p>
<p><strong>那么问题来了,为何会有这种选择?</strong></p>
<p>原因是 <code>Cargo.lock</code> 会详尽描述上一次成功构建的各种信息环境状态、依赖、版本等等Cargo 可以使用它提供确定性的构建环境和流程,无论何时何地。这种特性对于终端服务是非常重要的:能确定、稳定的在用户环境中运行起来是终端服务最重要的特性之一。</p>
<p>而对于三方库来说,情况就有些不同。它不仅仅被库的开发者所使用,还会间接影响依赖链下游的使用者。用户引入了三方库是不会去看它的 <code>Cargo.lock</code> 信息的,也不应该受这个库的确定性运行条件所限制。</p>
<p>还有个原因,在项目中,可能会有几个依赖库引用同一个三方库的同一个版本,那如果该三方库使用了 <code>Cargo.lock</code> 文件,那可能三方库的多个版本会被引入使用,这时就会造成版本冲突。换句话说,通过指定版本的方式引用一个依赖库是无法看到该依赖库的完整情况的,而只有终端的产品才会看到这些完整的情况。</p>
<h2 id="假设没有-cargolock"><a class="header" href="#假设没有-cargolock">假设没有 <code>Cargo.lock</code></a></h2>
<p><code>Cargo.toml</code> 是一个清单文件( <code>manifest</code> )包含了我们 <code>package</code> 的描述元数据。例如,通过以下内容可以说明对另一个 <code>package</code> 的依赖 :</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>[package]
name = "hello_world"
version = "0.1.0"
[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }
<span class="boring">}</span></code></pre></pre>
<p>可以看到,只有一个依赖,且该依赖的来源是 <code>GitHub</code> 上一个特定的仓库。由于我们没有指定任何版本信息,<code>Cargo</code> 会自动拉取该依赖库的最新版本( <code>master</code><code>main</code> 分支上的最新 <code>commit</code> )。</p>
<p>这种使用方式,其实就错失了包管理工具的最大的优点:版本管理。例如你在今天构建使用了版本 <code>A</code>,然后过了一段时间后,由于依赖包的升级,新的构建却使用了大更新版本 <code>B</code>,结果因为版本不兼容,导致了构建失败。</p>
<p>可以看出,确保依赖版本的确定性是非常重要的:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git", rev = "9f9f693" }
<span class="boring">}</span></code></pre></pre>
<p>这次,我们使用了指定 <code>rev</code> ( <code>revision</code> ) 的方式来构建,那么不管未来何时再次构建,使用的依赖库都会是该 <code>rev</code> ,而不是最新的 <code>commit</code></p>
<p>但是,这里还有一个问题:<code>rev</code> 需要手动的管理,你需要在每次更新包的时候都思考下 <code>SHA-1</code>,这显然非常麻烦。</p>
<h2 id="当有了-cargolock-后"><a class="header" href="#当有了-cargolock-后">当有了 <code>Cargo.lock</code></a></h2>
<p>当有了 <code>Cargo.lock</code> 后,我们无需手动追踪依赖库的 <code>rev</code><code>Cargo</code> 会自动帮我们完成,还是之前的清单:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>[package]
name = "hello_world"
version = "0.1.0"
[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }
<span class="boring">}</span></code></pre></pre>
<p>第一次构建时,<code>Cargo</code> 依然会拉取最新的 <code>master commit</code>,然后将以下信息写到 <code>Cargo.lock</code> 文件中:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>[[package]]
name = "hello_world"
version = "0.1.0"
dependencies = [
"regex 1.5.0 (git+https://github.com/rust-lang/regex.git#9f9f693768c584971a4d53bc3c586c33ed3a6831)",
]
[[package]]
name = "regex"
version = "1.5.0"
source = "git+https://github.com/rust-lang/regex.git#9f9f693768c584971a4d53bc3c586c33ed3a6831"
<span class="boring">}</span></code></pre></pre>
<p>可以看出,其中包含了依赖库的准确 <code>rev</code> 信息。当未来再次构建时,只要项目中还有该 <code>Cargo.lock</code> 文件,那构建依然会拉取同一个版本的依赖库,并且再也无需我们手动去管理 <code>rev</code><code>SHA</code> 信息!</p>
<h2 id="更新依赖"><a class="header" href="#更新依赖">更新依赖</a></h2>
<p>由于 <code>Cargo.lock</code> 会锁住依赖的版本,你需要通过手动的方式将依赖更新到新的版本:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>$ cargo update # 更新所有依赖
$ cargo update -p regex # 只更新 “regex”
<span class="boring">}</span></code></pre></pre>
<p>以上命令将使用新的版本信息重新生成 <code>Cargo.lock</code> ,需要注意的是 <code>cargo update -p regex</code> 传递的参数实际上是一个 <code>Package ID</code> <code>regex</code> 只是一个简写形式。</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../cargo/guide/package-layout.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../cargo/guide/tests-ci.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../cargo/guide/package-layout.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../cargo/guide/tests-ci.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../../ace.js"></script>
<script src="../../editor.js"></script>
<script src="../../mode-rust.js"></script>
<script src="../../theme-dawn.js"></script>
<script src="../../theme-tomorrow_night.js"></script>
<script src="../../elasticlunr.min.js"></script>
<script src="../../mark.min.js"></script>
<script src="../../searcher.js"></script>
<script src="../../clipboard.min.js"></script>
<script src="../../highlight.js"></script>
<script src="../../book.js"></script>
<!-- Custom JS scripts -->
<script src="../../assets/custom2.js"></script>
<script src="../../assets/bigPicture.js"></script>
</div>
</body>
</html>