Skip to main content

hakari/
lib.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4#![warn(missing_docs)]
5
6//! `hakari` is the library underlying [`cargo hakari`](https://docs.rs/cargo-hakari/*), a tool to
7//! manage `workspace-hack` packages.
8//!
9//! # Examples
10//!
11//! ```rust
12//! use guppy::MetadataCommand;
13//! use hakari::{HakariBuilder, HakariOutputOptions};
14//!
15//! // Use this workspace's PackageGraph for these tests.
16//! let package_graph = MetadataCommand::new()
17//!     .build_graph()
18//!     .expect("obtained cargo-guppy's PackageGraph");
19//!
20//! // The second argument to HakariBuilder::new specifies a Hakari (workspace-hack) package.
21//! // In this repository, the package is called "guppy-workspace-hack".
22//! let hakari_package = package_graph.workspace().member_by_name("guppy-workspace-hack").unwrap().id();
23//! let hakari_builder = HakariBuilder::new(&package_graph, Some(hakari_package))
24//!     .expect("HakariBuilder was constructed");
25//!
26//! // HakariBuilder has a number of config options. For this example, use the defaults.
27//! let hakari = hakari_builder.compute();
28//!
29//! // hakari can be used to build a TOML representation that forms part of a Cargo.toml file.
30//! // Existing Cargo.toml files can be managed using Hakari::read_toml.
31//! let toml = hakari.to_toml_string(&HakariOutputOptions::default()).expect("TOML output was constructed");
32//!
33//! // toml contains the Cargo.toml [dependencies] that would go in the Hakari package. It can be
34//! // written out through `HakariCargoToml` (returned by Hakari::read_toml) or manually.
35//! println!("Cargo.toml contents:\n{}", toml);
36//! ```
37//!
38//! The `cargo-guppy` repository uses a workspace-hack crate managed by `cargo hakari`. [See the
39//! generated `Cargo.toml`.](https://github.com/guppy-rs/guppy/blob/main/workspace-hack/Cargo.toml)
40//!
41//! The `cargo-guppy` repository also has a number of fixtures that demonstrate Hakari's output.
42//! [Here is an example.](https://github.com/guppy-rs/guppy/blob/main/fixtures/guppy/hakari/metadata_guppy_869476c-1.toml)
43//!
44//! # How `hakari` works
45//!
46//! Hakari follows a three-step process.
47//!
48//! ## 1. Configuration
49//!
50//! A [`HakariBuilder`] provides options to configure how a Hakari computation is done. Options supported
51//! include:
52//! * [the location of the `workspace-hack` package](HakariBuilder::new)
53//! * [platforms to simulate Cargo builds on](HakariBuilder::set_platforms)
54//! * [the version of the Cargo resolver to use](HakariBuilder::set_resolver)
55//! * [packages to be excluded during computation](HakariBuilder::add_traversal_excludes)
56//! * [packages to be excluded from the final output](HakariBuilder::add_final_excludes)
57//!
58//! With the optional `cli-support` feature, `HakariBuilder` options can be
59//! [read from](HakariBuilder::from_summary) or [written to](HakariBuilder::to_summary)
60//! a file as TOML or some other format.
61//!
62//! ## 2. Computation
63//!
64//! Once a `HakariBuilder` is configured, its [`compute`](HakariBuilder::compute) method can be
65//! called to create a `Hakari` instance. The algorithm runs in three steps:
66//!
67//! 1. Use guppy to [simulate a Cargo build](guppy::graph::cargo) for every workspace package and
68//!    every given platform, with no features, default features and all features. Collect the
69//!    results into
70//!    [a map](internals::ComputedMap) indexed by every dependency and the different sets of
71//!    features it was built with.
72//! 2. Scan through the map to figure out which dependencies are built with two or more
73//!    different feature sets, collecting them into an [output map](internals::OutputMap).
74//! 3. If one assumes that the output map will be written out to the `workspace-hack` package
75//!    through step 3 below, it is possible that it causes some extra packages to be built with a
76//!    second feature set. Look for such packages, add them to the output map, and iterate until a
77//!    fixpoint is reached and no new packages are built more than one way.
78//!
79//! This computation is done in a parallel fashion, using the [Rayon](rayon) library.
80//!
81//! The result of this computation is a [`Hakari`] instance.
82//!
83//! Third-party packages that depend on the workspace-hack, or on a workspace
84//! member that hakari manages, are never unified, since adding them would form
85//! a cycle. These are called [_structural excludes_](Hakari::structural_excludes).
86//! Note that their own dependencies are still unified as usual.
87//!
88//! ## 3. Serialization
89//!
90//! The last step is to serialize the contents of the output map into the `workspace-hack` package's
91//! `Cargo.toml` file.
92//!
93//! 1. [`Hakari::read_toml`] reads an existing `Cargo.toml` file on disk. This file is
94//!    *partially generated*:
95//!
96//!    ```toml
97//!    [package]
98//!    name = "workspace-hack"
99//!    version = "0.1.0"
100//!    # more options...
101//!
102//!    #### BEGIN HAKARI SECTION
103//!    ...
104//!    #### END HAKARI SECTION
105//!    ```
106//!
107//!    The contents outside the `BEGIN HAKARI SECTION` and `END HAKARI SECTION` lines may be
108//!    edited by hand. The contents within this section are automatically generated.
109//!
110//!    On success, a [`HakariCargoToml`] is returned.
111//!
112//! 2. [`Hakari::to_toml_string`](Hakari::to_toml_string) returns the new contents of the
113//!    automatically generated section.
114//! 3. [`HakariCargoToml::write_to_file`](HakariCargoToml::write_to_file) writes out the contents
115//!    to disk.
116//!
117//! `HakariCargoToml` also supports serializing contents to memory and producing diffs.
118//!
119//! # Future work
120//!
121//! `hakari` is still missing a few features:
122//!
123//! * Simulating cross-compilations
124//! * Platform-specific excludes
125//! * Only including a subset of packages in the final result (e.g. unifying core packages like
126//!   `syn` but not any others)
127//!
128//! These features will be added as time permits.
129
130mod cargo_toml;
131#[cfg(feature = "cli-support")]
132pub mod cli_ops;
133pub mod explain;
134mod hakari;
135mod helpers;
136#[cfg(feature = "proptest1")]
137mod proptest_helpers;
138mod registry;
139#[cfg(feature = "cli-support")]
140pub mod summaries;
141mod toml_out;
142pub mod verify;
143
144pub use crate::{
145    cargo_toml::*,
146    hakari::{DepFormatVersion, Hakari, HakariBuilder, UnifyTargetHost, WorkspaceHackLineStyle},
147    toml_out::*,
148};
149
150pub mod internals {
151    //! Access to internal Hakari data structures.
152    //!
153    //! These are provided in case some post-processing needs to be done.
154
155    pub use crate::hakari::{
156        ComputedInnerMap, ComputedInnerValue, ComputedMap, ComputedValue, OutputKey, OutputMap,
157    };
158}
159
160/// Re-export diffy.
161pub use diffy;