My file structure looks like this (yes this is for advent of code):
days.rs
days/
day01.rs
day02.rs
...
I generate a days.in
file in build.rs
which looks something like this:
pub mod day01;
pub mod day02;
...
I then include!
this file in days.rs
:
include!(concat!(env!("OUT_DIR"), "/days.in"));
When I then cargo run
or cargo build
, I get this error repeated for each day:
error[E0583]: file not found for module `day01`
--> D:Projectsadvent-of-code-2024targetdebugbuildadvent-of-code-2024-59c0dcca155b0f0eoutdays.in:1:1
|
1 | pub mod day01;
| ^^^^^^^^^^^^^^
|
= help: to create the module `day01`, create file "D:Projectsadvent-of-code-2024targetdebugbuildadvent-of-code-2024-59c0dcca155b0f0eoutday01.rs" or "D:Projectsadvent-of-code-2024targetdebugbuildadvent-of-code-2024-59c0dcca155b0f0eoutday01mod.rs"
= note: if there is a `mod day01` elsewhere in the crate already, import it with `use crate::...` instead
I notice that the error is located in days.in
, while I would expect it to be in days.rs
.
I suspect this is where I’m perhaps misunderstanding something. Does include!
not just directly “copy-and-paste” the contents of the target file in place of the macro?
When I manually include the file, by just copying the contents of days.in
and replacing the include!
with it, it works perfectly fine!
1
Your assumption simply is wrong, include!
does not “copy-and-paste” anything it:
Parses a file as an expression or an item according to the context.
Parsing a file involves declaring modules so they still remain relative to the included file, not the including one.
1
For future viewers with a similar question, this is what my build.rs
ended up looking like. The important part is to add #[path = "<absolute path to the mod file>"]
for each mod
. (But really I’ll probably just manually attach the files, since that seems to give better IDE support.)
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::path::Path;
use std::{env, fs};
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/days");
// Collect days `(mod name, mod path)`
let mut days = vec![];
for entry in fs::read_dir("src/days").unwrap() {
let entry = entry.unwrap();
let day_name = entry
.path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string();
let day_path = entry
.path()
.to_str()
.unwrap()
.to_string()
.replace('\', "\\");
// Skip day template
if day_name == "dayN" {
continue;
}
days.push((day_name, day_path));
}
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_days_path = Path::new(&out_dir).join("days.in");
let out_days_file = File::create(out_days_path).unwrap();
let mut out_days_writer = BufWriter::new(out_days_file);
// Attach mods, with absolute path specified to the actual mod file
for (day_name, day_path) in &days {
writeln!(out_days_writer, "#[path = "{}"]", day_path).unwrap();
writeln!(out_days_writer, "pub mod {};", day_name).unwrap();
}
// Implement wrapper functions
writeln!(out_days_writer, "impl_days! {{").unwrap();
for (day_name, _) in &days {
writeln!(out_days_writer, " {},", day_name).unwrap();
}
writeln!(out_days_writer, "}}").unwrap();
out_days_writer.flush().unwrap();
}
1