Skip to content

Commit 9623db9

Browse files
committed
Allow to run arbitrary commands in conainers using cross-util run ...
1 parent ed713d9 commit 9623db9

File tree

10 files changed

+180
-38
lines changed

10 files changed

+180
-38
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased] - ReleaseDate
99

10+
- #1266 Allow to run arbitrary commands in containers using `cross-util run --target <target> --command <command>`
11+
1012
## [v0.2.5] - 2023-02-04
1113

1214
## Fixed

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

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/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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::sync::atomic::Ordering;
55

66
use super::shared::*;
77
use crate::errors::Result;
8-
use crate::extensions::CommandExt;
8+
use crate::extensions::{CommandExt, SafeCommand};
99
use crate::file::{PathExt, ToUtf8};
1010
use crate::shell::{MessageInfo, Stream};
1111
use eyre::Context;
@@ -29,14 +29,14 @@ fn mount(
2929
pub(crate) fn run(
3030
options: DockerOptions,
3131
paths: DockerPaths,
32+
mut cmd: SafeCommand,
3233
args: &[String],
3334
msg_info: &mut MessageInfo,
3435
) -> Result<ExitStatus> {
3536
let engine = &options.engine;
3637
let toolchain_dirs = paths.directories.toolchain_directories();
3738
let package_dirs = paths.directories.package_directories();
3839

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

4242
let mut docker = engine.subcommand("run");

src/docker/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub use image::{Architecture, Image, ImagePlatform, Os as ContainerOs, PossibleI
1717
use std::process::ExitStatus;
1818

1919
use crate::errors::*;
20+
use crate::extensions::SafeCommand;
2021
use crate::shell::MessageInfo;
2122

2223
#[derive(Debug)]
@@ -44,6 +45,7 @@ pub fn image_name(target: &str, sub: Option<&str>, repository: &str, tag: &str)
4445
pub fn run(
4546
options: DockerOptions,
4647
paths: DockerPaths,
48+
command: SafeCommand,
4749
args: &[String],
4850
subcommand: Option<crate::Subcommand>,
4951
msg_info: &mut MessageInfo,
@@ -55,9 +57,9 @@ pub fn run(
5557
);
5658
}
5759
if options.is_remote() {
58-
remote::run(options, paths, args, subcommand, msg_info)
60+
remote::run(options, paths, command, args, subcommand, msg_info)
5961
.wrap_err("could not complete remote run")
6062
} else {
61-
local::run(options, paths, args, msg_info)
63+
local::run(options, paths, command, args, msg_info)
6264
}
6365
}

src/docker/remote.rs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ use super::engine::Engine;
1010
use super::shared::*;
1111
use crate::config::bool_from_envvar;
1212
use crate::errors::Result;
13-
use crate::extensions::CommandExt;
13+
use crate::extensions::{CommandExt, SafeCommand};
1414
use crate::file::{self, PathExt, ToUtf8};
1515
use crate::rustc::{self, QualifiedToolchain, VersionMetaExt};
1616
use crate::shell::{MessageInfo, Stream};
17-
use crate::temp;
1817
use crate::TargetTriple;
18+
use crate::{temp, CargoVariant};
1919

2020
// prevent further commands from running if we handled
2121
// a signal earlier, and the volume is exited.
@@ -667,6 +667,7 @@ impl QualifiedToolchain {
667667
pub(crate) fn run(
668668
options: DockerOptions,
669669
paths: DockerPaths,
670+
mut cmd: SafeCommand,
670671
args: &[String],
671672
subcommand: Option<crate::Subcommand>,
672673
msg_info: &mut MessageInfo,
@@ -895,34 +896,38 @@ pub(crate) fn run(
895896
}
896897
}
897898

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

927932
// 5. create symlinks for copied data
928933
let mut symlink = vec!["set -e pipefail".to_owned()];

src/docker/shared.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1271,7 +1271,7 @@ pub fn get_image_name(config: &Config, target: &Target, uses_zig: bool) -> Resul
12711271
.image_name(CROSS_IMAGE, version))
12721272
}
12731273

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

src/lib.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ use self::errors::Context;
6363
use self::shell::{MessageInfo, Verbosity};
6464

6565
pub use self::errors::{install_panic_hook, install_termination_hook, Result};
66-
pub use self::extensions::{CommandExt, OutputExt};
66+
pub use self::extensions::{CommandExt, OutputExt, SafeCommand};
6767
pub use self::file::{pretty_path, ToUtf8};
6868
pub use self::rustc::{TargetList, VersionMetaExt};
6969

@@ -447,6 +447,7 @@ pub enum CargoVariant {
447447
Cargo,
448448
Xargo,
449449
Zig,
450+
None,
450451
}
451452

452453
impl CargoVariant {
@@ -464,6 +465,7 @@ impl CargoVariant {
464465
CargoVariant::Cargo => "cargo",
465466
CargoVariant::Xargo => "xargo",
466467
CargoVariant::Zig => "cargo-zigbuild",
468+
CargoVariant::None => "",
467469
}
468470
}
469471

@@ -615,9 +617,16 @@ pub fn run(
615617
&options,
616618
msg_info,
617619
)?;
618-
619-
let status = docker::run(options, paths, &filtered_args, args.subcommand, msg_info)
620-
.wrap_err("could not run container")?;
620+
let cmd = options.cargo_variant.safe_command();
621+
let status = docker::run(
622+
options,
623+
paths,
624+
cmd,
625+
&filtered_args,
626+
args.subcommand,
627+
msg_info,
628+
)
629+
.wrap_err("could not run container")?;
621630
let needs_host = args.subcommand.map_or(false, |sc| sc.needs_host(is_remote));
622631
if !status.success() {
623632
warn_on_failure(&target, &toolchain, msg_info)?;
@@ -864,7 +873,7 @@ macro_rules! commit_info {
864873
///
865874
/// The values from `CROSS_CONFIG` or `Cross.toml` are concatenated with the package
866875
/// metadata in `Cargo.toml`, with `Cross.toml` having the highest priority.
867-
fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<Option<CrossToml>> {
876+
pub fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<Option<CrossToml>> {
868877
let root = &metadata.workspace_root;
869878
let cross_config_path = match env::var("CROSS_CONFIG") {
870879
Ok(var) => PathBuf::from(var),

0 commit comments

Comments
 (0)