1mod enum_definition;
2mod enum_impl;
3mod execution_impl;
4mod shared;
5mod state;
6
7use crate::build::enum_definition::{
8 collect_enum_definitions_from_dependencies, process_enum_definition,
9 process_pending_enum_definitions,
10};
11use crate::build::enum_impl::{
12 collect_original_enum_decoding_impls_from_dependencies, process_enum_impl,
13 process_pending_enum_impls,
14};
15use crate::build::execution_impl::{
16 collect_enum_csr_impls_from_dependencies,
17 collect_original_enum_execution_impls_from_dependencies, process_execution_impl,
18 process_pending_enum_execution_impls,
19};
20use crate::build::state::State;
21use ab_riscv_macros_common::code_utils::pre_process_rust_code;
22use anyhow::Context;
23use quote::ToTokens;
24use std::path::{Path, PathBuf};
25use std::{env, fs, io, iter};
26use syn::Item;
27
28pub fn process_instruction_macros() -> anyhow::Result<()> {
30 let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").context(
31 "Failed to retrieve `CARGO_MANIFEST_DIR` environment variable, make sure to call \
32 `process_instruction_macros` from `build.rs`",
33 )?;
34 let out_dir = env::var_os("OUT_DIR").context(
35 "Failed to retrieve `OUT_DIR` environment variable, make sure to call \
36 `process_instruction_macros` from `build.rs`",
37 )?;
38 let out_dir = Path::new(&out_dir);
39
40 let mut state = State::new();
41
42 for maybe_enum_definition in collect_enum_definitions_from_dependencies() {
43 let (
44 original_item_enum,
45 item_enum,
46 ignored_instructions,
47 direct_dependencies,
48 dependencies_for_enablement,
49 source,
50 ) = maybe_enum_definition?;
51
52 state.insert_known_enum_definition(
53 original_item_enum,
54 item_enum,
55 ignored_instructions,
56 direct_dependencies,
57 dependencies_for_enablement,
58 source,
59 )?;
60 }
61 for maybe_enum_impl in collect_original_enum_decoding_impls_from_dependencies() {
62 let (item_impl, source) = maybe_enum_impl?;
63 state.insert_known_original_enum_decoding_impl(item_impl, source)?;
64 }
65 for maybe_enum_csr_impl in collect_enum_csr_impls_from_dependencies() {
66 let (item_impl, source) = maybe_enum_csr_impl?;
67 state.insert_known_enum_csr_impl(item_impl, source)?;
68 }
69 for maybe_enum_execution_impl in collect_original_enum_execution_impls_from_dependencies() {
70 let (item_impl, source) = maybe_enum_execution_impl?;
71 state.insert_known_original_enum_execution_impl(item_impl, source)?;
72 }
73
74 for maybe_rust_file in rust_files_in(Path::new(&manifest_dir).join("src")) {
75 let rust_file = maybe_rust_file.context("Failed to collect Rust files")?;
76 process_rust_file(&rust_file, out_dir, &mut state)
77 .with_context(|| format!("Failed to process Rust file `{}`", rust_file.display()))?;
78 }
79
80 process_pending_enum_definitions(out_dir, &mut state)?;
81 process_pending_enum_impls(out_dir, &mut state)?;
82 process_pending_enum_execution_impls(out_dir, &mut state)
83}
84
85fn rust_files_in(dir: PathBuf) -> Box<dyn Iterator<Item = io::Result<PathBuf>>> {
86 fn walk(dir: PathBuf) -> Box<dyn Iterator<Item = io::Result<PathBuf>>> {
87 let read_dir = match fs::read_dir(dir) {
88 Ok(iter) => iter,
89 Err(error) => {
90 return Box::new(iter::once(Err(error))) as Box<_>;
91 }
92 };
93
94 Box::new(read_dir.flat_map(move |entry_res| {
95 let entry = match entry_res {
96 Ok(entry) => entry,
97 Err(error) => {
98 return Box::new(iter::once(Err(error))) as Box<_>;
99 }
100 };
101
102 let path = entry.path();
103
104 if path.is_dir() {
105 walk(path)
106 } else if path
107 .extension()
108 .and_then(|ext| ext.to_str())
109 .is_some_and(|ext| ext == "rs")
110 {
111 Box::new(iter::once(Ok(path))) as Box<_>
112 } else {
113 Box::new(iter::empty::<io::Result<PathBuf>>()) as Box<_>
114 }
115 }))
116 }
117
118 walk(dir)
119}
120
121fn process_rust_file(source: &Path, out_dir: &Path, state: &mut State) -> anyhow::Result<()> {
122 let mut file_contents = fs::read_to_string(source).context("Failed to read Rust file")?;
123 if !file_contents.contains("\n#[instruction") {
124 return Ok(());
128 }
129
130 pre_process_rust_code(&mut file_contents);
131
132 let file = syn::parse_file(&file_contents).context("Failed to parse Rust file")?;
133
134 for item in file.items {
135 match item {
136 Item::Enum(item_enum) => {
137 let enum_name = item_enum.ident.clone();
138 process_enum_definition(item_enum, out_dir, state).with_context(|| {
139 format!(
140 "Failed to process enum `{enum_name}` in file `{}`",
141 source.display()
142 )
143 })?;
144 }
145 Item::Impl(item_impl) => {
146 let trait_name = item_impl.trait_.as_ref().map(|(path, _)| {
147 path.segments
148 .last()
149 .expect("Path is never empty; qed")
150 .ident
151 .clone()
152 });
153 let type_name = item_impl.self_ty.clone();
154 if let Some(result) = process_enum_impl(item_impl.clone(), out_dir, state) {
155 result.with_context(|| {
156 format!(
157 "Failed to process impl block (`{:?}` for `{}`) in file `{}`",
158 trait_name.to_token_stream(),
159 type_name.to_token_stream(),
160 source.display()
161 )
162 })?;
163 } else if let Some(result) = process_execution_impl(item_impl, out_dir, state) {
164 result.with_context(|| {
165 format!(
166 "Failed to process impl block (`{:?}` for `{}`) in file `{}`",
167 trait_name.to_token_stream(),
168 type_name.to_token_stream(),
169 source.display()
170 )
171 })?;
172 }
173 }
174 _ => {
175 }
177 }
178 }
179
180 Ok(())
181}