Skip to content

Commit 1511a28

Browse files
bors[bot]lumasepa
andauthored
Merge #1280
1280: Allow to run arbitrary commands in containers using cross-util run ... r=Emilgardis a=lumasepa Implementation for #1266 Co-authored-by: Sergio Medina <[email protected]>
2 parents ed713d9 + 1aab4cf commit 1511a28

File tree

10 files changed

+207
-51
lines changed

10 files changed

+207
-51
lines changed

.changes/1280.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"description": "Allow to run arbitrary commands in containers using `cross-util run ...`",
3+
"issues": [1266],
4+
"type": "added",
5+
"breaking": false
6+
}

src/bin/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
mod clean;
22
mod containers;
33
mod images;
4+
mod run;
45

56
pub use self::clean::*;
67
pub use self::containers::*;
78
pub use self::images::*;
9+
pub use self::run::*;

src/bin/commands/run.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use clap::Args as ClapArgs;
2+
use cross::config::Config;
3+
use cross::shell::{MessageInfo, Verbosity};
4+
use cross::{
5+
cargo_metadata_with_args, cli::Args, docker, rustc, setup, toml, CommandVariant, CrossSetup,
6+
Target,
7+
};
8+
use eyre::Context;
9+
10+
#[derive(ClapArgs, Debug)]
11+
pub struct Run {
12+
/// Provide verbose diagnostic output.
13+
#[clap(short, long)]
14+
pub verbose: bool,
15+
/// Do not print cross log messages.
16+
#[clap(short, long)]
17+
pub quiet: bool,
18+
/// Coloring: auto, always, never
19+
#[clap(long)]
20+
pub color: Option<String>,
21+
/// Container engine (such as docker or podman).
22+
#[clap(long)]
23+
pub engine: Option<String>,
24+
/// Target
25+
#[clap(short, long)]
26+
pub target: String,
27+
/// Interactive session
28+
#[clap(short, long, default_value = "false")]
29+
pub interactive: bool,
30+
/// Command to run, will be run in a shell
31+
#[clap(last = true)]
32+
pub command: String,
33+
}
34+
35+
impl Run {
36+
pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {
37+
let target_list = rustc::target_list(&mut Verbosity::Quiet.into())?;
38+
let target = Target::from(&self.target, &target_list);
39+
40+
let cwd = std::env::current_dir()?;
41+
let host_version_meta = rustc::version_meta()?;
42+
43+
let args = Args {
44+
cargo_args: vec![],
45+
rest_args: vec![],
46+
subcommand: None,
47+
channel: None,
48+
target: Some(target.clone()),
49+
features: vec![],
50+
target_dir: None,
51+
manifest_path: None,
52+
version: false,
53+
verbose: if self.verbose { 1 } else { 0 },
54+
quiet: self.quiet,
55+
color: self.color.clone(),
56+
};
57+
58+
if let Some(metadata) = cargo_metadata_with_args(None, Some(&args), msg_info)? {
59+
let CrossSetup { toolchain, .. } =
60+
match setup(&host_version_meta, &metadata, &args, target_list, msg_info)? {
61+
Some(setup) => setup,
62+
_ => {
63+
eyre::bail!("Error: cannot setup cross environment");
64+
}
65+
};
66+
67+
let toml = toml(&metadata, msg_info)?;
68+
let config = Config::new(toml);
69+
70+
let image = match docker::get_image(&config, &target, false) {
71+
Ok(i) => i,
72+
Err(err) => {
73+
msg_info.warn(&err)?;
74+
eyre::bail!("Error: {}", &err);
75+
}
76+
};
77+
78+
let image = image.to_definite_with(&engine, msg_info);
79+
80+
let paths = docker::DockerPaths::create(&engine, metadata, cwd, toolchain, msg_info)?;
81+
let options = docker::DockerOptions::new(
82+
engine,
83+
target,
84+
config,
85+
image,
86+
CommandVariant::Shell,
87+
None,
88+
self.interactive,
89+
);
90+
91+
let mut args = vec![String::from("-c")];
92+
args.push(self.command.clone());
93+
94+
docker::run(options, paths, &args, None, msg_info)
95+
.wrap_err("could not run container")?;
96+
}
97+
98+
Ok(())
99+
}
100+
101+
pub fn engine(&self) -> Option<&str> {
102+
self.engine.as_deref()
103+
}
104+
105+
pub fn verbose(&self) -> bool {
106+
self.verbose
107+
}
108+
109+
pub fn quiet(&self) -> bool {
110+
self.quiet
111+
}
112+
113+
pub fn color(&self) -> Option<&str> {
114+
self.color.as_deref()
115+
}
116+
}

src/bin/cross-util.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ enum Commands {
3737
/// Work with cross containers in local storage.
3838
#[clap(subcommand)]
3939
Containers(commands::Containers),
40+
/// Run in cross container.
41+
Run(commands::Run),
4042
/// Clean all cross data in local storage.
4143
Clean(commands::Clean),
4244
}
@@ -103,6 +105,11 @@ pub fn main() -> cross::Result<()> {
103105
let engine = get_engine!(args, false, msg_info)?;
104106
args.run(engine, &mut msg_info)?;
105107
}
108+
Commands::Run(args) => {
109+
let mut msg_info = get_msg_info!(args)?;
110+
let engine = get_engine!(args, false, msg_info)?;
111+
args.run(engine, &mut msg_info)?;
112+
}
106113
}
107114

108115
Ok(())

src/docker/custom.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl<'a> Dockerfile<'a> {
8585
build_args: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
8686
msg_info: &mut MessageInfo,
8787
) -> Result<String> {
88-
let uses_zig = options.cargo_variant.uses_zig();
88+
let uses_zig = options.command_variant.uses_zig();
8989
let mut docker_build = options.engine.command();
9090
docker_build.invoke_build_command();
9191
docker_build.disable_scan_suggest();

src/docker/image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct PossibleImage {
2727
}
2828

2929
impl PossibleImage {
30-
pub(crate) fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
30+
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
3131
if self.toolchain.is_empty() {
3232
Image {
3333
name: self.name.clone(),

src/docker/local.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub(crate) fn run(
3636
let toolchain_dirs = paths.directories.toolchain_directories();
3737
let package_dirs = paths.directories.package_directories();
3838

39-
let mut cmd = options.cargo_variant.safe_command();
39+
let mut cmd = options.command_variant.safe_command();
4040
cmd.args(args);
4141

4242
let mut docker = engine.subcommand("run");
@@ -134,6 +134,11 @@ pub(crate) fn run(
134134
if io::Stdin::is_atty() && io::Stdout::is_atty() && io::Stderr::is_atty() {
135135
docker.arg("-t");
136136
}
137+
138+
if options.interactive {
139+
docker.arg("-i");
140+
}
141+
137142
let mut image_name = options.image.name.clone();
138143
if options.needs_custom_image() {
139144
image_name = options

src/docker/remote.rs

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -895,34 +895,40 @@ pub(crate) fn run(
895895
}
896896
}
897897

898-
// `clean` doesn't handle symlinks: it will just unlink the target
899-
// directory, so we should just substitute it our target directory
900-
// for it. we'll still have the same end behavior
901-
let mut final_args = vec![];
902-
let mut iter = args.iter().cloned();
903-
let mut has_target_dir = false;
904-
while let Some(arg) = iter.next() {
905-
if arg == "--target-dir" {
906-
has_target_dir = true;
907-
final_args.push(arg);
908-
if iter.next().is_some() {
909-
final_args.push(target_dir.clone());
910-
}
911-
} else if arg.starts_with("--target-dir=") {
912-
has_target_dir = true;
913-
if arg.split_once('=').is_some() {
914-
final_args.push(format!("--target-dir={target_dir}"));
898+
let mut cmd = options.command_variant.safe_command();
899+
900+
if !options.command_variant.is_shell() {
901+
// `clean` doesn't handle symlinks: it will just unlink the target
902+
// directory, so we should just substitute it our target directory
903+
// for it. we'll still have the same end behavior
904+
let mut final_args = vec![];
905+
let mut iter = args.iter().cloned();
906+
let mut has_target_dir = false;
907+
while let Some(arg) = iter.next() {
908+
if arg == "--target-dir" {
909+
has_target_dir = true;
910+
final_args.push(arg);
911+
if iter.next().is_some() {
912+
final_args.push(target_dir.clone());
913+
}
914+
} else if arg.starts_with("--target-dir=") {
915+
has_target_dir = true;
916+
if arg.split_once('=').is_some() {
917+
final_args.push(format!("--target-dir={target_dir}"));
918+
}
919+
} else {
920+
final_args.push(arg);
915921
}
916-
} else {
917-
final_args.push(arg);
918922
}
923+
if !has_target_dir && subcommand.map_or(true, |s| s.needs_target_in_command()) {
924+
final_args.push("--target-dir".to_owned());
925+
final_args.push(target_dir.clone());
926+
}
927+
928+
cmd.args(final_args);
929+
} else {
930+
cmd.args(args);
919931
}
920-
if !has_target_dir && subcommand.map_or(true, |s| s.needs_target_in_command()) {
921-
final_args.push("--target-dir".to_owned());
922-
final_args.push(target_dir.clone());
923-
}
924-
let mut cmd = options.cargo_variant.safe_command();
925-
cmd.args(final_args);
926932

927933
// 5. create symlinks for copied data
928934
let mut symlink = vec!["set -e pipefail".to_owned()];
@@ -971,6 +977,11 @@ symlink_recurse \"${{prefix}}\"
971977
docker.add_cwd(&paths)?;
972978
docker.arg(&container_id);
973979
docker.add_build_command(toolchain_dirs, &cmd);
980+
981+
if options.interactive {
982+
docker.arg("-i");
983+
}
984+
974985
bail_container_exited!();
975986
let status = docker
976987
.run_and_get_status(msg_info, false)

src/docker/shared.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::file::{self, write_file, PathExt, ToUtf8};
1717
use crate::id;
1818
use crate::rustc::QualifiedToolchain;
1919
use crate::shell::{ColorChoice, MessageInfo, Verbosity};
20-
use crate::{CargoVariant, OutputExt, Target, TargetTriple};
20+
use crate::{CommandVariant, OutputExt, Target, TargetTriple};
2121

2222
use rustc_version::Version as RustcVersion;
2323

@@ -33,9 +33,10 @@ pub struct DockerOptions {
3333
pub target: Target,
3434
pub config: Config,
3535
pub image: Image,
36-
pub cargo_variant: CargoVariant,
36+
pub command_variant: CommandVariant,
3737
// not all toolchains will provide this
3838
pub rustc_version: Option<RustcVersion>,
39+
pub interactive: bool,
3940
}
4041

4142
impl DockerOptions {
@@ -44,16 +45,18 @@ impl DockerOptions {
4445
target: Target,
4546
config: Config,
4647
image: Image,
47-
cargo_variant: CargoVariant,
48+
cargo_variant: CommandVariant,
4849
rustc_version: Option<RustcVersion>,
50+
interactive: bool,
4951
) -> DockerOptions {
5052
DockerOptions {
5153
engine,
5254
target,
5355
config,
5456
image,
55-
cargo_variant,
57+
command_variant: cargo_variant,
5658
rustc_version,
59+
interactive,
5760
}
5861
}
5962

@@ -928,8 +931,8 @@ fn validate_env_var<'a>(
928931
Ok((key, value))
929932
}
930933

931-
impl CargoVariant {
932-
pub(crate) fn safe_command(self) -> SafeCommand {
934+
impl CommandVariant {
935+
pub(crate) fn safe_command(&self) -> SafeCommand {
933936
SafeCommand::new(self.to_str())
934937
}
935938
}
@@ -1041,7 +1044,7 @@ impl DockerCommandExt for Command {
10411044
])
10421045
.args(["-e", "CARGO_TARGET_DIR=/target"])
10431046
.args(["-e", &cross_runner]);
1044-
if options.cargo_variant.uses_zig() {
1047+
if options.command_variant.uses_zig() {
10451048
// otherwise, zig has a permission error trying to create the cache
10461049
self.args(["-e", "XDG_CACHE_HOME=/target/.zig-cache"]);
10471050
}
@@ -1271,7 +1274,7 @@ pub fn get_image_name(config: &Config, target: &Target, uses_zig: bool) -> Resul
12711274
.image_name(CROSS_IMAGE, version))
12721275
}
12731276

1274-
pub(crate) fn get_image(config: &Config, target: &Target, uses_zig: bool) -> Result<PossibleImage> {
1277+
pub fn get_image(config: &Config, target: &Target, uses_zig: bool) -> Result<PossibleImage> {
12751278
if let Some(image) = config.image(target)? {
12761279
return Ok(image);
12771280
}

0 commit comments

Comments
 (0)