nextest_runner/cargo_config/custom_platform.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
// Copyright (c) The nextest Contributors
// SPDX-License-Identifier: MIT OR Apache-2.0
use super::TargetTripleSource;
use crate::errors::TargetTripleError;
use camino::{Utf8Path, Utf8PathBuf};
use camino_tempfile::Utf8TempDir;
/// Represents a custom platform that was extracted from stored metadata.
///
/// The platform is stored in a temporary directory, and is deleted when this struct is dropped.
#[derive(Debug)]
pub struct ExtractedCustomPlatform {
source: TargetTripleSource,
dir: Utf8TempDir,
path: Utf8PathBuf,
}
impl ExtractedCustomPlatform {
/// Writes the custom JSON to a temporary directory.
pub fn new(
triple_str: &str,
json: &str,
source: TargetTripleSource,
) -> Result<Self, TargetTripleError> {
// Extract the JSON to a temporary file. Cargo requires that the file name be of the form
// `<triple_str>.json`.
let temp_dir = camino_tempfile::Builder::new()
.prefix("nextest-custom-target-")
.rand_bytes(5)
.tempdir()
.map_err(|error| TargetTripleError::CustomPlatformTempDirError {
source: source.clone(),
error,
})?;
let path = temp_dir.path().join(format!("{triple_str}.json"));
std::fs::write(&path, json).map_err(|error| {
TargetTripleError::CustomPlatformWriteError {
source: source.clone(),
path: path.clone(),
error,
}
})?;
Ok(Self {
source,
dir: temp_dir,
path,
})
}
/// Returns the source of the custom platform.
pub fn source(&self) -> &TargetTripleSource {
&self.source
}
/// Returns the temporary directory.
pub fn dir(&self) -> &Utf8TempDir {
&self.dir
}
/// Returns the path to the JSON file containing the custom platform.
pub fn path(&self) -> &Utf8Path {
&self.path
}
/// Close the temporary directory.
///
/// The directory is deleted when this struct is dropped, but this method can be used to detect
/// errors during cleanup.
pub fn close(self) -> Result<(), TargetTripleError> {
let dir_path = self.dir.path().to_owned();
self.dir
.close()
.map_err(|error| TargetTripleError::CustomPlatformCloseError {
source: self.source,
dir_path,
error,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cargo_config::{
test_helpers::setup_temp_dir, CargoTargetArg, TargetDefinitionLocation, TargetTriple,
};
use color_eyre::eyre::{bail, eyre, Context, Result};
#[test]
fn test_extracted_custom_platform() -> Result<()> {
// Integration testing a full custom platform is hard because it requires a build of std. So
// we just do a limited unit test: use the existing custom platform fixture, and run through
// the serialize/extract process, ensuring that the initial and final platform instances
// produced are the same.
let target = {
// Put this in here to ensure that this temp dir is dropped -- once the target is read
// there should be no further access to the temp dir.
let temp_dir = setup_temp_dir()?;
let platform_path = temp_dir.path().join("custom-target/my-target.json");
// Read in the custom platform and turn it into a `TargetTriple`.
TargetTriple::custom_from_path(
&platform_path,
TargetTripleSource::CliOption,
temp_dir.path(),
)?
};
// Serialize the `TargetTriple` to a `PlatformSummary`.
let summary = target.platform.to_summary();
// Now deserialize the `PlatformSummary` back into a `TargetTriple`.
let target2 = TargetTriple::deserialize(Some(summary))
.wrap_err("deserializing target triple")?
.ok_or_else(|| eyre!("deserializing target triple resulted in None"))?;
assert_eq!(target2.source, TargetTripleSource::Metadata);
assert!(
matches!(
target2.location,
TargetDefinitionLocation::MetadataCustom(_)
),
"triple2.location should be MetadataCustom: {:?}",
target2.location
);
// Now attempt to extract the custom platform.
let arg = target2
.to_cargo_target_arg()
.wrap_err("converting to cargo target arg")?;
let extracted = match &arg {
CargoTargetArg::Extracted(extracted) => extracted,
_ => bail!("expected CargoTargetArg::Extracted, found {:?}", arg),
};
// Generally ensure that Cargo will work with the extracted path.
assert!(extracted.path().is_absolute(), "path should be absolute");
assert!(
extracted.path().ends_with("my-target.json"),
"extracted path should end with 'my-target.json'"
);
assert_eq!(
arg.to_string(),
extracted.path(),
"arg matches extracted path"
);
// Now, read in the path and turn it into another TargetTriple.
let target3 = TargetTriple::custom_from_path(
extracted.path(),
TargetTripleSource::CliOption,
extracted.dir().path(),
)?;
assert_eq!(target3.platform, target.platform, "platform roundtrips");
Ok(())
}
}