mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-11-12 23:45:57 +00:00
replace rustlings-macros with build script
This turns rustlings into a single crate, which is less complicated to publish releases for. A follow-up step might be to remove the workspace configuration. The motivation was this discussion on Zulip: https://rust-lang.zulipchat.com/#narrow/channel/334454-t-lang-docs.2Frustlings/topic/Trusted.20publishing/near/638396712
This commit is contained in:
+1
-1
@@ -52,7 +52,7 @@ When updating an exercise, check if its solution needs to be updated.
|
|||||||
- Make sure to put in some helpful links, and link to sections of The Book in `exercises/yourTopic/README.md`.
|
- Make sure to put in some helpful links, and link to sections of The Book in `exercises/yourTopic/README.md`.
|
||||||
- In the exercise, add a `// TODO: …` comment where user changes are required.
|
- In the exercise, add a `// TODO: …` comment where user changes are required.
|
||||||
- Add a solution at `solutions/yourTopic/yourTopicN.rs` with comments explaining it.
|
- Add a solution at `solutions/yourTopic/yourTopicN.rs` with comments explaining it.
|
||||||
- Add the [metadata for your exercise](#exercise-metadata) in the `rustlings-macros/info.toml` file.
|
- Add the [metadata for your exercise](#exercise-metadata) in the `info.toml` file.
|
||||||
- [Open a pull request](#pull-requests).
|
- [Open a pull request](#pull-requests).
|
||||||
|
|
||||||
### Exercise Metadata
|
### Exercise Metadata
|
||||||
|
|||||||
Generated
+1
-10
@@ -434,8 +434,8 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"notify",
|
"notify",
|
||||||
|
"quote",
|
||||||
"rustix",
|
"rustix",
|
||||||
"rustlings-macros",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shlex",
|
"shlex",
|
||||||
@@ -444,15 +444,6 @@ dependencies = [
|
|||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustlings-macros"
|
|
||||||
version = "6.5.0"
|
|
||||||
dependencies = [
|
|
||||||
"quote",
|
|
||||||
"serde",
|
|
||||||
"toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|||||||
+7
-1
@@ -36,10 +36,12 @@ keywords = [
|
|||||||
]
|
]
|
||||||
include = [
|
include = [
|
||||||
"/src/",
|
"/src/",
|
||||||
|
"/build.rs",
|
||||||
"/exercises/",
|
"/exercises/",
|
||||||
"/solutions/",
|
"/solutions/",
|
||||||
# A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded.
|
# A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded.
|
||||||
"/dev-Cargo.toml",
|
"/dev-Cargo.toml",
|
||||||
|
"/info.toml",
|
||||||
"/README.md",
|
"/README.md",
|
||||||
"/LICENSE",
|
"/LICENSE",
|
||||||
]
|
]
|
||||||
@@ -49,7 +51,6 @@ anyhow = "1"
|
|||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] }
|
crossterm = { version = "0.29", default-features = false, features = ["windows", "events"] }
|
||||||
notify = "8"
|
notify = "8"
|
||||||
rustlings-macros = { path = "rustlings-macros", version = "=6.5.0" }
|
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
shlex = "2"
|
shlex = "2"
|
||||||
@@ -59,6 +60,11 @@ wait-timeout = "0.2"
|
|||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
|
rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
quote = "1"
|
||||||
|
serde.workspace = true
|
||||||
|
toml.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,98 @@
|
|||||||
|
use quote::quote;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Fix building from source on Windows because it can't handle file links.
|
// Fix building from source on Windows because it can't handle file links.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let _ = std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml");
|
let _ = std::fs::copy("dev/Cargo.toml", "dev-Cargo.toml");
|
||||||
|
|
||||||
|
process_info_toml();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ExerciseInfo<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
dir: &'a str,
|
||||||
|
#[serde(default)]
|
||||||
|
input_files: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct InfoFile<'a> {
|
||||||
|
#[serde(borrow)]
|
||||||
|
exercises: Vec<ExerciseInfo<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translates the data in info.toml to Rust data structures to be embedded in
|
||||||
|
/// the final binary.
|
||||||
|
pub fn process_info_toml() {
|
||||||
|
let info_file = include_str!("info.toml");
|
||||||
|
let info = toml::de::from_str::<InfoFile>(info_file).expect("Failed to parse `info.toml`");
|
||||||
|
let exercises = info.exercises;
|
||||||
|
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||||
|
|
||||||
|
let exercise_files = exercises.iter().map(|exercise| {
|
||||||
|
format!(
|
||||||
|
"{manifest_dir}/exercises/{}/{}.rs",
|
||||||
|
exercise.dir, exercise.name
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let solution_files = exercises.iter().map(|exercise| {
|
||||||
|
format!(
|
||||||
|
"{manifest_dir}/solutions/{}/{}.rs",
|
||||||
|
exercise.dir, exercise.name
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut dirs = Vec::with_capacity(32);
|
||||||
|
let mut dir_inds = vec![0; exercises.len()];
|
||||||
|
|
||||||
|
for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) {
|
||||||
|
// The directory is often the last one inserted.
|
||||||
|
if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) {
|
||||||
|
*dir_ind = dirs.len() - 1 - ind;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs.push(exercise.dir);
|
||||||
|
*dir_ind = dirs.len() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_files = exercises.iter().map(|exercise| {
|
||||||
|
let names = exercise.input_files.iter();
|
||||||
|
let paths = exercise
|
||||||
|
.input_files
|
||||||
|
.iter()
|
||||||
|
.map(|f| format!("{manifest_dir}/exercises/{}/{}", exercise.dir, f));
|
||||||
|
quote! {
|
||||||
|
&[#(InputFile {
|
||||||
|
name: #names,
|
||||||
|
content: include_str!(#paths),
|
||||||
|
}),*]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let readmes = dirs
|
||||||
|
.iter()
|
||||||
|
.map(|dir| format!("{manifest_dir}/exercises/{dir}/README.md"));
|
||||||
|
|
||||||
|
let embedded_files = quote! {
|
||||||
|
EmbeddedFiles {
|
||||||
|
info_file: #info_file,
|
||||||
|
exercise_files: &[#(ExerciseFiles {
|
||||||
|
exercise: include_bytes!(#exercise_files),
|
||||||
|
solution: include_bytes!(#solution_files),
|
||||||
|
dir_ind: #dir_inds,
|
||||||
|
input_files: #input_files,
|
||||||
|
}),*],
|
||||||
|
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
format!("{out_dir}/embedded_files.rs"),
|
||||||
|
embedded_files.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "rustlings-macros"
|
|
||||||
description = "A macros crate intended to be used only by Rustlings"
|
|
||||||
version.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
include = [
|
|
||||||
"/src/",
|
|
||||||
"/info.toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
quote = "1"
|
|
||||||
serde.workspace = true
|
|
||||||
toml.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ExerciseInfo<'a> {
|
|
||||||
name: &'a str,
|
|
||||||
dir: &'a str,
|
|
||||||
#[serde(default)]
|
|
||||||
input_files: Vec<&'a str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct InfoFile<'a> {
|
|
||||||
#[serde(borrow)]
|
|
||||||
exercises: Vec<ExerciseInfo<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn include_files(_: TokenStream) -> TokenStream {
|
|
||||||
let info_file = include_str!("../info.toml");
|
|
||||||
let info = toml::de::from_str::<InfoFile>(info_file).expect("Failed to parse `info.toml`");
|
|
||||||
let exercises = info.exercises;
|
|
||||||
|
|
||||||
let exercise_files = exercises
|
|
||||||
.iter()
|
|
||||||
.map(|exercise| format!("../exercises/{}/{}.rs", exercise.dir, exercise.name));
|
|
||||||
let solution_files = exercises
|
|
||||||
.iter()
|
|
||||||
.map(|exercise| format!("../solutions/{}/{}.rs", exercise.dir, exercise.name));
|
|
||||||
|
|
||||||
let mut dirs = Vec::with_capacity(32);
|
|
||||||
let mut dir_inds = vec![0; exercises.len()];
|
|
||||||
|
|
||||||
for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) {
|
|
||||||
// The directory is often the last one inserted.
|
|
||||||
if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) {
|
|
||||||
*dir_ind = dirs.len() - 1 - ind;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirs.push(exercise.dir);
|
|
||||||
*dir_ind = dirs.len() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let input_files = exercises.iter().map(|exercise| {
|
|
||||||
let names = exercise.input_files.iter();
|
|
||||||
let paths = exercise
|
|
||||||
.input_files
|
|
||||||
.iter()
|
|
||||||
.map(|f| format!("../exercises/{}/{}", exercise.dir, f));
|
|
||||||
quote! {
|
|
||||||
&[#(InputFile {
|
|
||||||
name: #names,
|
|
||||||
content: include_str!(#paths),
|
|
||||||
}),*]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let readmes = dirs
|
|
||||||
.iter()
|
|
||||||
.map(|dir| format!("../exercises/{dir}/README.md"));
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
EmbeddedFiles {
|
|
||||||
info_file: #info_file,
|
|
||||||
exercise_files: &[#(ExerciseFiles {
|
|
||||||
exercise: include_bytes!(#exercise_files),
|
|
||||||
solution: include_bytes!(#solution_files),
|
|
||||||
dir_ind: #dir_inds,
|
|
||||||
input_files: #input_files,
|
|
||||||
}),*],
|
|
||||||
exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
+1
-1
@@ -7,7 +7,7 @@ use std::{
|
|||||||
use crate::info_file::ExerciseInfo;
|
use crate::info_file::ExerciseInfo;
|
||||||
|
|
||||||
/// Contains all embedded files.
|
/// Contains all embedded files.
|
||||||
pub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!();
|
pub static EMBEDDED_FILES: EmbeddedFiles = include!(concat!(env!("OUT_DIR"), "/embedded_files.rs"));
|
||||||
|
|
||||||
// Files related to one exercise.
|
// Files related to one exercise.
|
||||||
pub struct ExerciseFiles {
|
pub struct ExerciseFiles {
|
||||||
|
|||||||
Reference in New Issue
Block a user