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

add way to run script with pre-build #910

Merged
merged 1 commit into from
Jul 8, 2022
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added

- #913 - added the `x86_64-unknown-illumos` target.
- #910 - `pre-build` can now take a string pointing to a script file to run.
- #905 - added `qemu-runner` for musl images, allowing use of native or emulated runners.
- #905 - added qemu emulation to `i586-unknown-linux-gnu`, `i686-unknown-linux-musl`, and `i586-unknown-linux-gnu`, so they can run on an `x86` CPU, rather than an `x86_64` CPU.
- #900 - add the option to skip copying build artifacts back to host when using remote cross via `CROSS_REMOTE_SKIP_BUILD_ARTIFACTS`.
- #891 - support custom user namespace overrides by setting the `CROSS_CONTAINER_USER_NAMESPACE` environment variable.
- #891 - support custom user namespace overrides by setting the `CROSS_CONTAINER_USER_NAMESPACE` environment variable.
- #890 - support rootless docker via the `CROSS_ROOTLESS_CONTAINER_ENGINE` environment variable.
- #878 - added an image `ghcr.io/cross-rs/cross` containing cross.

Expand Down
20 changes: 19 additions & 1 deletion docs/cross_toml.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,28 @@ The `target` key allows you to specify parameters for specific compilation targe
xargo = false
build-std = false
image = "test-image"
pre-build = ["apt-get update"]
pre-build = ["apt-get update"] # can also be the path to a file to run
runner = "custom-runner"
```

# `target.TARGET.pre-build`

Emilgardis marked this conversation as resolved.
Show resolved Hide resolved
The `pre-build` field can also reference a file to copy and run. This file is relative to the container context, which would be the workspace root, or the current directory if `--manifest-path` is used. For more involved scripts, consider using `target.TARGET.dockerfile` instead to directly control the execution.

This script will be invoked as `RUN ./pre-build-script $CROSS_TARGET` where `$CROSS_TARGET` is the target triple.

```toml
[target.aarch64-unknown-linux-gnu]
pre-build = "./scripts/my-script.sh"
```

```sh
$ cat ./scripts/my-script.sh
#!/usr/bin/env bash

apt-get install libssl-dev -y
```

# `target.TARGET.env`

The `target` key allows you to specify environment variables that should be used for a specific compilation target.
Expand Down
27 changes: 21 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::docker::custom::PreBuild;
use crate::shell::MessageInfo;
use crate::{CrossToml, Result, Target, TargetList};

Expand Down Expand Up @@ -77,9 +78,17 @@ impl Environment {
self.get_values_for("DOCKERFILE_CONTEXT", target, |s| s.to_owned())
}

fn pre_build(&self, target: &Target) -> (Option<Vec<String>>, Option<Vec<String>>) {
fn pre_build(&self, target: &Target) -> (Option<PreBuild>, Option<PreBuild>) {
self.get_values_for("PRE_BUILD", target, |v| {
v.split('\n').map(String::from).collect()
let v: Vec<_> = v.split('\n').map(String::from).collect();
if v.len() == 1 {
PreBuild::Single {
line: v.into_iter().next().expect("should contain one item"),
env: true,
}
} else {
PreBuild::Lines(v)
}
})
}

Expand Down Expand Up @@ -326,7 +335,7 @@ impl Config {
.map_or(Ok(None), |t| Ok(t.dockerfile_build_args(target)))
}

pub fn pre_build(&self, target: &Target) -> Result<Option<Vec<String>>> {
pub fn pre_build(&self, target: &Target) -> Result<Option<PreBuild>> {
self.get_from_ref(target, Environment::pre_build, CrossToml::pre_build)
}

Expand Down Expand Up @@ -494,7 +503,10 @@ mod tests {
assert_eq!(config.build_std(&target()), None);
assert_eq!(
config.pre_build(&target())?,
Some(vec![s!("apt-get update"), s!("apt-get install zlib-dev")])
Some(PreBuild::Lines(vec![
s!("apt-get update"),
s!("apt-get install zlib-dev")
]))
);

Ok(())
Expand Down Expand Up @@ -530,7 +542,7 @@ mod tests {
}

#[test]
pub fn env_target_and_toml_build_pre_build_then_use_toml() -> Result<()> {
pub fn env_target_and_toml_build_pre_build_then_use_env() -> Result<()> {
let mut map = HashMap::new();
map.insert(
"CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_PRE_BUILD",
Expand All @@ -541,7 +553,10 @@ mod tests {
let config = Config::new_with(Some(toml(TOML_BUILD_PRE_BUILD)?), env);
assert_eq!(
config.pre_build(&target())?,
Some(vec![s!("dpkg --add-architecture arm64")])
Some(PreBuild::Single {
line: s!("dpkg --add-architecture arm64"),
env: true
})
);

Ok(())
Expand Down
99 changes: 87 additions & 12 deletions src/cross_toml.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![doc = include_str!("../docs/cross_toml.md")]

use crate::docker::custom::PreBuild;
use crate::shell::MessageInfo;
use crate::{config, errors::*};
use crate::{Target, TargetList};
Expand All @@ -24,7 +25,8 @@ pub struct CrossBuildConfig {
xargo: Option<bool>,
build_std: Option<bool>,
default_target: Option<String>,
pre_build: Option<Vec<String>>,
#[serde(default, deserialize_with = "opt_string_or_string_vec")]
pre_build: Option<PreBuild>,
#[serde(default, deserialize_with = "opt_string_or_struct")]
dockerfile: Option<CrossTargetDockerfileConfig>,
}
Expand All @@ -38,7 +40,8 @@ pub struct CrossTargetConfig {
image: Option<String>,
#[serde(default, deserialize_with = "opt_string_or_struct")]
dockerfile: Option<CrossTargetDockerfileConfig>,
pre_build: Option<Vec<String>>,
#[serde(default, deserialize_with = "opt_string_or_string_vec")]
pre_build: Option<PreBuild>,
runner: Option<String>,
#[serde(default)]
env: CrossEnvConfig,
Expand Down Expand Up @@ -251,12 +254,8 @@ impl CrossToml {
}

/// Returns the `build.dockerfile.pre-build` and `target.{}.dockerfile.pre-build` part of `Cross.toml`
pub fn pre_build(&self, target: &Target) -> (Option<&[String]>, Option<&[String]>) {
self.get_ref(
target,
|b| b.pre_build.as_deref(),
|t| t.pre_build.as_deref(),
)
pub fn pre_build(&self, target: &Target) -> (Option<&PreBuild>, Option<&PreBuild>) {
self.get_ref(target, |b| b.pre_build.as_ref(), |t| t.pre_build.as_ref())
}

/// Returns the `target.{}.runner` part of `Cross.toml`
Expand Down Expand Up @@ -395,6 +394,64 @@ where
deserializer.deserialize_any(StringOrStruct(PhantomData))
}

fn opt_string_or_string_vec<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: Deserialize<'de> + std::str::FromStr<Err = std::convert::Infallible> + From<Vec<String>>,
D: serde::Deserializer<'de>,
{
use std::{fmt, marker::PhantomData};

use serde::de::{self, SeqAccess, Visitor};

struct StringOrStringVec<T>(PhantomData<fn() -> T>);

impl<'de, T> Visitor<'de> for StringOrStringVec<T>
where
T: Deserialize<'de> + FromStr<Err = std::convert::Infallible> + From<Vec<String>>,
{
type Value = Option<T>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("string or seq")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(FromStr::from_str(value).ok())
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut vec: Vec<String> = vec![];

while let Some(inner) = seq.next_element::<String>()? {
vec.push(inner);
}
Ok(Some(vec.into()))
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}

fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(None)
}
}

deserializer.deserialize_any(StringOrStringVec(PhantomData))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -438,7 +495,7 @@ mod tests {
xargo: Some(true),
build_std: None,
default_target: None,
pre_build: Some(vec![s!("echo 'Hello World!'")]),
pre_build: Some(PreBuild::Lines(vec![s!("echo 'Hello World!'")])),
dockerfile: None,
},
};
Expand Down Expand Up @@ -477,7 +534,7 @@ mod tests {
image: Some(s!("test-image")),
runner: None,
dockerfile: None,
pre_build: Some(vec![]),
pre_build: Some(PreBuild::Lines(vec![])),
},
);

Expand Down Expand Up @@ -520,7 +577,7 @@ mod tests {
context: None,
build_args: None,
}),
pre_build: Some(vec![s!("echo 'Hello'")]),
pre_build: Some(PreBuild::Lines(vec![s!("echo 'Hello'")])),
runner: None,
env: CrossEnvConfig {
passthrough: None,
Expand All @@ -539,7 +596,7 @@ mod tests {
xargo: Some(true),
build_std: None,
default_target: None,
pre_build: Some(vec![]),
pre_build: Some(PreBuild::Lines(vec![])),
dockerfile: None,
},
};
Expand Down Expand Up @@ -771,4 +828,22 @@ mod tests {

Ok(())
}

#[test]
fn pre_build_script() -> Result<()> {
let toml_str = r#"
[target.aarch64-unknown-linux-gnu]
pre-build = "./my-script.sh"

[build]
pre-build = ["echo Hello World"]
"#;
let (toml, unused) = CrossToml::parse_from_cross(toml_str, &mut m!())?;
assert!(unused.is_empty());
assert!(matches!(
toml.pre_build(&Target::new_built_in("aarch64-unknown-linux-gnu")),
(Some(&PreBuild::Lines(_)), Some(&PreBuild::Single { .. }))
));
Ok(())
}
}
40 changes: 39 additions & 1 deletion src/docker/custom.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;

use crate::docker::{DockerOptions, DockerPaths};
use crate::shell::MessageInfo;
Expand All @@ -22,6 +23,43 @@ pub enum Dockerfile<'a> {
},
}

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum PreBuild {
Emilgardis marked this conversation as resolved.
Show resolved Hide resolved
/// A path to a file to copy or a single line to `RUN` if line comes from env
Single { line: String, env: bool },
/// Lines to execute in a single `RUN`
Lines(Vec<String>),
}

impl FromStr for PreBuild {
type Err = std::convert::Infallible;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(PreBuild::Single {
line: s.to_owned(),
env: false,
})
}
}

impl From<Vec<String>> for PreBuild {
fn from(vec: Vec<String>) -> Self {
PreBuild::Lines(vec)
}
}

impl PreBuild {
#[must_use]
pub fn is_single(&self) -> bool {
matches!(self, Self::Single { .. })
}

#[must_use]
pub fn is_lines(&self) -> bool {
matches!(self, Self::Lines(..))
}
}

impl<'a> Dockerfile<'a> {
pub fn build(
&self,
Expand Down Expand Up @@ -96,7 +134,7 @@ impl<'a> Dockerfile<'a> {
if let Some(context) = self.context() {
docker_build.arg(&context);
} else {
docker_build.arg(".");
docker_build.arg(paths.host_root());
}

docker_build.run(msg_info, true)?;
Expand Down
2 changes: 1 addition & 1 deletion src/docker/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod custom;
pub mod custom;
mod engine;
mod local;
pub mod remote;
Expand Down
Loading