Shared scratch for build scripts
================================

[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/scratch-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/scratch)
[<img alt="crates.io" src="https://img.shields.io/crates/v/scratch.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/scratch)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-scratch-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/scratch)
[<img alt="build status" src="https://img.shields.io/github/workflow/status/dtolnay/scratch/CI/master?style=for-the-badge" height="20">](https://github.com/dtolnay/scratch/actions?query=branch%3Amaster)

This crate exposes a compile-time temporary directory sharable by multiple
crates in a build graph and erased by `cargo clean`.

The intended usage is from a build.rs Cargo build script, or more likely from a
library which is called by other crates' build scripts.

```toml
# Cargo.toml

[build-dependencies]
scratch = "1.0"
```

```rust
// build.rs

fn main() {
    let dir = scratch::path("mycrate");
    // ... write or read inside of that path
}
```

<br>

## Comparisons

Comparison to **`std::env::var_os("OUT_DIR")`**:

- This functionality is different from OUT\_DIR in that the same directory path
  will be seen by *all* crates whose build passes a matching `suffix` argument,
  and each crate can see content placed into the directory by those other
  crates' build scripts that have already run.

- This functionality is similar to OUT\_DIR in that both are erased when a
  `cargo clean` is executed.

Comparison to **`std::env::temp_dir()`**:

- This functionality is similar to temp\_dir() in that stuff that goes in is
  visible to subsequently running build scripts.

- This functionality is different from temp\_dir() in that `cargo clean` cleans
  up the contents.

<br>

## Tips

You'll want to consider what happens when Cargo runs multiple build scripts
concurrently that access the same scratch dir. In some use cases you likely want
some synchronization over the contents of the scratch directory, such as by an
advisory [file lock]. On Unix-like and Windows host systems the simplest way to
sequence the build scripts such that each one gets exclusive access one after
the other is something like:

[file lock]: https://man7.org/linux/man-pages/man2/flock.2.html

```rust
use std::fs::File;
use std::io;

fn main() -> io::Result<()> {
    let dir = scratch::path("demo");
    let flock = File::create(dir.join(".lock"))?;
    fs2::FileExt::lock_exclusive(&flock)?;

    // ... now do work
}
```

This simplest approach is fine for a cache which is slow to fill (maybe a large
download) but fast/almost immediate to use. On the other hand if the build
scripts using your cache will take a while to complete even if they only read
from the scratch directory, a different approach which allows readers to make
progress in parallel would perform better.

```rust
use std::fs::{self, File};
use std::io;

fn main() -> io::Result<()> {
    let dir = scratch::path("demo");
    let flock = File::create(dir.join(".lock"))?;
    let sdk = dir.join("thing.sdk");

    if !sdk.exists() {
        fs2::FileExt::lock_exclusive(&flock)?;
        if !sdk.exists() {
            let download_location = sdk.with_file_name("thing.sdk.partial");
            download_sdk_to(&download_location)?;
            fs::rename(&download_location, &sdk)?;
        }
        fs2::FileExt::unlock(&flock)?;
    }

    // ... now use the SDK
}
```

For use cases that are not just a matter of the first build script writing to
the directory and the rest reading, more elaborate schemes involving
`lock_shared` might be something to consider.

<br>

#### License

<sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
</sup>

<br>

<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
</sub>
