Skip to content

Commit

Permalink
Allow to run arbitrary commands in conainers using cross-util run ...
Browse files Browse the repository at this point in the history
  • Loading branch information
lumasepa committed Jun 22, 2023
1 parent ed713d9 commit 9623db9
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 38 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate

- #1266 Allow to run arbitrary commands in containers using `cross-util run --target <target> --command <command>`

## [v0.2.5] - 2023-02-04

## Fixed
Expand Down
2 changes: 2 additions & 0 deletions src/bin/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod clean;
mod containers;
mod images;
mod run;

pub use self::clean::*;
pub use self::containers::*;
pub use self::images::*;
pub use self::run::*;
115 changes: 115 additions & 0 deletions src/bin/commands/run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use clap::Args as ClapArgs;
use cross::config::Config;
use cross::shell::{MessageInfo, Verbosity};
use cross::SafeCommand;
use cross::{
cargo_metadata_with_args, cli::Args, docker, rustc, setup, toml, CargoVariant, CrossSetup,
Target,
};
use eyre::Context;

#[derive(ClapArgs, Debug)]
pub struct Run {
/// Provide verbose diagnostic output.
#[clap(short, long)]
pub verbose: bool,
/// Do not print cross log messages.
#[clap(short, long)]
pub quiet: bool,
/// Coloring: auto, always, never
#[clap(long)]
pub color: Option<String>,
/// Container engine (such as docker or podman).
#[clap(long)]
pub engine: Option<String>,

#[clap(short, long)]
pub target: String,

#[clap(short, long)]
pub command: String,
}

impl Run {
pub fn run(&self, engine: docker::Engine, msg_info: &mut MessageInfo) -> cross::Result<()> {
let target_list = rustc::target_list(&mut Verbosity::Quiet.into())?;
let target = Target::from(&self.target, &target_list);

let cwd = std::env::current_dir()?;
let host_version_meta = rustc::version_meta()?;

let args = Args {
cargo_args: vec![],
rest_args: vec![],
subcommand: None,
channel: None,
target: Some(target.clone()),
features: vec![],
target_dir: None,
manifest_path: None,
version: false,
verbose: if self.verbose { 1 } else { 0 },
quiet: self.quiet,
color: self.color.clone(),
};

if let Some(metadata) = cargo_metadata_with_args(None, Some(&args), msg_info)? {
let CrossSetup { toolchain, .. } =
match setup(&host_version_meta, &metadata, &args, target_list, msg_info)? {
Some(setup) => setup,
_ => {
eyre::bail!("Error: cannot setup cross environment");
}
};

let toml = toml(&metadata, msg_info)?;
let config = Config::new(toml);

let image = match docker::get_image(&config, &target, false) {
Ok(i) => i,
Err(err) => {
msg_info.warn(&err)?;
eyre::bail!("Error: {}", &err);
}
};

let image = image.to_definite_with(&engine, msg_info);

let paths =
docker::DockerPaths::create(&engine, metadata, cwd, toolchain, msg_info)?;
let options = docker::DockerOptions::new(
engine,
target,
config,
image,
CargoVariant::None,
None,
);

let command = SafeCommand::new(&"sh");
let mut args = vec![String::from("-c")];
args.push(self.command.clone());

docker::run(options, paths, command, &args, None, msg_info)
.wrap_err("could not run container")?;
}

Ok(())
}

pub fn engine(&self) -> Option<&str> {
self.engine.as_deref()
}

pub fn verbose(&self) -> bool {
self.verbose
}

pub fn quiet(&self) -> bool {
self.quiet
}

pub fn color(&self) -> Option<&str> {
self.color.as_deref()
}
}
7 changes: 7 additions & 0 deletions src/bin/cross-util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ enum Commands {
/// Work with cross containers in local storage.
#[clap(subcommand)]
Containers(commands::Containers),
/// Run in cross container.
Run(commands::Run),
/// Clean all cross data in local storage.
Clean(commands::Clean),
}
Expand Down Expand Up @@ -103,6 +105,11 @@ pub fn main() -> cross::Result<()> {
let engine = get_engine!(args, false, msg_info)?;
args.run(engine, &mut msg_info)?;
}
Commands::Run(args) => {
let mut msg_info = get_msg_info!(args)?;
let engine = get_engine!(args, false, msg_info)?;
args.run(engine, &mut msg_info)?;
}
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion src/docker/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub struct PossibleImage {
}

impl PossibleImage {
pub(crate) fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
pub fn to_definite_with(&self, engine: &Engine, msg_info: &mut MessageInfo) -> Image {
if self.toolchain.is_empty() {
Image {
name: self.name.clone(),
Expand Down
4 changes: 2 additions & 2 deletions src/docker/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::atomic::Ordering;

use super::shared::*;
use crate::errors::Result;
use crate::extensions::CommandExt;
use crate::extensions::{CommandExt, SafeCommand};
use crate::file::{PathExt, ToUtf8};
use crate::shell::{MessageInfo, Stream};
use eyre::Context;
Expand All @@ -29,14 +29,14 @@ fn mount(
pub(crate) fn run(
options: DockerOptions,
paths: DockerPaths,
mut cmd: SafeCommand,
args: &[String],
msg_info: &mut MessageInfo,
) -> Result<ExitStatus> {
let engine = &options.engine;
let toolchain_dirs = paths.directories.toolchain_directories();
let package_dirs = paths.directories.package_directories();

let mut cmd = options.cargo_variant.safe_command();
cmd.args(args);

let mut docker = engine.subcommand("run");
Expand Down
6 changes: 4 additions & 2 deletions src/docker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub use image::{Architecture, Image, ImagePlatform, Os as ContainerOs, PossibleI
use std::process::ExitStatus;

use crate::errors::*;
use crate::extensions::SafeCommand;
use crate::shell::MessageInfo;

#[derive(Debug)]
Expand Down Expand Up @@ -44,6 +45,7 @@ pub fn image_name(target: &str, sub: Option<&str>, repository: &str, tag: &str)
pub fn run(
options: DockerOptions,
paths: DockerPaths,
command: SafeCommand,
args: &[String],
subcommand: Option<crate::Subcommand>,
msg_info: &mut MessageInfo,
Expand All @@ -55,9 +57,9 @@ pub fn run(
);
}
if options.is_remote() {
remote::run(options, paths, args, subcommand, msg_info)
remote::run(options, paths, command, args, subcommand, msg_info)
.wrap_err("could not complete remote run")
} else {
local::run(options, paths, args, msg_info)
local::run(options, paths, command, args, msg_info)
}
}
59 changes: 32 additions & 27 deletions src/docker/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use super::engine::Engine;
use super::shared::*;
use crate::config::bool_from_envvar;
use crate::errors::Result;
use crate::extensions::CommandExt;
use crate::extensions::{CommandExt, SafeCommand};
use crate::file::{self, PathExt, ToUtf8};
use crate::rustc::{self, QualifiedToolchain, VersionMetaExt};
use crate::shell::{MessageInfo, Stream};
use crate::temp;
use crate::TargetTriple;
use crate::{temp, CargoVariant};

// prevent further commands from running if we handled
// a signal earlier, and the volume is exited.
Expand Down Expand Up @@ -667,6 +667,7 @@ impl QualifiedToolchain {
pub(crate) fn run(
options: DockerOptions,
paths: DockerPaths,
mut cmd: SafeCommand,
args: &[String],
subcommand: Option<crate::Subcommand>,
msg_info: &mut MessageInfo,
Expand Down Expand Up @@ -895,34 +896,38 @@ pub(crate) fn run(
}
}

// `clean` doesn't handle symlinks: it will just unlink the target
// directory, so we should just substitute it our target directory
// for it. we'll still have the same end behavior
let mut final_args = vec![];
let mut iter = args.iter().cloned();
let mut has_target_dir = false;
while let Some(arg) = iter.next() {
if arg == "--target-dir" {
has_target_dir = true;
final_args.push(arg);
if iter.next().is_some() {
final_args.push(target_dir.clone());
}
} else if arg.starts_with("--target-dir=") {
has_target_dir = true;
if arg.split_once('=').is_some() {
final_args.push(format!("--target-dir={target_dir}"));
if options.cargo_variant != CargoVariant::None {
// `clean` doesn't handle symlinks: it will just unlink the target
// directory, so we should just substitute it our target directory
// for it. we'll still have the same end behavior
let mut final_args = vec![];
let mut iter = args.iter().cloned();
let mut has_target_dir = false;
while let Some(arg) = iter.next() {
if arg == "--target-dir" {
has_target_dir = true;
final_args.push(arg);
if iter.next().is_some() {
final_args.push(target_dir.clone());
}
} else if arg.starts_with("--target-dir=") {
has_target_dir = true;
if arg.split_once('=').is_some() {
final_args.push(format!("--target-dir={target_dir}"));
}
} else {
final_args.push(arg);
}
} else {
final_args.push(arg);
}
if !has_target_dir && subcommand.map_or(true, |s| s.needs_target_in_command()) {
final_args.push("--target-dir".to_owned());
final_args.push(target_dir.clone());
}

cmd.args(final_args);
} else {
cmd.args(args);
}
if !has_target_dir && subcommand.map_or(true, |s| s.needs_target_in_command()) {
final_args.push("--target-dir".to_owned());
final_args.push(target_dir.clone());
}
let mut cmd = options.cargo_variant.safe_command();
cmd.args(final_args);

// 5. create symlinks for copied data
let mut symlink = vec!["set -e pipefail".to_owned()];
Expand Down
2 changes: 1 addition & 1 deletion src/docker/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,7 @@ pub fn get_image_name(config: &Config, target: &Target, uses_zig: bool) -> Resul
.image_name(CROSS_IMAGE, version))
}

pub(crate) fn get_image(config: &Config, target: &Target, uses_zig: bool) -> Result<PossibleImage> {
pub fn get_image(config: &Config, target: &Target, uses_zig: bool) -> Result<PossibleImage> {
if let Some(image) = config.image(target)? {
return Ok(image);
}
Expand Down
19 changes: 14 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ use self::errors::Context;
use self::shell::{MessageInfo, Verbosity};

pub use self::errors::{install_panic_hook, install_termination_hook, Result};
pub use self::extensions::{CommandExt, OutputExt};
pub use self::extensions::{CommandExt, OutputExt, SafeCommand};
pub use self::file::{pretty_path, ToUtf8};
pub use self::rustc::{TargetList, VersionMetaExt};

Expand Down Expand Up @@ -447,6 +447,7 @@ pub enum CargoVariant {
Cargo,
Xargo,
Zig,
None,
}

impl CargoVariant {
Expand All @@ -464,6 +465,7 @@ impl CargoVariant {
CargoVariant::Cargo => "cargo",
CargoVariant::Xargo => "xargo",
CargoVariant::Zig => "cargo-zigbuild",
CargoVariant::None => "",
}
}

Expand Down Expand Up @@ -615,9 +617,16 @@ pub fn run(
&options,
msg_info,
)?;

let status = docker::run(options, paths, &filtered_args, args.subcommand, msg_info)
.wrap_err("could not run container")?;
let cmd = options.cargo_variant.safe_command();
let status = docker::run(
options,
paths,
cmd,
&filtered_args,
args.subcommand,
msg_info,
)
.wrap_err("could not run container")?;
let needs_host = args.subcommand.map_or(false, |sc| sc.needs_host(is_remote));
if !status.success() {
warn_on_failure(&target, &toolchain, msg_info)?;
Expand Down Expand Up @@ -864,7 +873,7 @@ macro_rules! commit_info {
///
/// The values from `CROSS_CONFIG` or `Cross.toml` are concatenated with the package
/// metadata in `Cargo.toml`, with `Cross.toml` having the highest priority.
fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<Option<CrossToml>> {
pub fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<Option<CrossToml>> {
let root = &metadata.workspace_root;
let cross_config_path = match env::var("CROSS_CONFIG") {
Ok(var) => PathBuf::from(var),
Expand Down

0 comments on commit 9623db9

Please sign in to comment.