Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename build script rather and include it as bytes #86

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 14 additions & 15 deletions src/util/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ use anyhow::{anyhow, bail, ensure, Context, Result};
use once_cell::sync::Lazy;
use std::{
env,
fs::{canonicalize, set_permissions, Permissions},
fs::canonicalize,
io::Write,
os::unix::{ffi::OsStrExt, fs::PermissionsExt},
os::unix::ffi::OsStrExt,
path::Path,
process::{Command, Output, Stdio},
str::Utf8Error,
};
use tempfile::NamedTempFile;

#[allow(dead_code)]
const DEFAULT_PROFILE: &str = r#"(version 1)
Expand All @@ -30,8 +29,8 @@ const DEFAULT_PROFILE: &str = r#"(version 1)
/// Executes `command`, forwards its output to stdout and stderr, and optionally checks whether
/// `command` succeeded.
///
/// Called by [`unpack_and_exec`]. Since this file is included in the wrapper build script's
/// src/main.rs file, `exec_forwarding_output` should appear here, alongside [`unpack_and_exec`].
/// Called by [`exec_sibling`]. Since this file is included in the wrapper build script's
/// src/main.rs file, `exec_forwarding_output` should appear here, alongside [`exec_sibling`].
///
/// # Errors
///
Expand Down Expand Up @@ -64,14 +63,16 @@ pub fn exec_forwarding_output(mut command: Command, failure_is_error: bool) -> R
/// Essentially the body of the wrapper build script's `main` function. Not called by `build-wrap`
/// itself.
#[allow(dead_code)]
fn unpack_and_exec(bytes: &[u8]) -> Result<()> {
let (mut file, temp_path) = NamedTempFile::new().map(NamedTempFile::into_parts)?;
fn exec_sibling(sibling_path_as_str: &str) -> Result<()> {
let current_exe = env::current_exe()?;

file.write_all(bytes)?;
let parent = current_exe
.parent()
.ok_or_else(|| anyhow!("failed to get `current_exe` parent"))?;

drop(file);
let sibling_path = Path::new(sibling_path_as_str);

set_permissions(&temp_path, Permissions::from_mode(0o755))?;
assert!(sibling_path.starts_with(parent));

// smoelius: The `BUILD_WRAP_CMD` used is the one set when set when the wrapper build script is
// compiled, not when it is run. So if the wrapped build script prints the following and the
Expand All @@ -81,7 +82,7 @@ fn unpack_and_exec(bytes: &[u8]) -> Result<()> {
// cargo:rerun-if-env-changed=BUILD_WRAP_CMD
// ```
// They will cause the wrapped build script to be rerun, however.
let expanded_args = split_and_expand(&temp_path)?;
let expanded_args = split_and_expand(sibling_path)?;

let allow_enabled = enabled("BUILD_WRAP_ALLOW");

Expand All @@ -93,12 +94,10 @@ fn unpack_and_exec(bytes: &[u8]) -> Result<()> {
// `BUILD_WRAP_ALLOW` is enabled.
if !output.status.success() {
debug_assert!(allow_enabled);
let command = Command::new(&temp_path);
let command = Command::new(sibling_path);
let _: Output = exec_forwarding_output(command, true)?;
}

drop(temp_path);

Ok(())
}

Expand Down Expand Up @@ -239,7 +238,7 @@ fn expand(mut cmd: &str, build_script_path: Option<&Path>) -> Result<String> {

#[cfg(target_os = "macos")]
static BUILD_WRAP_PROFILE_PATH: Lazy<String> = Lazy::new(|| {
let tempfile = NamedTempFile::new().unwrap();
let tempfile = tempfile::NamedTempFile::new().unwrap();
let (mut file, temp_path) = tempfile.into_parts();
let profile = var("BUILD_WRAP_PROFILE").unwrap_or(DEFAULT_PROFILE.to_owned());
let expanded_profile = expand(&profile, None).unwrap();
Expand Down
28 changes: 19 additions & 9 deletions src/wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
use crate::util::ToUtf8;
use anyhow::Result;
use anyhow::{anyhow, Result};
use std::{
fs::{create_dir, write},
fs::{create_dir, rename, write},
path::Path,
};
use tempfile::{tempdir, TempDir};
use tempfile::{tempdir, NamedTempFile, TempDir};

#[allow(clippy::disallowed_methods)]
pub fn package(build_script_path: &Path) -> Result<TempDir> {
let build_script_path_as_str = build_script_path.to_utf8()?;
let parent = build_script_path
.parent()
.ok_or_else(|| anyhow!("failed to get `build_script_path` parent"))?;

let temp_file = NamedTempFile::new_in(parent)?;

let (_file, sibling_path) = temp_file.keep()?;

rename(build_script_path, &sibling_path)?;

let sibling_path_as_str = sibling_path.to_utf8()?;

let tempdir = tempdir()?;

write(tempdir.path().join("Cargo.toml"), CARGO_TOML)?;
create_dir(tempdir.path().join("src"))?;
write(
tempdir.path().join("src/main.rs"),
main_rs(build_script_path_as_str),
main_rs(sibling_path_as_str),
)?;

Ok(tempdir)
Expand All @@ -39,19 +49,19 @@ tempfile = "3.10"
/// A wrapper build script's src/main.rs consists of the following:
///
/// - the contents of util/common.rs (included verbatim)
/// - the original build script as a byte slice (`BYTES`)
/// - the path of the renamed original build script (`PATH`)
/// - a `main` function
///
/// See [`package`].
fn main_rs(build_script_path_as_str: &str) -> Vec<u8> {
fn main_rs(sibling_path_as_str: &str) -> Vec<u8> {
[
COMMON_RS,
format!(
r#"
const BYTES: &[u8] = include_bytes!("{build_script_path_as_str}");
const PATH: &str = "{sibling_path_as_str}";

fn main() -> Result<()> {{
unpack_and_exec(BYTES)
exec_sibling(PATH)
}}
"#,
)
Expand Down
15 changes: 13 additions & 2 deletions tests/integration/cargo_config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::util;
use std::{env::temp_dir, path::Path, process::Command};
use std::{path::Path, process::Command};

const DIR: &str = "fixtures/cargo_config";

Expand All @@ -16,8 +16,19 @@ fn cargo_config() {
// smoelius: When `build-wrap` builds the wrapper package, it expects the target directory to be
// `target`. So building the wrapper package in `fixtures/cargo_config` would fail because it
// contains a `.cargo/config.toml` that sets the target directory to `target-custom`.

// smoelius: The build script in `fixtures/cargo_config` prints the path of the current
// executable, i.e., the wrapped, original build script. Previously, this was unpacked into
// `std::env::temp_dir()`. However, `build-wrap` now renames the original build script so that
// it is a sibling of the wrapper build script. Hence, when this test is run, the current
// executable should be in `target-custom` alongside the wrapper build script.
let command = util::build_with_build_wrap();
test_build(command, &temp_dir());
test_build(
command,
&Path::new(env!("CARGO_MANIFEST_DIR"))
.join(DIR)
.join("target-custom/debug"),
);
}

fn test_build(mut command: Command, expected_dir: &Path) {
Expand Down
7 changes: 3 additions & 4 deletions tests/integration/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use std::{
path::Path,
process::Command,
};
use tempfile::{tempdir_in, TempDir};

#[path = "../../src/util/mod.rs"]
mod main_util;
Expand Down Expand Up @@ -55,7 +54,7 @@ pub fn build_with_default_linker() -> Command {
pub fn temp_package<'a, 'b>(
build_script_path: Option<impl AsRef<Path>>,
dependencies: impl IntoIterator<Item = (&'a str, &'b str)>,
) -> Result<TempDir> {
) -> Result<tempfile::TempDir> {
let tempdir = tempdir()?;

write(tempdir.path().join("Cargo.toml"), CARGO_TOML)?;
Expand Down Expand Up @@ -98,8 +97,8 @@ static METADATA: Lazy<Metadata> = Lazy::new(|| MetadataCommand::new().no_deps().
///
/// Useful if you want to verify that writing outside of the temporary directory is forbidden, but
/// `/tmp` is writeable, for example.
pub fn tempdir() -> Result<TempDir> {
tempdir_in(&METADATA.target_directory).map_err(Into::into)
pub fn tempdir() -> Result<tempfile::TempDir> {
tempfile::tempdir_in(&METADATA.target_directory).map_err(Into::into)
}

#[derive(Debug)]
Expand Down