Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ Build system for the Lingua Franca coordination language
Usage: lingo [OPTIONS] <COMMAND>

Commands:
init Initialize a Lingua Franca package
build Compile one or multiple binaries in a Lingua Franca package
update Update the dependencies and potentially build tools
run Build and run binaries
clean Remove build artifacts
help Print this message or the help of the given subcommand(s)
init Initialize a Lingua Franca package
build Compile one or multiple binaries in a Lingua Franca package
update Remove build artifacts, update the dependencies, and build
run Build and run binaries
clean Remove build artifacts
help Print this message or the help of the given subcommand(s)

Options:
-q, --quiet Do not produce any output
Expand Down
2 changes: 1 addition & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ General workflow that is happening.

```mermaid
graph TD
A[Start] --> B(Handeling User Input)
A[Start] --> B(Handling User Input)
B --> C(Parsing Lingo.toml)
C --> D(Configuring LFC)
D --> E(Invoking LFC)
Expand Down
12 changes: 12 additions & 0 deletions src/backends/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ pub fn execute_command<'a>(

match command {
CommandSpec::Build(_build) => {
log::info!(
"{} starting dependency resolution ({} declared dependencies)",
"Build step:",
dependencies.len()
);
let manager = match DependencyManager::from_dependencies(
dependencies.clone(),
&PathBuf::from(OUTPUT_DIRECTORY),
Expand All @@ -44,6 +49,7 @@ pub fn execute_command<'a>(
};

// enriching the apps with the target properties from the libraries
log::info!("Build step: merging dependency target properties");
let library_properties = manager.get_target_properties().expect("lib properties");

// merging app with library target properties
Expand All @@ -67,6 +73,12 @@ pub fn execute_command<'a>(
}

for (build_system, apps) in by_build_system {
log::info!(
"Build step: dispatching {} app(s) to {:?}/{:?}",
apps.len(),
build_system.0,
build_system.1
);
let mut sub_res = BatchBuildResults::for_apps(&apps);

sub_res.map(|app| {
Expand Down
86 changes: 82 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use liblingo::args::TargetLanguage;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, io};
use std::{env, fs, io};

use clap::Parser;
use git2::BranchType::{Local, Remote};
Expand Down Expand Up @@ -91,10 +91,55 @@ fn do_read_to_string(p: &Path) -> io::Result<String> {
std::fs::read_to_string(p)
}

fn remove_if_exists(path: &Path) -> io::Result<()> {
if path.is_dir() {
match fs::remove_dir_all(path) {
Ok(()) => log::info!("Deleted {}", path.display()),
Err(err) => {
log::error!("Failed to delete {}: {}", path.display(), err);
return Err(err);
}
}
} else if path.is_file() {
match fs::remove_file(path) {
Ok(()) => log::info!("Deleted {}", path.display()),
Err(err) => {
log::error!("Failed to delete {}: {}", path.display(), err);
return Err(err);
}
}
}
Ok(())
}

fn update(project_root: &Path) -> BuildResult {
remove_if_exists(&project_root.join("build"))?;
let lock_path = project_root.join("Lingo.lock");
if lock_path.is_file() {
let backup_path = (0..)
.map(|n| project_root.join(format!("Lingo.lock.bak{}", n)))
.find(|p| !p.exists())
.unwrap();
fs::rename(&lock_path, &backup_path)?;
log::info!("Backed up Lingo.lock to {}", backup_path.display());
}
Ok(())
}

fn main() {
print_logger::new().init().unwrap();
// parses command line arguments
let args = CommandLineArgs::parse();
let level_filter = if args.quiet {
Comment thread
tanneberger marked this conversation as resolved.
print_logger::LevelFilter::Error
} else if args.verbose {
print_logger::LevelFilter::Debug
} else {
print_logger::LevelFilter::Info
};
print_logger::new()
.level_filter(level_filter)
.init()
.unwrap();

// Finds Lingo.toml recursively inside the parent directories.
// If it exists the returned path is absolute.
Expand All @@ -116,6 +161,7 @@ fn main() {
let result = execute_command(
&mut wrapped_config,
args.command,
lingo_path.as_deref(),
Box::new(do_which),
Box::new(do_clone_and_checkout),
);
Expand Down Expand Up @@ -161,6 +207,7 @@ fn validate(config: &mut Option<Config>, command: &ConsoleCommand) -> BuildResul
fn execute_command<'a>(
config: &'a mut Option<Config>,
command: ConsoleCommand,
lingo_path: Option<&Path>,
_which_capability: WhichCapability,
git_clone_capability: GitCloneAndCheckoutCap,
) -> CommandResult<'a> {
Expand All @@ -187,7 +234,34 @@ fn execute_command<'a>(
(Some(config), ConsoleCommand::Clean) => {
CommandResult::Batch(run_command(CommandSpec::Clean, config, true))
}
_ => todo!(),
(Some(config), ConsoleCommand::Update) => {
let update_result = lingo_path
.and_then(Path::parent)
.ok_or_else(|| {
Box::new(io::Error::new(
ErrorKind::NotFound,
"Error: Missing Lingo.toml file",
)) as Box<dyn std::error::Error + Send + Sync>
})
.and_then(update);
match update_result {
Err(e) => CommandResult::Single(Err(e)),
Ok(()) => {
let default_args = BuildArgs {
build_system: None,
language: None,
platform: None,
lfc: None,
no_compile: false,
keep_going: false,
release: false,
apps: vec![],
threads: 0,
};
CommandResult::Batch(build(&default_args, config))
}
}
}
}
}

Expand Down Expand Up @@ -216,7 +290,11 @@ fn build<'a>(args: &BuildArgs, config: &'a mut Config) -> BatchBuildResults<'a>
)
}

fn run_command(task: CommandSpec, config: &mut Config, _fail_at_end: bool) -> BatchBuildResults {
fn run_command(
task: CommandSpec,
config: &mut Config,
_fail_at_end: bool,
) -> BatchBuildResults<'_> {
let _apps = config.apps.iter().collect::<Vec<_>>();
liblingo::backends::execute_command(
&task,
Expand Down
24 changes: 16 additions & 8 deletions src/package/lock.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use crate::util::sha1dir;
use colored::Colorize;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use versions::Versioning;

use log::error;
use log::{error, info};
use serde::de::Error as DeserializationError;
use serde::ser::Error as SerializationError;
use std::cmp::PartialEq;
Expand Down Expand Up @@ -213,18 +212,32 @@ impl DependencyLock {
lfc_include_folder: &Path,
git_clone_and_checkout_cap: &GitCloneAndCheckoutCap,
) -> anyhow::Result<()> {
info!(
"Build step: checking lock entries in {}",
lfc_include_folder.display()
);
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)?;
info!(
"Fetching {} from {}+{}",
lock.name, lock.source.source_type, lock.source.uri
);

details
.fetch(&temp, git_clone_and_checkout_cap)
.expect("cannot pull package");
}

info!(
"Build step: computing checksum for locked dependency {} at {}",
lock.name,
temp.display()
);
let hash = sha1dir::checksum_current_dir(&temp, false);
info!("Build step: checksum complete for {}", lock.name);

if hash.to_string() != lock.checksum {
error!("checksum does not match aborting!");
Expand All @@ -233,12 +246,7 @@ impl DependencyLock {
let lingo_toml_text = fs::read_to_string(temp.join("Lingo.toml"))?;
let read_toml = toml::from_str::<ConfigFile>(&lingo_toml_text)?.to_config(&temp);

println!(
"{} {} ... {}",
"Reading".green().bold(),
lock.name,
read_toml.package.version
);
info!("Reading {} ... {}", lock.name, read_toml.package.version);
Comment thread
edwardalee marked this conversation as resolved.

let lib = match read_toml.library {
Some(value) => value,
Expand Down
75 changes: 62 additions & 13 deletions src/package/management.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use colored::Colorize;
use log::error;
use anyhow::Context;
use log::{error, info};
use versions::{Requirement, Versioning};

use crate::util::sha1dir;
Expand All @@ -10,6 +10,7 @@ use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::Instant;
use url::{ParseError, Url};

use crate::package::lock::{PackageLockSource, PackageLockSourceType};
Expand Down Expand Up @@ -75,8 +76,12 @@ impl PackageDetails {
) -> anyhow::Result<()> {
match &self.mutual_exclusive {
ProjectSource::Path(path_buf) => {
let src = fs::canonicalize(path_buf)?;
let dst = fs::canonicalize(library_path)?;
let src = fs::canonicalize(path_buf).with_context(|| {
Comment thread
edwardalee marked this conversation as resolved.
format!("dependency path not found: {}", path_buf.display())
})?;
let dst = fs::canonicalize(library_path).with_context(|| {
format!("library path not found: {}", library_path.display())
})?;
Ok(copy_dir_all(src, dst)?)
}
ProjectSource::Git(git_url) => {
Expand All @@ -98,22 +103,39 @@ impl DependencyManager {
target_path: &Path,
git_clone_and_checkout_cap: &GitCloneAndCheckoutCap,
) -> anyhow::Result<DependencyManager> {
info!(
"Build step: resolving dependencies in {}",
target_path.display()
);
// create library folder
let library_path = target_path.join(LIBRARY_DIRECTORY);
fs::create_dir_all(&library_path)?;
fs::create_dir_all(&library_path).with_context(|| {
format!(
"failed to create library directory: {}",
library_path.display()
)
})?;

let mut manager;
let mut lock: DependencyLock;
let lock_file = target_path.join("../Lingo.lock");

// checks if a Lingo.lock file exists
if lock_file.exists() {
info!("Build step: loading lock file {}", lock_file.display());
// reads and parses Lockfile
lock = toml::from_str::<DependencyLock>(&fs::read_to_string(lock_file)?)
lock =
toml::from_str::<DependencyLock>(&fs::read_to_string(&lock_file).with_context(
|| format!("failed to read lock file: {}", lock_file.display()),
)?)
.expect("cannot parse lock");

// if a lock file is present it will load the dependencies from it and checks
// integrity of the build directory
info!(
"Build step: validating lock dependencies in {}",
target_path.join("lfc_include").display()
);
if let Ok(()) = lock.init(&target_path.join("lfc_include"), git_clone_and_checkout_cap)
{
return Ok(DependencyManager {
Expand All @@ -140,9 +162,14 @@ impl DependencyManager {
lock = DependencyLock::create(selection);

// writes the lock file down
let mut lock_file = File::create(target_path.join("../Lingo.lock"))?;

println!("{:?}", lock.dependencies);
let lock_file_path = target_path.join("../Lingo.lock");
let mut lock_file = File::create(&lock_file_path)
.with_context(|| format!("failed to create lock file: {}", lock_file_path.display()))?;

info!(
"Build step: selected lock dependencies: {:?}",
lock.dependencies
);
let serialized_toml = toml::to_string(&lock).expect("cannot generate toml");

lock_file.write_all(serialized_toml.as_ref())?;
Expand All @@ -168,11 +195,16 @@ impl DependencyManager {
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)?;
fs::create_dir_all(&sub_dependency_path).with_context(|| {
format!(
"failed to create directory: {}",
sub_dependency_path.display()
)
})?;

while !self.pulling_queue.is_empty() {
if let Some((package_name, package_details)) = self.pulling_queue.pop() {
print!("{} {} ...", "Cloning".green().bold(), package_name);
info!("Cloning {} ...", package_name);
Comment thread
edwardalee marked this conversation as resolved.
let node = match self.non_recursive_fetching(
&package_name,
package_details,
Expand Down Expand Up @@ -216,15 +248,32 @@ impl DependencyManager {
fs::create_dir_all(&temporary_path)?;

// cloning the specified package
info!(
"Build step: fetching dependency {} into {}",
name,
temporary_path.display()
);
package.fetch(&temporary_path, git_clone_and_checkout_cap)?;

info!(
"Build step: computing checksum for {}",
temporary_path.display()
);
let checksum_started_at = Instant::now();
let hash = sha1dir::checksum_current_dir(&temporary_path, false);
info!(
"Build step: checksum complete for {} in {:?}",
temporary_path.display(),
checksum_started_at.elapsed()
);
let include_path = library_path.join(hash.to_string());

let lingo_toml_text = fs::read_to_string(temporary_path.clone().join("Lingo.toml"))?;
let lingo_toml_path = temporary_path.join("Lingo.toml");
let lingo_toml_text = fs::read_to_string(&lingo_toml_path)
.with_context(|| format!("failed to read {}", lingo_toml_path.display()))?;
let read_toml = toml::from_str::<ConfigFile>(&lingo_toml_text)?.to_config(&temporary_path);

println!(" {}", read_toml.package.version);
info!("Resolved dependency version {}", read_toml.package.version);

let config = match read_toml.library {
Some(value) => value,
Expand Down
Loading
Loading