// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. // rustpkg - a package manager and build system for Rust #[crate_id = "rustpkg#0.10-pre"]; #[license = "MIT/ASL2"]; #[crate_type = "dylib"]; #[feature(globs, managed_boxes)]; extern mod extra; extern mod rustc; extern mod syntax; use std::{os, run, str, task}; use std::io::process; use std::io; use std::io::fs; pub use std::path::Path; use extra::workcache; use rustc::driver::{driver, session}; use rustc::metadata::creader::Loader; use rustc::metadata::filesearch; use rustc::metadata::filesearch::rust_path; use rustc::util::sha2; use syntax::{ast, diagnostic}; use syntax::crateid::CrateId; use messages::{error, warn, note}; use parse_args::{ParseResult, parse_args}; use path_util::{build_pkg_id_in_workspace, built_test_in_workspace}; use path_util::in_rust_path; use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace, dir_has_crate_file}; use source_control::{CheckedOutSources, is_git_dir, make_read_only}; use workspace::{each_pkg_parent_workspace, pkg_parent_workspaces, cwd_to_workspace}; use workspace::determine_destination; use context::{BuildContext, Trans, Nothing, Pretty, Analysis, LLVMAssemble, LLVMCompileBitcode}; use context::{Command, BuildCmd, CleanCmd, DoCmd, HelpCmd, InfoCmd, InstallCmd, ListCmd, PreferCmd, TestCmd, InitCmd, UninstallCmd, UnpreferCmd}; use package_source::PkgSrc; use target::{WhatToBuild, Everything, is_lib, is_main, is_test, is_bench}; use target::{Main, Tests, MaybeCustom, Inferred, JustOne}; use workcache_support::digest_only_date; use exit_codes::{COPY_FAILED_CODE}; pub mod api; mod conditions; pub mod context; mod crate; pub mod exit_codes; mod installed_packages; mod messages; pub mod package_source; mod parse_args; mod path_util; mod source_control; mod target; #[cfg(not(windows), test)] // FIXME test failure on windows: #10471 mod tests; mod util; pub mod version; pub mod workcache_support; mod workspace; pub mod usage; /// A PkgScript represents user-supplied custom logic for /// special build hooks. This only exists for packages with /// an explicit package script. struct PkgScript<'a> { /// Uniquely identifies this package id: &'a CrateId, /// File path for the package script input: Path, /// The session to use *only* for compiling the custom /// build script sess: session::Session, /// The config for compiling the custom build script cfg: ast::CrateConfig, /// The crate and ast_map for the custom build script crate_and_map: Option<(ast::Crate, syntax::ast_map::Map)>, /// Directory in which to store build output build_dir: Path } impl<'a> PkgScript<'a> { /// Given the path name for a package script /// and a package ID, parse the package script into /// a PkgScript that we can then execute fn parse<'a>(sysroot: Path, script: Path, workspace: &Path, id: &'a CrateId) -> PkgScript<'a> { // Get the executable name that was invoked let binary = os::args()[0]; // Build the rustc session data structures to pass // to the compiler debug!("pkgscript parse: {}", sysroot.display()); let options = @session::Options { binary: binary, maybe_sysroot: Some(@sysroot), outputs: ~[session::OutputExecutable], .. (*session::basic_options()).clone() }; let input = driver::FileInput(script.clone()); let sess = driver::build_session(options, @diagnostic::DefaultEmitter as @diagnostic::Emitter); let cfg = driver::build_configuration(sess); let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input); let loader = &mut Loader::new(sess); let crate_and_map = driver::phase_2_configure_and_expand(sess, cfg.clone(), loader, crate); let work_dir = build_pkg_id_in_workspace(id, workspace); debug!("Returning package script with id {}", id.to_str()); PkgScript { id: id, input: script, sess: sess, cfg: cfg, crate_and_map: Some(crate_and_map), build_dir: work_dir } } fn build_custom(&mut self, exec: &mut workcache::Exec) -> ~str { let sess = self.sess; debug!("Working directory = {}", self.build_dir.display()); // Collect together any user-defined commands in the package script let (crate, ast_map) = self.crate_and_map.take_unwrap(); let crate = util::ready_crate(sess, crate); debug!("Building output filenames with script name {}", driver::source_name(&driver::FileInput(self.input.clone()))); let exe = self.build_dir.join("pkg" + util::exe_suffix()); util::compile_crate_from_input(&self.input, exec, Nothing, &self.build_dir, sess, crate, ast_map, Main); // Discover the output // FIXME (#9639): This needs to handle non-utf8 paths // Discover the output exec.discover_output("binary", exe.as_str().unwrap().to_owned(), digest_only_date(&exe)); exe.as_str().unwrap().to_owned() } /// Run the contents of this package script, where /// is the command to pass to it (e.g., "build", "clean", "install") /// Returns a pair of an exit code and list of configs (obtained by /// calling the package script's configs() function if it exists, or /// None if `exe` could not be started. fn run_custom(exe: &Path, sysroot: &Path) -> Option<(~[~str], process::ProcessExit)> { debug!("Running program: {} {} {}", exe.as_str().unwrap().to_owned(), sysroot.display(), "install"); // FIXME #7401 should support commands besides `install` // FIXME (#9639): This needs to handle non-utf8 paths let opt_status = run::process_status(exe.as_str().unwrap(), [sysroot.as_str().unwrap().to_owned(), ~"install"]); match opt_status { Some(status) => { if !status.success() { debug!("run_custom: first pkg command failed with {:?}", status); Some((~[], status)) } else { debug!("Running program (configs): {} {} {}", exe.display(), sysroot.display(), "configs"); // FIXME (#9639): This needs to handle non-utf8 paths let opt_output = run::process_output(exe.as_str().unwrap(), [sysroot.as_str().unwrap().to_owned(), ~"configs"]); match opt_output { Some(output) => { debug!("run_custom: second pkg command did {:?}", output.status); // Run the configs() function to get the configs let cfgs = str::from_utf8(output.output).unwrap().words() .map(|w| w.to_owned()).collect(); Some((cfgs, output.status)) }, None => { debug!("run_custom: second pkg command failed to start"); Some((~[], status)) } } } }, None => { debug!("run_custom: first pkg command failed to start"); None } } } } pub trait CtxMethods { fn run(&self, cmd: Command, args: ~[~str]); fn do_cmd(&self, _cmd: &str, _pkgname: &str); /// Returns a pair of the selected package ID, and the destination workspace fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(CrateId, Path)>; /// Returns the destination workspace fn build(&self, pkg_src: &mut PkgSrc, what: &WhatToBuild); fn clean(&self, workspace: &Path, id: &CrateId); fn info(&self); /// Returns a pair. First component is a list of installed paths, /// second is a list of declared and discovered inputs fn install(&self, src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]); /// Returns a list of installed files fn install_no_build(&self, build_workspace: &Path, build_inputs: &[Path], target_workspace: &Path, id: &CrateId) -> ~[~str]; fn prefer(&self, _id: &str, _vers: Option<~str>); fn test(&self, id: &CrateId, workspace: &Path); fn uninstall(&self, _id: &str, _vers: Option<~str>); fn unprefer(&self, _id: &str, _vers: Option<~str>); fn init(&self); } impl CtxMethods for BuildContext { fn build_args(&self, args: ~[~str], what: &WhatToBuild) -> Option<(CrateId, Path)> { let cwd = os::getcwd(); if args.len() < 1 { match cwd_to_workspace() { None if dir_has_crate_file(&cwd) => { // FIXME (#9639): This needs to handle non-utf8 paths let crateid = from_str(cwd.filename_str().unwrap()).expect("valid crate id"); let mut pkg_src = PkgSrc::new(cwd, default_workspace(), true, crateid); self.build(&mut pkg_src, what); match pkg_src { PkgSrc { destination_workspace: ws, id: id, .. } => { Some((id, ws)) } } } None => { usage::build(); None } Some((ws, crateid)) => { let mut pkg_src = PkgSrc::new(ws.clone(), ws, false, crateid); self.build(&mut pkg_src, what); match pkg_src { PkgSrc { destination_workspace: ws, id: id, .. } => { Some((id, ws)) } } } } } else { // The package id is presumed to be the first command-line // argument let crateid = from_str(args[0]).expect("valid crate id"); let mut dest_ws = default_workspace(); each_pkg_parent_workspace(&self.context, &crateid, |workspace| { debug!("found pkg {} in workspace {}, trying to build", crateid.to_str(), workspace.display()); dest_ws = determine_destination(os::getcwd(), self.context.use_rust_path_hack, workspace); let mut pkg_src = PkgSrc::new(workspace.clone(), dest_ws.clone(), false, crateid.clone()); self.build(&mut pkg_src, what); true }); // n.b. If this builds multiple packages, it only returns the workspace for // the last one. The whole building-multiple-packages-with-the-same-ID is weird // anyway and there are no tests for it, so maybe take it out Some((crateid, dest_ws)) } } fn run(&self, cmd: Command, args: ~[~str]) { let cwd = os::getcwd(); match cmd { BuildCmd => { self.build_args(args, &WhatToBuild::new(MaybeCustom, Everything)); } CleanCmd => { if args.len() < 1 { match cwd_to_workspace() { None => { usage::clean(); return } // tjc: Maybe clean should clean all the packages in the // current workspace, though? Some((ws, crateid)) => self.clean(&ws, &crateid) } } else { // The package id is presumed to be the first command-line // argument let crateid = from_str(args[0]).expect("valid crate id"); self.clean(&cwd, &crateid); // tjc: should use workspace, not cwd } } DoCmd => { if args.len() < 2 { return usage::do_cmd(); } self.do_cmd(args[0].clone(), args[1].clone()); } HelpCmd => { if args.len() != 1 { return usage::general(); } match FromStr::from_str(args[0]) { Some(help_cmd) => usage::usage_for_command(help_cmd), None => { usage::general(); error(format!("{} is not a recognized command", args[0])) } } } InfoCmd => { self.info(); } InstallCmd => { if args.len() < 1 { match cwd_to_workspace() { None if dir_has_crate_file(&cwd) => { // FIXME (#9639): This needs to handle non-utf8 paths let inferred_crateid = from_str(cwd.filename_str().unwrap()).expect("valid crate id"); self.install(PkgSrc::new(cwd, default_workspace(), true, inferred_crateid), &WhatToBuild::new(MaybeCustom, Everything)); } None => { usage::install(); return; } Some((ws, crateid)) => { let pkg_src = PkgSrc::new(ws.clone(), ws.clone(), false, crateid); self.install(pkg_src, &WhatToBuild::new(MaybeCustom, Everything)); } } } else { // The package id is presumed to be the first command-line // argument let crateid = from_str(args[0]).expect("valid crate id"); let workspaces = pkg_parent_workspaces(&self.context, &crateid); debug!("package ID = {}, found it in {:?} workspaces", crateid.to_str(), workspaces.len()); if workspaces.is_empty() { let d = default_workspace(); let src = PkgSrc::new(d.clone(), d, false, crateid.clone()); self.install(src, &WhatToBuild::new(MaybeCustom, Everything)); } else { for workspace in workspaces.iter() { let dest = determine_destination(os::getcwd(), self.context.use_rust_path_hack, workspace); let src = PkgSrc::new(workspace.clone(), dest, self.context.use_rust_path_hack, crateid.clone()); self.install(src, &WhatToBuild::new(MaybeCustom, Everything)); }; } } } ListCmd => { println!("Installed packages:"); installed_packages::list_installed_packages(|pkg_id| { println!("{}", pkg_id.path); true }); } PreferCmd => { if args.len() < 1 { return usage::prefer(); } self.prefer(args[0], None); } TestCmd => { // Build the test executable let maybe_id_and_workspace = self.build_args(args, &WhatToBuild::new(MaybeCustom, Tests)); match maybe_id_and_workspace { Some((pkg_id, workspace)) => { // Assuming it's built, run the tests self.test(&pkg_id, &workspace); } None => { error("Testing failed because building the specified package failed."); } } } InitCmd => { if args.len() != 0 { return usage::init(); } else { self.init(); } } UninstallCmd => { if args.len() < 1 { return usage::uninstall(); } let crateid = from_str(args[0]).expect("valid crate id"); if !installed_packages::package_is_installed(&crateid) { warn(format!("Package {} doesn't seem to be installed! \ Doing nothing.", args[0])); return; } else { let rp = rust_path(); assert!(!rp.is_empty()); each_pkg_parent_workspace(&self.context, &crateid, |workspace| { path_util::uninstall_package_from(workspace, &crateid); note(format!("Uninstalled package {} (was installed in {})", crateid.to_str(), workspace.display())); true }); } } UnpreferCmd => { if args.len() < 1 { return usage::unprefer(); } self.unprefer(args[0], None); } } } fn do_cmd(&self, _cmd: &str, _pkgname: &str) { // stub fail!("`do` not yet implemented"); } fn build(&self, pkg_src: &mut PkgSrc, what_to_build: &WhatToBuild) { use conditions::git_checkout_failed::cond; let workspace = pkg_src.source_workspace.clone(); let crateid = pkg_src.id.clone(); let path = crateid.path.as_slice(); debug!("build: workspace = {} (in Rust path? {:?} is git dir? {:?} \ crateid = {} pkgsrc start_dir = {}", workspace.display(), in_rust_path(&workspace), is_git_dir(&workspace.join(path)), crateid.to_str(), pkg_src.start_dir.display()); debug!("build: what to build = {:?}", what_to_build); // If workspace isn't in the RUST_PATH, and it's a git repo, // then clone it into the first entry in RUST_PATH, and repeat if !in_rust_path(&workspace) && is_git_dir(&workspace.join(path)) { let mut out_dir = default_workspace().join("src"); out_dir.push(path); let git_result = source_control::safe_git_clone(&workspace.join(path), &crateid.version, &out_dir); match git_result { CheckedOutSources => make_read_only(&out_dir), _ => cond.raise((path.to_owned(), out_dir.clone())) }; let default_ws = default_workspace(); debug!("Calling build recursively with {:?} and {:?}", default_ws.display(), crateid.to_str()); return self.build(&mut PkgSrc::new(default_ws.clone(), default_ws, false, crateid.clone()), what_to_build); } // Is there custom build logic? If so, use it let mut custom = false; debug!("Package source directory = {}", pkg_src.to_str()); let opt = pkg_src.package_script_option(); debug!("Calling pkg_script_option on {:?}", opt); let cfgs = match (pkg_src.package_script_option(), what_to_build.build_type) { (Some(package_script_path), MaybeCustom) => { let sysroot = self.sysroot_to_use(); // Build the package script if needed let script_build = format!("build_package_script({})", package_script_path.display()); let pkg_exe = self.workcache_context.with_prep(script_build, |prep| { let subsysroot = sysroot.clone(); let psp = package_script_path.clone(); let ws = workspace.clone(); let pid = crateid.clone(); prep.exec(proc(exec) { let mut pscript = PkgScript::parse(subsysroot.clone(), psp.clone(), &ws, &pid); pscript.build_custom(exec) }) }); // We always *run* the package script match PkgScript::run_custom(&Path::new(pkg_exe), &sysroot) { Some((cfgs, hook_result)) => { debug!("Command return code = {:?}", hook_result); if !hook_result.success() { fail!("Error running custom build command") } custom = true; // otherwise, the package script succeeded cfgs }, None => { fail!("Error starting custom build command") } } } (Some(_), Inferred) => { debug!("There is a package script, but we're ignoring it"); ~[] } (None, _) => { debug!("No package script, continuing"); ~[] } } + self.context.cfgs; // If there was a package script, it should have finished // the build already. Otherwise... if !custom { match what_to_build.sources { // Find crates inside the workspace Everything => pkg_src.find_crates(), // Find only tests Tests => pkg_src.find_crates_with_filter(|s| { is_test(&Path::new(s)) }), // Don't infer any crates -- just build the one that was requested JustOne(ref p) => { // We expect that p is relative to the package source's start directory, // so check that assumption debug!("JustOne: p = {}", p.display()); assert!(pkg_src.start_dir.join(p).exists()); if is_lib(p) { PkgSrc::push_crate(&mut pkg_src.libs, 0, p); } else if is_main(p) { PkgSrc::push_crate(&mut pkg_src.mains, 0, p); } else if is_test(p) { PkgSrc::push_crate(&mut pkg_src.tests, 0, p); } else if is_bench(p) { PkgSrc::push_crate(&mut pkg_src.benchs, 0, p); } else { warn(format!("Not building any crates for dependency {}", p.display())); return; } } } // Build it! pkg_src.build(self, cfgs, []); } } fn clean(&self, workspace: &Path, id: &CrateId) { // Could also support a custom build hook in the pkg // script for cleaning files rustpkg doesn't know about. // Do something reasonable for now let dir = build_pkg_id_in_workspace(id, workspace); note(format!("Cleaning package {} (removing directory {})", id.to_str(), dir.display())); if dir.exists() { fs::rmdir_recursive(&dir); note(format!("Removed directory {}", dir.display())); } note(format!("Cleaned package {}", id.to_str())); } fn info(&self) { // stub fail!("info not yet implemented"); } fn install(&self, mut pkg_src: PkgSrc, what: &WhatToBuild) -> (~[Path], ~[(~str, ~str)]) { let id = pkg_src.id.clone(); let mut installed_files = ~[]; let mut inputs = ~[]; let mut build_inputs = ~[]; debug!("Installing package source: {}", pkg_src.to_str()); // workcache only knows about *crates*. Building a package // just means inferring all the crates in it, then building each one. self.build(&mut pkg_src, what); debug!("Done building package source {}", pkg_src.to_str()); let to_do = ~[pkg_src.libs.clone(), pkg_src.mains.clone(), pkg_src.tests.clone(), pkg_src.benchs.clone()]; debug!("In declare inputs for {}", id.to_str()); for cs in to_do.iter() { for c in cs.iter() { let path = pkg_src.start_dir.join(&c.file); debug!("Recording input: {}", path.display()); // FIXME (#9639): This needs to handle non-utf8 paths inputs.push((~"file", path.as_str().unwrap().to_owned())); build_inputs.push(path); } } let result = self.install_no_build(pkg_src.build_workspace(), build_inputs, &pkg_src.destination_workspace, &id).map(|s| Path::new(s.as_slice())); installed_files = installed_files + result; note(format!("Installed package {} to {}", id.to_str(), pkg_src.destination_workspace.display())); (installed_files, inputs) } // again, working around lack of Encodable for Path fn install_no_build(&self, build_workspace: &Path, build_inputs: &[Path], target_workspace: &Path, id: &CrateId) -> ~[~str] { debug!("install_no_build: assuming {} comes from {} with target {}", id.to_str(), build_workspace.display(), target_workspace.display()); // Now copy stuff into the install dirs let maybe_executable = built_executable_in_workspace(id, build_workspace); let maybe_library = built_library_in_workspace(id, build_workspace); let target_exec = target_executable_in_workspace(id, target_workspace); let target_lib = maybe_library.as_ref() .map(|_| target_library_in_workspace(id, target_workspace)); debug!("target_exec = {} target_lib = {:?} \ maybe_executable = {:?} maybe_library = {:?}", target_exec.display(), target_lib, maybe_executable, maybe_library); let install_tag = format!("install({}-{})", id.path, id.version_or_default()); self.workcache_context.with_prep(install_tag, |prep| { for ee in maybe_executable.iter() { // FIXME (#9639): This needs to handle non-utf8 paths prep.declare_input("binary", ee.as_str().unwrap(), workcache_support::digest_only_date(ee)); } for ll in maybe_library.iter() { // FIXME (#9639): This needs to handle non-utf8 paths prep.declare_input("binary", ll.as_str().unwrap(), workcache_support::digest_only_date(ll)); } let subex = maybe_executable.clone(); let sublib = maybe_library.clone(); let sub_target_ex = target_exec.clone(); let sub_target_lib = target_lib.clone(); let sub_build_inputs = build_inputs.to_owned(); prep.exec(proc(exe_thing) { let mut outputs = ~[]; // Declare all the *inputs* to the declared input too, as inputs for executable in subex.iter() { exe_thing.discover_input("binary", executable.as_str().unwrap().to_owned(), workcache_support::digest_only_date(executable)); } for library in sublib.iter() { exe_thing.discover_input("binary", library.as_str().unwrap().to_owned(), workcache_support::digest_only_date(library)); } for transitive_dependency in sub_build_inputs.iter() { exe_thing.discover_input( "file", transitive_dependency.as_str().unwrap().to_owned(), workcache_support::digest_file_with_date(transitive_dependency)); } for exec in subex.iter() { debug!("Copying: {} -> {}", exec.display(), sub_target_ex.display()); fs::mkdir_recursive(&sub_target_ex.dir_path(), io::UserRWX); fs::copy(exec, &sub_target_ex); // FIXME (#9639): This needs to handle non-utf8 paths exe_thing.discover_output("binary", sub_target_ex.as_str().unwrap(), workcache_support::digest_only_date(&sub_target_ex)); outputs.push(sub_target_ex.as_str().unwrap().to_owned()); } for lib in sublib.iter() { let mut target_lib = sub_target_lib .clone().expect(format!("I built {} but apparently \ didn't install it!", lib.display())); target_lib.set_filename(lib.filename().expect("weird target lib")); fs::mkdir_recursive(&target_lib.dir_path(), io::UserRWX); fs::copy(lib, &target_lib); debug!("3. discovering output {}", target_lib.display()); exe_thing.discover_output("binary", target_lib.as_str().unwrap(), workcache_support::digest_only_date(&target_lib)); outputs.push(target_lib.as_str().unwrap().to_owned()); } outputs }) }) } fn prefer(&self, _id: &str, _vers: Option<~str>) { fail!("prefer not yet implemented"); } fn test(&self, crateid: &CrateId, workspace: &Path) { match built_test_in_workspace(crateid, workspace) { Some(test_exec) => { debug!("test: test_exec = {}", test_exec.display()); // FIXME (#9639): This needs to handle non-utf8 paths let opt_status = run::process_status(test_exec.as_str().unwrap(), [~"--test"]); match opt_status { Some(status) => { if !status.success() { fail!("Some tests failed"); } }, None => fail!("Could not exec `{}`", test_exec.display()) } } None => { error(format!("Internal error: test executable for package ID {} in workspace {} \ wasn't built! Please report this as a bug.", crateid.to_str(), workspace.display())); } } } fn init(&self) { fs::mkdir_recursive(&Path::new("src"), io::UserRWX); fs::mkdir_recursive(&Path::new("bin"), io::UserRWX); fs::mkdir_recursive(&Path::new("lib"), io::UserRWX); fs::mkdir_recursive(&Path::new("build"), io::UserRWX); } fn uninstall(&self, _id: &str, _vers: Option<~str>) { fail!("uninstall not yet implemented"); } fn unprefer(&self, _id: &str, _vers: Option<~str>) { fail!("unprefer not yet implemented"); } } pub fn main() { println!("WARNING: The Rust package manager is experimental and may be unstable"); os::set_exit_status(main_args(os::args())); } pub fn main_args(args: &[~str]) -> int { let (command, args, context, supplied_sysroot) = match parse_args(args) { Ok(ParseResult { command: cmd, args: args, context: ctx, sysroot: sroot}) => (cmd, args, ctx, sroot), Err(error_code) => { debug!("Parsing failed. Returning error code {}", error_code); return error_code } }; debug!("Finished parsing commandline args {:?}", args); debug!(" Using command: {:?}", command); debug!(" Using args {:?}", args); debug!(" Using cflags: {:?}", context.rustc_flags); debug!(" Using rust_path_hack {:b}", context.use_rust_path_hack); debug!(" Using cfgs: {:?}", context.cfgs); debug!(" Using supplied_sysroot: {:?}", supplied_sysroot); let sysroot = match supplied_sysroot { Some(s) => Path::new(s), _ => filesearch::get_or_default_sysroot() }; debug!("Using sysroot: {}", sysroot.display()); let ws = default_workspace(); debug!("Will store workcache in {}", ws.display()); // Wrap the rest in task::try in case of a condition failure in a task let result = do task::try { BuildContext { context: context, sysroot: sysroot.clone(), // Currently, only tests override this workcache_context: api::default_context(sysroot.clone(), default_workspace()).workcache_context }.run(command, args.clone()) }; // FIXME #9262: This is using the same error code for all errors, // and at least one test case succeeds if rustpkg returns COPY_FAILED_CODE, // when actually, it might set the exit code for that even if a different // unhandled condition got raised. if result.is_err() { return COPY_FAILED_CODE; } return 0; }