Skip to content

Commit 09db96d

Browse files
committed
Add axconfig-gen-macros
1 parent e049deb commit 09db96d

File tree

20 files changed

+421
-32
lines changed

20 files changed

+421
-32
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,13 @@ jobs:
3636
contents: write
3737
env:
3838
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
39-
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
39+
RUSTDOCFLAGS: -Zunstable-options --enable-index-page -D rustdoc::broken_intra_doc_links -D missing-docs
4040
steps:
4141
- uses: actions/checkout@v4
4242
- uses: dtolnay/rust-toolchain@nightly
4343
- name: Build docs
4444
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
45-
run: |
46-
cargo doc --no-deps --all-features
47-
printf '<meta http-equiv="refresh" content="0;url=axconfig_gen/index.html">' > target/doc/index.html
45+
run: cargo doc --no-deps --all-features
4846
- name: Deploy to Github Pages
4947
if: ${{ github.ref == env.default-branch }}
5048
uses: JamesIves/github-pages-deploy-action@v4

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
[package]
2-
name = "axconfig-gen"
1+
[workspace]
2+
resolver = "2"
3+
4+
members = [
5+
"axconfig-gen",
6+
"axconfig-gen-macros",
7+
]
8+
9+
[workspace.package]
310
version = "0.1.0"
411
edition = "2021"
512
authors = ["Yuekai Jia <equation618@gmail.com>"]
6-
description = "A TOML-based configuration generator for ArceOS"
713
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
814
homepage = "https://github.com/arceos-org/arceos"
915
repository = "https://github.com/arceos-org/axconfig-gen"
10-
documentation = "https://docs.rs/axconfig-gen"
1116
keywords = ["arceos", "config", "toml"]
12-
categories = ["config", "parsing", "parser-implementations"]
13-
14-
[dependencies]
15-
toml_edit = { version = "0.22" }
16-
clap = { version = "4", features = ["derive"] }

README.md

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# axconfig-gen
22

3-
A TOML-based configuration generation tool for [ArceOS](https://github.com/arceos-org/arceos).
3+
[![CI](https://github.com/arceos-org/axconfig-gen/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/arceos-org/axconfig-gen/actions/workflows/ci.yml)
44

5-
## Usage
5+
* [axconfig-gen](axconfig-gen): A TOML-based configuration generation tool for [ArceOS](https://github.com/arceos-org/arceos). [![Crates.io](https://img.shields.io/crates/v/axconfig-gen)](https://crates.io/crates/axconfig-gen)[![Docs.rs](https://docs.rs/axconfig-gen/badge.svg)](https://docs.rs/axconfig-gen)
6+
* [axconfig-gen-macros](axconfig-gen-macros): Procedural macros for converting TOML format configurations to Rust constants. [![Crates.io](https://img.shields.io/crates/v/axconfig-gen-macros)](https://crates.io/crates/axconfig-gen-macros)[![Docs.rs](https://docs.rs/axconfig-gen-macros/badge.svg)](https://docs.rs/axconfig-gen-macros)
67

7-
```
8+
### Executable Usage
9+
10+
```text
811
axconfig-gen [OPTIONS] --spec <SPEC>
912
1013
Options:
@@ -22,4 +25,75 @@ For example, to generate a config file `.axconfig.toml` from the config specific
2225
axconfig-gen -s a.toml -s b.toml -o .axconfig.toml -f toml
2326
```
2427

25-
See [defconfig.toml](example_configs/defconfig.toml) for an example of a config specification file.
28+
See [defconfig.toml](example-configs/defconfig.toml) for an example of a config specification file.
29+
30+
Value types can be specified by the comment following the config item. Currently supported types are `bool`, `int`, `uint`, `str`, `(type1, type2, ...)` for tuples, and `[type]` for arrays. If no type is specified, it will try to infer the type from the value.
31+
32+
### Library Usage
33+
34+
```rust
35+
use axconfig_gen::{Config, OutputFormat};
36+
37+
let config_toml = r#"
38+
are-you-ok = true
39+
one-two-three = 123
40+
41+
[hello]
42+
"one-two-three" = "456" # int
43+
array = [1, 2, 3] # [uint]
44+
tuple = [1, "abc", 3]
45+
"#;
46+
47+
let config = Config::from_toml(config_toml).unwrap();
48+
let rust_code = config.dump(OutputFormat::Rust).unwrap();
49+
50+
assert_eq!(rust_code, r#"
51+
pub const ARE_YOU_OK: bool = true;
52+
pub const ONE_TWO_THREE: usize = 123;
53+
54+
pub mod hello {
55+
pub const ARRAY: &[usize] = &[1, 2, 3];
56+
pub const ONE_TWO_THREE: isize = 456;
57+
pub const TUPLE: (usize, &str, usize) = (1, "abc", 3);
58+
}
59+
"#);
60+
```
61+
62+
### Macro Usage
63+
64+
```rust
65+
axconfig_gen_macros::parse_configs!(r#"
66+
are-you-ok = true
67+
one-two-three = 123
68+
69+
[hello]
70+
"one-two-three" = "456" # int
71+
array = [1, 2, 3] # [uint]
72+
tuple = [1, "abc", 3]
73+
"#);
74+
75+
assert_eq!(ARE_YOU_OK, true);
76+
assert_eq!(ONE_TWO_THREE, 123usize);
77+
assert_eq!(hello::ONE_TWO_THREE, 456isize);
78+
assert_eq!(hello::ARRAY, [1, 2, 3]);
79+
assert_eq!(hello::TUPLE, (1, "abc", 3));
80+
```
81+
82+
The above example will generate the following constants:
83+
84+
```rust
85+
pub const ARE_YOU_OK: bool = true;
86+
pub const ONE_TWO_THREE: usize = 123;
87+
88+
pub mod hello {
89+
pub const ARRAY: &[usize] = &[1, 2, 3];
90+
pub const ONE_TWO_THREE: isize = 456;
91+
pub const TUPLE: (usize, &str, usize) = (1, "abc", 3);
92+
}
93+
```
94+
95+
You can also include the configuration file directly:
96+
97+
```rust
98+
axconfig_gen_macros::include_configs!("/path/to/config.toml");
99+
```

axconfig-gen-macros/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "axconfig-gen-macros"
3+
description = "Procedural macros for converting TOML format configurations to Rust constants."
4+
documentation = "https://docs.rs/axconfig-gen-macros"
5+
categories = ["development-tools::procedural-macro-helpers", "config", "parsing", "parser-implementations"]
6+
keywords.workspace = true
7+
version.workspace = true
8+
edition.workspace = true
9+
authors.workspace = true
10+
license.workspace = true
11+
homepage.workspace = true
12+
repository.workspace = true
13+
14+
[features]
15+
nightly = []
16+
17+
[dependencies]
18+
proc-macro2 = "1.0"
19+
quote = "1.0"
20+
syn = { version = "2.0", features = ["full"] }
21+
axconfig-gen = { path = "../axconfig-gen" }
22+
23+
[lib]
24+
proc-macro = true

axconfig-gen-macros/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# axconfig-gen-macros
2+
3+
Procedural macros for converting TOML format configurations to equivalent Rust constants.
4+
5+
## Example
6+
7+
```rust
8+
axconfig_gen_macros::parse_configs!(r#"
9+
are-you-ok = true
10+
one-two-three = 123
11+
12+
[hello]
13+
"one-two-three" = "456" # int
14+
array = [1, 2, 3] # [uint]
15+
tuple = [1, "abc", 3]
16+
"#);
17+
18+
assert_eq!(ARE_YOU_OK, true);
19+
assert_eq!(ONE_TWO_THREE, 123usize);
20+
assert_eq!(hello::ONE_TWO_THREE, 456isize);
21+
assert_eq!(hello::ARRAY, [1, 2, 3]);
22+
assert_eq!(hello::TUPLE, (1, "abc", 3));
23+
```
24+
25+
Value types can be specified by the comment following the config item. Currently supported types are `bool`, `int`, `uint`, `str`, `(type1, type2, ...)` for tuples, and `[type]` for arrays. If no type is specified, it will try to infer the type from the value.
26+
27+
The above example will generate the following constants:
28+
29+
```rust
30+
pub const ARE_YOU_OK: bool = true;
31+
pub const ONE_TWO_THREE: usize = 123;
32+
33+
pub mod hello {
34+
pub const ARRAY: &[usize] = &[1, 2, 3];
35+
pub const ONE_TWO_THREE: isize = 456;
36+
pub const TUPLE: (usize, &str, usize) = (1, "abc", 3);
37+
}
38+
```
39+
40+
You can also include the configuration file directly:
41+
42+
```rust,ignore
43+
axconfig_gen_macros::include_configs!("/path/to/config.toml");
44+
```

axconfig-gen-macros/src/lib.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#![cfg_attr(feature = "nightly", feature(proc_macro_expand))]
2+
#![doc = include_str!("../README.md")]
3+
4+
use proc_macro::{LexError, TokenStream};
5+
use quote::{quote, ToTokens};
6+
use syn::parse_macro_input;
7+
use syn::{Error, LitStr};
8+
9+
use axconfig_gen::{Config, OutputFormat};
10+
11+
fn compiler_error<T: ToTokens>(tokens: T, msg: String) -> TokenStream {
12+
Error::new_spanned(tokens, msg).to_compile_error().into()
13+
}
14+
15+
/// Parses TOML config content and expands it into Rust code.
16+
///
17+
/// # Example
18+
///
19+
/// See the [crate-level documentation][crate].
20+
#[proc_macro]
21+
pub fn parse_configs(config_toml: TokenStream) -> TokenStream {
22+
#[cfg(feature = "nightly")]
23+
let config_toml = match config_toml.expand_expr() {
24+
Ok(s) => s,
25+
Err(e) => {
26+
return Error::new(proc_macro2::Span::call_site(), e.to_string())
27+
.to_compile_error()
28+
.into()
29+
}
30+
};
31+
32+
let config_toml = parse_macro_input!(config_toml as LitStr).value();
33+
let code = Config::from_toml(&config_toml).and_then(|cfg| cfg.dump(OutputFormat::Rust));
34+
match code {
35+
Ok(code) => code
36+
.parse()
37+
.unwrap_or_else(|e: LexError| compiler_error(config_toml, e.to_string())),
38+
Err(e) => compiler_error(config_toml, e.to_string()),
39+
}
40+
}
41+
42+
/// Includes a TOML format config file and expands it into Rust code.
43+
///
44+
/// The given path should be an absolute path or a path relative to your
45+
/// project's `Cargo.toml`.
46+
///
47+
/// # Example
48+
///
49+
/// See the [crate-level documentation][crate].
50+
#[proc_macro]
51+
pub fn include_configs(path: TokenStream) -> TokenStream {
52+
let path = parse_macro_input!(path as LitStr);
53+
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
54+
let cfg_path = std::path::Path::new(&root).join(path.value());
55+
56+
let Ok(config_toml) = std::fs::read_to_string(&cfg_path) else {
57+
return compiler_error(path, format!("Failed to read config file: {:?}", cfg_path));
58+
};
59+
60+
quote! {
61+
::axconfig_gen_macros::parse_configs!(#config_toml);
62+
}
63+
.into()
64+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#[macro_use]
2+
extern crate axconfig_gen_macros;
3+
4+
mod config {
5+
include_configs!("../example-configs/defconfig.toml"); // root: CARGO_MANIFEST_DIR
6+
}
7+
8+
#[cfg(feature = "nightly")]
9+
mod config2 {
10+
parse_configs!(include_str!("../../example-configs/defconfig.toml"));
11+
}
12+
13+
mod config_expect {
14+
include!("../../example-configs/output.rs");
15+
}
16+
17+
macro_rules! mod_cmp {
18+
($mod1:ident, $mod2:ident) => {
19+
assert_eq!($mod1::ARCH, $mod2::ARCH);
20+
assert_eq!($mod1::PLAT, $mod2::PLAT);
21+
assert_eq!($mod1::SMP, $mod2::SMP);
22+
23+
assert_eq!($mod1::device::MMIO_REGIONS, $mod2::device::MMIO_REGIONS);
24+
assert_eq!($mod1::device::PCI_BUS_END, $mod2::device::PCI_BUS_END);
25+
assert_eq!($mod1::device::PCI_ECAM_BASE, $mod2::device::PCI_ECAM_BASE);
26+
assert_eq!($mod1::device::PCI_RANGES, $mod2::device::PCI_RANGES);
27+
assert_eq!(
28+
$mod1::device::VIRTIO_MMIO_REGIONS,
29+
$mod2::device::VIRTIO_MMIO_REGIONS
30+
);
31+
32+
assert_eq!(
33+
$mod1::kernel::TASK_STACK_SIZE,
34+
$mod2::kernel::TASK_STACK_SIZE
35+
);
36+
assert_eq!($mod1::kernel::TICKS_PER_SEC, $mod2::kernel::TICKS_PER_SEC);
37+
38+
assert_eq!(
39+
$mod1::platform::KERNEL_ASPACE_BASE,
40+
$mod2::platform::KERNEL_ASPACE_BASE
41+
);
42+
assert_eq!(
43+
$mod1::platform::KERNEL_ASPACE_SIZE,
44+
$mod2::platform::KERNEL_ASPACE_SIZE
45+
);
46+
assert_eq!(
47+
$mod1::platform::KERNEL_BASE_PADDR,
48+
$mod2::platform::KERNEL_BASE_PADDR
49+
);
50+
assert_eq!(
51+
$mod1::platform::KERNEL_BASE_VADDR,
52+
$mod2::platform::KERNEL_BASE_VADDR
53+
);
54+
assert_eq!(
55+
$mod1::platform::PHYS_BUS_OFFSET,
56+
$mod2::platform::PHYS_BUS_OFFSET
57+
);
58+
assert_eq!(
59+
$mod1::platform::PHYS_MEMORY_BASE,
60+
$mod2::platform::PHYS_MEMORY_BASE
61+
);
62+
assert_eq!(
63+
$mod1::platform::PHYS_MEMORY_SIZE,
64+
$mod2::platform::PHYS_MEMORY_SIZE
65+
);
66+
assert_eq!(
67+
$mod1::platform::PHYS_VIRT_OFFSET,
68+
$mod2::platform::PHYS_VIRT_OFFSET
69+
);
70+
assert_eq!(
71+
$mod1::platform::TIMER_FREQUENCY,
72+
$mod2::platform::TIMER_FREQUENCY
73+
);
74+
};
75+
}
76+
77+
#[test]
78+
fn test_include_configs() {
79+
mod_cmp!(config, config_expect);
80+
}
81+
82+
#[cfg(feature = "nightly")]
83+
#[test]
84+
fn test_parse_configs() {
85+
mod_cmp!(config2, config_expect);
86+
}

axconfig-gen/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "axconfig-gen"
3+
description = "A TOML-based configuration generator for ArceOS"
4+
documentation = "https://docs.rs/axconfig-gen"
5+
categories = ["config", "parsing", "parser-implementations"]
6+
keywords.workspace = true
7+
version.workspace = true
8+
edition.workspace = true
9+
authors.workspace = true
10+
license.workspace = true
11+
homepage.workspace = true
12+
repository.workspace = true
13+
14+
[dependencies]
15+
toml_edit = { version = "0.22" }
16+
clap = { version = "4", features = ["derive"] }

0 commit comments

Comments
 (0)