-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: refactor script and add
perl
as an interpreter (#1229)
- Loading branch information
Showing
8 changed files
with
491 additions
and
384 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use std::path::PathBuf; | ||
|
||
use rattler_conda_types::Platform; | ||
use rattler_shell::shell; | ||
|
||
use crate::script::{interpreter::DEBUG_HELP, run_process_with_replacements, ExecutionArgs}; | ||
|
||
use super::{find_interpreter, Interpreter}; | ||
|
||
const BASH_PREAMBLE: &str = r#"#!/bin/bash | ||
## Start of bash preamble | ||
if [ -z ${CONDA_BUILD+x} ]; then | ||
source ((script_path)) | ||
fi | ||
# enable debug mode for the rest of the script | ||
set -x | ||
## End of preamble | ||
"#; | ||
|
||
pub(crate) struct BashInterpreter; | ||
|
||
impl Interpreter for BashInterpreter { | ||
async fn run(&self, args: ExecutionArgs) -> Result<(), std::io::Error> { | ||
let script = self.get_script(&args, shell::Bash).unwrap(); | ||
|
||
let build_env_path = args.work_dir.join("build_env.sh"); | ||
let build_script_path = args.work_dir.join("conda_build.sh"); | ||
|
||
tokio::fs::write(&build_env_path, script).await?; | ||
|
||
let preamble = BASH_PREAMBLE.replace("((script_path))", &build_env_path.to_string_lossy()); | ||
let script = format!("{}\n{}", preamble, args.script.script()); | ||
tokio::fs::write(&build_script_path, script).await?; | ||
|
||
let build_script_path_str = build_script_path.to_string_lossy().to_string(); | ||
let cmd_args = ["bash", "-e", &build_script_path_str]; | ||
|
||
let output = run_process_with_replacements( | ||
&cmd_args, | ||
&args.work_dir, | ||
&args.replacements("$((var))"), | ||
) | ||
.await?; | ||
|
||
if !output.status.success() { | ||
let status_code = output.status.code().unwrap_or(1); | ||
tracing::error!("Script failed with status {}", status_code); | ||
tracing::error!("Work directory: '{}'", args.work_dir.display()); | ||
tracing::error!("{}", DEBUG_HELP); | ||
return Err(std::io::Error::new( | ||
std::io::ErrorKind::Other, | ||
"Script failed".to_string(), | ||
)); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn find_interpreter( | ||
&self, | ||
build_prefix: Option<&PathBuf>, | ||
platform: &Platform, | ||
) -> Result<Option<PathBuf>, which::Error> { | ||
find_interpreter("bash", build_prefix, platform) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use std::path::PathBuf; | ||
|
||
use rattler_conda_types::Platform; | ||
use rattler_shell::shell; | ||
|
||
use crate::script::{interpreter::DEBUG_HELP, run_process_with_replacements, ExecutionArgs}; | ||
|
||
use super::{find_interpreter, Interpreter}; | ||
|
||
const CMDEXE_PREAMBLE: &str = r#" | ||
@chcp 65001 > nul | ||
@echo on | ||
IF "%CONDA_BUILD%" == "" ( | ||
@rem special behavior from conda-build for Windows | ||
call ((script_path)) | ||
) | ||
@rem re-enable echo because the activation scripts might have messed with it | ||
@echo on | ||
"#; | ||
|
||
pub(crate) struct CmdExeInterpreter; | ||
|
||
impl Interpreter for CmdExeInterpreter { | ||
async fn run(&self, args: ExecutionArgs) -> Result<(), std::io::Error> { | ||
let script = self.get_script(&args, shell::CmdExe).unwrap(); | ||
|
||
let build_env_path = args.work_dir.join("build_env.bat"); | ||
let build_script_path = args.work_dir.join("conda_build.bat"); | ||
|
||
tokio::fs::write(&build_env_path, script).await?; | ||
|
||
let build_script = format!( | ||
"{}\n{}", | ||
CMDEXE_PREAMBLE.replace("((script_path))", &build_env_path.to_string_lossy()), | ||
args.script.script() | ||
); | ||
tokio::fs::write( | ||
&build_script_path, | ||
&build_script.replace('\n', "\r\n").as_bytes(), | ||
) | ||
.await?; | ||
|
||
let build_script_path_str = build_script_path.to_string_lossy().to_string(); | ||
let cmd_args = ["cmd.exe", "/d", "/c", &build_script_path_str]; | ||
|
||
let output = run_process_with_replacements( | ||
&cmd_args, | ||
&args.work_dir, | ||
&args.replacements("%((var))%"), | ||
) | ||
.await?; | ||
|
||
if !output.status.success() { | ||
let status_code = output.status.code().unwrap_or(1); | ||
tracing::error!("Script failed with status {}", status_code); | ||
tracing::error!("Work directory: '{}'", args.work_dir.display()); | ||
tracing::error!("{}", DEBUG_HELP); | ||
return Err(std::io::Error::new( | ||
std::io::ErrorKind::Other, | ||
"Script failed".to_string(), | ||
)); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn find_interpreter( | ||
&self, | ||
build_prefix: Option<&PathBuf>, | ||
platform: &Platform, | ||
) -> Result<Option<PathBuf>, which::Error> { | ||
// check if COMSPEC is set to cmd.exe | ||
if let Ok(comspec) = std::env::var("COMSPEC") { | ||
if comspec.to_lowercase().contains("cmd.exe") { | ||
return Ok(Some(PathBuf::from(comspec))); | ||
} | ||
} | ||
|
||
// check if cmd.exe is in PATH | ||
find_interpreter("cmd", build_prefix, platform) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
mod bash; | ||
mod cmd_exe; | ||
mod nushell; | ||
mod perl; | ||
mod python; | ||
|
||
use std::path::PathBuf; | ||
|
||
pub(crate) use bash::BashInterpreter; | ||
pub(crate) use cmd_exe::CmdExeInterpreter; | ||
pub(crate) use nushell::NuShellInterpreter; | ||
pub(crate) use perl::PerlInterpreter; | ||
pub(crate) use python::PythonInterpreter; | ||
|
||
use rattler_conda_types::Platform; | ||
use rattler_shell::{ | ||
activation::{prefix_path_entries, ActivationError, ActivationVariables, Activator}, | ||
shell::{self, Shell}, | ||
}; | ||
|
||
use super::ExecutionArgs; | ||
|
||
const DEBUG_HELP : &str = "To debug the build, run it manually in the work directory (execute the `./conda_build.sh` or `conda_build.bat` script)"; | ||
|
||
fn find_interpreter( | ||
name: &str, | ||
build_prefix: Option<&PathBuf>, | ||
platform: &Platform, | ||
) -> Result<Option<PathBuf>, which::Error> { | ||
let exe_name = format!("{}{}", name, std::env::consts::EXE_SUFFIX); | ||
|
||
let path = std::env::var("PATH").unwrap_or_default(); | ||
if let Some(build_prefix) = build_prefix { | ||
let mut prepend_path = prefix_path_entries(build_prefix, platform) | ||
.into_iter() | ||
.collect::<Vec<_>>(); | ||
prepend_path.extend(std::env::split_paths(&path)); | ||
return Ok( | ||
which::which_in_global(exe_name, std::env::join_paths(prepend_path).ok())?.next(), | ||
); | ||
} | ||
|
||
Ok(which::which_in_global(exe_name, Some(path))?.next()) | ||
} | ||
|
||
pub trait Interpreter { | ||
fn get_script<T: Shell + Copy + 'static>( | ||
&self, | ||
args: &ExecutionArgs, | ||
shell_type: T, | ||
) -> Result<String, ActivationError> { | ||
let mut shell_script = shell::ShellScript::new(shell_type, Platform::current()); | ||
for (k, v) in args.env_vars.iter() { | ||
shell_script.set_env_var(k, v)?; | ||
} | ||
let host_prefix_activator = | ||
Activator::from_path(&args.run_prefix, shell_type, args.execution_platform)?; | ||
|
||
let current_path = std::env::var(shell_type.path_var(&args.execution_platform)) | ||
.ok() | ||
.map(|p| std::env::split_paths(&p).collect::<Vec<_>>()); | ||
let conda_prefix = std::env::var("CONDA_PREFIX").ok().map(|p| p.into()); | ||
|
||
let activation_vars = ActivationVariables { | ||
conda_prefix, | ||
path: current_path, | ||
path_modification_behavior: Default::default(), | ||
}; | ||
|
||
let host_activation = host_prefix_activator.activation(activation_vars)?; | ||
|
||
if let Some(build_prefix) = &args.build_prefix { | ||
let build_prefix_activator = | ||
Activator::from_path(build_prefix, shell_type, args.execution_platform)?; | ||
|
||
let activation_vars = ActivationVariables { | ||
conda_prefix: None, | ||
path: Some(host_activation.path.clone()), | ||
path_modification_behavior: Default::default(), | ||
}; | ||
|
||
let build_activation = build_prefix_activator.activation(activation_vars)?; | ||
shell_script.append_script(&host_activation.script); | ||
shell_script.append_script(&build_activation.script); | ||
} else { | ||
shell_script.append_script(&host_activation.script); | ||
} | ||
|
||
Ok(shell_script.contents()?) | ||
} | ||
|
||
async fn run(&self, args: ExecutionArgs) -> Result<(), std::io::Error>; | ||
|
||
#[allow(dead_code)] | ||
async fn find_interpreter( | ||
&self, | ||
build_prefix: Option<&PathBuf>, | ||
platform: &Platform, | ||
) -> Result<Option<PathBuf>, which::Error>; | ||
} |
Oops, something went wrong.