scratch/lib.rs
1//! This crate exposes a compile-time temporary directory shareable by multiple
2//! crates in a build graph and erased by `cargo clean`.
3//!
4//! The intended usage is from a build.rs Cargo build script, or more likely
5//! from a library which is called by other crates' build scripts.
6//!
7//! ```toml
8//! # Cargo.toml
9//!
10//! [build-dependencies]
11//! scratch = "1.0"
12//! ```
13//!
14//! ```edition2021
15//! // build.rs
16//!
17//! fn main() {
18//! let dir = scratch::path("mycrate");
19//! // ... write or read inside of that path
20//! }
21//! ```
22//!
23//! <br>
24//!
25//! # Comparisons
26//!
27//! Comparison to **`std::env::var_os("OUT_DIR")`**:
28//!
29//! - This functionality is different from OUT_DIR in that the same directory
30//! path will be seen by *all* crates whose build passes a matching `suffix`
31//! argument, and each crate can see content placed into the directory by
32//! those other crates' build scripts that have already run.
33//!
34//! - This functionality is similar to OUT_DIR in that both are erased when a
35//! `cargo clean` is executed.
36//!
37//! Comparison to **`std::env::temp_dir()`**:
38//!
39//! - This functionality is similar to temp_dir() in that stuff that goes in is
40//! visible to subsequently running build scripts.
41//!
42//! - This functionality is different from temp_dir() in that `cargo clean`
43//! cleans up the contents.
44//!
45//! <br>
46//!
47//! # Tips
48//!
49//! You'll want to consider what happens when Cargo runs multiple build scripts
50//! concurrently that access the same scratch dir. In some use cases you likely
51//! want some synchronization over the contents of the scratch directory, such
52//! as by an advisory [file lock]. On Unix-like and Windows host systems the
53//! simplest way to sequence the build scripts such that each one gets exclusive
54//! access one after the other is something like:
55//!
56//! [file lock]: https://man7.org/linux/man-pages/man2/flock.2.html
57//!
58//! ```edition2021
59//! use std::fs::File;
60//! use std::io;
61//!
62//! fn main() -> io::Result<()> {
63//! let dir = scratch::path("demo");
64//! let flock = File::create(dir.join(".lock"))?;
65//! fs2::FileExt::lock_exclusive(&flock)?;
66//!
67//! // ... now do work
68//! # Ok(())
69//! }
70//! ```
71//!
72//! This simplest approach is fine for a cache which is slow to fill (maybe a
73//! large download) but fast/almost immediate to use. On the other hand if the
74//! build scripts using your cache will take a while to complete even if they
75//! only read from the scratch directory, a different approach which allows
76//! readers to make progress in parallel would perform better.
77//!
78//! ```edition2021
79//! use std::fs::{self, File};
80//! use std::io;
81//!
82//! fn main() -> io::Result<()> {
83//! let dir = scratch::path("demo");
84//! let flock = File::create(dir.join(".lock"))?;
85//! let sdk = dir.join("thing.sdk");
86//!
87//! if !sdk.exists() {
88//! fs2::FileExt::lock_exclusive(&flock)?;
89//! if !sdk.exists() {
90//! let download_location = sdk.with_file_name("thing.sdk.partial");
91//! download_sdk_to(&download_location)?;
92//! fs::rename(&download_location, &sdk)?;
93//! }
94//! fs2::FileExt::unlock(&flock)?;
95//! }
96//!
97//! // ... now use the SDK
98//! # Ok(())
99//! }
100//! #
101//! # use std::path::Path;
102//! #
103//! # fn download_sdk_to(location: &Path) -> io::Result<()> {
104//! # fs::write(location, "...")
105//! # }
106//! ```
107//!
108//! For use cases that are not just a matter of the first build script writing
109//! to the directory and the rest reading, more elaborate schemes involving
110//! `lock_shared` might be something to consider.
111
112#![doc(html_root_url = "https://docs.rs/scratch/1.0.8")]
113#![cfg_attr(clippy, allow(renamed_and_removed_lints))]
114#![cfg_attr(clippy, allow(doc_markdown, must_use_candidate, needless_doctest_main))]
115
116use std::fs;
117use std::path::{Path, PathBuf};
118
119pub fn path(suffix: &str) -> PathBuf {
120 let p = Path::new(env!("OUT_DIR")).join(suffix);
121 let _ = fs::create_dir(&p);
122 p
123}