Skip to content
Open
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
6 changes: 4 additions & 2 deletions book/src/libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,10 @@ POUs and Global variables included in the list are marked as external, the imple

### Library Location

Libraries marked as `Copy` will be copied during the compilation to the defined [Library Location](using_rusty/build_configuration.md#--lib-location).
By default this is the same as the [Build Location](using_rusty/build_configuration.md#--build-location) unless overridden by the `--lib-location` parameter.
During `plc build`, libraries marked as `Copy` are copied to the configured [Library Location](using_rusty/build_configuration.md#lib-location).
By default this is the same as the [Build Location](using_rusty/build_configuration.md#build-location), unless overridden by `--lib-location`.

`--lib-location` is currently a `build` subcommand option.

### Using environment variables

Expand Down
3 changes: 3 additions & 0 deletions book/src/using_rusty.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ file extension depending on the output file format.
A minimal invocation looks like this:
`plc input.st` this will take in the file `input.st` and compile it into a static object that will be written to a file named `input.o`.

`--build-location` is a global option and can be used with regular compilation commands (not only `plc build`) to control where intermediate artifacts are written.
If it is not set for non-`build` commands, intermediate files may be created in the OS temporary directory.

More examples:
- `plc --ir file1.st file2.st` will compile file1.st and file2.st.
- `plc --ir file1.cfc file2.st` will compile file1.cfc and file2.st.
Expand Down
30 changes: 18 additions & 12 deletions book/src/using_rusty/build_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,19 +122,24 @@ The `package_commands` keyword is optional.

## Build Parameters

The `build` subcommand exposes the following optional parameters:

### `--build-location`

The build location is the location all build files will be copied to. </br>
By default the build location is the `build` folder in the root of the project (the location of the `plc.json`).</br>
This can be overriden with the `--build-location` command line parameter.
`--build-location` is a global `plc` option.</br>
It controls where intermediate build artifacts are written.

- With `plc build`, the default is `build` in the project root (the location of `plc.json`)
- With non-`build` commands, no default build directory is used unless `--build-location` is provided

When `--build-location` is not provided for non-`build` commands, RuSTy may place intermediate object files in the OS temporary directory. This is especially relevant for multi-file compilation, where intermediate objects are generated first and then passed to the linker to produce the final output artifact.

### `--lib-location`

The lib location is where all libraries marked with `Copy` will be copied. </br>
By default it is the same as the `build-location`.</br>
This can be overriden with the `--lib-location` command line parameter.
`--lib-location` is available on the `build` subcommand.</br>
It controls where libraries marked with `Copy` are copied.

For `plc build`, if `--lib-location` is not set, RuSTy falls back to:
1. `--build-location` (if set)
2. `build`

### Additional linker options

Expand Down Expand Up @@ -167,13 +172,14 @@ Example targets are:

### `BUILD_LOCATION`

`BUILD_LOCATION` is the folder where the build will be saved.
This is the value of either the [`--build-location`](#build-location) parameter or the default build location.
`BUILD_LOCATION` is the folder where build artifacts are written.
For `plc build`, this is either [`--build-location`](#build-location) or the default `build` directory.
For non-`build` commands, it is only set when `--build-location` is provided.

### `LIB_LOCATION`

`LIB_LOCATION` is the folder where the lib will be saved.
This is the value of either the [`--lib-location`](#lib-location) parameter or the [build location](#build-location).
`LIB_LOCATION` is the folder where libraries marked with `Copy` are saved.
This is the value of [`--lib-location`](#lib-location), or the [build location](#build-location) fallback used by `plc build`.

### Usage

Expand Down
71 changes: 57 additions & 14 deletions compiler/plc_driver/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,14 @@ pub struct CompileParameters {
)]
pub header_output: Option<String>,

#[clap(
name = "build-location",
long,
help = "Write intermediate build artifacts to the given directory",
global = true
)]
pub build_location: Option<String>,

#[clap(subcommand)]
pub commands: Option<SubCommands>,
}
Expand All @@ -393,10 +401,11 @@ pub enum SubCommands {
)]
build_config: Option<String>,

#[clap(name = "build-location", long)]
build_location: Option<String>,

#[clap(name = "lib-location", long)]
#[clap(
name = "lib-location",
long,
help = "Copy libraries marked as `Copy` to the given directory (build subcommand only)"
)]
lib_location: Option<String>,
},

Expand Down Expand Up @@ -629,19 +638,20 @@ impl CompileParameters {

/// Returns the location where the build artifacts should be stored / output
pub fn get_build_location(&self) -> Option<PathBuf> {
match &self.commands {
Some(SubCommands::Build { build_location, .. }) => {
build_location.as_deref().or(Some("build")).map(PathBuf::from)
}
_ => None,
if matches!(&self.commands, Some(SubCommands::Build { .. })) {
self.build_location.as_deref().or(Some("build")).map(PathBuf::from)
} else {
self.build_location.as_deref().map(PathBuf::from)
}
}

pub fn get_lib_location(&self) -> Option<PathBuf> {
match &self.commands {
Some(SubCommands::Build { build_location, lib_location, .. }) => {
lib_location.as_deref().or(build_location.as_deref()).or(Some("build")).map(PathBuf::from)
}
Some(SubCommands::Build { lib_location, .. }) => lib_location
.as_deref()
.or(self.build_location.as_deref())
.or(Some("build"))
.map(PathBuf::from),
_ => None,
}
}
Expand Down Expand Up @@ -714,6 +724,7 @@ mod cli_tests {
use pretty_assertions::assert_eq;
use std::ffi::OsStr;
use std::fmt::Debug;
use std::path::PathBuf;

#[test]
fn verify_cli() {
Expand Down Expand Up @@ -1132,19 +1143,51 @@ mod cli_tests {
.unwrap();
if let Some(commands) = parameters.commands {
match commands {
SubCommands::Build { build_config, build_location, lib_location, .. } => {
SubCommands::Build { build_config, lib_location, .. } => {
assert_eq!(build_config, Some("src/ProjectPlc.json".to_string()));
assert_eq!(build_location, Some("bin/build".to_string()));
assert_eq!(lib_location, Some("bin/build/libs".to_string()));
}
_ => panic!("Unexpected command"),
};
assert_eq!(parameters.build_location, Some("bin/build".to_string()));
assert_eq!(parameters.sysroot, Some("sysroot1".to_string()));
assert_eq!(parameters.target, Some("targettest".into()));
assert_eq!(parameters.linker, Some("cc".to_string()));
}
}

#[test]
fn global_build_location_available_for_non_build_commands() {
let parameters =
CompileParameters::parse(vec_of_strings!("input.st", "--build-location", "bin/build", "-c"))
.unwrap();

assert_eq!(parameters.get_build_location(), Some(PathBuf::from("bin/build")));
assert_eq!(parameters.get_lib_location(), None);
}

#[test]
fn build_subcommand_lib_location_falls_back_to_global_build_location() {
let parameters = CompileParameters::parse(vec_of_strings!(
"build",
"src/ProjectPlc.json",
"--build-location",
"bin/build"
))
.unwrap();

assert_eq!(parameters.get_build_location(), Some(PathBuf::from("bin/build")));
assert_eq!(parameters.get_lib_location(), Some(PathBuf::from("bin/build")));
}

#[test]
fn build_subcommand_defaults_build_and_lib_locations_to_build() {
let parameters = CompileParameters::parse(vec_of_strings!("build", "src/ProjectPlc.json")).unwrap();

assert_eq!(parameters.get_build_location(), Some(PathBuf::from("build")));
assert_eq!(parameters.get_lib_location(), Some(PathBuf::from("build")));
}

#[test]
fn check_subcommand() {
let parameters = CompileParameters::parse(vec_of_strings!("check", "src/ProjectPlc.json")).unwrap();
Expand Down
34 changes: 34 additions & 0 deletions tests/integration/command_line_compile.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
use std::fs;
use std::fs::File;
use std::io::Read;
use std::path::Path;

use insta::assert_snapshot;

use crate::get_test_file;
use driver::compile;

fn contains_file_recursive(path: &Path) -> bool {
let Ok(entries) = fs::read_dir(path) else {
return false;
};

entries.flatten().any(|entry| {
let entry_path = entry.path();
if entry_path.is_file() {
true
} else if entry_path.is_dir() {
contains_file_recursive(&entry_path)
} else {
false
}
})
}

#[test]
fn ir_generation_full_pass() {
let file = get_test_file("command_line.st");
Expand Down Expand Up @@ -79,6 +97,22 @@ fn stdlib_string_function_headers_compile_to_ir() {
)
}

#[test]
fn global_build_location_is_used_for_non_build_compile_temp_artifacts() {
let file = get_test_file("json/simple_program.st");
let build_dir = tempfile::tempdir().unwrap();
let output_dir = tempfile::tempdir().unwrap();
let output_file = output_dir.path().join("simple_program.o");
let build_dir_str = build_dir.path().to_string_lossy().to_string();
let output_file_str = output_file.to_string_lossy().to_string();

compile(&["plc", file.as_str(), "--build-location", &build_dir_str, "-c", "-o", &output_file_str])
.unwrap();

assert!(output_file.is_file());
assert!(contains_file_recursive(build_dir.path()));
}

#[test]
#[cfg_attr(target_os = "windows", ignore = "linker is not available for windows")]
#[cfg_attr(target_os = "macos", ignore)]
Expand Down
Loading