Archives are the primary output of cargo-dist: a single file (zip or tarball) containing prebuilt executables/binaries for an app, along with additional static files like READMEs, LICENSEs, and CHANGELOGs. The docs previously referred to these as "executable-zips", so if you ever see that term floating around, this is what's being talked about.
"Find XYZ*" means we will look for a file whose name starts with "XYZ" in the same directory as the Cargo.toml for a package that defines the app. If no such file is found, we will also search for it in the same directory as the workspace's Cargo.toml (so packages "inherit" these files from the workspace).
+
It is generally assumed that a directory only contains one of each kind of file. If multiple possible matches are in the same directory we will arbitrarily pick the first one we saw, so don't rely on that.
+
Auto-detected files are first and foremost auto-included into the archive, however they can also be used for other things. For instance, the autodetected CHANGELOG is fed into our CHANGELOG features.
The "root" of an archive is either the actual root directory of the archive (zips); or a directory with the same name as the archive, but without the extension (tarballs). This difference is for compatibility/legacy reasons, and can be smoothed away by unpacking tarballs with tar's --strip-components=1.
+
An app's archive always includes its binaries at the root.
+
By default auto-detected files for a package are auto-included into its archives at the root of the package. The auto-includes config controls this behaviour.
+
The include can be used to manually add specific files/directories to the root of the archive.
By default we build with --workspaceto keep things consistent, but this can be configured with the precise-builds config (see those docs for details on when precise-builds will be force-enabled).
+
By default we build your packages with default features, but this can be configured with the features, default-features, and all-features configs.
+
When targeting windows-msvc we will unconditionally append "-Ctarget-feature=+crt-static" to your RUSTFLAGS, which should just be the default for rustc but isn't for legacy reasons.
+
We don't really support cross-compilation, but we'll faithfully attempt the compile by telling rustup to install the toolchain and passing --target to cargo as instructed -- it will probably just fail. On macOS cross-compiles between Intel and Apple Silicon will work. linux-musl is slated for a future version.
"Code Signing" is a very overloaded term, with wildly varying implementations that accomplish different goals. For instance, Linux users are currently very big on sigstore as a fairly turn-key code signing solution, but neither Windows nor macOS acknowledge its existence (and likely never will, as the benefits of sigstore completely defeat the stated purpose of code signing requirements on those platforms).
+
Roughly speaking, codesigning can be broken up into "Is this app made by the developer?" and "Can I trust apps made by this developer?". Tools like sigstore are focused on the former, while Windows/macOS only care about the latter. They want you to pay some money and jump through administrative hoops. They also expect you to pay completely different groups and go through completely different hoops, so each platform requires a completely different solution.
By default cargo-dist will generate a matching checksum file for each archive it generates. The default checksum is sha256, so for instance my-app-x86_64-pc-windows-msvc.zip will also come with my-app-x86_64-pc-windows-msvc.zip.sha256 that tools like sha256sum can use. This can be configured with the checksum config.
+
Fetching installers can also use these checksums (or ones baked into them) to validate the integrity of the files they download. With https and unsigned checksums the security benefit is minimal, but it can catch more boring problems like data corruption.
+
The homebrew installer actually ignores your checksum setting and always uses sha256 hashes that are baked into it, as required by homebrew itself.
cargo-dist exists to help you distribute your binaries, which involves generating a lot of different files which we call Artifacts. Archives are the baseline artifacts that contain your binaries, and installers are the fancy artifacts that make it easy to install or run the binaries.
This feature is currently disabled pending a rework, but basically we want to save your debuginfo/symbols/sourcemaps in the form of pdbs, dSYMs, etc. This will automatically happen as a side-effect of building archives.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book/ayu-highlight.css b/book/ayu-highlight.css
new file mode 100644
index 000000000..32c943222
--- /dev/null
+++ b/book/ayu-highlight.css
@@ -0,0 +1,78 @@
+/*
+Based off of the Ayu theme
+Original by Dempfi (https://github.com/dempfi/ayu)
+*/
+
+.hljs {
+ display: block;
+ overflow-x: auto;
+ background: #191f26;
+ color: #e6e1cf;
+}
+
+.hljs-comment,
+.hljs-quote {
+ color: #5c6773;
+ font-style: italic;
+}
+
+.hljs-variable,
+.hljs-template-variable,
+.hljs-attribute,
+.hljs-attr,
+.hljs-regexp,
+.hljs-link,
+.hljs-selector-id,
+.hljs-selector-class {
+ color: #ff7733;
+}
+
+.hljs-number,
+.hljs-meta,
+.hljs-builtin-name,
+.hljs-literal,
+.hljs-type,
+.hljs-params {
+ color: #ffee99;
+}
+
+.hljs-string,
+.hljs-bullet {
+ color: #b8cc52;
+}
+
+.hljs-title,
+.hljs-built_in,
+.hljs-section {
+ color: #ffb454;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-symbol {
+ color: #ff7733;
+}
+
+.hljs-name {
+ color: #36a3d9;
+}
+
+.hljs-tag {
+ color: #00568d;
+}
+
+.hljs-emphasis {
+ font-style: italic;
+}
+
+.hljs-strong {
+ font-weight: bold;
+}
+
+.hljs-addition {
+ color: #91b362;
+}
+
+.hljs-deletion {
+ color: #d96c75;
+}
diff --git a/book/book.js b/book/book.js
new file mode 100644
index 000000000..adbd6a3c5
--- /dev/null
+++ b/book/book.js
@@ -0,0 +1,713 @@
+"use strict";
+
+// Fix back button cache problem
+window.onunload = function () { };
+
+// Global variable, shared between modules
+function playground_text(playground, hidden = true) {
+ let code_block = playground.querySelector("code");
+
+ if (window.ace && code_block.classList.contains("editable")) {
+ let editor = window.ace.edit(code_block);
+ return editor.getValue();
+ } else if (hidden) {
+ return code_block.textContent;
+ } else {
+ return code_block.innerText;
+ }
+}
+
+(function codeSnippets() {
+ function fetch_with_timeout(url, options, timeout = 6000) {
+ return Promise.race([
+ fetch(url, options),
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
+ ]);
+ }
+
+ var playgrounds = Array.from(document.querySelectorAll(".playground"));
+ if (playgrounds.length > 0) {
+ fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
+ headers: {
+ 'Content-Type': "application/json",
+ },
+ method: 'POST',
+ mode: 'cors',
+ })
+ .then(response => response.json())
+ .then(response => {
+ // get list of crates available in the rust playground
+ let playground_crates = response.crates.map(item => item["id"]);
+ playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
+ });
+ }
+
+ function handle_crate_list_update(playground_block, playground_crates) {
+ // update the play buttons after receiving the response
+ update_play_button(playground_block, playground_crates);
+
+ // and install on change listener to dynamically update ACE editors
+ if (window.ace) {
+ let code_block = playground_block.querySelector("code");
+ if (code_block.classList.contains("editable")) {
+ let editor = window.ace.edit(code_block);
+ editor.addEventListener("change", function (e) {
+ update_play_button(playground_block, playground_crates);
+ });
+ // add Ctrl-Enter command to execute rust code
+ editor.commands.addCommand({
+ name: "run",
+ bindKey: {
+ win: "Ctrl-Enter",
+ mac: "Ctrl-Enter"
+ },
+ exec: _editor => run_rust_code(playground_block)
+ });
+ }
+ }
+ }
+
+ // updates the visibility of play button based on `no_run` class and
+ // used crates vs ones available on http://play.rust-lang.org
+ function update_play_button(pre_block, playground_crates) {
+ var play_button = pre_block.querySelector(".play-button");
+
+ // skip if code is `no_run`
+ if (pre_block.querySelector('code').classList.contains("no_run")) {
+ play_button.classList.add("hidden");
+ return;
+ }
+
+ // get list of `extern crate`'s from snippet
+ var txt = playground_text(pre_block);
+ var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
+ var snippet_crates = [];
+ var item;
+ while (item = re.exec(txt)) {
+ snippet_crates.push(item[1]);
+ }
+
+ // check if all used crates are available on play.rust-lang.org
+ var all_available = snippet_crates.every(function (elem) {
+ return playground_crates.indexOf(elem) > -1;
+ });
+
+ if (all_available) {
+ play_button.classList.remove("hidden");
+ } else {
+ play_button.classList.add("hidden");
+ }
+ }
+
+ function run_rust_code(code_block) {
+ var result_block = code_block.querySelector(".result");
+ if (!result_block) {
+ result_block = document.createElement('code');
+ result_block.className = 'result hljs language-bash';
+
+ code_block.append(result_block);
+ }
+
+ let text = playground_text(code_block);
+ let classes = code_block.querySelector('code').classList;
+ let edition = "2015";
+ if(classes.contains("edition2018")) {
+ edition = "2018";
+ } else if(classes.contains("edition2021")) {
+ edition = "2021";
+ }
+ var params = {
+ version: "stable",
+ optimize: "0",
+ code: text,
+ edition: edition
+ };
+
+ if (text.indexOf("#![feature") !== -1) {
+ params.version = "nightly";
+ }
+
+ result_block.innerText = "Running...";
+
+ fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
+ headers: {
+ 'Content-Type': "application/json",
+ },
+ method: 'POST',
+ mode: 'cors',
+ body: JSON.stringify(params)
+ })
+ .then(response => response.json())
+ .then(response => {
+ if (response.result.trim() === '') {
+ result_block.innerText = "No output";
+ result_block.classList.add("result-no-output");
+ } else {
+ result_block.innerText = response.result;
+ result_block.classList.remove("result-no-output");
+ }
+ })
+ .catch(error => result_block.innerText = "Playground Communication: " + error.message);
+ }
+
+ // Syntax highlighting Configuration
+ hljs.configure({
+ tabReplace: ' ', // 4 spaces
+ languages: [], // Languages used for auto-detection
+ });
+
+ let code_nodes = Array
+ .from(document.querySelectorAll('code'))
+ // Don't highlight `inline code` blocks in headers.
+ .filter(function (node) {return !node.parentElement.classList.contains("header"); });
+
+ if (window.ace) {
+ // language-rust class needs to be removed for editable
+ // blocks or highlightjs will capture events
+ code_nodes
+ .filter(function (node) {return node.classList.contains("editable"); })
+ .forEach(function (block) { block.classList.remove('language-rust'); });
+
+ code_nodes
+ .filter(function (node) {return !node.classList.contains("editable"); })
+ .forEach(function (block) { hljs.highlightBlock(block); });
+ } else {
+ code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
+ }
+
+ // Adding the hljs class gives code blocks the color css
+ // even if highlighting doesn't apply
+ code_nodes.forEach(function (block) { block.classList.add('hljs'); });
+
+ Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
+
+ var lines = Array.from(block.querySelectorAll('.boring'));
+ // If no lines were hidden, return
+ if (!lines.length) { return; }
+ block.classList.add("hide-boring");
+
+ var buttons = document.createElement('div');
+ buttons.className = 'buttons';
+ buttons.innerHTML = "";
+
+ // add expand button
+ var pre_block = block.parentNode;
+ pre_block.insertBefore(buttons, pre_block.firstChild);
+
+ pre_block.querySelector('.buttons').addEventListener('click', function (e) {
+ if (e.target.classList.contains('fa-eye')) {
+ e.target.classList.remove('fa-eye');
+ e.target.classList.add('fa-eye-slash');
+ e.target.title = 'Hide lines';
+ e.target.setAttribute('aria-label', e.target.title);
+
+ block.classList.remove('hide-boring');
+ } else if (e.target.classList.contains('fa-eye-slash')) {
+ e.target.classList.remove('fa-eye-slash');
+ e.target.classList.add('fa-eye');
+ e.target.title = 'Show hidden lines';
+ e.target.setAttribute('aria-label', e.target.title);
+
+ block.classList.add('hide-boring');
+ }
+ });
+ });
+
+ if (window.playground_copyable) {
+ Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
+ var pre_block = block.parentNode;
+ if (!pre_block.classList.contains('playground')) {
+ var buttons = pre_block.querySelector(".buttons");
+ if (!buttons) {
+ buttons = document.createElement('div');
+ buttons.className = 'buttons';
+ pre_block.insertBefore(buttons, pre_block.firstChild);
+ }
+
+ var clipButton = document.createElement('button');
+ clipButton.className = 'fa fa-copy clip-button';
+ clipButton.title = 'Copy to clipboard';
+ clipButton.setAttribute('aria-label', clipButton.title);
+ clipButton.innerHTML = '';
+
+ buttons.insertBefore(clipButton, buttons.firstChild);
+ }
+ });
+ }
+
+ // Process playground code blocks
+ Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) {
+ // Add play button
+ var buttons = pre_block.querySelector(".buttons");
+ if (!buttons) {
+ buttons = document.createElement('div');
+ buttons.className = 'buttons';
+ pre_block.insertBefore(buttons, pre_block.firstChild);
+ }
+
+ var runCodeButton = document.createElement('button');
+ runCodeButton.className = 'fa fa-play play-button';
+ runCodeButton.hidden = true;
+ runCodeButton.title = 'Run this code';
+ runCodeButton.setAttribute('aria-label', runCodeButton.title);
+
+ buttons.insertBefore(runCodeButton, buttons.firstChild);
+ runCodeButton.addEventListener('click', function (e) {
+ run_rust_code(pre_block);
+ });
+
+ if (window.playground_copyable) {
+ var copyCodeClipboardButton = document.createElement('button');
+ copyCodeClipboardButton.className = 'fa fa-copy clip-button';
+ copyCodeClipboardButton.innerHTML = '';
+ copyCodeClipboardButton.title = 'Copy to clipboard';
+ copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
+
+ buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
+ }
+
+ let code_block = pre_block.querySelector("code");
+ if (window.ace && code_block.classList.contains("editable")) {
+ var undoChangesButton = document.createElement('button');
+ undoChangesButton.className = 'fa fa-history reset-button';
+ undoChangesButton.title = 'Undo changes';
+ undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
+
+ buttons.insertBefore(undoChangesButton, buttons.firstChild);
+
+ undoChangesButton.addEventListener('click', function () {
+ let editor = window.ace.edit(code_block);
+ editor.setValue(editor.originalCode);
+ editor.clearSelection();
+ });
+ }
+ });
+})();
+
+(function themes() {
+ var html = document.querySelector('html');
+ var themeToggleButton = document.getElementById('theme-toggle');
+ var themePopup = document.getElementById('theme-list');
+ var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
+ var stylesheets = {
+ ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
+ tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
+ orandaHighlight: document.querySelector("[href$='oranda-highlight.css']"),
+ highlight: document.querySelector("[href$='highlight.css']"),
+ };
+
+ function showThemes() {
+ themePopup.style.display = 'block';
+ themeToggleButton.setAttribute('aria-expanded', true);
+ themePopup.querySelector("button#" + get_theme()).focus();
+ }
+
+ function updateThemeSelected() {
+ themePopup.querySelectorAll('.theme-selected').forEach(function (el) {
+ el.classList.remove('theme-selected');
+ });
+ themePopup.querySelector("button#" + get_theme()).classList.add('theme-selected');
+ }
+
+ function hideThemes() {
+ themePopup.style.display = 'none';
+ themeToggleButton.setAttribute('aria-expanded', false);
+ themeToggleButton.focus();
+ }
+
+ function checkThemeCacheValidity() {
+ // We use the display name of the default_theme to determine if the oranda
+ // user has changed the default theme. If they have, we invalidate the stored
+ // theme so that we'll revert back to the default_theme.
+ var oldLabel;
+ try {
+ oldLabel = localStorage.getItem('orandamdbook-theme-default-label');
+ } catch (e) { }
+ var curLabel = themePopup.querySelector("button#" + default_theme).innerText;
+
+ if (oldLabel != curLabel) {
+ // Default changed, toss everything
+ localStorage.removeItem('orandamdbook-theme')
+ }
+ // Remember the current default label
+ localStorage.setItem('orandamdbook-theme-default-label', curLabel);
+ }
+
+ function get_theme() {
+ checkThemeCacheValidity();
+ var theme;
+ try { theme = localStorage.getItem('orandamdbook-theme'); } catch (e) { }
+
+ if (theme === null || theme === undefined || themePopup.querySelector("button#" + theme) === null) {
+ return default_theme;
+ } else {
+ return theme;
+ }
+ }
+
+ function set_theme(theme, store = true) {
+ let ace_theme;
+
+ // disable all syntax stylesheets, then enable the right one
+ for (const name in stylesheets) {
+ stylesheets[name].disabled = true;
+ }
+
+ if (theme == 'coal' || theme == 'navy') {
+ stylesheets.tomorrowNight.disabled = false;
+ ace_theme = "ace/theme/tomorrow_night";
+ } else if (theme == 'ayu') {
+ stylesheets.ayuHighlight.disabled = false;
+ ace_theme = "ace/theme/tomorrow_night";
+ } else if (theme == "oranda-dark" || theme == "oranda-light") {
+ stylesheets.orandaHighlight.disabled = false;
+ ace_theme = "ace/theme/tomorrow_night";
+ } else {
+ stylesheets.highlight.disabled = false;
+ ace_theme = "ace/theme/dawn";
+ }
+
+ setTimeout(function () {
+ themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
+ }, 1);
+
+ if (window.ace && window.editors) {
+ window.editors.forEach(function (editor) {
+ editor.setTheme(ace_theme);
+ });
+ }
+
+ var previousTheme = get_theme();
+
+ if (store) {
+ // We use a custom key here to avoid breaking other mdbooks running on localhost,
+ // because they will share localStorage with us, and if they see the selected
+ // theme is "oranda" they sadly will crash instead of ignoring it.
+ try { localStorage.setItem('orandamdbook-theme', theme); } catch (e) { }
+ }
+
+ html.classList.remove(previousTheme);
+ html.classList.add(theme);
+ updateThemeSelected();
+ }
+
+ // Set theme
+ var theme = get_theme();
+
+ set_theme(theme, false);
+
+ themeToggleButton.addEventListener('click', function () {
+ if (themePopup.style.display === 'block') {
+ hideThemes();
+ } else {
+ showThemes();
+ }
+ });
+
+ themePopup.addEventListener('click', function (e) {
+ var theme;
+ if (e.target.className === "theme") {
+ theme = e.target.id;
+ } else if (e.target.parentElement.className === "theme") {
+ theme = e.target.parentElement.id;
+ } else {
+ return;
+ }
+ set_theme(theme);
+ });
+
+ themePopup.addEventListener('focusout', function(e) {
+ // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
+ if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
+ hideThemes();
+ }
+ });
+
+ // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
+ document.addEventListener('click', function(e) {
+ if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
+ hideThemes();
+ }
+ });
+
+ document.addEventListener('keydown', function (e) {
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+ if (!themePopup.contains(e.target)) { return; }
+
+ switch (e.key) {
+ case 'Escape':
+ e.preventDefault();
+ hideThemes();
+ break;
+ case 'ArrowUp':
+ e.preventDefault();
+ var li = document.activeElement.parentElement;
+ if (li && li.previousElementSibling) {
+ li.previousElementSibling.querySelector('button').focus();
+ }
+ break;
+ case 'ArrowDown':
+ e.preventDefault();
+ var li = document.activeElement.parentElement;
+ if (li && li.nextElementSibling) {
+ li.nextElementSibling.querySelector('button').focus();
+ }
+ break;
+ case 'Home':
+ e.preventDefault();
+ themePopup.querySelector('li:first-child button').focus();
+ break;
+ case 'End':
+ e.preventDefault();
+ themePopup.querySelector('li:last-child button').focus();
+ break;
+ }
+ });
+})();
+
+(function sidebar() {
+ var html = document.querySelector("html");
+ var sidebar = document.getElementById("sidebar");
+ var sidebarLinks = document.querySelectorAll('#sidebar a');
+ var sidebarToggleButton = document.getElementById("sidebar-toggle");
+ var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
+ var firstContact = null;
+
+ function showSidebar() {
+ html.classList.remove('sidebar-hidden')
+ html.classList.add('sidebar-visible');
+ Array.from(sidebarLinks).forEach(function (link) {
+ link.setAttribute('tabIndex', 0);
+ });
+ sidebarToggleButton.setAttribute('aria-expanded', true);
+ sidebar.setAttribute('aria-hidden', false);
+ try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
+ }
+
+
+ var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
+
+ function toggleSection(ev) {
+ ev.currentTarget.parentElement.classList.toggle('expanded');
+ }
+
+ Array.from(sidebarAnchorToggles).forEach(function (el) {
+ el.addEventListener('click', toggleSection);
+ });
+
+ function hideSidebar() {
+ html.classList.remove('sidebar-visible')
+ html.classList.add('sidebar-hidden');
+ Array.from(sidebarLinks).forEach(function (link) {
+ link.setAttribute('tabIndex', -1);
+ });
+ sidebarToggleButton.setAttribute('aria-expanded', false);
+ sidebar.setAttribute('aria-hidden', true);
+ try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
+ }
+
+ // Toggle sidebar
+ sidebarToggleButton.addEventListener('click', function sidebarToggle() {
+ if (html.classList.contains("sidebar-hidden")) {
+ var current_width = parseInt(
+ document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
+ if (current_width < 150) {
+ document.documentElement.style.setProperty('--sidebar-width', '150px');
+ }
+ showSidebar();
+ } else if (html.classList.contains("sidebar-visible")) {
+ hideSidebar();
+ } else {
+ if (getComputedStyle(sidebar)['transform'] === 'none') {
+ hideSidebar();
+ } else {
+ showSidebar();
+ }
+ }
+ });
+
+ sidebarResizeHandle.addEventListener('mousedown', initResize, false);
+
+ function initResize(e) {
+ window.addEventListener('mousemove', resize, false);
+ window.addEventListener('mouseup', stopResize, false);
+ html.classList.add('sidebar-resizing');
+ }
+ function resize(e) {
+ var pos = (e.clientX - sidebar.offsetLeft);
+ if (pos < 20) {
+ hideSidebar();
+ } else {
+ if (html.classList.contains("sidebar-hidden")) {
+ showSidebar();
+ }
+ pos = Math.min(pos, window.innerWidth - 100);
+ document.documentElement.style.setProperty('--sidebar-width', pos + 'px');
+ }
+ }
+ //on mouseup remove windows functions mousemove & mouseup
+ function stopResize(e) {
+ html.classList.remove('sidebar-resizing');
+ window.removeEventListener('mousemove', resize, false);
+ window.removeEventListener('mouseup', stopResize, false);
+ }
+
+ document.addEventListener('touchstart', function (e) {
+ firstContact = {
+ x: e.touches[0].clientX,
+ time: Date.now()
+ };
+ }, { passive: true });
+
+ document.addEventListener('touchmove', function (e) {
+ if (!firstContact)
+ return;
+
+ var curX = e.touches[0].clientX;
+ var xDiff = curX - firstContact.x,
+ tDiff = Date.now() - firstContact.time;
+
+ if (tDiff < 250 && Math.abs(xDiff) >= 150) {
+ if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
+ showSidebar();
+ else if (xDiff < 0 && curX < 300)
+ hideSidebar();
+
+ firstContact = null;
+ }
+ }, { passive: true });
+
+ // Scroll sidebar to current active section
+ var activeSection = document.getElementById("sidebar").querySelector(".active");
+ if (activeSection) {
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
+ activeSection.scrollIntoView({ block: 'center' });
+ }
+})();
+
+(function chapterNavigation() {
+ document.addEventListener('keydown', function (e) {
+ if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
+ if (window.search && window.search.hasFocus()) { return; }
+
+ switch (e.key) {
+ case 'ArrowRight':
+ e.preventDefault();
+ var nextButton = document.querySelector('.nav-chapters.next');
+ if (nextButton) {
+ window.location.href = nextButton.href;
+ }
+ break;
+ case 'ArrowLeft':
+ e.preventDefault();
+ var previousButton = document.querySelector('.nav-chapters.previous');
+ if (previousButton) {
+ window.location.href = previousButton.href;
+ }
+ break;
+ }
+ });
+})();
+
+(function clipboard() {
+ var clipButtons = document.querySelectorAll('.clip-button');
+
+ function hideTooltip(elem) {
+ elem.firstChild.innerText = "";
+ elem.className = 'fa fa-copy clip-button';
+ }
+
+ function showTooltip(elem, msg) {
+ elem.firstChild.innerText = msg;
+ elem.className = 'fa fa-copy tooltipped';
+ }
+
+ var clipboardSnippets = new ClipboardJS('.clip-button', {
+ text: function (trigger) {
+ hideTooltip(trigger);
+ let playground = trigger.closest("pre");
+ return playground_text(playground, false);
+ }
+ });
+
+ Array.from(clipButtons).forEach(function (clipButton) {
+ clipButton.addEventListener('mouseout', function (e) {
+ hideTooltip(e.currentTarget);
+ });
+ });
+
+ clipboardSnippets.on('success', function (e) {
+ e.clearSelection();
+ showTooltip(e.trigger, "Copied!");
+ });
+
+ clipboardSnippets.on('error', function (e) {
+ showTooltip(e.trigger, "Clipboard error!");
+ });
+})();
+
+(function scrollToTop () {
+ var menuTitle = document.querySelector('.menu-title');
+
+ menuTitle.addEventListener('click', function () {
+ document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
+ });
+})();
+
+(function controllMenu() {
+ var menu = document.getElementById('menu-bar');
+
+ (function controllPosition() {
+ var scrollTop = document.scrollingElement.scrollTop;
+ var prevScrollTop = scrollTop;
+ var minMenuY = -menu.clientHeight - 50;
+ // When the script loads, the page can be at any scroll (e.g. if you reforesh it).
+ menu.style.top = scrollTop + 'px';
+ // Same as parseInt(menu.style.top.slice(0, -2), but faster
+ var topCache = menu.style.top.slice(0, -2);
+ menu.classList.remove('sticky');
+ var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
+ document.addEventListener('scroll', function () {
+ scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
+ // `null` means that it doesn't need to be updated
+ var nextSticky = null;
+ var nextTop = null;
+ var scrollDown = scrollTop > prevScrollTop;
+ var menuPosAbsoluteY = topCache - scrollTop;
+ if (scrollDown) {
+ nextSticky = false;
+ if (menuPosAbsoluteY > 0) {
+ nextTop = prevScrollTop;
+ }
+ } else {
+ if (menuPosAbsoluteY > 0) {
+ nextSticky = true;
+ } else if (menuPosAbsoluteY < minMenuY) {
+ nextTop = prevScrollTop + minMenuY;
+ }
+ }
+ if (nextSticky === true && stickyCache === false) {
+ menu.classList.add('sticky');
+ stickyCache = true;
+ } else if (nextSticky === false && stickyCache === true) {
+ menu.classList.remove('sticky');
+ stickyCache = false;
+ }
+ if (nextTop !== null) {
+ menu.style.top = nextTop + 'px';
+ topCache = nextTop;
+ }
+ prevScrollTop = scrollTop;
+ }, { passive: true });
+ })();
+ (function controllBorder() {
+ menu.classList.remove('bordered');
+ document.addEventListener('scroll', function () {
+ if (menu.offsetTop === 0) {
+ menu.classList.remove('bordered');
+ } else {
+ menu.classList.add('bordered');
+ }
+ }, { passive: true });
+ })();
+})();
diff --git a/book/ci/github.html b/book/ci/github.html
new file mode 100644
index 000000000..39ebb9a98
--- /dev/null
+++ b/book/ci/github.html
@@ -0,0 +1,381 @@
+
+
+
+
+
+ github - cargo-dist
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The GitHub CI backend provides a "Release" Workflow that is triggered by pushing a tagged commit to your repository. It uses the tag to determine which packages you're trying to publish, and builds and uploads them to a GitHub Release.
cargo-dist is currently very eager to setup the GitHub CI backend, so it's pretty easy to do! Most likely it was automatically setup the first time you ran cargo dist init. If you followed the way-too-quickstart, then you should also have it setup.
You probably already have it set, but if you don't, now's the time to do it. We need to know the URL of your GitHub repository for several features, and the next step will fail without it.
If you did the previous step, you should get prompted to "enable Github CI and Releases?", with the default answer being "yes". Choose yes.
+
You will also get prompted to "check your release process in pull requests?", with the default answer being "plan - run 'cargo dist plan' on PRs (recommended)". Choose that option.
+
Once init completes, some changes will be made to your project, check all of them in:
+
+
ci = ["github"] should be added to [workspace.metadata.dist]
+
./github/workflows/release.yml should be created, this is your Release Workflow
The easiest testing option for this is to open a pull-request for everything you checked in -- it should run the plan step of your release CI as part of the PR.
By default, cargo-dist will run the plan step on every pull request but won't perform a full release build. If these builds are turned on, the resulting pull request artifacts won't be uploaded to a release but will be available as a download from within the CI job. To enable this, select the "upload" option from the "check your release process in pull requests" question in cargo-dist-init or set the pr-run-mode key to "upload" in Cargo.toml's cargo-dist config. For example:
By default, cargo-dist will want to create its own GitHub Release and set the title/body with things like your CHANGELOG/RELEASES and some info about how to install the release. However if you have your own process for generating the contents of GitHub Release, we support that.
+
If you set create-release = false in your cargo-dist config, cargo-dist will assume a draft Github Release for the current git tag already exists with the title/body you want, and just upload artifacts to it. At the end of a successful publish it will undraft the GitHub Release for you.
cargo-dist's CI can be configured to call additional jobs on top of the ones it has builtin. Currently, we support adding extra jobs to the publish step; in the future, we'll allow extending all of the lifecycle steps of the CI workflow. To add one, you need to follow two steps:
+
+
Define the new job as a reusable workflow using the standard method defined by your CI system. For GitHub actions, see the documentation on reusable workflows.
+
Add the name of your new workflow file to the publish-jobs array in your Cargo.toml's cargo-dist config, prefixed with a ./. For example, if your job name is .github/workflows/my-publish.yml, you would write it like this:
+
+
publish-jobs = ["./my-publish"]
+
+
Here's an example reusable workflow written using GitHub Actions. It won't do any real publishing, just echo text to the CI output. First, create a file named .github/workflows/publish-greeter.yml with these contents:
+
name: Greeter
+
+on:
+ # Defining workflow_call means that this workflow can be called from
+ # your main workflow job
+ workflow_call:
+ # cargo-dist exposes the plan from the plan step, as a JSON string,
+ # to your job if it needs it
+ inputs:
+ plan:
+ required: true
+ type: string
+
+jobs:
+ greeter:
+ runs-on: ubuntu-latest
+ # This is optional; it exposes the plan to your job as an environment variable
+ env:
+ PLAN: ${{ inputs.plan }}
+ steps:
+ - name: Step 1
+ run: |
+ echo "Hello!"
+ echo "Plan is: ${PLAN}"
+
+
Then, add the following to your publish-jobs array:
+
publish-jobs = ["./publish-greeter"]
+
+
Running cargo-dist init for your tool will update your GitHub Actions configuration to make use of the new reusable workflow during the publish step.
Sometimes, you may need extra packages from the system package manager to be installed before in the builder before cargo-dist begins building your software. Cargo-dist can do this for you by adding the dependencies setting to your Cargo.toml. When set, the packages you request will be fetched and installed in the step before build. Additionally, on macOS, the cargo build process will be wrapped in brew bundle exec to ensure that your dependencies can be found no matter where Homebrew placed them.
+
Sometimes, you may want to make sure your users also have these dependencies available when they install your software. If you use a package manager-based installer, cargo-dist has the ability to specify these dependencies. By default, cargo-dist will examine your program to try to detect which dependencies it thinks will be necessary. At the moment, Homebrew is the only supported package manager installer. You can also specify these dependencies manually.
Currently, the only supported package managers are Apt (Linux), Chocolatey (Windows) and Homebrew (macOS).
+
GitHub currently only provides x86_64 macOS runners. When you request packages, the Intel versions will always be installed regardless of build targets. While Apple Silicon builds can use CLI tools installed this way, you will not be able to build software for Apple Silicon if it requires C libraries from Homebrew.
The happy-path of cargo-dist has us completely managing release.yml, and since 0.3.0 we will actually consider it an error for there to be any edits or out of date information in release.yml.
+
If there's something that cargo-dist can't do that makes you want to hand-edit the file, we'd love to hear about it so that you can stay on the happy-path!
+
However we know you sometimes really need to do those hand-edits, so there is a way to opt into it. If you set allow-dirty = ["ci"] in your cargo-dist config, cargo-dist will stop trying to update the file and stop checking if it's out of date.
+
Although you're not "using cargo-dist wrong" if you do this, be aware that you are losing access to a lot of the convenience and UX benefits of cargo-dist. Every piece of documentation that says "just run cargo dist init" may not work correctly, as a new feature may require the CI template to be updated. Even things as simple as "updating cargo-dist" will stop working.
+
We have put a lot of effort into minimizing those situations, with plan increasingly being responsible for dynamically computing what the CI should do, but that's not perfect, and there's no guarantees that future versions of cargo-dist won't completely change the way CI is structured.
Here's a grab-bag of more random settings you probably don't want to use, but exist in case you need them.
+
By default cargo-dist lets all the build tasks keep running even if one of them fails, to try to get you as much as possible when things go wrong. fail-fast = true can be set to disable this.
+
By default cargo-dist breaks build tasks onto more machines than strictly necessary to create the maximum opportunities for concurrency and to increase fault-tolerance. For instance if you want to build for both arm64 macOS and x64 macOS, that could be done on the same machine, but we put it on two machines so they can be in parallel and succeed/fail independently. merge-tasks = true can be set to disable this.
Although most Rust builds are statically linked and contain their own Rust dependencies, some crates will end up dynamically linking against system libraries. It's useful to know what your software picked up—sometimes this will help you catch things you may not have intended, like dynamically linking to OpenSSL, or allow you to check for package manager-provided libraries your users will need to have installed in order to be able to run your software.
+
cargo-dist provides a linkage report during your CI build in order to allow you to check for this. For macOS and Linux, it's able to categorize the targets it linked against to help you gauge whether or not it's likely to cause problems for your users. To view this, check the detailed view of your CI build and consult the "Build" step from the upload-local artifacts jobs.
+
This feature is defined for advanced users; most users won't need to use it. It's most useful for developers with specialized build setups who want to ensure that their binaries will be safe for all of their users. A few examples of users who may need to use it:
+
+
Users with custom runners with extra packages installed beyond what's included in the operating system;
+
Users who have installed extra packages using cargo-dist's system dependency feature;
+
Users whose cargo buildsystems include extra C dependencies.
+
+
The report is divided into categories to help you make sense of where these libraries are from and what it might mean for your users. These categories are:
+
+
System: Libraries that come with your operating system. On Linux, these packages are all provided by the system's package manager, and the linkage report includes information about which package includes each library. Some of these packages will be included in the base OS, and will be safe to rely on, while you'll need to ensure your users have others. If you're using standard base images like GitHub Actions's and haven't installed additional packages using apt, the packages in this list should be preinstalled for your users. On macOS, these packages are shipped with the operating system and not managed by a package manager; you can always rely on these being there within the same version of macOS.
+
Homebrew (macOS only): Libraries that are provided by the Homebrew package manager for macOS. These packages are not installed by default, so your users will need to have them installed in order to be able to use your software.
+
Public (unmanaged): Libraries which are present in public locations, but which are not managed or provided by the system or a package manager. Because these are not standard parts of the operating system, your users will be unlikely to have them.
+
Frameworks (macOS only): Frameworks, a special type of library provided by macOS. Frameworks installed in the /System directory come with the operating system and are available to all users.
+
Other: A catch-all category for any libraries which don't fall in the previous categories.
+
+
Here's an example of what a linkage report looks like for a Linux binary;
All of the distribute functionality of cargo-dist depends on some kind of CI integration to provide things like file hosting, secret keys, and the ability to spin up multiple machines.
The following CI providers have been requested, and we're open to supporting them, but we have no specific timeline for when they will be implemented. Providing additional info/feedback on them helps us prioritize the work:
Although cargo-dist was originally designed specifically for Cargo-based builds, we've introduced experimental features to allow you to use it to build and distribute any software written in any language. This feature is currently a prerelease: details may change before it becomes stable.
In order for cargo-dist to recognize your application, it requires a TOML configuration file named dist.toml. This file is similar to Cargo's Cargo.toml, so users who are already familiar with Cargo should feel comfortable right away. Many of dist.toml's fields are identical to Cargo.toml, but there are a few extra fields specific to this file.
+
dist.toml has two mandatory sections: package, which you write yourself and which contains information about your application; and dist, which contains cargo-dist's configuration and which cargo-dist init generates for you.
+
To get started, write a dist.toml containing just a package section. A simple one looks like this:
+
[package]
+# Your app's name
+name = "my_app"
+# The current version; make sure to keep this up to date!
+version = "0.1.0"
+# The URL to the git repository; this is used
+repository = "https://example.com"
+# The executables your app produces
+binaries = ["main"]
+# The build command cargo-dist runs to produce those binaries
+build-command = ["make"]
+
Once you've produced a configuration file, you can run cargo dist init and let cargo-dist generate its own configuration. From here, the build and usage process looks very much like the normal cargo-dist setup; for more information, check the main quickstart documentation.
Build commands are the core difference between these builds and regular cargo-dist. Since we don't have Cargo to rely on to tell us how to build your package, it's up to you to tell us how instead.
+
As an example, the above application is a C program with a simple makefile-based buildsystem. All you need to run to build this program is make, so we specified build-command = ["make"]. If your app has a more complex build that will require multiple commands to run, it may be easier for you to add a build script to your repository. In that case, build-command can simply be a reference to executing it:
+
build-command = ["./build.sh"]
+
+
We expose a special environment variable called CARGO_DIST_TARGET into your build. It contains a Rust-style target triple for the platform we expect your build to build for. Depending on the language of the software you're building, you may need to use this to set appropriate cross-compilation flags. For example, when cargo-dist is building for an Apple Silicon Mac, we'll set aarch64-apple-darwin in order to allow your build to know when it should build for aarch64 even if the host is x86_64.
+
On macOS, we expose several additional environment variables to help your buildsystem find dependencies. In the future, we may add more environment variables on all platforms.
+
+
CFLAGS/CPPFLAGS: Flags used by the C preprocessor and C compiler while building.
+
LDFLAGS: Flags used by the C linker.
+
PKG_CONFIG_PATH/PKG_CONFIG_LIBDIR: Paths for pkg-config to help it locate packages.
+
CMAKE_INCLUDE_PATH/CMAKE_LIBRARY_PATH: Paths for cmake to help it locate packages' configuration files.
These package fields are mandatory for cargo-dist to be able to build your package:
+
+
name: Your application's name.
+
version: The application's version. Currently, this must be in a Semver-compatible format.
+
repository: The URL to a git repository containing your application's source code.
+
binaries: An array of one or more executables your application's build will produce. The strings within this array are paths relative to your application's build directory; for example, if you produce a binary named main within the ./src directory, you can specify ["src/main"].
+
build-command: The command cargo-dist should run in order to build your application. This is an array of one or more strings; the first string is the command cargo-dist will run, and any subsequent strings are arguments to pass to that command.
The build functionality can be used on its own if you just want some tarballs and installers, but everything really comes together when you use the distribution functionality too.
That's a short list because "we make installers" is doing a lot of heavy lifting. Each installer could be (and sometimes is!) an entire standalone tool with its own documentation and ecosystem.
As a distribution tool, cargo-dist gets to flex its biggest superpower: it generates its own CI scripts. For instance, enabling GitHub CI with cargo dist init will generate release.yml, which implements the full pipeline of plan, build, host, publish, announce:
+
+
Plan
+
+
Waits for you to push a git tag for a new version (v1.0.0, my-app-1.0.0...)
+
Selects what apps in your workspace to announce new releases for based on that tag
Adds relevant release notes from your RELEASES/CHANGELOG
+
+
+
+
(Ideally "host" would come cleanly before "publish", but GitHub Releases doesn't really properly support this kind of staging, so we're forced to race the steps a bit here. Future work may provide a more robust release process.)
+
Most of the scripts roughly amount to "install cargo-dist", "run it exactly once", "upload the artifacts it reported". We want you to be able to copy that one cargo-dist invocation CI did, run it on your machine, and get the same results without any fuss (not to bit-level precision, but to the kinds of precision normal people expect from cargo builds). No setting up docker, no weird linux-only shell scripts that assume a bunch of tools were setup in earlier CI steps.
+
Of course even if we perfectly achieve this ideal, "you can run it locally" and "you want to run it locally" are different statements.
To that point, release.yml can now run partially in pull-requests. The default is to only run the "plan" step, which includes many integrity checks to help prevent "oops the release process is broken and we only found out when we tried to cut a release".
+
+
You can also crank the pull-request mode up to include the "build" step, in which case the PR Workflow Summary will include an artifacts.zip containing all the build results. We don't recommend keeping this on all the time (it's slow and wasteful), but it can be useful to temporarily turn on while testing a PR.