From 2c2a9c1c9f56ef2665976cb1cf57bc0f0601504a Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Thu, 29 Feb 2024 18:40:30 -0800 Subject: [PATCH 01/10] Begin to support use of lingo as a library --- src/backends/cmake.rs | 2 +- src/lib.rs | 4 ++++ src/util/mod.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/lib.rs diff --git a/src/backends/cmake.rs b/src/backends/cmake.rs index b71b9c6..3e3d42a 100644 --- a/src/backends/cmake.rs +++ b/src/backends/cmake.rs @@ -2,8 +2,8 @@ use std::fs; use std::process::Command; +use crate::package::App; use crate::util::execute_command_to_build_result; -use crate::App; use crate::backends::{ BatchBackend, BatchBuildResults, BuildCommandOptions, BuildProfile, BuildResult, CommandSpec, diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..654526e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +mod args; +mod backends; +pub mod package; +mod util; diff --git a/src/util/mod.rs b/src/util/mod.rs index 1e4ac4a..8f6987e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -64,7 +64,7 @@ pub fn default_build_clean(out_dir: &Path) -> io::Result<()> { ) } -pub fn find_lfc_exec(args: &crate::BuildArgs) -> Result { +pub fn find_lfc_exec(args: &crate::args::BuildArgs) -> Result { if let Some(lfc) = &args.lfc { if lfc.exists() { return Ok(lfc.clone()); From 7e24f795ef5afc0150b9da066f19a106a9632b05 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Thu, 29 Feb 2024 19:59:58 -0800 Subject: [PATCH 02/10] Make LfcJsonArgs public. --- src/backends/lfc.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backends/lfc.rs b/src/backends/lfc.rs index 27e4747..5a2ead5 100644 --- a/src/backends/lfc.rs +++ b/src/backends/lfc.rs @@ -60,7 +60,7 @@ impl BatchBackend for LFC { /// Formats LFC arguments to JSON. #[derive(Serialize, Clone)] -struct LfcJsonArgs<'a> { +pub struct LfcJsonArgs<'a> { /// Path to the LF source file containing the main reactor. pub src: &'a Path, /// Path to the directory into which build artifacts like diff --git a/src/lib.rs b/src/lib.rs index 654526e..b4f89d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ mod args; -mod backends; +pub mod backends; pub mod package; mod util; From f77354a058161afdc4f7df23efe7819ab4f3135d Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Thu, 29 Feb 2024 22:35:00 -0800 Subject: [PATCH 03/10] Isolate deps that can't wasm --- Cargo.toml | 14 ++++++++++---- src/backends/mod.rs | 9 +++++++-- src/lib.rs | 42 ++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 33 ++++++++++++++++++--------------- src/package/mod.rs | 25 +++++++++++++------------ src/util/mod.rs | 9 +++++++-- 6 files changed, 95 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0585f2e..6c63604 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,15 @@ homepage = "https://lf-lang.org" repository = "https://github.com/lf-lang/lingo" license = "BSD-2-Clause" +[lib] +name = "liblingo" +path = "src/lib.rs" + [[bin]] name = "lingo" publish = true path = "./src/main.rs" +required-features = ["binary"] [dependencies] @@ -20,13 +25,14 @@ os-version = "0.2" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" -which = "5.0" regex = "1.8" lazy_static = "1.4" rayon = "1.7" -toml = {version = "0.8"} +toml = { version = "0.8" } crossbeam = "0.8" -termion = "2.0" -git2 = "0.18" run_script = "0.10" tempfile = "3.0" + +[target.'cfg(binary)'.dependencies] +which = "5.0" +git2 = "0.18" diff --git a/src/backends/mod.rs b/src/backends/mod.rs index ad0bdba..7c24bca 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -8,18 +8,23 @@ use rayon::prelude::*; use crate::args::{BuildSystem, Platform}; use crate::package::App; use crate::util::errors::{AnyError, BuildResult, LingoError}; +use crate::WhichType; pub mod cmake; pub mod lfc; pub mod npm; pub mod pnpm; -pub fn execute_command<'a>(command: &CommandSpec, apps: &[&'a App]) -> BatchBuildResults<'a> { +pub fn execute_command<'a>( + command: &CommandSpec, + apps: &[&'a App], + which: WhichType, +) -> BatchBuildResults<'a> { // Group apps by build system let mut by_build_system = HashMap::>::new(); for &app in apps { by_build_system - .entry(app.build_system()) + .entry(app.build_system(which)) .or_default() .push(app); } diff --git a/src/lib.rs b/src/lib.rs index b4f89d2..0c38b35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,42 @@ -mod args; +use std::path::Display; + +pub mod args; pub mod backends; pub mod package; -mod util; +pub mod util; +#[derive(Debug)] +pub enum WhichError { + /// An executable binary with that name was not found + CannotFindBinaryPath, + /// There was nowhere to search and the provided name wasn't an absolute path + CannotGetCurrentDirAndPathListEmpty, + /// Failed to canonicalize the path found + CannotCanonicalize, +} +#[derive(Debug)] +pub struct GitCloneError(String); + +impl std::fmt::Display for WhichError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WhichError::CannotFindBinaryPath => write!(f, "cannot find binary"), + WhichError::CannotGetCurrentDirAndPathListEmpty => { + write!(f, "cannot get current dir and path list empty") + } + WhichError::CannotCanonicalize => write!(f, "cannot canonicalize"), + } + } +} + +impl std::fmt::Display for GitCloneError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::error::Error for WhichError {} + +impl std::error::Error for GitCloneError {} + +pub type WhichType = fn(binary_name: &str) -> Result; +pub type GitCloneType = fn(url: &str, into: &std::path::Path) -> Result<(), GitCloneError>; diff --git a/src/main.rs b/src/main.rs index 19648bd..59724c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,18 +5,20 @@ use std::{env, io}; use clap::Parser; -use crate::args::InitArgs; -use args::{BuildArgs, Command as ConsoleCommand, CommandLineArgs}; -use package::App; +use git2::Repository; +use liblingo::args::InitArgs; +use liblingo::args::{BuildArgs, Command as ConsoleCommand, CommandLineArgs}; +use liblingo::package::App; -use crate::backends::{BatchBuildResults, BuildCommandOptions, CommandSpec}; -use crate::package::{Config, ConfigFile}; -use crate::util::errors::{BuildResult, LingoError}; +use liblingo::backends::{BatchBuildResults, BuildCommandOptions, CommandSpec}; +use liblingo::package::{Config, ConfigFile}; +use liblingo::util::errors::{BuildResult, LingoError}; -pub mod args; -pub mod backends; -pub mod package; -pub(crate) mod util; +// pub mod args; +// pub mod backends; +// pub mod package; +// pub(crate) mod util; +// pub mod fn main() { // parses command line arguments @@ -24,7 +26,7 @@ fn main() { // Finds Lingo.toml recursively inside the parent directories. // If it exists the returned path is absolute. - let lingo_path = util::find_toml(&env::current_dir().unwrap()); + let lingo_path = liblingo::util::find_toml(&env::current_dir().unwrap()); // tries to read Lingo.toml let mut wrapped_config = lingo_path.as_ref().and_then(|path| { @@ -94,7 +96,7 @@ fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandR let mut res = build(&build_command_args, config); res.map(|app| { let mut command = Command::new(app.executable_path()); - util::run_and_capture(&mut command)?; + liblingo::util::run_and_capture(&mut command)?; Ok(()) }); CommandResult::Batch(res) @@ -109,7 +111,7 @@ fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandR fn do_init(init_config: InitArgs) -> BuildResult { let initial_config = ConfigFile::new_for_init_task(init_config)?; initial_config.write(Path::new("./Lingo.toml"))?; - initial_config.setup_example() + initial_config.setup_example(Repository::clone) } fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { @@ -117,7 +119,8 @@ fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { CommandSpec::Build(BuildCommandOptions { profile: args.build_profile(), compile_target_code: !args.no_compile, - lfc_exec_path: util::find_lfc_exec(args).expect("TODO replace me"), + lfc_exec_path: liblingo::util::find_lfc_exec(args, which::which::<&str>) + .expect("TODO replace me"), max_threads: args.threads, keep_going: args.keep_going, }), @@ -128,7 +131,7 @@ fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { fn run_command(task: CommandSpec, config: &Config, _fail_at_end: bool) -> BatchBuildResults { let apps = config.apps.iter().collect::>(); - backends::execute_command(&task, &apps) + liblingo::backends::execute_command(&task, &apps, which::which) } enum CommandResult<'a> { diff --git a/src/package/mod.rs b/src/package/mod.rs index 70a55f3..4cd6777 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -1,5 +1,6 @@ use crate::args::{BuildSystem, InitArgs, Platform, TargetLanguage}; use crate::util::{analyzer, copy_recursively}; +use crate::{GitCloneType, WhichType}; use serde_derive::{Deserialize, Serialize}; @@ -12,9 +13,9 @@ use std::{env, io}; use crate::args::BuildSystem::{CMake, LFC}; use crate::util::errors::{BuildResult, LingoError}; -use git2::Repository; +// use git2::Repository; use tempfile::tempdir; -use which::which; +// use which::which; fn is_valid_location_for_project(path: &std::path::Path) -> bool { !path.join("src").exists() && !path.join(".git").exists() && !path.join("application").exists() @@ -91,7 +92,7 @@ pub struct App { } impl App { - pub fn build_system(&self) -> BuildSystem { + pub fn build_system(&self, which: WhichType) -> BuildSystem { match self.target { TargetLanguage::C => LFC, TargetLanguage::Cpp => CMake, @@ -233,10 +234,10 @@ impl ConfigFile { Ok(()) } - fn setup_template_repo(&self, url: &str) -> BuildResult { + fn setup_template_repo(&self, url: &str, clone: GitCloneType) -> BuildResult { let dir = tempdir()?; let tmp_path = dir.path(); - Repository::clone(url, tmp_path)?; + clone(url, &tmp_path)?; // Copy the cloned template repo into the project directory copy_recursively(tmp_path, Path::new("."))?; // Remove temporary folder @@ -245,9 +246,9 @@ impl ConfigFile { } // Sets up a LF project with Zephyr as the target platform. - fn setup_zephyr(&self) -> BuildResult { + fn setup_zephyr(&self, clone: GitCloneType) -> BuildResult { let url = "https://github.com/lf-lang/lf-west-template"; - self.setup_template_repo(url)?; + self.setup_template_repo(url, clone)?; remove_file(".gitignore")?; remove_dir_all(Path::new(".git"))?; Ok(()) @@ -255,19 +256,19 @@ impl ConfigFile { // Sets up a LF project with RP2040 MCU as the target platform. // Initializes a repo using the lf-pico-template - fn setup_rp2040(&self) -> BuildResult { + fn setup_rp2040(&self, clone: GitCloneType) -> BuildResult { let url = "https://github.com/lf-lang/lf-pico-template"; // leave git artifacts - self.setup_template_repo(url)?; + self.setup_template_repo(url, clone)?; Ok(()) } - pub fn setup_example(&self) -> BuildResult { + pub fn setup_example(&self, clone: GitCloneType) -> BuildResult { if is_valid_location_for_project(Path::new(".")) { match self.apps[0].platform { Some(Platform::Native) => self.setup_native(), - Some(Platform::Zephyr) => self.setup_zephyr(), - Some(Platform::RP2040) => self.setup_rp2040(), + Some(Platform::Zephyr) => self.setup_zephyr(clone), + Some(Platform::RP2040) => self.setup_rp2040(clone), _ => Ok(()), } } else { diff --git a/src/util/mod.rs b/src/util/mod.rs index 8f6987e..0aeac49 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; -use which::which; +// use which::which; pub mod analyzer; mod command_line; @@ -9,6 +9,8 @@ pub mod errors; pub use command_line::*; +use crate::WhichType; + /// finds toml file recurisvely pub fn find_toml(input_path: &Path) -> Option { let mut path = fs::canonicalize(input_path).ok()?; @@ -64,7 +66,10 @@ pub fn default_build_clean(out_dir: &Path) -> io::Result<()> { ) } -pub fn find_lfc_exec(args: &crate::args::BuildArgs) -> Result { +pub fn find_lfc_exec( + args: &crate::args::BuildArgs, + which: WhichType, +) -> Result { if let Some(lfc) = &args.lfc { if lfc.exists() { return Ok(lfc.clone()); From cec460def8f548a3fc1a7227b3747d6f5aa873bd Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Sat, 2 Mar 2024 01:06:26 -0800 Subject: [PATCH 04/10] Capabilitify read_to_string --- Cargo.toml | 12 +++++++++--- src/backends/mod.rs | 6 +++--- src/lib.rs | 24 ++++++++++++++++++++---- src/main.rs | 37 ++++++++++++++++++++++++++----------- src/package/mod.rs | 37 +++++++++++++++++++++++-------------- src/util/mod.rs | 6 ++---- 6 files changed, 83 insertions(+), 39 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c63604..1e2df43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ publish = true path = "./src/main.rs" required-features = ["binary"] +[features] +default = ["binary"] +binary = ["which", "git2"] + [dependencies] clap = { version = "4.1", features = ["derive"] } @@ -32,7 +36,9 @@ toml = { version = "0.8" } crossbeam = "0.8" run_script = "0.10" tempfile = "3.0" +which = { version = "5.0", optional = true } +git2 = { version = "0.18", optional = true } -[target.'cfg(binary)'.dependencies] -which = "5.0" -git2 = "0.18" +[dependencies.web-sys] +version = "0.3.68" +features = ["console"] diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 7c24bca..078ed00 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -8,7 +8,7 @@ use rayon::prelude::*; use crate::args::{BuildSystem, Platform}; use crate::package::App; use crate::util::errors::{AnyError, BuildResult, LingoError}; -use crate::WhichType; +use crate::WhichCapability; pub mod cmake; pub mod lfc; @@ -18,13 +18,13 @@ pub mod pnpm; pub fn execute_command<'a>( command: &CommandSpec, apps: &[&'a App], - which: WhichType, + which: WhichCapability, ) -> BatchBuildResults<'a> { // Group apps by build system let mut by_build_system = HashMap::>::new(); for &app in apps { by_build_system - .entry(app.build_system(which)) + .entry(app.build_system(&which)) .or_default() .push(app); } diff --git a/src/lib.rs b/src/lib.rs index 0c38b35..78634ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,10 @@ -use std::path::Display; +use std::io; pub mod args; pub mod backends; pub mod package; pub mod util; + #[derive(Debug)] pub enum WhichError { /// An executable binary with that name was not found @@ -14,7 +15,7 @@ pub enum WhichError { CannotCanonicalize, } #[derive(Debug)] -pub struct GitCloneError(String); +pub struct GitCloneError(pub String); // TODO: create a more domain-specific error time like the actual git2::Error impl std::fmt::Display for WhichError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -38,5 +39,20 @@ impl std::error::Error for WhichError {} impl std::error::Error for GitCloneError {} -pub type WhichType = fn(binary_name: &str) -> Result; -pub type GitCloneType = fn(url: &str, into: &std::path::Path) -> Result<(), GitCloneError>; +pub struct GitUrl<'a>(&'a str); + +impl<'a> From<&'a str> for GitUrl<'a> { + fn from(value: &'a str) -> Self { + GitUrl(value) + } +} + +impl<'a> From> for &'a str { + fn from(value: GitUrl<'a>) -> Self { + value.0 + } +} + +pub type WhichCapability = Box Result>; +pub type GitCloneCapability = Box Result<(), GitCloneError>>; +pub type FsReadCapability = Box io::Result>; diff --git a/src/main.rs b/src/main.rs index 59724c6..2347f80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use std::io::ErrorKind; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; use std::{env, io}; @@ -8,17 +8,32 @@ use clap::Parser; use git2::Repository; use liblingo::args::InitArgs; use liblingo::args::{BuildArgs, Command as ConsoleCommand, CommandLineArgs}; -use liblingo::package::App; use liblingo::backends::{BatchBuildResults, BuildCommandOptions, CommandSpec}; use liblingo::package::{Config, ConfigFile}; use liblingo::util::errors::{BuildResult, LingoError}; +use liblingo::{GitCloneError, GitUrl, WhichError}; -// pub mod args; -// pub mod backends; -// pub mod package; -// pub(crate) mod util; -// pub mod +fn do_repo_clone(url: GitUrl, into: &std::path::Path) -> Result<(), GitCloneError> { + Repository::clone(url.into(), into).map_or_else( + |err: git2::Error| Err(GitCloneError(format!("{}", err))), + |_| Ok(()), + ) +} + +fn do_which(cmd: &str) -> Result { + which::which(cmd).map_err(|err| match err { + which::Error::CannotFindBinaryPath => WhichError::CannotFindBinaryPath, + which::Error::CannotGetCurrentDirAndPathListEmpty => { + WhichError::CannotGetCurrentDirAndPathListEmpty + } + which::Error::CannotCanonicalize => WhichError::CannotCanonicalize, + }) +} + +fn do_read_to_string(p: &Path) -> io::Result { + std::fs::read_to_string(p) +} fn main() { // parses command line arguments @@ -30,7 +45,7 @@ fn main() { // tries to read Lingo.toml let mut wrapped_config = lingo_path.as_ref().and_then(|path| { - ConfigFile::from(path) + ConfigFile::from(path, Box::new(do_read_to_string)) .map_err(|err| println!("Error while reading Lingo.toml: {}", err)) .ok() .map(|cf| cf.to_config(path.parent().unwrap())) @@ -111,7 +126,7 @@ fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandR fn do_init(init_config: InitArgs) -> BuildResult { let initial_config = ConfigFile::new_for_init_task(init_config)?; initial_config.write(Path::new("./Lingo.toml"))?; - initial_config.setup_example(Repository::clone) + initial_config.setup_example(Box::new(do_repo_clone)) } fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { @@ -119,7 +134,7 @@ fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { CommandSpec::Build(BuildCommandOptions { profile: args.build_profile(), compile_target_code: !args.no_compile, - lfc_exec_path: liblingo::util::find_lfc_exec(args, which::which::<&str>) + lfc_exec_path: liblingo::util::find_lfc_exec(args, Box::new(do_which)) .expect("TODO replace me"), max_threads: args.threads, keep_going: args.keep_going, @@ -131,7 +146,7 @@ fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { fn run_command(task: CommandSpec, config: &Config, _fail_at_end: bool) -> BatchBuildResults { let apps = config.apps.iter().collect::>(); - liblingo::backends::execute_command(&task, &apps, which::which) + liblingo::backends::execute_command(&task, &apps, Box::new(do_which)) } enum CommandResult<'a> { diff --git a/src/package/mod.rs b/src/package/mod.rs index 4cd6777..016a565 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -1,21 +1,20 @@ use crate::args::{BuildSystem, InitArgs, Platform, TargetLanguage}; use crate::util::{analyzer, copy_recursively}; -use crate::{GitCloneType, WhichType}; +use crate::{FsReadCapability, GitCloneCapability, GitUrl, WhichCapability}; use serde_derive::{Deserialize, Serialize}; +use web_sys::wasm_bindgen::JsValue; use std::collections::HashMap; -use std::fs::{read_to_string, remove_dir_all, remove_file, write}; +use std::fs::{remove_dir_all, remove_file, write}; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::{env, io}; use crate::args::BuildSystem::{CMake, LFC}; use crate::util::errors::{BuildResult, LingoError}; -// use git2::Repository; use tempfile::tempdir; -// use which::which; fn is_valid_location_for_project(path: &std::path::Path) -> bool { !path.join("src").exists() && !path.join(".git").exists() && !path.join("application").exists() @@ -92,7 +91,7 @@ pub struct App { } impl App { - pub fn build_system(&self, which: WhichType) -> BuildSystem { + pub fn build_system(&self, which: &WhichCapability) -> BuildSystem { match self.target { TargetLanguage::C => LFC, TargetLanguage::Cpp => CMake, @@ -212,10 +211,20 @@ impl ConfigFile { write(path, toml_string) } - pub fn from(path: &Path) -> io::Result { - read_to_string(path).and_then(|contents| { - toml::from_str(&contents) - .map_err(|e| io::Error::new(ErrorKind::InvalidData, format!("{}", e))) + pub fn from(path: &Path, fsr: FsReadCapability) -> io::Result { + web_sys::console::log_1(&JsValue::from_str("reading contents from path...")); + let contents = fsr(path); + web_sys::console::log_1(&JsValue::from_str(&format!( + "contents read from path with result: {:?}", + contents + ))); + contents.and_then(|contents| { + toml::from_str(&contents).map_err(|e| { + io::Error::new( + ErrorKind::InvalidData, + format!("failed to convert string to toml: {}", e), + ) + }) }) } @@ -234,10 +243,10 @@ impl ConfigFile { Ok(()) } - fn setup_template_repo(&self, url: &str, clone: GitCloneType) -> BuildResult { + fn setup_template_repo(&self, url: &str, clone: GitCloneCapability) -> BuildResult { let dir = tempdir()?; let tmp_path = dir.path(); - clone(url, &tmp_path)?; + clone(GitUrl::from(url), tmp_path)?; // Copy the cloned template repo into the project directory copy_recursively(tmp_path, Path::new("."))?; // Remove temporary folder @@ -246,7 +255,7 @@ impl ConfigFile { } // Sets up a LF project with Zephyr as the target platform. - fn setup_zephyr(&self, clone: GitCloneType) -> BuildResult { + fn setup_zephyr(&self, clone: GitCloneCapability) -> BuildResult { let url = "https://github.com/lf-lang/lf-west-template"; self.setup_template_repo(url, clone)?; remove_file(".gitignore")?; @@ -256,14 +265,14 @@ impl ConfigFile { // Sets up a LF project with RP2040 MCU as the target platform. // Initializes a repo using the lf-pico-template - fn setup_rp2040(&self, clone: GitCloneType) -> BuildResult { + fn setup_rp2040(&self, clone: GitCloneCapability) -> BuildResult { let url = "https://github.com/lf-lang/lf-pico-template"; // leave git artifacts self.setup_template_repo(url, clone)?; Ok(()) } - pub fn setup_example(&self, clone: GitCloneType) -> BuildResult { + pub fn setup_example(&self, clone: GitCloneCapability) -> BuildResult { if is_valid_location_for_project(Path::new(".")) { match self.apps[0].platform { Some(Platform::Native) => self.setup_native(), diff --git a/src/util/mod.rs b/src/util/mod.rs index 0aeac49..f385991 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,15 +1,13 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; -// use which::which; - pub mod analyzer; mod command_line; pub mod errors; pub use command_line::*; -use crate::WhichType; +use crate::WhichCapability; /// finds toml file recurisvely pub fn find_toml(input_path: &Path) -> Option { @@ -68,7 +66,7 @@ pub fn default_build_clean(out_dir: &Path) -> io::Result<()> { pub fn find_lfc_exec( args: &crate::args::BuildArgs, - which: WhichType, + which: WhichCapability, ) -> Result { if let Some(lfc) = &args.lfc { if lfc.exists() { From 92ec3abd852c3bf44a472688b9e872985cc66337 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Sat, 2 Mar 2024 01:17:27 -0800 Subject: [PATCH 05/10] Rename lingua-franca -> lingo for package name --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1e2df43..a2c00fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lingua-franca" +name = "lingo" version = "0.2.0" edition = "2021" From cbb0b2e5817212a0e47a200426ec65a16236112e Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Sat, 2 Mar 2024 15:19:13 -0800 Subject: [PATCH 06/10] Use lifetimes to improve restriction on capability --- src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 78634ad..c21f0bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,7 @@ impl<'a> From> for &'a str { } } -pub type WhichCapability = Box Result>; -pub type GitCloneCapability = Box Result<(), GitCloneError>>; -pub type FsReadCapability = Box io::Result>; +pub type WhichCapability<'a> = Box Result + 'a>; +pub type GitCloneCapability<'a> = + Box Result<(), GitCloneError> + 'a>; +pub type FsReadCapability<'a> = Box io::Result + 'a>; From f5153d367766dd08b617acd5eb4b5649d1276ce9 Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Sat, 2 Mar 2024 16:47:39 -0800 Subject: [PATCH 07/10] Make logging externally configurable --- Cargo.toml | 6 ++---- src/backends/mod.rs | 4 ++-- src/main.rs | 7 ++++--- src/package/mod.rs | 6 ------ src/util/analyzer.rs | 2 +- src/util/command_line.rs | 4 ++-- src/util/mod.rs | 2 +- 7 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a2c00fb..aa7d411 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,5 @@ run_script = "0.10" tempfile = "3.0" which = { version = "5.0", optional = true } git2 = { version = "0.18", optional = true } - -[dependencies.web-sys] -version = "0.3.68" -features = ["console"] +log = "0.4.21" +print_logger = "0.2.0" diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 078ed00..888f07a 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -126,10 +126,10 @@ impl<'a> BatchBuildResults<'a> { for (app, b) in &self.results { match b { Ok(()) => { - println!("- {}: Success", &app.name); + log::info!("- {}: Success", &app.name); } Err(e) => { - println!("- {}: Error: {}", &app.name, e); + log::error!("- {}: Error: {}", &app.name, e); } } } diff --git a/src/main.rs b/src/main.rs index 2347f80..aa69864 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ fn do_read_to_string(p: &Path) -> io::Result { } fn main() { + print_logger::new().init().unwrap(); // parses command line arguments let args = CommandLineArgs::parse(); @@ -46,7 +47,7 @@ fn main() { // tries to read Lingo.toml let mut wrapped_config = lingo_path.as_ref().and_then(|path| { ConfigFile::from(path, Box::new(do_read_to_string)) - .map_err(|err| println!("Error while reading Lingo.toml: {}", err)) + .map_err(|err| log::error!("Error while reading Lingo.toml: {}", err)) .ok() .map(|cf| cf.to_config(path.parent().unwrap())) }); @@ -68,7 +69,7 @@ fn print_res(result: BuildResult) { match result { Ok(_) => {} Err(errs) => { - println!("{}", errs); + log::error!("{}", errs); } } } @@ -104,7 +105,7 @@ fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandR "Error: Missing Lingo.toml file", )))), (Some(config), ConsoleCommand::Build(build_command_args)) => { - println!("Building ..."); + log::info!("Building ..."); CommandResult::Batch(build(&build_command_args, config)) } (Some(config), ConsoleCommand::Run(build_command_args)) => { diff --git a/src/package/mod.rs b/src/package/mod.rs index 016a565..5233d0c 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -3,7 +3,6 @@ use crate::util::{analyzer, copy_recursively}; use crate::{FsReadCapability, GitCloneCapability, GitUrl, WhichCapability}; use serde_derive::{Deserialize, Serialize}; -use web_sys::wasm_bindgen::JsValue; use std::collections::HashMap; @@ -212,12 +211,7 @@ impl ConfigFile { } pub fn from(path: &Path, fsr: FsReadCapability) -> io::Result { - web_sys::console::log_1(&JsValue::from_str("reading contents from path...")); let contents = fsr(path); - web_sys::console::log_1(&JsValue::from_str(&format!( - "contents read from path with result: {:?}", - contents - ))); contents.and_then(|contents| { toml::from_str(&contents).map_err(|e| { io::Error::new( diff --git a/src/util/analyzer.rs b/src/util/analyzer.rs index f0950cb..bcde30b 100644 --- a/src/util/analyzer.rs +++ b/src/util/analyzer.rs @@ -18,7 +18,7 @@ const DEFAULT_TARGET: TargetLanguage = TargetLanguage::C; /// this functions searches inside the file for a main reactor declaration fn search_inside_file(path: &Path) -> io::Result> { - println!("Searching File {:?}", path); + log::info!("Searching File {:?}", path); let content = std::fs::read_to_string(path)?; let mut target: TargetLanguage = DEFAULT_TARGET; diff --git a/src/util/command_line.rs b/src/util/command_line.rs index fde75ed..447d55b 100644 --- a/src/util/command_line.rs +++ b/src/util/command_line.rs @@ -38,7 +38,7 @@ pub fn run_and_capture(command: &mut Command) -> io::Result<(ExitStatus, Vec // These expects should be guaranteed to be ok because we used piped(). let mut child_stdout = child.stdout.take().expect("logic error getting stdout"); let mut child_stderr = child.stderr.take().expect("logic error getting stderr"); - println!("Running {:?}", command); + log::info!("Running {:?}", command); thread::scope(|s| { let stdout_thread = s.spawn(|_| -> io::Result> { @@ -72,7 +72,7 @@ pub fn run_and_capture(command: &mut Command) -> io::Result<(ExitStatus, Vec pub fn execute_command_to_build_result(mut command: Command) -> BuildResult { match run_and_capture(&mut command) { Err(e) => { - eprintln!("error occured while executing commandline: {:?}", &e); + log::error!("error occured while executing commandline: {:?}", &e); Err(Box::new(e)) } Ok((status, _, _)) if !status.success() => { diff --git a/src/util/mod.rs b/src/util/mod.rs index f385991..9bbbd2c 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -57,7 +57,7 @@ pub fn delete_subdirs(path_root: &Path, subdirs: &[&str]) -> io::Result<()> { } pub fn default_build_clean(out_dir: &Path) -> io::Result<()> { - println!("removing build artifacts in {:?}", out_dir); + log::info!("removing build artifacts in {:?}", out_dir); delete_subdirs( out_dir, &["bin", "include", "src-gen", "lib64", "share", "build"], From 3ebb4016933b78ef72bc02742fa2fae4cdcd3d2e Mon Sep 17 00:00:00 2001 From: Peter Donovan Date: Mon, 11 Mar 2024 15:20:05 -0700 Subject: [PATCH 08/10] Minor updates, including making CCpp a target TODO: cherry-pick this change out to a new branch --- src/args.rs | 1 + src/util/analyzer.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/args.rs b/src/args.rs index 60a5acd..c40e652 100644 --- a/src/args.rs +++ b/src/args.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; #[clap(rename_all = "lowercase")] pub enum TargetLanguage { C, + CCpp, Cpp, Rust, TypeScript, diff --git a/src/util/analyzer.rs b/src/util/analyzer.rs index bcde30b..710cbae 100644 --- a/src/util/analyzer.rs +++ b/src/util/analyzer.rs @@ -7,7 +7,7 @@ use regex::Regex; use crate::args::TargetLanguage; -lazy_static! { +lazy_static! { // FIXME: ad hoc parsing using regexes should be forbidden static ref TARGET_RE: Regex = Regex::new(r"\btarget\s+(\w+)\s*[{;]").unwrap(); } lazy_static! { @@ -23,10 +23,10 @@ fn search_inside_file(path: &Path) -> io::Result> { let mut target: TargetLanguage = DEFAULT_TARGET; for line in content.split('\n') { - if let Some(captures) = MAIN_REACTOR_RE.captures(line) { + if let Some(captures) = TARGET_RE.captures(line) { target = TargetLanguage::from_str(captures.get(1).unwrap().as_str(), true).unwrap(); } - if let Some(captures) = TARGET_RE.captures(line) { + if let Some(captures) = MAIN_REACTOR_RE.captures(line) { let name = captures.get(1).unwrap().as_str().into(); return Ok(Some(MainReactorSpec { name, From 5fb461cecde3de8247732c92aed9d720751371f1 Mon Sep 17 00:00:00 2001 From: tanneberger Date: Sat, 21 Sep 2024 00:55:15 +0200 Subject: [PATCH 09/10] init: lingo dependency management - fetching packages - libraries specification - add backend for the C target - lock files --- Cargo.lock | 783 +++++++++++++----------- Cargo.toml | 11 +- derivation.nix | 3 + flake.lock | 26 +- src/args.rs | 40 +- src/backends/cmake_c.rs | 124 ++++ src/backends/{cmake.rs => cmake_cpp.rs} | 38 +- src/backends/lfc.rs | 7 +- src/backends/mod.rs | 79 ++- src/main.rs | 23 +- src/package/lock.rs | 290 +++++++++ src/package/management.rs | 358 +++++++++++ src/package/mod.rs | 339 +++++++--- src/package/target_properties.rs | 171 ++++++ src/package/tree.rs | 93 +++ src/util/analyzer.rs | 1 - src/util/errors.rs | 14 + src/util/mod.rs | 4 +- 18 files changed, 1858 insertions(+), 546 deletions(-) create mode 100644 src/backends/cmake_c.rs rename src/backends/{cmake.rs => cmake_cpp.rs} (73%) create mode 100644 src/package/lock.rs create mode 100644 src/package/management.rs create mode 100644 src/package/target_properties.rs create mode 100644 src/package/tree.rs diff --git a/Cargo.lock b/Cargo.lock index 5cddd3e..b221d53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,99 +4,98 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] -name = "bitflags" -version = "2.4.1" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] name = "cc" -version = "1.0.83" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -107,9 +106,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.8" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -117,9 +116,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -129,9 +128,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", @@ -141,23 +140,41 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] [[package]] name = "crossbeam" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -167,77 +184,72 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] -name = "deranged" -version = "0.3.9" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "powerfmt", + "generic-array", + "typenum", ] [[package]] -name = "dunce" -version = "1.0.4" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -247,57 +259,46 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] -name = "fsio" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad0ce30be0cc441b325c5d705c8b613a0ca0d92b6a8953d41bd236dc09a36d0" -dependencies = [ - "dunce", - "rand", -] - -[[package]] -name = "getrandom" -version = "0.2.11" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "cfg-if", - "libc", - "wasi", + "typenum", + "version_check", ] [[package]] name = "git2" -version = "0.18.1" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd" +checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" dependencies = [ - "bitflags 2.4.1", + "bitflags", "libc", "libgit2-sys", "log", @@ -308,30 +309,36 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -339,46 +346,61 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libgit2-sys" -version = "0.16.1+1.7.1" +version = "0.16.2+1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2a2bb3680b094add03bb3732ec520ece34da31a8cd2d633d1389d0f0fb60d0c" +checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" dependencies = [ "cc", "libc", @@ -388,17 +410,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libredox" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" -dependencies = [ - "bitflags 2.4.1", - "libc", - "redox_syscall", -] - [[package]] name = "libssh2-sys" version = "0.3.0" @@ -415,9 +426,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -425,74 +436,99 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - [[package]] name = "lingua-franca" version = "0.2.0" dependencies = [ + "anyhow", "clap", + "colored", "crossbeam", "git2", "lazy_static", - "os-version", + "log", "rayon", "regex", - "run_script", "serde", "serde_derive", "serde_json", + "sha1dir", "tempfile", - "termion", "toml", + "url", + "versions", "which", ] [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "memoffset" -version = "0.9.0" +name = "memmap" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ - "autocfg", + "libc", + "winapi", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", ] [[package]] -name = "numtoa" -version = "0.1.0" +name = "num_cpus" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl-probe" @@ -502,9 +538,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -513,117 +549,63 @@ dependencies = [ ] [[package]] -name = "os-version" -version = "0.2.0" +name = "parking_lot" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8a1fed76ac765e39058ca106b6229a93c5a60292a1bd4b602ce2be11e1c020" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ - "anyhow", - "plist", - "uname", - "winapi", + "lock_api", + "parking_lot_core", ] [[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "plist" -version = "1.6.0" +name = "parking_lot_core" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "base64", - "indexmap", - "line-wrap", - "quick-xml", - "serde", - "time", + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", ] [[package]] -name = "powerfmt" -version = "0.2.0" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "pkg-config" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -631,9 +613,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -641,24 +623,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] -[[package]] -name = "redox_termios" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" - [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -668,9 +644,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -679,43 +655,28 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "run_script" -version = "0.10.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829f98fdc58d78989dd9af83be28bc15c94a7d77f9ecdb54abbbc0b1829ba9c7" -dependencies = [ - "fsio", -] +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustix" -version = "0.38.24" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -725,18 +686,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -745,9 +706,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -756,89 +717,77 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] [[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "2.0.39" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "tempfile" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +name = "sha1dir" +version = "1.0.10" +source = "git+https://github.com/tanneberger/sha1dir#cf5a15703edcb3946ca847353cec4af644fcba75" dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", + "clap", + "memmap", + "num_cpus", + "parking_lot", + "rayon", + "sha1", ] [[package]] -name = "termion" -version = "2.0.3" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4648c7def6f2043b2568617b9f9b75eae88ca185dbc1f1fda30e95a85d49d7d" -dependencies = [ - "libc", - "libredox", - "numtoa", - "redox_termios", -] +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] -name = "time" -version = "0.3.30" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" -dependencies = [ - "deranged", - "itoa", - "powerfmt", - "serde", - "time-core", - "time-macros", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "time-core" -version = "0.1.2" +name = "syn" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] [[package]] -name = "time-macros" -version = "0.2.15" +name = "tempfile" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ - "time-core", + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -851,9 +800,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.8" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -863,18 +812,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -884,19 +833,16 @@ dependencies = [ ] [[package]] -name = "uname" -version = "0.1.1" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -906,29 +852,30 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "url" -version = "2.4.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" @@ -937,22 +884,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "version_check" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "versions" +version = "6.1.0" +source = "git+https://github.com/tanneberger/rs-versions.git#7802574585abc55d06b5e21e20be7ced02b5ffab" +dependencies = [ + "itertools", + "nom", + "serde", +] [[package]] name = "which" -version = "5.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" dependencies = [ "either", "home", - "once_cell", "rustix", - "windows-sys", + "winsafe", ] [[package]] @@ -983,7 +939,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -992,13 +957,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1007,47 +988,101 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" -version = "0.5.19" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" diff --git a/Cargo.toml b/Cargo.toml index 0585f2e..6aa8f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,17 +16,20 @@ path = "./src/main.rs" [dependencies] clap = { version = "4.1", features = ["derive"] } -os-version = "0.2" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" -which = "5.0" +which = "6.0" regex = "1.8" lazy_static = "1.4" rayon = "1.7" toml = {version = "0.8"} crossbeam = "0.8" -termion = "2.0" git2 = "0.18" -run_script = "0.10" tempfile = "3.0" +url = { version = "2.5", features = ["serde"] } +anyhow = "1.0" +versions = { git = "https://github.com/tanneberger/rs-versions.git", features = ["serde"]} +log = "0.4" +sha1dir = { version = "1.0", git = "https://github.com/tanneberger/sha1dir" } +colored = "2.1.0" diff --git a/derivation.nix b/derivation.nix index 19f9a4c..6347902 100644 --- a/derivation.nix +++ b/derivation.nix @@ -7,6 +7,9 @@ naersk.buildPackage { src = ./.; cargoSha256 = lib.fakeSha256; + preBuild = '' + export CARGO_BIN_NAME=cargo + ''; nativeBuildInputs = [ pkg-config cmake zlib openssl glibc]; buildInputs = [ ]; diff --git a/flake.lock b/flake.lock index 11a72f9..6ad5f1f 100644 --- a/flake.lock +++ b/flake.lock @@ -20,12 +20,10 @@ }, "nixpkgs": { "locked": { - "lastModified": 1699343069, - "narHash": "sha256-s7BBhyLA6MI6FuJgs4F/SgpntHBzz40/qV0xLPW6A1Q=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "ec750fd01963ab6b20ee1f0cb488754e8036d89d", - "type": "github" + "lastModified": 0, + "narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=", + "path": "/nix/store/7ay2dd0w41iqvrnkx19v4vf4f5kkxvc2-source", + "type": "path" }, "original": { "id": "nixpkgs", @@ -34,12 +32,10 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1699343069, - "narHash": "sha256-s7BBhyLA6MI6FuJgs4F/SgpntHBzz40/qV0xLPW6A1Q=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "ec750fd01963ab6b20ee1f0cb488754e8036d89d", - "type": "github" + "lastModified": 0, + "narHash": "sha256-rL5LSYd85kplL5othxK5lmAtjyMOBg390sGBTb3LRMM=", + "path": "/nix/store/7ay2dd0w41iqvrnkx19v4vf4f5kkxvc2-source", + "type": "path" }, "original": { "id": "nixpkgs", @@ -73,11 +69,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { diff --git a/src/args.rs b/src/args.rs index 60a5acd..f391ba6 100644 --- a/src/args.rs +++ b/src/args.rs @@ -3,8 +3,8 @@ use clap::{Args, Parser, Subcommand}; use serde_derive::{Deserialize, Serialize}; use std::path::PathBuf; -#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] -#[clap(rename_all = "lowercase")] +#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] +#[value(rename_all = "lowercase")] pub enum TargetLanguage { C, Cpp, @@ -13,7 +13,7 @@ pub enum TargetLanguage { Python, } -#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] +#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] pub enum Platform { Native, Zephyr, @@ -33,39 +33,39 @@ pub enum BuildSystem { pub struct BuildArgs { /// Which build system to use /// TODO: discuss this - #[clap(short, long)] + #[arg(short, long)] pub build_system: Option, /// Which target to build - #[clap(short, long)] + #[arg(short, long)] pub language: Option, /// Overwrites any possible board definition in Lingo.toml - #[clap(long)] + #[arg(long)] pub platform: Option, /// Tell lingo where the lfc toolchain can be found - #[clap(long)] + #[arg(long)] pub lfc: Option, /// Skips building aka invoking the build system so it only generates code - #[clap(short, long, action)] + #[arg(short, long)] pub no_compile: bool, /// If one of the apps fails to build dont interrupt the build process - #[clap(short, long, action)] + #[arg(short, long)] pub keep_going: bool, /// Compiles the binaries with optimizations turned on and strips debug symbols - #[clap(short, long, action)] + #[arg(short, long)] pub release: bool, /// List of apps to build if left empty all apps are built - #[clap(short, long, value_delimiter = ',')] + #[arg(short, long, value_delimiter = ',')] pub apps: Vec, /// Number of threads to use for parallel builds. Zero means it will be determined automatically. - #[clap(short, long, default_value_t = 0)] + #[arg(short, long, default_value_t = 0)] pub threads: usize, } @@ -81,9 +81,9 @@ impl BuildArgs { #[derive(Args, Debug)] pub struct InitArgs { - #[clap(value_enum, short, long)] + #[arg(value_enum, short, long)] pub language: Option, - #[clap(value_enum, short, long, default_value_t = Platform::Native)] + #[arg(value_enum, short, long, default_value_t = Platform::Native)] pub platform: Platform, } @@ -120,20 +120,20 @@ pub enum Command { } #[derive(Parser)] -#[clap(name = "Lingua Franca package manager and build tool")] -#[clap(author = "tassilo.tanneberger@tu-dresden.de")] -#[clap(version = env!("CARGO_PKG_VERSION"))] -#[clap(about = "Build system for the Lingua Franca coordination language", long_about = None)] +#[command(name = "Lingua Franca package manager and build tool")] +#[command(author = "tassilo.tanneberger@tu-dresden.de")] +#[command(version = env!("CARGO_PKG_VERSION"))] +#[command(about = "Build system for the Lingua Franca coordination language", long_about = None)] pub struct CommandLineArgs { /// which command of lingo to use #[clap(subcommand)] pub command: Command, /// lingo wouldn't produce any output - #[clap(short, long, action)] + #[arg(short, long)] pub quiet: bool, /// lingo will give more detailed feedback - #[clap(short, long, action)] + #[arg(short, long)] pub verbose: bool, } diff --git a/src/backends/cmake_c.rs b/src/backends/cmake_c.rs new file mode 100644 index 0000000..f6075ef --- /dev/null +++ b/src/backends/cmake_c.rs @@ -0,0 +1,124 @@ +use std::fs; +use std::io::Write; +use std::process::Command; + +use crate::backends::{ + BatchBackend, BatchBuildResults, BuildCommandOptions, BuildProfile, BuildResult, CommandSpec, +}; +use crate::util::errors::LingoError; +use crate::util::execute_command_to_build_result; +use crate::App; + +pub struct CmakeC; + +fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { + let build_dir = app.output_root.join("build"); + fs::create_dir_all(&build_dir)?; + + // location of the cmake file + let app_build_folder = app.src_gen_dir().join(&app.main_reactor_name); + println!("creating {}", &app_build_folder.display()); + let _ = std::fs::create_dir_all(&app_build_folder); + let cmake_file = app_build_folder.clone().join("CMakeLists.txt"); + + println!("writing artifacts ..."); + // create potential files that come from the target properties + app.properties + .write_artifacts(&app_build_folder) + .expect("cannot write artifacts"); + + // read file and append cmake include to generated cmake file + println!("reading cmake file ... {:?}", &cmake_file); + let mut content = fs::read_to_string(&cmake_file)?; + let include_statement = "\ninclude(./aggregated_cmake_include.cmake)"; + content += &*include_statement; + + println!("writing cmake file ..."); + // overwrite cmake file + let mut f = fs::OpenOptions::new() + .write(true) + .open(&cmake_file) + .expect("cannot open file"); + f.write_all(content.as_ref()).expect("cannot write file"); + f.flush().expect("cannot flush"); + + println!("running file ..."); + + // cmake args + let mut cmake = Command::new("cmake"); + cmake.arg(format!( + "-DCMAKE_BUILD_TYPE={}", + if options.profile == BuildProfile::Release { + "RELEASE" + } else { + "DEBUG" + } + )); + cmake.arg(format!( + "-DCMAKE_INSTALL_PREFIX={}", + app.output_root.display() + )); + cmake.arg("-DCMAKE_INSTALL_BINDIR=bin"); + cmake.arg(&app_build_folder); + cmake.arg(format!("-B {}", app_build_folder.display())); + cmake.current_dir(&build_dir); + + execute_command_to_build_result(cmake) +} + +fn do_cmake_build(results: &mut BatchBuildResults, options: &BuildCommandOptions) { + // open lingo.toml of the dependency + // read the version + // cry loud when it doesn't match out specified version + + results.keep_going(options.keep_going); + super::lfc::LFC::do_parallel_lfc_codegen(options, results, false); + if !options.compile_target_code { + return; + } + results + // generate all CMake files ahead of time + .map(|app| gen_cmake_files(app, options)) + // Run cmake to build everything. + .map(|app| { + let app_build_folder = app.src_gen_dir().join(&app.main_reactor_name); + + // compile everything + let mut cmake = Command::new("cmake"); + cmake.current_dir(&app_build_folder); + cmake.args(["--build", "."]); + + // add one target arg for each app + let name = app + .main_reactor + .file_stem() + .ok_or(LingoError::InvalidMainReactor)?; + cmake.arg("--target"); + cmake.arg(name); + + execute_command_to_build_result(cmake) + }) + .map(|app| { + let bin_source = app + .src_gen_dir() + .join(&app.main_reactor_name) + .join(&app.main_reactor_name); + fs::rename(bin_source, app.executable_path())?; + Ok(()) + }); +} + +impl BatchBackend for CmakeC { + fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults) { + match command { + CommandSpec::Build(options) => do_cmake_build(results, options), + CommandSpec::Clean => { + results.par_map(|app| { + crate::util::default_build_clean(&app.output_root)?; + Ok(()) + }); + } + _ => todo!(), + } + } +} diff --git a/src/backends/cmake.rs b/src/backends/cmake_cpp.rs similarity index 73% rename from src/backends/cmake.rs rename to src/backends/cmake_cpp.rs index b71b9c6..1e049f9 100644 --- a/src/backends/cmake.rs +++ b/src/backends/cmake_cpp.rs @@ -1,4 +1,5 @@ use std::fs; +use std::io::Write; use std::process::Command; @@ -9,14 +10,37 @@ use crate::backends::{ BatchBackend, BatchBuildResults, BuildCommandOptions, BuildProfile, BuildResult, CommandSpec, }; -pub struct Cmake; +pub struct CmakeCpp; fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { let build_dir = app.output_root.join("build"); fs::create_dir_all(&build_dir)?; - let mut cmake = Command::new("cmake"); + // location of the cmake file + let app_build_folder = app.src_gen_dir().join(&app.main_reactor_name); + let cmake_file = app_build_folder.clone().join("CMakeLists.txt"); + + // create potential files that come from the target properties + app.properties.write_artifacts(&app_build_folder)?; + + // we need to modify the cmake file here to include our generated cmake files + let src_gen_dir = app.src_gen_dir(); + + // read file and append cmake include to generated cmake file + let mut content = fs::read_to_string(&cmake_file)?; + let include_statement = format!( + "\ninclude({}/aggregated_cmake_include.cmake)", + app_build_folder.display() + ); + content += &*include_statement; + + // overwrite cmake file + let mut f = fs::OpenOptions::new().write(true).open(&cmake_file)?; + f.write_all(content.as_ref())?; + f.flush()?; + // cmake args + let mut cmake = Command::new("cmake"); cmake.arg(format!( "-DCMAKE_BUILD_TYPE={}", if options.profile == BuildProfile::Release { @@ -39,7 +63,7 @@ fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { .expect("not a valid main reactor path") .display() )); - cmake.arg(app.src_gen_dir()); + cmake.arg(src_gen_dir); cmake.arg(format!("-B {}", build_dir.display())); cmake.current_dir(&build_dir); @@ -47,8 +71,12 @@ fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { } fn do_cmake_build(results: &mut BatchBuildResults, options: &BuildCommandOptions) { + // open lingo.toml of the dependency + // read the version + // cry loud when it doesn't match out specified version + results.keep_going(options.keep_going); - super::lfc::LFC::do_parallel_lfc_codegen(options, results, true); + super::lfc::LFC::do_parallel_lfc_codegen(options, results, false); if !options.compile_target_code { return; } @@ -89,7 +117,7 @@ fn do_cmake_build(results: &mut BatchBuildResults, options: &BuildCommandOptions }); } -impl BatchBackend for Cmake { +impl BatchBackend for CmakeCpp { fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults) { match command { CommandSpec::Build(options) => do_cmake_build(results, options), diff --git a/src/backends/lfc.rs b/src/backends/lfc.rs index 27e4747..6730f3d 100644 --- a/src/backends/lfc.rs +++ b/src/backends/lfc.rs @@ -67,17 +67,20 @@ struct LfcJsonArgs<'a> { /// the src-gen and bin directory are generated. pub out: &'a Path, /// Other properties, mapped to CLI args by LFC. - pub properties: &'a HashMap, + pub properties: HashMap<&'static str, serde_json::Value>, #[serde(skip)] no_compile: bool, } impl<'a> LfcJsonArgs<'a> { pub fn new(app: &'a App, compile_target_code: bool) -> Self { + let hash_map: HashMap<&str, serde_json::Value> = HashMap::new(); + //hash_map.insert("fast", serde_json::Value::Bool(app.properties.fast)); + Self { src: &app.main_reactor, out: &app.output_root, - properties: &app.properties, + properties: hash_map, no_compile: !compile_target_code, } } diff --git a/src/backends/mod.rs b/src/backends/mod.rs index ad0bdba..cbbc86d 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -1,30 +1,64 @@ +use log::error; +use rayon::prelude::*; + use std::collections::HashMap; use std::path::PathBuf; - use std::sync::Arc; -use rayon::prelude::*; - -use crate::args::{BuildSystem, Platform}; -use crate::package::App; +use crate::args::{BuildSystem, Platform, TargetLanguage}; +use crate::package::{ + management::DependencyManager, target_properties::MergeTargetProperties, App, Config, + OUTPUT_DIRECTORY, +}; use crate::util::errors::{AnyError, BuildResult, LingoError}; -pub mod cmake; +pub mod cmake_c; + +pub mod cmake_cpp; pub mod lfc; pub mod npm; pub mod pnpm; -pub fn execute_command<'a>(command: &CommandSpec, apps: &[&'a App]) -> BatchBuildResults<'a> { +pub fn execute_command<'a>(command: &CommandSpec, config: &'a mut Config) -> BatchBuildResults<'a> { + let mut result = BatchBuildResults::new(); + let dependencies = Vec::from_iter(config.dependencies.clone().into_iter()); + + match command { + CommandSpec::Build(_build) => { + let manager = match DependencyManager::from_dependencies( + dependencies.clone(), + &PathBuf::from(OUTPUT_DIRECTORY), + ) { + Ok(value) => value, + Err(e) => { + error!("failed to create dependency manager because of {e}"); + return result; + } + }; + + // enriching the apps with the target properties from the libraries + let library_properties = manager.get_target_properties().expect("lib properties"); + + // merging app with library target properties + for app in &mut config.apps { + if let Err(e) = app.properties.merge(&library_properties) { + error!("cannot merge properties from the libraries with the app. error: {e}"); + return result; + } + } + } + _ => {} + } + // Group apps by build system - let mut by_build_system = HashMap::>::new(); - for &app in apps { + let mut by_build_system = HashMap::<(BuildSystem, TargetLanguage), Vec<&App>>::new(); + for app in &config.apps { by_build_system - .entry(app.build_system()) + .entry((app.build_system(), app.target)) .or_default() .push(app); } - let mut result = BatchBuildResults::new(); for (build_system, apps) in by_build_system { let mut sub_res = BatchBuildResults::for_apps(&apps); @@ -38,11 +72,24 @@ pub fn execute_command<'a>(command: &CommandSpec, apps: &[&'a App]) -> BatchBuil }); match build_system { - BuildSystem::LFC => lfc::LFC.execute_command(command, &mut sub_res), - BuildSystem::CMake => cmake::Cmake.execute_command(command, &mut sub_res), - BuildSystem::Npm => npm::Npm.execute_command(command, &mut sub_res), - BuildSystem::Pnpm => pnpm::Pnpm.execute_command(command, &mut sub_res), - BuildSystem::Cargo => todo!(), + (BuildSystem::CMake, TargetLanguage::Cpp) => { + cmake_cpp::CmakeCpp.execute_command(command, &mut sub_res) + } + (BuildSystem::CMake, TargetLanguage::C) => { + cmake_c::CmakeC.execute_command(command, &mut sub_res) + } + (BuildSystem::Npm, TargetLanguage::TypeScript) => { + npm::Npm.execute_command(command, &mut sub_res) + } + (BuildSystem::Pnpm, TargetLanguage::TypeScript) => { + pnpm::Pnpm.execute_command(command, &mut sub_res) + } + (BuildSystem::LFC, _) => lfc::LFC.execute_command(command, &mut sub_res), + (BuildSystem::Cargo, _) => todo!(), + _ => { + error!("invalid combination of target and platform!"); + todo!() + } }; result.append(sub_res); } diff --git a/src/main.rs b/src/main.rs index 19648bd..519d39a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use std::{env, io}; use clap::Parser; -use crate::args::InitArgs; +use crate::args::{InitArgs, TargetLanguage}; use args::{BuildArgs, Command as ConsoleCommand, CommandLineArgs}; use package::App; @@ -39,7 +39,7 @@ fn main() { print_res(result) } - let result = execute_command(wrapped_config.as_ref(), args.command); + let result = execute_command(&mut wrapped_config, args.command); match result { CommandResult::Batch(res) => res.print_results(), @@ -79,7 +79,7 @@ fn validate(config: &mut Option, command: &ConsoleCommand) -> BuildResul } } -fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandResult { +fn execute_command(config: &mut Option, command: ConsoleCommand) -> CommandResult { match (config, command) { (_, ConsoleCommand::Init(init_config)) => CommandResult::Single(do_init(init_config)), (None, _) => CommandResult::Single(Err(Box::new(io::Error::new( @@ -87,7 +87,6 @@ fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandR "Error: Missing Lingo.toml file", )))), (Some(config), ConsoleCommand::Build(build_command_args)) => { - println!("Building ..."); CommandResult::Batch(build(&build_command_args, config)) } (Some(config), ConsoleCommand::Run(build_command_args)) => { @@ -107,12 +106,15 @@ fn execute_command(config: Option<&Config>, command: ConsoleCommand) -> CommandR } fn do_init(init_config: InitArgs) -> BuildResult { - let initial_config = ConfigFile::new_for_init_task(init_config)?; + let initial_config = ConfigFile::new_for_init_task(&init_config)?; initial_config.write(Path::new("./Lingo.toml"))?; - initial_config.setup_example() + initial_config.setup_example( + init_config.platform, + init_config.language.unwrap_or(TargetLanguage::Cpp), + ) } -fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { +fn build<'a>(args: &BuildArgs, config: &'a mut Config) -> BatchBuildResults<'a> { run_command( CommandSpec::Build(BuildCommandOptions { profile: args.build_profile(), @@ -126,9 +128,10 @@ fn build<'a>(args: &BuildArgs, config: &'a Config) -> BatchBuildResults<'a> { ) } -fn run_command(task: CommandSpec, config: &Config, _fail_at_end: bool) -> BatchBuildResults { - let apps = config.apps.iter().collect::>(); - backends::execute_command(&task, &apps) +fn run_command(task: CommandSpec, config: &mut Config, _fail_at_end: bool) -> BatchBuildResults { + //let apps = config.apps.iter().collect::>(); + //let dependencies = Vec::from_iter(config.dependencies.into_iter()); + backends::execute_command(&task, config) } enum CommandResult<'a> { diff --git a/src/package/lock.rs b/src/package/lock.rs new file mode 100644 index 0000000..dd6c39c --- /dev/null +++ b/src/package/lock.rs @@ -0,0 +1,290 @@ +use colored::Colorize; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use versions::Versioning; + +use log::error; +use serde::de::Error as DeserializationError; +use serde::ser::Error as SerializationError; +use std::cmp::PartialEq; +use std::collections::HashMap; +use std::fmt::Display; +use std::fs; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use crate::package::management::copy_dir_all; +use crate::package::{ + deserialize_version, serialize_version, + target_properties::{LibraryTargetProperties, MergeTargetProperties}, + tree::{DependencyTreeNode, PackageDetails, ProjectSource}, + ConfigFile, +}; +use crate::util::errors::LingoError; + +pub struct ParseLockSourceError {} + +/// Different package sources types, available inside the lock file. +#[derive(PartialEq)] +pub enum PackageLockSourceType { + REGISTRY, + GIT, + TARBALL, + PATH, +} + +/// Struct that saves the source uri string +pub struct PackageLockSource { + pub source_type: PackageLockSourceType, + pub uri: String, + pub rev: Option, +} + +// Tries to parse the enum value from given string +impl FromStr for PackageLockSourceType { + type Err = ParseLockSourceError; + + fn from_str(s: &str) -> Result { + match s { + "registry" => Ok(Self::REGISTRY), + "git" => Ok(Self::GIT), + "path" => Ok(Self::PATH), + "tar" => Ok(Self::TARBALL), + _ => Err(ParseLockSourceError {}), + } + } +} + +/// generates the corresponding string based on enum value +impl Display for PackageLockSourceType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + Self::REGISTRY => "registry".to_string(), + Self::GIT => "git".to_string(), + Self::PATH => "path".to_string(), + Self::TARBALL => "tar".to_string(), + }; + write!(f, "{}", str) + } +} + +impl From for PackageLockSourceType { + fn from(value: ProjectSource) -> Self { + match value { + ProjectSource::Git(_) => Self::GIT, + ProjectSource::TarBall(_) => Self::TARBALL, + ProjectSource::Path(_) => Self::PATH, + } + } +} + +/// Parses the whole source uri string of a package +/// the uri string follows the pattern +(#) +impl FromStr for PackageLockSource { + type Err = ParseLockSourceError; + + fn from_str(s: &str) -> Result { + match s.split_once("+") { + Some((source_type_string, mut uri)) => { + let source_type = PackageLockSourceType::from_str(source_type_string)?; + + let rev: Option = match source_type { + PackageLockSourceType::GIT => match uri.split_once("#") { + Some((url, rev)) => { + uri = url; + Some(rev.to_string()) + } + None => { + return Err(ParseLockSourceError {}); + } + }, + _ => None, + }; + + Ok(PackageLockSource { + source_type, + uri: uri.to_string(), + rev, + }) + } + None => Err(ParseLockSourceError {}), + } + } +} + +#[derive(Deserialize, Serialize)] +pub struct PackageLock { + pub name: String, + #[serde( + serialize_with = "serialize_version", + deserialize_with = "deserialize_version" + )] + pub version: Versioning, + pub source: PackageLockSource, + pub checksum: String, +} + +impl From for PackageLock { + fn from(value: DependencyTreeNode) -> Self { + let uri = match &value.package.mutual_exclusive { + ProjectSource::Git(git) => git.to_string(), + ProjectSource::TarBall(tar) => tar.to_string(), + ProjectSource::Path(path) => format!("{:?}", path), + }; + + PackageLock { + name: value.name, + version: value.version, + source: PackageLockSource { + source_type: PackageLockSourceType::from(value.package.mutual_exclusive), + uri, + rev: value.package.git_rev, + }, + checksum: value.hash, + } + } +} + +impl<'de> Deserialize<'de> for PackageLockSource { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + match PackageLockSource::from_str(&s) { + Ok(value) => Ok(value), + Err(_) => Err(D::Error::custom("cannot parse package source string!")), + } + } +} + +impl Serialize for PackageLockSource { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let source_type = self.source_type.to_string(); + let mut serialized_string = format!("{}+{}", source_type, self.uri); + + if self.source_type == PackageLockSourceType::GIT { + if let Some(rev) = self.rev.clone() { + serialized_string = format!("{}#{}", serialized_string, rev) + } else { + error!("expected and revision but got none during serialization of lock file!"); + return Err(S::Error::custom("expected revision but gone None")); + } + } + + serializer.serialize_str(&serialized_string) + } +} + +#[derive(Deserialize, Serialize, Default)] +pub struct DependencyLock { + /// mapping from package name to location + #[serde(flatten)] + pub dependencies: HashMap, + + /// this will be populated when the project is successfully loaded from the lock file + #[serde(skip)] + loaded_dependencies: Vec, +} + +impl DependencyLock { + pub(crate) fn create(selected_dependencies: Vec) -> DependencyLock { + let mut map = HashMap::new(); + for dependency in &selected_dependencies { + map.insert( + dependency.name.clone(), + PackageLock::from(dependency.clone()), + ); + } + Self { + dependencies: map, + loaded_dependencies: selected_dependencies, + } + } + + pub fn init(&mut self, lfc_include_folder: &Path) -> anyhow::Result<()> { + for (_, lock) in self.dependencies.iter() { + let temp = lfc_include_folder.join(&lock.name); + // the Lingo.toml for this dependency doesnt exists, hence we need to fetch this package + if !temp.join("Lingo.toml").exists() { + let mut details = PackageDetails::try_from(&lock.source)?; + + details.fetch(&temp).expect("cannot pull package"); + } + + let hash = sha1dir::checksum_current_dir(&temp, false); + + if hash.to_string() != lock.checksum { + error!("checksum does not match aborting!"); + } + + let lingo_toml_text = fs::read_to_string(&temp.join("Lingo.toml"))?; + let read_toml = toml::from_str::(&lingo_toml_text)?.to_config(&temp); + + println!( + "{} {} ... {}", + "Reading".green().bold(), + lock.name, + read_toml.package.version + ); + + let lib = match read_toml.library { + Some(value) => value, + None => { + // error we expected a library here + return Err(LingoError::NoLibraryInLingoToml( + temp.join("Lingo.toml").display().to_string(), + ) + .into()); + } + }; + + + + self.loaded_dependencies.push(DependencyTreeNode { + name: read_toml.package.name.clone(), + version: read_toml.package.version.clone(), + package: PackageDetails { + version: Default::default(), + mutual_exclusive: ProjectSource::Path(PathBuf::new()), + git_tag: None, + git_rev: None, + }, + location: temp.clone(), + include_path: lib.location.clone(), + hash: lock.checksum.clone(), + dependencies: vec![], + properties: lib.properties.clone(), + }); + } + + Ok(()) + } + + pub fn create_library_folder( + &self, + source_path: &PathBuf, + target_path: &PathBuf, + ) -> anyhow::Result<()> { + fs::create_dir_all(target_path)?; + for (_, dep) in self.dependencies.iter() { + let local_source = source_path.clone().join(&dep.checksum); + let find_source = target_path.clone().join(&dep.name); + fs::create_dir_all(&find_source)?; + copy_dir_all(&local_source, &find_source)?; + } + + return Ok(()); + } + + pub fn aggregate_target_properties(&self) -> anyhow::Result { + let mut i = LibraryTargetProperties::default(); + for tp in &self.loaded_dependencies { + i.merge(&tp.properties)?; + } + + return Ok(i); + } +} diff --git a/src/package/management.rs b/src/package/management.rs new file mode 100644 index 0000000..0a6bff9 --- /dev/null +++ b/src/package/management.rs @@ -0,0 +1,358 @@ +use colored::Colorize; +use log::error; +use versions::{Requirement, Versioning}; + +use std::collections::HashMap; +use std::fs; +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use url::{ParseError, Url}; + +use crate::package::lock::{PackageLockSource, PackageLockSourceType}; +use crate::package::{ + lock::DependencyLock, + target_properties::LibraryTargetProperties, + tree::{DependencyTreeNode, GitLock, PackageDetails, ProjectSource}, + ConfigFile, LIBRARY_DIRECTORY, +}; +use crate::util::errors::LingoError; + +#[derive(Default)] +pub struct DependencyManager { + /// queue of packages that need processing + pulling_queue: Vec<(String, PackageDetails)>, + /// the flatten dependency tree with selected packages from the dependency tree + lock: DependencyLock, +} + +/// this copies all the files recursively from one location to another +pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> std::io::Result<()> { + fs::create_dir_all(&dst)?; + + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + +impl TryFrom<&PackageLockSource> for PackageDetails { + type Error = ParseError; + + fn try_from(value: &PackageLockSource) -> Result { + let url = &value.uri; + Ok(PackageDetails { + version: Default::default(), + mutual_exclusive: match value.source_type { + PackageLockSourceType::REGISTRY => { + todo!() + } + PackageLockSourceType::GIT => ProjectSource::Git(Url::from_str(url)?), + PackageLockSourceType::TARBALL => ProjectSource::TarBall(Url::from_str(url)?), + PackageLockSourceType::PATH => ProjectSource::Path(PathBuf::from(url)), + }, + git_tag: value.rev.clone().map(|rev| GitLock::Rev(rev)), + git_rev: value.rev.clone(), + }) + } +} + +impl PackageDetails { + /// this function fetches the specified location and places it at the given location + pub fn fetch(&mut self, library_path: &PathBuf) -> anyhow::Result<()> { + match &self.mutual_exclusive { + ProjectSource::Path(path_buf) => { + let src = fs::canonicalize(path_buf)?; + let dst = fs::canonicalize(library_path)?; + Ok(copy_dir_all(src, dst)?) + } + ProjectSource::Git(git_url) => { + let repo = git2::Repository::clone(git_url.as_str(), library_path)?; + + if let Some(git_lock) = &self.git_tag { + let name = match git_lock { + GitLock::Tag(tag) => tag, + GitLock::Branch(branch) => branch, + GitLock::Rev(rev) => rev, + }; + + // TODO: this produces hard to debug output + let (object, reference) = repo.revparse_ext(&name)?; + repo.checkout_tree(&object, None)?; + + match reference { + // gref is an actual reference like branches or tags + Some(gref) => { + self.git_rev = gref.target().map(|v| v.to_string()); + repo.set_head(gref.name().unwrap()) + } + // this is a commit, not a reference + None => repo.set_head_detached(object.id()), + }? + } + + Ok(()) + } + _ => Ok(()), + } + } +} + +impl DependencyManager { + pub fn from_dependencies( + dependencies: Vec<(String, PackageDetails)>, + target_path: &PathBuf, + ) -> anyhow::Result { + // create library folder + let library_path = target_path.clone().join(LIBRARY_DIRECTORY); + fs::create_dir_all(&library_path)?; + + let mut manager; + let mut lock: DependencyLock; + let lock_file = target_path.clone().join("../Lingo.lock"); + + // checks if a Lingo.lock file exists + if lock_file.exists() { + // reads and parses Lockfile + lock = toml::from_str::(&fs::read_to_string(lock_file)?) + .expect("cannot parse lock"); + + // if a lock file is present it will load the dependencies from it and checks + // integrity of the build directory + if let Ok(()) = lock.init(&target_path.clone().join("lfc_include")) { + return Ok(DependencyManager { + pulling_queue: vec![], + lock, + }); + } + } + + // creates a new dependency manager object + manager = DependencyManager::default(); + + // starts recursively pulling dependencies + let root_nodes = manager.pull(dependencies.clone(), target_path)?; + + // flattens the dependency tree and makes the package selection + let selection = DependencyManager::flatten(root_nodes.clone())?; + + // creates a lock file struct from the selected packages + lock = DependencyLock::create(selection); + + // writes the lock file down + let mut lock_file = File::create(target_path.join("../Lingo.lock"))?; + let serialized_toml = toml::to_string(&lock).expect("cannot generate toml"); + + lock_file.write_all(serialized_toml.as_ref())?; + + // moves the selected packages into the include folder + let include_folder = target_path.clone().join("lfc_include"); + lock.create_library_folder(&library_path, &include_folder) + .expect("creating lock folder failed"); + + // saves the lockfile with the dependency manager + manager.lock = lock; + + Ok(manager) + } + + pub fn pull( + &mut self, + mut dependencies: Vec<(String, PackageDetails)>, + root_path: &Path, + ) -> anyhow::Result> { + let mut sub_dependencies = vec![]; + self.pulling_queue.append(&mut dependencies); + let sub_dependency_path = root_path.join("libraries"); + //fs::remove_dir_all(&sub_dependency_path)?; + fs::create_dir_all(&sub_dependency_path)?; + + while !self.pulling_queue.is_empty() { + if let Some((package_name, package_details)) = self.pulling_queue.pop() { + print!("{} {} ...", "Cloning".green().bold(), package_name); + let node = match self.non_recursive_fetching( + &package_name, + package_details, + &sub_dependency_path, + ) { + Ok(value) => value, + Err(e) => { + return Err(e); + } + }; + + sub_dependencies.push(node); + } else { + break; + } + } + + //dependencies + Ok(sub_dependencies) + } + + pub(crate) fn non_recursive_fetching( + &mut self, + name: &str, + mut package: PackageDetails, + base_path: &Path, + ) -> anyhow::Result { + // creating the directory where the library will be housed + let library_path = base_path; //.join("libs"); + // place where to drop the source + let temporary_path = library_path.join("temporary"); + let _ = fs::remove_dir_all(&temporary_path); + let _ = fs::create_dir_all(&temporary_path); + + // directory where the dependencies will be dropped + + // creating the necessary directories + fs::create_dir_all(&library_path)?; + fs::create_dir_all(&temporary_path)?; + + // cloning the specified package + package.fetch(&temporary_path)?; + + let hash = sha1dir::checksum_current_dir(&temporary_path, false); + let include_path = library_path.join(hash.to_string()); + + let lingo_toml_text = fs::read_to_string(temporary_path.clone().join("Lingo.toml"))?; + let read_toml = toml::from_str::(&lingo_toml_text)?.to_config(&temporary_path); + + println!(" {}", read_toml.package.version); + + let config = match read_toml.library { + Some(value) => value, + None => { + // error we expected a library here + return Err( + LingoError::NoLibraryInLingoToml(library_path.display().to_string()).into(), + ); + } + }; + + if !package.version.matches(&read_toml.package.version) { + error!("version mismatch between specified location and requested version requirement"); + return Err(LingoError::LingoVersionMismatch(format!( + "requested version {} got version {}", + package.version, read_toml.package.version + )) + .into()); + } + + let dependencies = vec![]; + + for dep in read_toml.dependencies { + self.pulling_queue.push(dep); + } + + fs::create_dir_all(&include_path)?; + copy_dir_all(&temporary_path, &include_path)?; + + Ok(DependencyTreeNode { + name: name.to_string(), + package: package.clone(), + location: include_path.clone(), + include_path: config.location.clone(), + dependencies: dependencies.clone(), + hash: hash.to_string(), + version: read_toml.package.version.clone(), + properties: config.properties, + }) + } + + fn flatten<'a>(root_nodes: Vec) -> anyhow::Result> { + // implementation idea: + // 1. we collect all the version requirements for packages => are the different + // constraints satisfiable ? + // 2. we collect all the different sources + // 3. finding the set of sources that satisfies the set of version constraints + // 4. pick the newest version from that set + // TODO: later we can probably do this in one pass + + let mut constraints = HashMap::<&String, Vec>::new(); + let mut sources = HashMap::<&String, Vec<&DependencyTreeNode>>::new(); + + // this basically flattens the + let mut nodes = Vec::new(); + for node in root_nodes { + let mut children = node.aggregate(); + nodes.append(&mut children); + } + + for node in &nodes { + let constraint = &node.package.version; + + constraints + .entry(&node.name) + .and_modify(|value| { + value.push(constraint.clone()); + }) + .or_insert(vec![constraint.clone()]); + + sources + .entry(&node.name) + .and_modify(move |value| { + value.push(&node); + }) + .or_insert(vec![&node]); + } + + let merged: Vec<(&String, Vec, Vec<&DependencyTreeNode>)> = constraints + .into_iter() + .filter_map(move |(key, requirements)| { + sources + .get_mut(&key) + .map(move |location| (key, requirements, location.clone())) + }) + .collect(); + + let mut selection = Vec::new(); + + for (_, requirements, location) in merged { + //TODO: replace this in the future by first merging all the requirements + // (determine upper and lower bound) + + let mut filtered_results: Vec<&DependencyTreeNode> = location + .into_iter() + .filter(|location| { + let filter = |version: &Versioning| { + for requirement in &requirements { + if !requirement.matches(version) { + return false; + } + } + true + }; + + filter(&location.version) + }) + .collect(); + + if filtered_results.is_empty() { + error!("no viable package was found that fulfills all the requirements"); + } + + filtered_results.sort_by_key(|value| value.version.clone()); + + let package = filtered_results + .last() + .expect("There should be at least one viable package remaining!"); + + selection.push((*package).clone()); + } + + Ok(selection) + } + + pub fn get_target_properties(&self) -> anyhow::Result { + self.lock.aggregate_target_properties() + } +} diff --git a/src/package/mod.rs b/src/package/mod.rs index 70a55f3..cc8258a 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -1,55 +1,137 @@ -use crate::args::{BuildSystem, InitArgs, Platform, TargetLanguage}; -use crate::util::{analyzer, copy_recursively}; +pub mod lock; +pub mod management; +pub mod tree; -use serde_derive::{Deserialize, Serialize}; +pub mod target_properties; +use git2::Repository; +use serde::de::{Error, Visitor}; +use serde::{Deserializer, Serializer}; +use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; +use tempfile::tempdir; +use versions::Versioning; +use which::which; use std::fs::{read_to_string, remove_dir_all, remove_file, write}; use std::io::ErrorKind; use std::path::{Path, PathBuf}; -use std::{env, io}; - -use crate::args::BuildSystem::{CMake, LFC}; -use crate::util::errors::{BuildResult, LingoError}; -use git2::Repository; -use tempfile::tempdir; -use which::which; +use std::str::FromStr; +use std::{env, fmt, io}; + +use crate::args::{ + BuildSystem, + BuildSystem::{CMake, LFC}, + InitArgs, Platform, TargetLanguage, +}; +use crate::package::{ + target_properties::{ + AppTargetProperties, AppTargetPropertiesFile, LibraryTargetProperties, + LibraryTargetPropertiesFile, + }, + tree::PackageDetails, +}; +use crate::util::{ + analyzer, copy_recursively, + errors::{BuildResult, LingoError}, +}; + +/// place where are the build artifacts will be dropped +pub const OUTPUT_DIRECTORY: &str = "build"; +/// name of the folder inside the `OUTPUT_DIRECTORY` where libraries +/// will be loaded (cloned, extracted, copied) into for further processing. +pub const LIBRARY_DIRECTORY: &str = "libraries"; + +/// default folder for lf executable files +const DEFAULT_EXECUTABLE_FOLDER: &str = "src"; + +/// default folder for lf library files +const DEFAULT_LIBRARY_FOLDER: &str = "lib"; fn is_valid_location_for_project(path: &std::path::Path) -> bool { - !path.join("src").exists() && !path.join(".git").exists() && !path.join("application").exists() + !path.join(DEFAULT_EXECUTABLE_FOLDER).exists() + && !path.join(".git").exists() + && !path.join(DEFAULT_LIBRARY_FOLDER).exists() } +/// list of apps inside a toml file #[derive(Deserialize, Serialize, Clone)] pub struct AppVec { pub app: Vec, } -/// the Lingo.toml format is defined by this struct +/// The Lingo.toml format is defined by this struct #[derive(Clone, Deserialize, Serialize)] pub struct ConfigFile { /// top level package description pub package: PackageDescription, - /// high level properties that are set for every app inside the package - pub properties: HashMap, - /// list of apps defined inside this package #[serde(rename = "app")] - pub apps: Vec, + pub apps: Option>, + + /// library exported by this Lingo Toml + #[serde(rename = "lib")] + pub library: Option, + + /// Dependencies for required to build this Lingua-Franca Project + pub dependencies: HashMap, } -#[derive(Clone, Deserialize, Serialize)] +/// This struct is used after filling in all the defaults +#[derive(Clone)] pub struct Config { /// top level package description pub package: PackageDescription, - /// high level properties that are set for every app inside the package - pub properties: HashMap, - /// list of apps defined inside this package - #[serde(rename = "app")] pub apps: Vec, + + /// library exported by this package + pub library: Option, + + /// Dependencies for required to build this Lingua-Franca Project + pub dependencies: HashMap, +} + +/// The Format inside the Lingo.toml under [lib] +#[derive(Clone, Deserialize, Serialize)] +pub struct LibraryFile { + /// if not specified will default to value specified in the package description + pub name: Option, + + /// if not specified will default to ./lib + pub location: Option, + + /// target language of the library + pub target: TargetLanguage, + + /// platform of this project + pub platform: Option, + + /// target properties of that lingua-franca app + pub properties: LibraryTargetPropertiesFile, +} + +#[derive(Clone)] +pub struct Library { + /// if not specified will default to value specified in the package description + pub name: String, + + /// if not specified will default to ./src + pub location: PathBuf, + + /// target of the app + pub target: TargetLanguage, + + /// platform of this project + pub platform: Platform, + + /// target properties of that lingua-franca app + pub properties: LibraryTargetProperties, + + /// Root directory where to place src-gen and other compilation-specifics stuff. + pub output_root: PathBuf, } /// Schema of the configuration parsed from the Lingo.toml @@ -64,14 +146,14 @@ pub struct AppFile { /// target of the app pub target: TargetLanguage, + /// platform of this project pub platform: Option, - pub dependencies: HashMap, - - pub properties: HashMap, + /// target properties of that lingua-franca app + pub properties: AppTargetPropertiesFile, } -#[derive(Clone, Deserialize, Serialize)] +#[derive(Clone)] pub struct App { /// Absolute path to the directory where the Lingo.toml file is located. pub root_path: PathBuf, @@ -81,19 +163,92 @@ pub struct App { pub output_root: PathBuf, /// Absolute path to the main reactor file. pub main_reactor: PathBuf, + /// main reactor name + pub main_reactor_name: String, /// target language of this lf program pub target: TargetLanguage, /// platform for which this program should be compiled pub platform: Platform, + /// target properties of that lingua-franca app + pub properties: AppTargetProperties, +} - pub dependencies: HashMap, - pub properties: HashMap, +impl AppFile { + const DEFAULT_MAIN_REACTOR_RELPATH: &'static str = "src/Main.lf"; + pub fn convert(self, package_name: &str, path: &Path) -> App { + let file_name: Option = match self.main.clone() { + Some(path) => path + .file_stem() + .to_owned() + .and_then(|x| x.to_str()) + .map(|x| x.to_string()), + None => None, + }; + let name = self + .name + .unwrap_or(file_name.unwrap_or(package_name.to_string()).to_string()); + + let mut abs = path.to_path_buf(); + abs.push( + self.main + .unwrap_or(Self::DEFAULT_MAIN_REACTOR_RELPATH.into()), + ); + + let temp = abs + .clone() + .file_name() + .expect("cannot extract file name") + .to_str() + .expect("cannot convert path to string") + .to_string(); + let main_reactor_name = &temp[..temp.len() - 3]; + + App { + root_path: path.to_path_buf(), + name, + output_root: path.join(OUTPUT_DIRECTORY), + main_reactor: abs, + main_reactor_name: main_reactor_name.to_string(), + target: self.target, + platform: self.platform.unwrap_or(Platform::Native), + properties: self.properties.from(path), + } + } +} + +impl LibraryFile { + pub fn convert(self, package_name: &str, path: &Path) -> Library { + let file_name: Option = match self.location.clone() { + Some(path) => path + .file_stem() + .to_owned() + .and_then(|x| x.to_str()) + .map(|x| x.to_string()), + None => None, + }; + let name = self + .name + .unwrap_or(file_name.unwrap_or(package_name.to_string()).to_string()); + + Library { + name, + location: { + let mut abs = path.to_path_buf(); + abs.push(self.location.unwrap_or(DEFAULT_LIBRARY_FOLDER.into())); + abs + }, + target: self.target, + platform: self.platform.unwrap_or(Platform::Native), + properties: self.properties.from(path), + output_root: path.join(OUTPUT_DIRECTORY), + } + } } impl App { pub fn build_system(&self) -> BuildSystem { match self.target { - TargetLanguage::C => LFC, + TargetLanguage::C => CMake, TargetLanguage::Cpp => CMake, TargetLanguage::TypeScript => { if which("pnpm").is_ok() { @@ -128,41 +283,54 @@ impl App { } } -/// Simple or DetailedDependency -#[derive(Clone, Deserialize, Serialize)] -pub enum FileDependency { - // the version string - Simple(String), - /// version string and source - Advanced(DetailedDependency), +fn serialize_version(version: &Versioning, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_str(&version.to_string()) } -/// Dependency with source and version -#[derive(Clone, Deserialize, Serialize)] -pub struct DetailedDependency { - version: String, - git: Option, - tarball: Option, - zip: Option, +struct VersioningVisitor; + +impl<'de> Visitor<'de> for VersioningVisitor { + type Value = Versioning; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an valid semantic version") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + Versioning::from_str(v).map_err(|_| E::custom("not a valid version")) + } +} + +fn deserialize_version<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + deserializer.deserialize_str(VersioningVisitor) } #[derive(Deserialize, Serialize, Clone)] pub struct PackageDescription { pub name: String, - pub version: String, + #[serde( + serialize_with = "serialize_version", + deserialize_with = "deserialize_version" + )] + pub version: Versioning, pub authors: Option>, pub website: Option, pub license: Option, pub description: Option, - pub homepage: Option, } impl ConfigFile { - // FIXME: The default should be that it searches the `src` directory for a main reactor - const DEFAULT_MAIN_REACTOR_RELPATH: &'static str = "src/Main.lf"; - - pub fn new_for_init_task(init_args: InitArgs) -> io::Result { - let src_path = Path::new("./src"); + pub fn new_for_init_task(init_args: &InitArgs) -> io::Result { + let src_path = Path::new(DEFAULT_EXECUTABLE_FOLDER); let main_reactors = if src_path.exists() { analyzer::find_main_reactors(src_path)? } else { @@ -179,8 +347,7 @@ impl ConfigFile { main: Some(spec.path), target: spec.target, platform: Some(init_args.platform), - dependencies: HashMap::new(), - properties: HashMap::new(), + properties: Default::default(), }) .collect::>(); @@ -193,21 +360,21 @@ impl ConfigFile { .expect("cannot get file name") .to_string_lossy() .to_string(), - version: "0.1.0".to_string(), + version: Versioning::from_str("0.1.0").unwrap(), authors: None, website: None, license: None, description: None, - homepage: None, }, - properties: HashMap::new(), - apps: app_specs, + dependencies: HashMap::default(), + apps: Some(app_specs), + library: Option::default(), }; Ok(result) } pub fn write(&self, path: &Path) -> io::Result<()> { - let toml_string = toml::to_string(&self).unwrap(); + let toml_string = toml::to_string(&self).expect("cannot serialize toml"); write(path, toml_string) } @@ -219,9 +386,9 @@ impl ConfigFile { } // Sets up a standard LF project for "native" development and deployment - pub fn setup_native(&self) -> BuildResult { + pub fn setup_native(&self, target_language: TargetLanguage) -> BuildResult { std::fs::create_dir_all("./src")?; - let hello_world_code: &'static str = match self.apps[0].target { + let hello_world_code: &'static str = match target_language { TargetLanguage::Cpp => include_str!("../../defaults/HelloCpp.lf"), TargetLanguage::C => include_str!("../../defaults/HelloC.lf"), TargetLanguage::Python => include_str!("../../defaults/HelloPy.lf"), @@ -262,17 +429,20 @@ impl ConfigFile { Ok(()) } - pub fn setup_example(&self) -> BuildResult { + pub fn setup_example( + &self, + platform: Platform, + target_language: TargetLanguage, + ) -> BuildResult { if is_valid_location_for_project(Path::new(".")) { - match self.apps[0].platform { - Some(Platform::Native) => self.setup_native(), - Some(Platform::Zephyr) => self.setup_zephyr(), - Some(Platform::RP2040) => self.setup_rp2040(), - _ => Ok(()), + match platform { + Platform::Native => self.setup_native(target_language), + Platform::Zephyr => self.setup_zephyr(), + Platform::RP2040 => self.setup_rp2040(), } } else { Err(Box::new(LingoError::InvalidProjectLocation( - env::current_dir().unwrap(), + env::current_dir().expect("cannot fetch current working directory"), ))) } } @@ -280,43 +450,18 @@ impl ConfigFile { /// The `path` is the path to the directory containing the Lingo.toml file. pub fn to_config(self, path: &Path) -> Config { let package_name = &self.package.name; + Config { - properties: self.properties, + //properties: self.properties, apps: self .apps + .unwrap_or_default() .into_iter() - .map(|app| { - let file_name: Option = match app.main.clone() { - Some(path) => path - .file_stem() - .to_owned() - .and_then(|x| x.to_str()) - .map(|x| x.to_string()), - None => None, - }; - let name = app - .name - .unwrap_or(file_name.unwrap_or(package_name.clone()).to_string()); - App { - root_path: path.to_path_buf(), - name, - output_root: path.join("target"), - main_reactor: { - let mut abs = path.to_path_buf(); - abs.push( - app.main - .unwrap_or(Self::DEFAULT_MAIN_REACTOR_RELPATH.into()), - ); - abs - }, - target: app.target, - platform: app.platform.unwrap_or(Platform::Native), - dependencies: app.dependencies, - properties: app.properties, - } - }) + .map(|app_file| app_file.convert(package_name, path)) .collect(), - package: self.package, + package: self.package.clone(), + library: self.library.map(|lib| lib.convert(package_name, path)), + dependencies: self.dependencies, } } } diff --git a/src/package/target_properties.rs b/src/package/target_properties.rs new file mode 100644 index 0000000..b275e86 --- /dev/null +++ b/src/package/target_properties.rs @@ -0,0 +1,171 @@ +use serde::{Deserialize, Serialize, Serializer}; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io::Write; +use std::path::{Path, PathBuf}; + +pub trait CMakeLoader { + fn read_file(&mut self, path: &str) -> anyhow::Result; +} + +impl Serialize for AutoCmakeLoad { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_none() + } +} + +impl Display for AutoCmakeLoad { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub enum GenericTargetPropertiesFile { + Library(LibraryTargetPropertiesFile), + App(AppTargetPropertiesFile), +} + +#[derive(Clone)] +pub enum GenericTargetProperties { + Library(LibraryTargetProperties), + App(AppTargetProperties), +} + +#[derive(Clone, Default)] +pub struct AutoCmakeLoad(String); + +impl MergeTargetProperty for AutoCmakeLoad { + fn merge(&mut self, parent: &AutoCmakeLoad) -> anyhow::Result<()> { + self.0 += &*("\n# ------------------------- \n".to_owned() + &*parent.0); + Ok(()) + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct LibraryTargetPropertiesFile { + /// cmake include only available for C and CPP + #[serde(rename = "cmake-include", default)] + cmake_include: Option, + + /// files that should be compiled and linked + #[serde(rename = "sources", default)] + sources: Vec, + + /// list of files that should be made available to the user + #[serde(rename = "sources", default)] + artifacts: Vec, +} + +#[derive(Clone, Default)] +pub struct LibraryTargetProperties { + /// cmake include only available for C and CPP + pub cmake_include: AutoCmakeLoad, + + /// files that should be compiled and linked + pub sources: Vec, + + /// list of files that should be made available to the user + pub artifacts: Vec, +} + +impl LibraryTargetPropertiesFile { + pub fn from(self, base_path: &Path) -> LibraryTargetProperties { + LibraryTargetProperties { + cmake_include: AutoCmakeLoad( + self.cmake_include + .map(|cmake_file| { + let absolute_path = base_path.join(cmake_file); + std::fs::read_to_string(absolute_path) + .expect("invalid file {absolute_path}") + }) + .unwrap_or_default(), + ), + sources: self.sources, + artifacts: self.artifacts, + } + } +} + +#[derive(Serialize, Deserialize, Clone, Default)] +pub struct AppTargetPropertiesFile { + /// cmake include only available for C and CPP + #[serde(rename = "cmake-include", default)] + cmake_include: Option, + + /// if the runtime should wait for physical time to catch up + #[serde(default)] + pub fast: bool, +} + +#[derive(Clone, Default)] +pub struct AppTargetProperties { + /// cmake include only available for C and CPP + cmake_include: AutoCmakeLoad, + + /// if the runtime should wait for physical time to catch up + pub fast: bool, +} + +impl AppTargetPropertiesFile { + pub fn from(self, base_path: &Path) -> AppTargetProperties { + AppTargetProperties { + cmake_include: AutoCmakeLoad( + self.cmake_include + .map(|cmake_file| { + let absolute_path = base_path.join(cmake_file); + std::fs::read_to_string(&absolute_path) + .expect("invalid file {absolute_path}") + }) + .unwrap_or_default(), + ), + fast: self.fast, + } + } +} + +pub trait MergeTargetProperty { + fn merge(&mut self, other: &Self) -> anyhow::Result<()>; +} + +pub trait MergeTargetProperties { + fn merge(&mut self, other: &LibraryTargetProperties) -> anyhow::Result<()>; +} + +impl MergeTargetProperties for LibraryTargetProperties { + fn merge(&mut self, partent: &LibraryTargetProperties) -> anyhow::Result<()> { + self.cmake_include.merge(&partent.cmake_include)?; + Ok(()) + } +} + +impl MergeTargetProperties for AppTargetProperties { + fn merge(&mut self, parent: &LibraryTargetProperties) -> anyhow::Result<()> { + self.cmake_include.merge(&parent.cmake_include)?; + Ok(()) + } +} + +impl MergeTargetProperties for GenericTargetProperties { + fn merge(&mut self, other: &LibraryTargetProperties) -> anyhow::Result<()> { + match self { + GenericTargetProperties::Library(own_lib) => own_lib.merge(other), + GenericTargetProperties::App(own_app) => own_app.merge(other), + } + } +} + +impl AppTargetProperties { + pub fn write_artifacts(&self, library_folder: &Path) -> anyhow::Result<()> { + let file = library_folder.join("aggregated_cmake_include.cmake"); + + let mut fd = std::fs::File::create(file)?; + fd.write_all(self.cmake_include.0.as_ref())?; + fd.flush()?; + + Ok(()) + } +} diff --git a/src/package/tree.rs b/src/package/tree.rs new file mode 100644 index 0000000..734065d --- /dev/null +++ b/src/package/tree.rs @@ -0,0 +1,93 @@ +use serde::{Deserialize, Serialize}; +use url::Url; +use versions::{Requirement, Versioning}; + +use std::path::PathBuf; + +use crate::package::target_properties::LibraryTargetProperties; + +#[derive(Clone, Deserialize, Serialize, Debug)] +pub enum ProjectSource { + #[serde(rename = "git")] + Git(Url), + #[serde(rename = "tarball")] + TarBall(Url), + #[serde(rename = "path")] + Path(PathBuf), + //#[serde(rename = "empty")] + //Empty, +} + +#[derive(Serialize, Deserialize, Clone)] +pub enum GitLock { + #[serde(rename = "tag")] + Tag(String), + + #[serde(rename = "branch")] + Branch(String), + + #[serde(rename = "rev")] + Rev(String), +} + +/// Dependency with source and version +#[derive(Clone, Deserialize, Serialize)] +pub struct PackageDetails { + #[serde( + deserialize_with = "Requirement::deserialize", + serialize_with = "Requirement::serialize" + )] + pub(crate) version: Requirement, + #[serde(flatten)] + pub(crate) mutual_exclusive: ProjectSource, + #[serde(flatten)] + pub(crate) git_tag: Option, + #[serde(skip)] + pub(crate) git_rev: Option, +} + +#[derive(Clone)] +pub struct DependencyTreeNode { + /// Name of this Package + pub(crate) name: String, + /// version specified in the Lingo.toml + pub(crate) version: Versioning, + /// source of this package + pub(crate) package: PackageDetails, + /// location of where the packed has been cloned to + pub(crate) location: PathBuf, + /// relative path inside the package where the to include contents live + pub(crate) include_path: PathBuf, + /// hash of the package + pub(crate) hash: String, + /// required dependencies to build this package + pub(crate) dependencies: Vec, + /// required dependencies to build this package + pub(crate) properties: LibraryTargetProperties, +} + +impl DependencyTreeNode { + pub fn shallow_clone(&self) -> Self { + Self { + name: self.name.clone(), + version: self.version.clone(), + package: self.package.clone(), + location: self.location.clone(), + include_path: self.include_path.clone(), + hash: self.hash.clone(), + dependencies: Vec::new(), + properties: Default::default(), + } + } + + pub fn aggregate(&self) -> Vec { + let mut aggregator = vec![self.shallow_clone()]; + + for dependency in &self.dependencies { + let mut aggregation = dependency.aggregate(); + aggregator.append(&mut aggregation); + } + + aggregator + } +} diff --git a/src/util/analyzer.rs b/src/util/analyzer.rs index f0950cb..6f82433 100644 --- a/src/util/analyzer.rs +++ b/src/util/analyzer.rs @@ -18,7 +18,6 @@ const DEFAULT_TARGET: TargetLanguage = TargetLanguage::C; /// this functions searches inside the file for a main reactor declaration fn search_inside_file(path: &Path) -> io::Result> { - println!("Searching File {:?}", path); let content = std::fs::read_to_string(path)?; let mut target: TargetLanguage = DEFAULT_TARGET; diff --git a/src/util/errors.rs b/src/util/errors.rs index 5cce66e..4f9daa2 100644 --- a/src/util/errors.rs +++ b/src/util/errors.rs @@ -16,6 +16,8 @@ pub enum LingoError { InvalidProjectLocation(PathBuf), UseWestBuildToBuildApp, InvalidMainReactor, + NoLibraryInLingoToml(String), + LingoVersionMismatch(String), } impl Display for LingoError { @@ -42,6 +44,18 @@ impl Display for LingoError { "Not a valid path path to a file that contains a main reactor" ) } + LingoError::NoLibraryInLingoToml(path) => { + write!( + f, + "A dependency was specified that doesn't export a library see {path}" + ) + } + LingoError::LingoVersionMismatch(message) => { + write!( + f, + "Version specified in Lingo.toml doesn't match the version in the location {message}" + ) + } } } } diff --git a/src/util/mod.rs b/src/util/mod.rs index 1e4ac4a..d96a9c2 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,8 +1,8 @@ +use which::which; + use std::path::{Path, PathBuf}; use std::{fs, io}; -use which::which; - pub mod analyzer; mod command_line; pub mod errors; From 05edd2ec64682e9a9d933b833cb0338121e0dd20 Mon Sep 17 00:00:00 2001 From: tanneberger Date: Sat, 21 Sep 2024 01:08:39 +0200 Subject: [PATCH 10/10] formatting and preparing for merge --- Cargo.lock | 9 +++++---- Cargo.toml | 2 +- src/args.rs | 1 - src/backends/cmake_c.rs | 8 +------- src/backends/cmake_cpp.rs | 10 ++++++---- src/backends/mod.rs | 4 ++-- src/package/lock.rs | 12 +++++------- src/package/management.rs | 20 ++++++++++---------- src/package/mod.rs | 2 +- 9 files changed, 31 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b221d53..02b52cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,9 +362,9 @@ checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -891,8 +891,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "versions" -version = "6.1.0" -source = "git+https://github.com/tanneberger/rs-versions.git#7802574585abc55d06b5e21e20be7ced02b5ffab" +version = "6.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139" dependencies = [ "itertools", "nom", diff --git a/Cargo.toml b/Cargo.toml index 6aa8f9d..1133fc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ git2 = "0.18" tempfile = "3.0" url = { version = "2.5", features = ["serde"] } anyhow = "1.0" -versions = { git = "https://github.com/tanneberger/rs-versions.git", features = ["serde"]} +versions = { version = "6.3.2", features = ["serde"]} log = "0.4" sha1dir = { version = "1.0", git = "https://github.com/tanneberger/sha1dir" } colored = "2.1.0" diff --git a/src/args.rs b/src/args.rs index f391ba6..f60d98d 100644 --- a/src/args.rs +++ b/src/args.rs @@ -32,7 +32,6 @@ pub enum BuildSystem { #[derive(Args, Debug)] pub struct BuildArgs { /// Which build system to use - /// TODO: discuss this #[arg(short, long)] pub build_system: Option, diff --git a/src/backends/cmake_c.rs b/src/backends/cmake_c.rs index f6075ef..a3718bf 100644 --- a/src/backends/cmake_c.rs +++ b/src/backends/cmake_c.rs @@ -17,23 +17,19 @@ fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { // location of the cmake file let app_build_folder = app.src_gen_dir().join(&app.main_reactor_name); - println!("creating {}", &app_build_folder.display()); let _ = std::fs::create_dir_all(&app_build_folder); let cmake_file = app_build_folder.clone().join("CMakeLists.txt"); - println!("writing artifacts ..."); // create potential files that come from the target properties app.properties .write_artifacts(&app_build_folder) .expect("cannot write artifacts"); // read file and append cmake include to generated cmake file - println!("reading cmake file ... {:?}", &cmake_file); let mut content = fs::read_to_string(&cmake_file)?; let include_statement = "\ninclude(./aggregated_cmake_include.cmake)"; - content += &*include_statement; + content += include_statement; - println!("writing cmake file ..."); // overwrite cmake file let mut f = fs::OpenOptions::new() .write(true) @@ -42,8 +38,6 @@ fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { f.write_all(content.as_ref()).expect("cannot write file"); f.flush().expect("cannot flush"); - println!("running file ..."); - // cmake args let mut cmake = Command::new("cmake"); cmake.arg(format!( diff --git a/src/backends/cmake_cpp.rs b/src/backends/cmake_cpp.rs index 1e049f9..eab56b5 100644 --- a/src/backends/cmake_cpp.rs +++ b/src/backends/cmake_cpp.rs @@ -71,15 +71,17 @@ fn gen_cmake_files(app: &App, options: &BuildCommandOptions) -> BuildResult { } fn do_cmake_build(results: &mut BatchBuildResults, options: &BuildCommandOptions) { - // open lingo.toml of the dependency - // read the version - // cry loud when it doesn't match out specified version - + // configure keep going parameter results.keep_going(options.keep_going); + + // start code-generation super::lfc::LFC::do_parallel_lfc_codegen(options, results, false); + + // stop process if the user request code-generation only if !options.compile_target_code { return; } + results // generate all CMake files ahead of time .map(|app| gen_cmake_files(app, options)) diff --git a/src/backends/mod.rs b/src/backends/mod.rs index cbbc86d..60ce8e2 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -13,15 +13,15 @@ use crate::package::{ use crate::util::errors::{AnyError, BuildResult, LingoError}; pub mod cmake_c; - pub mod cmake_cpp; pub mod lfc; pub mod npm; pub mod pnpm; +#[allow(clippy::single_match)] // there more options will be added to this match block pub fn execute_command<'a>(command: &CommandSpec, config: &'a mut Config) -> BatchBuildResults<'a> { let mut result = BatchBuildResults::new(); - let dependencies = Vec::from_iter(config.dependencies.clone().into_iter()); + let dependencies = Vec::from_iter(config.dependencies.clone()); match command { CommandSpec::Build(_build) => { diff --git a/src/package/lock.rs b/src/package/lock.rs index dd6c39c..5722b70 100644 --- a/src/package/lock.rs +++ b/src/package/lock.rs @@ -220,7 +220,7 @@ impl DependencyLock { error!("checksum does not match aborting!"); } - let lingo_toml_text = fs::read_to_string(&temp.join("Lingo.toml"))?; + let lingo_toml_text = fs::read_to_string(temp.join("Lingo.toml"))?; let read_toml = toml::from_str::(&lingo_toml_text)?.to_config(&temp); println!( @@ -241,8 +241,6 @@ impl DependencyLock { } }; - - self.loaded_dependencies.push(DependencyTreeNode { name: read_toml.package.name.clone(), version: read_toml.package.version.clone(), @@ -265,18 +263,18 @@ impl DependencyLock { pub fn create_library_folder( &self, - source_path: &PathBuf, + source_path: &Path, target_path: &PathBuf, ) -> anyhow::Result<()> { fs::create_dir_all(target_path)?; for (_, dep) in self.dependencies.iter() { - let local_source = source_path.clone().join(&dep.checksum); + let local_source = source_path.join(&dep.checksum); let find_source = target_path.clone().join(&dep.name); fs::create_dir_all(&find_source)?; copy_dir_all(&local_source, &find_source)?; } - return Ok(()); + Ok(()) } pub fn aggregate_target_properties(&self) -> anyhow::Result { @@ -285,6 +283,6 @@ impl DependencyLock { i.merge(&tp.properties)?; } - return Ok(i); + Ok(i) } } diff --git a/src/package/management.rs b/src/package/management.rs index 0a6bff9..a5c0dc5 100644 --- a/src/package/management.rs +++ b/src/package/management.rs @@ -58,7 +58,7 @@ impl TryFrom<&PackageLockSource> for PackageDetails { PackageLockSourceType::TARBALL => ProjectSource::TarBall(Url::from_str(url)?), PackageLockSourceType::PATH => ProjectSource::Path(PathBuf::from(url)), }, - git_tag: value.rev.clone().map(|rev| GitLock::Rev(rev)), + git_tag: value.rev.clone().map(GitLock::Rev), git_rev: value.rev.clone(), }) } @@ -84,7 +84,7 @@ impl PackageDetails { }; // TODO: this produces hard to debug output - let (object, reference) = repo.revparse_ext(&name)?; + let (object, reference) = repo.revparse_ext(name)?; repo.checkout_tree(&object, None)?; match reference { @@ -108,15 +108,15 @@ impl PackageDetails { impl DependencyManager { pub fn from_dependencies( dependencies: Vec<(String, PackageDetails)>, - target_path: &PathBuf, + target_path: &Path, ) -> anyhow::Result { // create library folder - let library_path = target_path.clone().join(LIBRARY_DIRECTORY); + let library_path = target_path.join(LIBRARY_DIRECTORY); fs::create_dir_all(&library_path)?; let mut manager; let mut lock: DependencyLock; - let lock_file = target_path.clone().join("../Lingo.lock"); + let lock_file = target_path.join("../Lingo.lock"); // checks if a Lingo.lock file exists if lock_file.exists() { @@ -126,7 +126,7 @@ impl DependencyManager { // if a lock file is present it will load the dependencies from it and checks // integrity of the build directory - if let Ok(()) = lock.init(&target_path.clone().join("lfc_include")) { + if let Ok(()) = lock.init(&target_path.join("lfc_include")) { return Ok(DependencyManager { pulling_queue: vec![], lock, @@ -153,7 +153,7 @@ impl DependencyManager { lock_file.write_all(serialized_toml.as_ref())?; // moves the selected packages into the include folder - let include_folder = target_path.clone().join("lfc_include"); + let include_folder = target_path.join("lfc_include"); lock.create_library_folder(&library_path, &include_folder) .expect("creating lock folder failed"); @@ -214,7 +214,7 @@ impl DependencyManager { // directory where the dependencies will be dropped // creating the necessary directories - fs::create_dir_all(&library_path)?; + fs::create_dir_all(library_path)?; fs::create_dir_all(&temporary_path)?; // cloning the specified package @@ -268,7 +268,7 @@ impl DependencyManager { }) } - fn flatten<'a>(root_nodes: Vec) -> anyhow::Result> { + fn flatten(root_nodes: Vec) -> anyhow::Result> { // implementation idea: // 1. we collect all the version requirements for packages => are the different // constraints satisfiable ? @@ -300,7 +300,7 @@ impl DependencyManager { sources .entry(&node.name) .and_modify(move |value| { - value.push(&node); + value.push(node); }) .or_insert(vec![&node]); } diff --git a/src/package/mod.rs b/src/package/mod.rs index cc8258a..1a5e494 100644 --- a/src/package/mod.rs +++ b/src/package/mod.rs @@ -46,7 +46,7 @@ pub const LIBRARY_DIRECTORY: &str = "libraries"; const DEFAULT_EXECUTABLE_FOLDER: &str = "src"; /// default folder for lf library files -const DEFAULT_LIBRARY_FOLDER: &str = "lib"; +const DEFAULT_LIBRARY_FOLDER: &str = "src/lib"; fn is_valid_location_for_project(path: &std::path::Path) -> bool { !path.join(DEFAULT_EXECUTABLE_FOLDER).exists()