Skip to content

Commit 23dcdfa

Browse files
authored
Merge pull request #41 from INTO-CPS-Association/cli
Cli
2 parents 1152c79 + 5542e47 commit 23dcdfa

File tree

16 files changed

+863
-83
lines changed

16 files changed

+863
-83
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[workspace]
22

3-
members = ["fmi2api", "integration-tests", "cli"]
3+
members = ["common", "fmi2api", "integration-tests", "cli"]

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,12 @@ docker run --name builder -it -v ${pwd}:/workdir unifmu-build # powershell
116116

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

119-
To build the code invoke the script `docker-build/build_all.sh` in the `workdir` of the container.
119+
To build the code invoke the script `docker-build/build_all.sh` in the `workdir` of the container:
120+
121+
``` bash
122+
bash ./docker-build/build_all.sh
123+
```
124+
120125
This generates and copies all relevant build artifacts into the `assets/auto_generated` directory:
121126

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

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

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

141-
**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.**
142-
143146
## Environment Variables
144147

145148
In addition to the systems environment variables, UniFMU defines the following variables in the process created during instantiation of a slave.

cli/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
authors = ["Christian Legaard <clegaard@outlook.com>"]
33
edition = "2018"
44
name = "unifmu"
5-
version = "0.0.4"
5+
version = "0.0.5"
66

77
[dependencies]
88

@@ -11,8 +11,15 @@ env_logger = "*"
1111
fs_extra = "*"
1212
lazy_static = "*"
1313
libc = "*"
14+
dlopen = "*"
15+
dlopen_derive = "*"
1416
log = "*"
1517
num_enum = "*"
1618
rust-embed = "6.0.0"
1719
structopt = "*"
1820
tempfile = "*"
21+
url = "*"
22+
walkdir = "*"
23+
zip = { version = "*", default-features = false, features = ["deflate"] }
24+
25+
common = { path = "../common" }

cli/src/benchmark.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::path::Path;
2+
3+
pub struct BenchmarkConfig {}
4+
5+
impl Default for BenchmarkConfig {
6+
fn default() -> Self {
7+
Self {}
8+
}
9+
}
10+
11+
pub fn benchmark(rootdir: &Path, config: &BenchmarkConfig) {
12+
todo!();
13+
}

cli/src/lib.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
use clap::arg_enum;
2-
use std::path::Path;
2+
use std::{fs::File, path::Path};
3+
use walkdir::WalkDir;
4+
use zip::{result::ZipError, CompressionMethod};
35

46
use fs_extra::dir::CopyOptions;
57
use lazy_static::lazy_static;
68
use log::info;
79
use rust_embed::RustEmbed;
810
use tempfile::TempDir;
911

12+
use crate::utils::zip_dir;
13+
14+
#[macro_use]
15+
extern crate dlopen_derive;
16+
1017
arg_enum! {
1118
#[derive(Debug)]
1219
pub enum Language {
@@ -20,6 +27,10 @@ pub enum Language {
2027
#[folder = "../assets"]
2128
struct Assets;
2229

30+
pub mod benchmark;
31+
pub mod utils;
32+
pub mod validation;
33+
2334
struct LanguageAssets {
2435
resources: Vec<(&'static str, &'static str)>,
2536
docker: Vec<(&'static str, &'static str)>,
@@ -67,6 +78,8 @@ lazy_static! {
6778
#[derive(Debug)]
6879
pub enum GenerateError {
6980
Error,
81+
FileExists,
82+
ZipError(ZipError),
7083
}
7184

7285
pub fn generate(
@@ -160,10 +173,38 @@ pub fn generate(
160173

161174
match zipped {
162175
// zip to temporary, change extension from 'zip' to 'fmu', then copy to output directory
163-
true => todo!(),
176+
true => {
177+
info!("Compressing contents into archive with path {:?}", outpath);
178+
179+
let file = match File::create(&outpath) {
180+
Ok(f) => f,
181+
Err(_) => return Err(GenerateError::FileExists),
182+
};
183+
184+
let walkdir = WalkDir::new(tmpdir.path());
185+
let it = walkdir.into_iter();
186+
187+
let method = CompressionMethod::Deflated;
188+
189+
match zip_dir(
190+
&mut it.filter_map(|e| e.ok()),
191+
tmpdir.path().to_str().unwrap(),
192+
file,
193+
method,
194+
) {
195+
Ok(_) => (),
196+
Err(e) => return Err(GenerateError::ZipError(e)),
197+
}
198+
Ok(())
199+
}
164200

165201
// copy from temporary directory to output directory
166202
false => {
203+
info!(
204+
"copying temporary dir from {:?} to output {:?}",
205+
tmpdir.path(),
206+
outpath,
207+
);
167208
let mut options = CopyOptions::default();
168209
options.copy_inside = true;
169210
options.content_only = true;

cli/src/main.rs

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
use env_logger::Builder;
2-
use log::info;
3-
use std::path::PathBuf;
2+
use log::{error, info};
3+
use std::{path::PathBuf, process::exit};
44
use structopt::StructOpt;
5-
use unifmu::{generate, Language};
5+
use unifmu::{
6+
benchmark::{benchmark, BenchmarkConfig},
7+
generate,
8+
validation::{validate, ValidationConfig},
9+
Language,
10+
};
611

712
#[derive(Debug, StructOpt)]
813
#[structopt(
914
name = "UniFMU",
1015
about = "Implement 'Functional Mock-up units' (FMUs) in various source languages."
1116
)]
12-
enum Subcommands {
17+
struct Arguments {
18+
#[structopt(subcommand)]
19+
cmd: Command,
20+
}
21+
22+
#[derive(Debug, StructOpt)]
23+
24+
enum Command {
1325
/// Create a new FMU using the specified source language
1426
Generate {
1527
/// Source language of the generated FMU
@@ -28,28 +40,75 @@ enum Subcommands {
2840
dockerize: bool,
2941
},
3042

31-
Validate {},
43+
/// Run a suite of checks to detect potential defects of the FMU
44+
Validate {
45+
/// Path to FMU directory or archive
46+
path: PathBuf,
47+
},
48+
49+
/// Benchmark the performance of the FMU
50+
Benchmark {
51+
/// Path to FMU directory or archive
52+
path: PathBuf,
53+
},
3254
}
3355

3456
fn main() {
35-
let opt = Subcommands::from_args();
57+
let opt = Arguments::from_args();
3658

3759
let mut b = Builder::new();
38-
b.filter_level(log::LevelFilter::Info);
39-
b.init();
60+
b.filter_level(log::LevelFilter::Info)
61+
.format_timestamp(None)
62+
.format_target(false)
63+
.format_module_path(false)
64+
.init();
4065

41-
match opt {
42-
Subcommands::Generate {
66+
match opt.cmd {
67+
Command::Generate {
4368
language,
4469
outpath,
4570
zipped,
4671
dockerize,
4772
} => match generate(&language, &outpath, zipped, dockerize) {
4873
Ok(_) => {
49-
info!("The FMU was generated succesfully");
74+
info!("the FMU was generated succesfully");
75+
}
76+
Err(e) => {
77+
error!("an error ocurred during the generation of the FMU: {:?}", e);
78+
exit(-1);
5079
}
51-
Err(_) => todo!(),
5280
},
53-
Subcommands::Validate {} => todo!(),
81+
Command::Validate { path } => {
82+
let config = ValidationConfig::default();
83+
84+
let path = match path.is_absolute() {
85+
true => path,
86+
false => std::env::current_dir().unwrap().join(path),
87+
};
88+
89+
if !path.exists() {
90+
error!("Unable to open FMU, the specified path is neither a directory or a file.");
91+
exit(-1);
92+
}
93+
94+
// let path = path.canonicalize().unwrap();
95+
96+
info!(
97+
"validating the following FMU {:?} with the following checks {:?}",
98+
path, config
99+
);
100+
101+
match validate(&path, &config) {
102+
Ok(_) => info!("no errors detected during validation of the FMU"),
103+
Err(e) => {
104+
error!(
105+
"a defect was detected during the validation of the FMU: {:?} ",
106+
e
107+
);
108+
exit(-1);
109+
}
110+
}
111+
}
112+
Command::Benchmark { path } => benchmark(&path, &BenchmarkConfig::default()),
54113
}
55114
}

cli/src/utils.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use log::info;
2+
use std::fs::File;
3+
use std::io::prelude::*;
4+
use std::io::{Seek, Write};
5+
use std::iter::Iterator;
6+
use std::path::Path;
7+
use walkdir::{DirEntry, WalkDir};
8+
use zip::result::ZipError;
9+
use zip::write::FileOptions;
10+
11+
pub fn zip_dir<T>(
12+
it: &mut dyn Iterator<Item = DirEntry>,
13+
prefix: &str,
14+
writer: T,
15+
method: zip::CompressionMethod,
16+
) -> zip::result::ZipResult<()>
17+
where
18+
T: Write + Seek,
19+
{
20+
let mut zip = zip::ZipWriter::new(writer);
21+
let options = FileOptions::default()
22+
.compression_method(method)
23+
.unix_permissions(0o755);
24+
25+
let mut buffer = Vec::new();
26+
for entry in it {
27+
let path = entry.path();
28+
let name = path.strip_prefix(Path::new(prefix)).unwrap();
29+
30+
// Write file or directory explicitly
31+
// Some unzip tools unzip files with directory paths correctly, some do not!
32+
if path.is_file() {
33+
info!("adding file {:?} as {:?} ...", path, name);
34+
#[allow(deprecated)]
35+
zip.start_file_from_path(name, options)?;
36+
let mut f = File::open(path)?;
37+
38+
f.read_to_end(&mut buffer)?;
39+
zip.write_all(&*buffer)?;
40+
buffer.clear();
41+
} else if name.as_os_str().len() != 0 {
42+
// Only if not root! Avoids path spec / warning
43+
// and mapname conversion failed error on unzip
44+
info!("adding dir {:?} as {:?} ...", path, name);
45+
#[allow(deprecated)]
46+
zip.add_directory_from_path(name, options)?;
47+
}
48+
}
49+
zip.finish()?;
50+
Result::Ok(())
51+
}

0 commit comments

Comments
 (0)