diff options
Diffstat (limited to 'src/commands/build')
| -rw-r--r-- | src/commands/build/macros.rs | 74 | ||||
| -rw-r--r-- | src/commands/build/mod.rs | 132 |
2 files changed, 206 insertions, 0 deletions
diff --git a/src/commands/build/macros.rs b/src/commands/build/macros.rs new file mode 100644 index 0000000..4281dff --- /dev/null +++ b/src/commands/build/macros.rs @@ -0,0 +1,74 @@ +// This file is part of Chorus <https://github.com/Fuwn/chorus>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +/// "None if empty string, or with preformat" +/// +/// By default, this macro evaluates whether the input `$string` is empty by +/// calling `is_empty`. However, by specifying the type `i32` at the end of the +/// macro invocation, you can tell this macro to evaluate based on `== 0` +/// instead of `is_empty`. +#[macro_export] +macro_rules! niesowp { + ($preformat:literal, $string:expr) => {{ + if $string.is_empty() { + "".to_string() + } else { + format!("{}{}", $preformat, $string) + } + }}; + ($preformat:literal, $number:expr,i32) => {{ + if $number == 0 { + "".to_string() + } else { + format!("{}{}", $preformat, $number) + } + }}; +} + +#[macro_export] +macro_rules! compiler_get_or { + ($compiler:ident, $key:ident, $default:literal) => { + $crate::config::CONFIG.build.$compiler.as_ref().map_or_else( + || $default.into(), + |compiler| { + compiler + .$key + .as_ref() + .map_or_else(|| $default.into(), Clone::clone) + }, + ) + }; +} + +/// A helper to assist with the sanity checks +#[macro_export] +macro_rules! entry_exists { + ($entries:ident, $path:ident, $entry:literal, $context:literal) => { + let entry = $entry.replace("\\", "/"); + + if !$entries.contains(&PathBuf::from_slash( + format!("{}/{}", $path.as_path().display(), entry).to_string(), + )) { + return Err(anyhow::anyhow!( + "{} \"{}\" does not exist", + $context, + format!("{}/{}", $path.as_path().display(), entry) + )); + } + }; +} diff --git a/src/commands/build/mod.rs b/src/commands/build/mod.rs new file mode 100644 index 0000000..4f22303 --- /dev/null +++ b/src/commands/build/mod.rs @@ -0,0 +1,132 @@ +// This file is part of Chorus <https://github.com/Fuwn/chorus>. +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// Copyright (C) 2022-2022 Fuwn <[email protected]> +// SPDX-License-Identifier: GPL-3.0-only + +mod macros; + +use std::path::PathBuf; + +use anyhow::{anyhow, Context}; +use path_slash::PathBufExt; +use walkdir::{DirEntry, WalkDir}; + +use crate::{ + compiler_get_or, + compilers::gnucobol, + config::CONFIG, + create_directory, + entry_exists, + niesowp, +}; + +/// Build an existing Chorus package +pub fn build( + path: &Option<PathBuf>, + output: &Option<PathBuf>, +) -> anyhow::Result<()> { + let our_path: PathBuf = path.clone().unwrap_or_else(|| PathBuf::from(".")); + let entries = WalkDir::new(&our_path) + .into_iter() + .filter_map(Result::ok) + .into_iter() + .map(|e: DirEntry| e.path().to_owned()) + .collect::<Vec<PathBuf>>(); + + // Sanity checks to make sure the target directory is a Chorus package + entry_exists!(entries, our_path, "src", "directory"); + entry_exists!(entries, our_path, "src/main.cbl", "file"); + entry_exists!(entries, our_path, "package.toml", "directory"); + + std::env::set_current_dir(&our_path)?; + + let our_output = output.clone().unwrap_or_else(|| { + PathBuf::from(format!("out/{}", CONFIG.package.name).as_str()) + }); + + if !PathBuf::from("out").exists() { + create_directory!("out"); + } + + if !our_output + .parent() + .ok_or_else(|| { + anyhow!( + "parent directory of output directory \"{}\" does not exist", + our_output.display() + ) + })? + .exists() + { + return Err(anyhow!( + "output directory \"{}\" does not exist", + our_output.display() + )); + } + + dispatch_build( + &compiler_get_or!(gnucobol, format, "free"), + &niesowp!("-", compiler_get_or!(gnucobol, optimization, "")), + &niesowp!("-std=", compiler_get_or!(gnucobol, std, "")), + &our_output, + )?; + + Ok(()) +} + +fn dispatch_build( + format: &str, + optimization: &str, + standard: &str, + our_output: &std::path::Path, +) -> anyhow::Result<()> { + if let Some(mode) = &CONFIG.build.mode { + if mode.eq_ignore_ascii_case("incremental") { + if let Err(why) = gnucobol::compile_executable_object( + "cobc", + &format!("-{} {} {}", format, optimization, standard), + "src/main.cbl", + &format!("{}.o", &our_output.display().to_string()), + ) { + return Err(anyhow!(why.to_string())); + } + if let Err(why) = gnucobol::compile_executable_single( + "cobc", + &format!("-{} {} {}", format, optimization, standard), + &format!("{}.o", &our_output.display().to_string()), + &our_output.display().to_string(), + ) { + return Err(anyhow!(why.to_string())); + } + } else if let Err(why) = gnucobol::compile_executable_single( + "cobc", + &format!("-{} {} {}", format, optimization, standard), + "src/main.cbl", + &our_output.display().to_string(), + ) { + return Err(anyhow!(why.to_string())); + } + } else if let Err(why) = gnucobol::compile_executable_single( + "cobc", + &format!("-{} {} {}", format, optimization, standard), + "src/main.cbl", + &our_output.display().to_string(), + ) { + return Err(anyhow!(why.to_string())); + } + + Ok(()) +} |