Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catalog and catch the main proc macro usage error #212

Merged
merged 3 commits into from
Jan 12, 2025
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
5 changes: 5 additions & 0 deletions core/cu29_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ proc-macro = true

[dependencies]
cu29-runtime = { workspace = true }
cu29-traits = { workspace = true }
syn = { workspace = true }
quote = { workspace = true }
proc-macro2 = { workspace = true }
Expand All @@ -27,6 +28,10 @@ convert_case = "0.6.0"
[build-dependencies]
cu29-unifiedlog = { workspace = true } # needed

[dev-dependencies]
trybuild = "1.0"
cu29 = { workspace = true } # needed for compile_fail tests

[features]
default = []
# enables a more verbose build log showing the code generation.
Expand Down
29 changes: 29 additions & 0 deletions core/cu29_derive/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::path::{Path, PathBuf};
use std::{fs, io};

fn main() {
println!("cargo::rerun-if-changed=tests/config");
let trybuild_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap())
.ancestors()
.nth(4)
.unwrap()
.to_path_buf()
.join("tests/trybuild/cu29-derive/config");
let config_dir =
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("tests/config");
copy_dir_all(config_dir, trybuild_dir).unwrap();
}

fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
fs::create_dir_all(&dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
} else {
fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
71 changes: 56 additions & 15 deletions core/cu29_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ use cu29_runtime::config::CuConfig;
use cu29_runtime::curuntime::{
compute_runtime_plan, find_task_type_for_id, CuExecutionLoop, CuExecutionUnit, CuTaskType,
};
use cu29_traits::CuResult;

#[cfg(feature = "macro_debug")]
use format::{highlight_rust_code, rustfmt_generated_code};
use proc_macro2::Ident;
use proc_macro2::{Ident, Span};

mod format;
mod utils;
Expand All @@ -32,17 +33,34 @@ fn int2sliceindex(i: u32) -> syn::Index {
syn::Index::from(i as usize)
}

#[inline(always)]
fn return_error(msg: String) -> TokenStream {
syn::Error::new(Span::call_site(), msg)
.to_compile_error()
.into()
}

/// Generates the CopperList content type from a config.
/// gen_cumsgs!("path/to/config.toml")
/// It will create a new type called CuMsgs you can pass to the log reader for decoding:
#[proc_macro]
pub fn gen_cumsgs(config_path_lit: TokenStream) -> TokenStream {
let config = parse_macro_input!(config_path_lit as LitStr).value();
if !std::path::Path::new(&config_full_path(&config)).exists() {
return return_error(format!(
"The configuration file `{}` does not exist. Please provide a valid path.",
config
));
}
#[cfg(feature = "macro_debug")]
eprintln!("[gen culist support with {:?}]", config);
let cuconfig = read_config(&config);
let runtime_plan: CuExecutionLoop =
compute_runtime_plan(&cuconfig).expect("Could not compute runtime plan");
let Ok(cuconfig) = read_config(&config) else {
return return_error(format!("Failed to read the configuration file: {}", config));
};
let runtime_plan: CuExecutionLoop = match compute_runtime_plan(&cuconfig) {
Ok(plan) => plan,
Err(e) => return return_error(format!("Could not compute runtime plan: {}", e)),
};

// Give a name compatible with a struct to match the task ids to their output in the CuMsgs tuple.
let all_tasks_member_ids: Vec<String> = cuconfig
Expand Down Expand Up @@ -243,19 +261,33 @@ pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream {
parse_macro_input!(args with attribute_config_parser);

// Check if the config file was provided
let config_file = config_file
.expect("Expected config file attribute like #[CopperRuntime(config = \"path\")]")
.value();
let config_file = match config_file {
Some(file) => file.value(),
None => {
return return_error(
"Expected config file attribute like #[CopperRuntime(config = \"path\")]"
.to_string(),
)
}
};

let copper_config = read_config(&config_file);
let copper_config_content = read_to_string(config_full_path(config_file.as_str())).expect(
"Could not read the config file (should not happen because we just succeeded just before).",
);
let Ok(copper_config) = read_config(&config_file) else {
return return_error(format!(
"Failed to read the configuration file: {}",
config_file
));
};
let copper_config_content = match read_to_string(config_full_path(config_file.as_str())) {
Ok(ok) => ok,
Err(e) => return return_error(format!("Could not read the config file (should not happen because we just succeeded just before). {}", e))
};

#[cfg(feature = "macro_debug")]
eprintln!("[runtime plan]");
let runtime_plan: CuExecutionLoop =
compute_runtime_plan(&copper_config).expect("Could not compute runtime plan");
let runtime_plan: CuExecutionLoop = match compute_runtime_plan(&copper_config) {
Ok(plan) => plan,
Err(e) => return return_error(format!("Could not compute runtime plan: {}", e)),
};
#[cfg(feature = "macro_debug")]
eprintln!("{:?}", runtime_plan);

Expand Down Expand Up @@ -1239,11 +1271,10 @@ pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream {
tokens
}

fn read_config(config_file: &str) -> CuConfig {
fn read_config(config_file: &str) -> CuResult<CuConfig> {
let filename = config_full_path(config_file);

read_configuration(filename.as_str())
.unwrap_or_else(|_| panic!("Failed to read configuration file: {filename}"))
}

fn config_full_path(config_file: &str) -> String {
Expand Down Expand Up @@ -1390,3 +1421,13 @@ fn build_culist_tuple_debug(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl
}
}
}

#[cfg(test)]
mod tests {
// See tests/compile_file directory for more information
#[test]
fn test_compile_fail() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/compile_fail/*/*.rs");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use cu29_derive::copper_runtime;

const CONFIG_FILE: &str = "/path/to/config.ron";

#[copper_runtime(config = CONFIG_FILE)]
struct MyApplicationStruct;

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: expected string literal
--> tests/compile_fail/copper_runtime/invalid_attribute.rs:5:27
|
5 | #[copper_runtime(config = CONFIG_FILE)]
| ^^^^^^^^^^^
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use cu29_derive::copper_runtime;

#[copper_runtime(config = "config/invalid_config.ron")]
struct MyApplicationStruct;

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: custom attribute panicked
--> tests/compile_fail/copper_runtime/invalid_config_file.rs:3:1
|
3 | #[copper_runtime(config = "config/invalid_config.ron")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: message: Syntax Error in config: Expected opening `[` at position 2:12
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use cu29_derive::copper_runtime;

#[copper_runtime(config = "/path/to/config.ron")]
struct MyApplicationStruct;

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: Failed to read the configuration file: /path/to/config.ron
--> tests/compile_fail/copper_runtime/invalid_file_path.rs:3:1
|
3 | #[copper_runtime(config = "/path/to/config.ron")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `copper_runtime` (in Nightly builds, run with -Z macro-backtrace for more info)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use cu29_derive::gen_cumsgs;

const CONFIG_FILE: &str = "/path/to/config.ron";

gen_cumsgs!(CONFIG_FILE);

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: expected string literal
--> tests/compile_fail/cu_msg/invalid_attribute.rs:5:13
|
5 | gen_cumsgs!(CONFIG_FILE);
| ^^^^^^^^^^^
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use cu29_derive::gen_cumsgs;

gen_cumsgs!("config/invalid_config.ron");

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: proc macro panicked
--> tests/compile_fail/cu_msg/invalid_config_file.rs:3:1
|
3 | gen_cumsgs!("config/invalid_config.ron");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: message: Syntax Error in config: Expected opening `[` at position 2:12
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use cu29_derive::gen_cumsg;

gen_cumsg!("invalid/path/to/config.ron");

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error[E0432]: unresolved import `cu29_derive::gen_cumsg`
--> tests/compile_fail/cu_msg/invalid_file_path.rs:1:5
|
1 | use cu29_derive::gen_cumsg;
| ^^^^^^^^^^^^^---------
| | |
| | help: a similar name exists in the module: `gen_cumsgs`
| no `gen_cumsg` in the root
9 changes: 9 additions & 0 deletions core/cu29_derive/tests/compile_fail/cu_msg/non_existent_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use cu29_derive::gen_cumsgs;

struct MyMsg;
struct FlippingSource;
struct FlippingSourceTwo;

gen_cumsgs!("config/non_existent_id.ron");

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: proc macro panicked
--> tests/compile_fail/cu_msg/non_existent_id.rs:7:1
|
7 | gen_cumsgs!("config/non_existent_id.ron");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: message: Source node not found
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use cu29_derive::gen_cumsgs;

gen_cumsgs!("config/non_existent_message.ron");

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error[E0412]: cannot find type `MyMsg` in this scope
--> tests/compile_fail/cu_msg/non_existent_message.rs:3:1
|
3 | gen_cumsgs!("config/non_existent_message.ron");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
= note: this error originates in the macro `gen_cumsgs` (in Nightly builds, run with -Z macro-backtrace for more info)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use cu29_derive::gen_cumsgs;

struct MyMsg;

gen_cumsgs!("config/non_existent_task_type.ron");

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0412]: cannot find type `MyMsg` in this scope
--> tests/compile_fail/cu_msg/non_existent_task_type.rs:5:1
|
5 | gen_cumsgs!("config/non_existent_task_type.ron");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
= help: consider importing this struct:
crate::MyMsg
= note: this error originates in the macro `gen_cumsgs` (in Nightly builds, run with -Z macro-backtrace for more info)
21 changes: 21 additions & 0 deletions core/cu29_derive/tests/config/invalid_config.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(
tasks: (
(
id: "task0",
type: "tasks::ExampleSrc",
),
(
id: "task1",
type: "tasks::ExampleTask",
),
(
id: "task2",
type: "tasks::ExampleSink",
),
),
cnx: [
(src: "task0", dst: "task1", msg: "i32"),
(src: "task1", dst: "task2", msg: "i32"),
],
monitor: (type: "ExampleMonitor")
)
15 changes: 15 additions & 0 deletions core/cu29_derive/tests/config/non_existent_id.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(
tasks: [
(
id: "src",
type: "FlippingSource",
),
(
id: "gpio",
type: "FlippingSourceTwo",
),
],
cnx: [
(src: "unknown_src", dst: "gpio", msg: "MyMsg"),
],
)
15 changes: 15 additions & 0 deletions core/cu29_derive/tests/config/non_existent_message.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(
tasks: [
(
id: "src",
type: "FlippingSource",
),
(
id: "gpio",
type: "FlippingSourceTwo",
),
],
cnx: [
(src: "src", dst: "gpio", msg: "MyMsg"),
],
)
15 changes: 15 additions & 0 deletions core/cu29_derive/tests/config/non_existent_task_type.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
(
tasks: [
(
id: "src",
type: "FlippingSource",
),
(
id: "gpio",
type: "FlippingSourceTwo",
),
],
cnx: [
(src: "src", dst: "gpio", msg: "MyMsg"),
],
)
Loading
Loading