Skip to content

Commit ac8a601

Browse files
Build Description File (#523)
* validate Pointer incl. tests * using cargo clippy for coding style as well as cargo fmt * multi-type declarations incl. tests * subcommand as well as simple build description file * improved subcommand * improved build description file * adding libraries and parsing them * documentation and minor improvements * Pull Request improvements * improvements from review * adding sysroot and target-triple as inline parameters for subcommand Co-authored-by: Ghaith Hachem <ghaith.hachem@gmail.com>
1 parent 4b5e8ff commit ac8a601

File tree

8 files changed

+461
-39
lines changed

8 files changed

+461
-39
lines changed

book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [RuSTy](./intro_1.md)
44
- [Build and Install](./build_and_install.md)
55
- [Using RuSTy](./using_rusty.md)
6+
- [Build descrpition File](using_rusty/build_description_file.md)
67
- [Writing ST Programs]()
78
- [Libraries](libraries.md)
89
- [External Functions](libraries/external_functions.md)

book/src/using_rusty.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ written as [glob patterns](https://en.wikipedia.org/wiki/Glob_(programming)).
99
Note that you can only specify at most one output format. In the case that no output
1010
format switch has been specified, the compiler will select `--static` by default.
1111

12-
Similarily, if you do not specify an output filename via the `-o` or `--output` options,
12+
Similarly, if you do not specify an output filename via the `-o` or `--output` options,
1313
the output filename will consist of the first input filename, but with an appropriate
1414
file extension depending on the output file format. A minimal invocation looks like this:
1515

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Build description File
2+
3+
In addition to the comprehensive help, `rustyc` offers a build description file, that simplifies the build process. Instead of having numerous inline arguments, using the build description file makes passing the arguments easier and neater. The build description file needs to be safed as a [json](https://en.wikipedia.org/wiki/JSON) format.
4+
5+
`rustyc build [Config]`
6+
7+
Note that if `rustyc` cannot find the `plc.json` file, it will throw an error and request the path. The default location for the build file is the current directory. The command for building with an additional path looks like this:
8+
9+
`rustyc build src/plc.json`
10+
11+
12+
# Plc.json
13+
14+
For the build description file to work, the build description file must be the [json](https://en.wikipedia.org/wiki/JavaScript_Object_Notation) format. All the keys used in the build description file are described in the following sections.
15+
16+
17+
## files
18+
19+
The key `files` is the equivalent to the `input` parameter, which adds all the `.st` files that needs to be compiled. The value of `files` is an array of strings, definied as followed:
20+
```json
21+
"files" : [
22+
"examples/hello_world.st",
23+
"examples/hw.st"
24+
]
25+
```
26+
27+
28+
## optimization
29+
30+
`rustyc` offers 4 levels of optimization which correspond to the levels established by llvm respectively [clang](https://clang.llvm.org/docs/CommandGuide/clang.html#code-generation-options) (`none` to `agressive`).
31+
To use an optimization, the key `optimization` is required:
32+
- `"optimization" : "none"`
33+
- `"optimization" : "less"`
34+
- `"optimization" : "default"`
35+
- `"optimization" : "aggressive"`
36+
37+
By default `rustyc` will use `default` which corresponds to clang's `-o2`.
38+
39+
40+
## error_format
41+
42+
`rustyc` offers 2 levels of formatting errors. To specify which error format is wanted, the key `error_format` is required:
43+
- `"error_format" : "Clang"` This is used to get fewer error messaged
44+
- `"error_format" : "Rich"` This is used to get a verbose error description.
45+
46+
47+
## libraries
48+
49+
To link several executables `rustyc` has the option to add libraries and automatically build and like them together. if no compile type has been selected `rustyc` will link the files on default.
50+
51+
```json
52+
"libraries" : [
53+
{
54+
"name" : "iec61131std",
55+
"path" : "path/to/lib/",
56+
"include_path" : [
57+
"examples/hw.st",
58+
"examples/hello_world.st"
59+
]
60+
}
61+
]
62+
```
63+
64+
## output
65+
66+
Similarly to specifying an output file via the `-o` or `--output` option, in the build file we use `"output" : "output.so"` to define the output file. The default location is likewise to the location for the build file, namely the current directory.
67+
68+
69+
70+
## Optional Keys
71+
### sysroot
72+
73+
`rustyc` is using the `sysroot` key for linking purposes. It is considered to be the root directory for the purpose of locating headers and libraries.
74+
75+
76+
### target
77+
78+
To build and compile [structured text](https://en.wikipedia.org/wiki/Structured_text) for the rigth platform we need to specify the `target`. As `rustyc` is using [LLVM](https://en.wikipedia.org/wiki/LLVM) a target-tripple supported by LLVM needs to be selected. The default `target` is `x86_64-linux-gnu`.
79+
80+
81+
### compile_type
82+
There are six options for choosing the `compile_type`. The valid options are:
83+
<!-- TODO we should probably describe what each of those options do -->
84+
- `Static` bindings have to be done at compile time
85+
- `PIC` Position Independent Code
86+
- `Shared` (dynamic) binginds will be done dynamically
87+
- `Relocatable` generates Relocatable
88+
- `Bitcode` adds bitcode alongside machine code in executable file
89+
- `IR` Intermediate Representation
90+
91+
To specify which of the above mentioned compile formats is wanted, it needs to be added to the build description file as followed: `"compile_type" : "Shared"`.
92+
93+
# Example
94+
```json
95+
{
96+
"files" : [
97+
"examples/hw.st",
98+
"examples/hello_world.st",
99+
"examples/ExternalFunctions.st"
100+
],
101+
"compile_type" : "Shared",
102+
"optimization" : "Default",
103+
"output" : "proj.so",
104+
"error_format": "Rich",
105+
"libraries" : [
106+
{
107+
"name" : "iec61131std",
108+
"path" : "path/to/lib",
109+
"include_path" : [
110+
"examples/hw.st"
111+
]
112+
},
113+
{
114+
"name" : "other_lib",
115+
"path" : "path/to/lib",
116+
"include_path" : [
117+
"examples/hello_world.st"
118+
]
119+
}
120+
]
121+
}
122+
```

examples/plc.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"files" : [
3+
"examples/hw.st",
4+
"examples/hello_world.st",
5+
"examples/ExternalFunctions.st"
6+
],
7+
"compile_type" : "Shared",
8+
"optimization" : "Default",
9+
"output" : "proj.so",
10+
"error_format": "Rich",
11+
"libraries" : [
12+
{
13+
"name" : "iec61131std",
14+
"path" : "",
15+
"include_path" : [
16+
"examples/hw.st"
17+
]
18+
},
19+
{
20+
"name" : "other_iec61131std",
21+
"path" : "examples/hw.st",
22+
"include_path" : [
23+
"examples/hello_world.st"
24+
]
25+
}
26+
]
27+
}

src/build.rs

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use crate::diagnostics::Diagnostic;
2+
use crate::diagnostics::ErrNo;
3+
use crate::ErrorFormat;
4+
use crate::FilePath;
5+
use crate::FormatOption;
6+
use crate::OptimizationLevel;
7+
use serde::{Deserialize, Serialize};
8+
use std::fs;
9+
use std::path::Path;
10+
11+
#[derive(Serialize, Deserialize)]
12+
pub struct Libraries {
13+
pub name: String,
14+
pub path: String,
15+
pub include_path: Vec<String>,
16+
}
17+
18+
#[derive(Serialize, Deserialize)]
19+
pub struct Proj {
20+
pub files: Vec<String>,
21+
pub compile_type: Option<FormatOption>,
22+
pub optimization: Option<OptimizationLevel>,
23+
pub output: String,
24+
pub error_format: ErrorFormat,
25+
pub libraries: Option<Vec<Libraries>>,
26+
}
27+
28+
pub fn get_project_from_file(build_config: Option<String>) -> Result<Proj, Diagnostic> {
29+
let filepath = build_config.unwrap_or_else(|| String::from("plc.json"));
30+
31+
//read from file
32+
let content = fs::read_to_string(filepath);
33+
34+
let content = match content {
35+
Ok(file_content) => file_content,
36+
Err(e) => {
37+
return Err(Diagnostic::GeneralError {
38+
message: e.to_string(),
39+
err_no: ErrNo::general__io_err,
40+
})
41+
}
42+
};
43+
44+
//convert file to Object
45+
let project = serde_json::from_str(&content);
46+
let project: Proj = match project {
47+
Ok(project) => project,
48+
Err(_e) => {
49+
return Err(Diagnostic::GeneralError {
50+
message: String::from(r#"An error occured whilest parsing!"#),
51+
err_no: ErrNo::general__io_err,
52+
})
53+
}
54+
};
55+
56+
let project = get_path_when_empty(project)?;
57+
Ok(project)
58+
}
59+
60+
pub fn string_to_filepath(content: Vec<String>) -> Vec<FilePath> {
61+
let mut filepath: Vec<FilePath> = vec![];
62+
for item in content {
63+
filepath.push(FilePath::from(item));
64+
}
65+
filepath
66+
}
67+
68+
fn get_path_when_empty(p: Proj) -> Result<Proj, Diagnostic> {
69+
if let Some(ref libraries) = p.libraries {
70+
for library in libraries {
71+
let path = if library.path.is_empty() {
72+
None
73+
} else {
74+
Some(&library.path)
75+
};
76+
let path = path
77+
.map_or(Path::new("."), Path::new)
78+
.join(&format!("lib{}.so", library.name));
79+
if !Path::new(&path).is_file() {
80+
return Err(Diagnostic::GeneralError {
81+
message: format!(
82+
"The library could not be found at : {}",
83+
path.to_string_lossy()
84+
),
85+
err_no: ErrNo::general__io_err,
86+
});
87+
}
88+
}
89+
}
90+
Ok(p)
91+
}

src/cli.rs

+57-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2021 Ghaith Hachem and Mathias Rieder
2-
use clap::{ArgGroup, Parser};
2+
use clap::{ArgGroup, Parser, Subcommand};
33
use encoding_rs::Encoding;
44
use std::{ffi::OsStr, path::Path};
55

@@ -17,6 +17,8 @@ pub type ParameterError = clap::Error;
1717
about = "IEC61131-3 Structured Text compiler powered by Rust & LLVM ",
1818
version,
1919
)]
20+
#[clap(propagate_version = true)]
21+
#[clap(subcommand_negates_reqs = true)]
2022
pub struct CompileParameters {
2123
#[clap(
2224
short,
@@ -104,7 +106,7 @@ pub struct CompileParameters {
104106
short = 'L',
105107
help = "Search path for libraries, used for linking"
106108
)]
107-
pub library_pathes: Vec<String>,
109+
pub library_paths: Vec<String>,
108110

109111
#[clap(name = "library", long, short = 'l', help = "Library name to link")]
110112
pub libraries: Vec<String>,
@@ -148,6 +150,31 @@ pub struct CompileParameters {
148150
default_value = "rich"
149151
)]
150152
pub error_format: ErrorFormat,
153+
154+
#[clap(subcommand)]
155+
pub commands: Option<SubCommands>,
156+
}
157+
158+
#[derive(Debug, Subcommand)]
159+
pub enum SubCommands {
160+
/// Uses build description file.
161+
/// Supported format: json build <plc.json> --sysroot <sysroot> --target <target-triple>
162+
Build {
163+
#[clap(
164+
parse(try_from_str = validate_config)
165+
)]
166+
build_config: Option<String>,
167+
168+
#[clap(long, name = "sysroot", help = "Path to system root, used for linking")]
169+
sysroot: Option<String>,
170+
171+
#[clap(
172+
long,
173+
name = "target-triple",
174+
help = "A target-tripple supported by LLVM"
175+
)]
176+
target: Option<String>,
177+
},
151178
}
152179

153180
fn parse_encoding(encoding: &str) -> Result<&'static Encoding, String> {
@@ -248,7 +275,7 @@ impl CompileParameters {
248275

249276
#[cfg(test)]
250277
mod cli_tests {
251-
use super::CompileParameters;
278+
use super::{CompileParameters, SubCommands};
252279
use crate::{ConfigFormat, ErrorFormat, FormatOption, OptimizationLevel};
253280
use clap::ErrorKind;
254281
use pretty_assertions::assert_eq;
@@ -552,7 +579,7 @@ mod cli_tests {
552579
"-L/tmp"
553580
))
554581
.unwrap();
555-
assert_eq!(parameters.library_pathes, vec!["xxx", "test", ".", "/tmp"]);
582+
assert_eq!(parameters.library_paths, vec!["xxx", "test", ".", "/tmp"]);
556583
}
557584

558585
#[test]
@@ -585,6 +612,32 @@ mod cli_tests {
585612
}
586613
}
587614

615+
#[test]
616+
fn build_subcommand() {
617+
let parameters = CompileParameters::parse(vec_of_strings!(
618+
"build",
619+
"src/ProjectPlc.json",
620+
"--sysroot",
621+
"systest",
622+
"--target",
623+
"targettest"
624+
))
625+
.unwrap();
626+
if let Some(commands) = parameters.commands {
627+
match commands {
628+
SubCommands::Build {
629+
build_config,
630+
sysroot,
631+
target,
632+
} => {
633+
assert_eq!(build_config, Some("src/ProjectPlc.json".to_string()));
634+
assert_eq!(sysroot, Some("systest".to_string()));
635+
assert_eq!(target, Some("targettest".to_string()));
636+
}
637+
};
638+
}
639+
}
640+
588641
#[test]
589642
fn sysroot_added() {
590643
let parameters =

0 commit comments

Comments
 (0)