Skip to content

Commit

Permalink
Merge pull request #41 from INTO-CPS-Association/cli
Browse files Browse the repository at this point in the history
Cli
  • Loading branch information
clegaard authored Sep 1, 2021
2 parents 1152c79 + 5542e47 commit 23dcdfa
Show file tree
Hide file tree
Showing 16 changed files with 863 additions and 83 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]

members = ["fmi2api", "integration-tests", "cli"]
members = ["common", "fmi2api", "integration-tests", "cli"]
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,12 @@ docker run --name builder -it -v ${pwd}:/workdir unifmu-build # powershell

**Note: On windows you may have to enable the use of shared folders through the dockers interface, otherwise the container fails to start.**

To build the code invoke the script `docker-build/build_all.sh` in the `workdir` of the container.
To build the code invoke the script `docker-build/build_all.sh` in the `workdir` of the container:

``` bash
bash ./docker-build/build_all.sh
```

This generates and copies all relevant build artifacts into the `assets/auto_generated` directory:

```
Expand All @@ -129,7 +134,7 @@ This generates and copies all relevant build artifacts into the `assets/auto_gen
┗ 📜unifmu_fmi2_pb2.py
```

**Note: On windows Git may be configured to replace LF line-endings with CRLF. **
**Note: On windows Git may be configured to replace LF line-endings with CRLF, which are not compatible with bash.**

Following this the cli is compiled for each platform, including the assets that were just compiled.
The final standalone executables can be found in the target folder, under the host tripple:
Expand All @@ -138,8 +143,6 @@ The final standalone executables can be found in the target folder, under the ho
- windows: unifmu-x86_64-pc-windows-gnu-0.0.4.zip
- macOS: unifmu-x86_64-apple-darwin-0.0.4.zip

**Note: The executable for any platform embeds implementations of the FMI api for all other platforms. In other words the windows executable can generate FMUs that run on all other platforms.**

## Environment Variables

In addition to the systems environment variables, UniFMU defines the following variables in the process created during instantiation of a slave.
Expand Down
9 changes: 8 additions & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Christian Legaard <[email protected]>"]
edition = "2018"
name = "unifmu"
version = "0.0.4"
version = "0.0.5"

[dependencies]

Expand All @@ -11,8 +11,15 @@ env_logger = "*"
fs_extra = "*"
lazy_static = "*"
libc = "*"
dlopen = "*"
dlopen_derive = "*"
log = "*"
num_enum = "*"
rust-embed = "6.0.0"
structopt = "*"
tempfile = "*"
url = "*"
walkdir = "*"
zip = { version = "*", default-features = false, features = ["deflate"] }

common = { path = "../common" }
13 changes: 13 additions & 0 deletions cli/src/benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::path::Path;

pub struct BenchmarkConfig {}

impl Default for BenchmarkConfig {
fn default() -> Self {
Self {}
}
}

pub fn benchmark(rootdir: &Path, config: &BenchmarkConfig) {
todo!();
}
45 changes: 43 additions & 2 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
use clap::arg_enum;
use std::path::Path;
use std::{fs::File, path::Path};
use walkdir::WalkDir;
use zip::{result::ZipError, CompressionMethod};

use fs_extra::dir::CopyOptions;
use lazy_static::lazy_static;
use log::info;
use rust_embed::RustEmbed;
use tempfile::TempDir;

use crate::utils::zip_dir;

#[macro_use]
extern crate dlopen_derive;

arg_enum! {
#[derive(Debug)]
pub enum Language {
Expand All @@ -20,6 +27,10 @@ pub enum Language {
#[folder = "../assets"]
struct Assets;

pub mod benchmark;
pub mod utils;
pub mod validation;

struct LanguageAssets {
resources: Vec<(&'static str, &'static str)>,
docker: Vec<(&'static str, &'static str)>,
Expand Down Expand Up @@ -67,6 +78,8 @@ lazy_static! {
#[derive(Debug)]
pub enum GenerateError {
Error,
FileExists,
ZipError(ZipError),
}

pub fn generate(
Expand Down Expand Up @@ -160,10 +173,38 @@ pub fn generate(

match zipped {
// zip to temporary, change extension from 'zip' to 'fmu', then copy to output directory
true => todo!(),
true => {
info!("Compressing contents into archive with path {:?}", outpath);

let file = match File::create(&outpath) {
Ok(f) => f,
Err(_) => return Err(GenerateError::FileExists),
};

let walkdir = WalkDir::new(tmpdir.path());
let it = walkdir.into_iter();

let method = CompressionMethod::Deflated;

match zip_dir(
&mut it.filter_map(|e| e.ok()),
tmpdir.path().to_str().unwrap(),
file,
method,
) {
Ok(_) => (),
Err(e) => return Err(GenerateError::ZipError(e)),
}
Ok(())
}

// copy from temporary directory to output directory
false => {
info!(
"copying temporary dir from {:?} to output {:?}",
tmpdir.path(),
outpath,
);
let mut options = CopyOptions::default();
options.copy_inside = true;
options.content_only = true;
Expand Down
85 changes: 72 additions & 13 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
use env_logger::Builder;
use log::info;
use std::path::PathBuf;
use log::{error, info};
use std::{path::PathBuf, process::exit};
use structopt::StructOpt;
use unifmu::{generate, Language};
use unifmu::{
benchmark::{benchmark, BenchmarkConfig},
generate,
validation::{validate, ValidationConfig},
Language,
};

#[derive(Debug, StructOpt)]
#[structopt(
name = "UniFMU",
about = "Implement 'Functional Mock-up units' (FMUs) in various source languages."
)]
enum Subcommands {
struct Arguments {
#[structopt(subcommand)]
cmd: Command,
}

#[derive(Debug, StructOpt)]

enum Command {
/// Create a new FMU using the specified source language
Generate {
/// Source language of the generated FMU
Expand All @@ -28,28 +40,75 @@ enum Subcommands {
dockerize: bool,
},

Validate {},
/// Run a suite of checks to detect potential defects of the FMU
Validate {
/// Path to FMU directory or archive
path: PathBuf,
},

/// Benchmark the performance of the FMU
Benchmark {
/// Path to FMU directory or archive
path: PathBuf,
},
}

fn main() {
let opt = Subcommands::from_args();
let opt = Arguments::from_args();

let mut b = Builder::new();
b.filter_level(log::LevelFilter::Info);
b.init();
b.filter_level(log::LevelFilter::Info)
.format_timestamp(None)
.format_target(false)
.format_module_path(false)
.init();

match opt {
Subcommands::Generate {
match opt.cmd {
Command::Generate {
language,
outpath,
zipped,
dockerize,
} => match generate(&language, &outpath, zipped, dockerize) {
Ok(_) => {
info!("The FMU was generated succesfully");
info!("the FMU was generated succesfully");
}
Err(e) => {
error!("an error ocurred during the generation of the FMU: {:?}", e);
exit(-1);
}
Err(_) => todo!(),
},
Subcommands::Validate {} => todo!(),
Command::Validate { path } => {
let config = ValidationConfig::default();

let path = match path.is_absolute() {
true => path,
false => std::env::current_dir().unwrap().join(path),
};

if !path.exists() {
error!("Unable to open FMU, the specified path is neither a directory or a file.");
exit(-1);
}

// let path = path.canonicalize().unwrap();

info!(
"validating the following FMU {:?} with the following checks {:?}",
path, config
);

match validate(&path, &config) {
Ok(_) => info!("no errors detected during validation of the FMU"),
Err(e) => {
error!(
"a defect was detected during the validation of the FMU: {:?} ",
e
);
exit(-1);
}
}
}
Command::Benchmark { path } => benchmark(&path, &BenchmarkConfig::default()),
}
}
51 changes: 51 additions & 0 deletions cli/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use log::info;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Seek, Write};
use std::iter::Iterator;
use std::path::Path;
use walkdir::{DirEntry, WalkDir};
use zip::result::ZipError;
use zip::write::FileOptions;

pub fn zip_dir<T>(
it: &mut dyn Iterator<Item = DirEntry>,
prefix: &str,
writer: T,
method: zip::CompressionMethod,
) -> zip::result::ZipResult<()>
where
T: Write + Seek,
{
let mut zip = zip::ZipWriter::new(writer);
let options = FileOptions::default()
.compression_method(method)
.unix_permissions(0o755);

let mut buffer = Vec::new();
for entry in it {
let path = entry.path();
let name = path.strip_prefix(Path::new(prefix)).unwrap();

// Write file or directory explicitly
// Some unzip tools unzip files with directory paths correctly, some do not!
if path.is_file() {
info!("adding file {:?} as {:?} ...", path, name);
#[allow(deprecated)]
zip.start_file_from_path(name, options)?;
let mut f = File::open(path)?;

f.read_to_end(&mut buffer)?;
zip.write_all(&*buffer)?;
buffer.clear();
} else if name.as_os_str().len() != 0 {
// Only if not root! Avoids path spec / warning
// and mapname conversion failed error on unzip
info!("adding dir {:?} as {:?} ...", path, name);
#[allow(deprecated)]
zip.add_directory_from_path(name, options)?;
}
}
zip.finish()?;
Result::Ok(())
}
Loading

0 comments on commit 23dcdfa

Please sign in to comment.