Skip to content

Commit

Permalink
Add axconfig-gen-macros
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Dec 21, 2024
1 parent e049deb commit 8db0eb4
Show file tree
Hide file tree
Showing 20 changed files with 421 additions and 32 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,13 @@ jobs:
contents: write
env:
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
RUSTDOCFLAGS: -Zunstable-options --enable-index-page -D rustdoc::broken_intra_doc_links -D missing-docs
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
cargo doc --no-deps --all-features
printf '<meta http-equiv="refresh" content="0;url=axconfig_gen/index.html">' > target/doc/index.html
run: cargo doc --no-deps --all-features
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
Expand Down
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
[package]
name = "axconfig-gen"
[workspace]
resolver = "2"

members = [
"axconfig-gen",
"axconfig-gen-macros",
]

[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Yuekai Jia <[email protected]>"]
description = "A TOML-based configuration generator for ArceOS"
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
homepage = "https://github.com/arceos-org/arceos"
repository = "https://github.com/arceos-org/axconfig-gen"
documentation = "https://docs.rs/axconfig-gen"
keywords = ["arceos", "config", "toml"]
categories = ["config", "parsing", "parser-implementations"]

[dependencies]
toml_edit = { version = "0.22" }
clap = { version = "4", features = ["derive"] }
82 changes: 78 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# axconfig-gen

A TOML-based configuration generation tool for [ArceOS](https://github.com/arceos-org/arceos).
[![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)

## Usage
* [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)
* [axconfig-gen-macros](axconfig-gen-macros): Procedural macros for converting TOML format configurations to Rust constant definitions. [![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)

```
### Executable Usage

```text
axconfig-gen [OPTIONS] --spec <SPEC>
Options:
Expand All @@ -22,4 +25,75 @@ For example, to generate a config file `.axconfig.toml` from the config specific
axconfig-gen -s a.toml -s b.toml -o .axconfig.toml -f toml
```

See [defconfig.toml](example_configs/defconfig.toml) for an example of a config specification file.
See [defconfig.toml](example-configs/defconfig.toml) for an example of a config specification file.

Value types are necessary for generating Rust constant definitions. 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.

### Library Usage

```rust
use axconfig_gen::{Config, OutputFormat};

let config_toml = r#"
are-you-ok = true
one-two-three = 123
[hello]
"one-two-three" = "456" # int
array = [1, 2, 3] # [uint]
tuple = [1, "abc", 3]
"#;

let config = Config::from_toml(config_toml).unwrap();
let rust_code = config.dump(OutputFormat::Rust).unwrap();

assert_eq!(rust_code, r#"
pub const ARE_YOU_OK: bool = true;
pub const ONE_TWO_THREE: usize = 123;
pub mod hello {
pub const ARRAY: &[usize] = &[1, 2, 3];
pub const ONE_TWO_THREE: isize = 456;
pub const TUPLE: (usize, &str, usize) = (1, "abc", 3);
}
"#);
```

### Macro Usage

```rust
axconfig_gen_macros::parse_configs!(r#"
are-you-ok = true
one-two-three = 123
[hello]
"one-two-three" = "456" # int
array = [1, 2, 3] # [uint]
tuple = [1, "abc", 3]
"#);

assert_eq!(ARE_YOU_OK, true);
assert_eq!(ONE_TWO_THREE, 123usize);
assert_eq!(hello::ONE_TWO_THREE, 456isize);
assert_eq!(hello::ARRAY, [1, 2, 3]);
assert_eq!(hello::TUPLE, (1, "abc", 3));
```

The above example will generate the following constants:

```rust
pub const ARE_YOU_OK: bool = true;
pub const ONE_TWO_THREE: usize = 123;

pub mod hello {
pub const ARRAY: &[usize] = &[1, 2, 3];
pub const ONE_TWO_THREE: isize = 456;
pub const TUPLE: (usize, &str, usize) = (1, "abc", 3);
}
```

You can also include the configuration file directly:

```rust
axconfig_gen_macros::include_configs!("/path/to/config.toml");
```
24 changes: 24 additions & 0 deletions axconfig-gen-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "axconfig-gen-macros"
description = "Procedural macros for converting TOML format configurations to Rust constant definitions."
documentation = "https://docs.rs/axconfig-gen-macros"
categories = ["development-tools::procedural-macro-helpers", "config", "parsing", "parser-implementations"]
keywords.workspace = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[features]
nightly = []

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
axconfig-gen = { path = "../axconfig-gen" }

[lib]
proc-macro = true
44 changes: 44 additions & 0 deletions axconfig-gen-macros/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# axconfig-gen-macros

Procedural macros for converting TOML format configurations to equivalent Rust constant definitions.

## Example

```rust
axconfig_gen_macros::parse_configs!(r#"
are-you-ok = true
one-two-three = 123
[hello]
"one-two-three" = "456" # int
array = [1, 2, 3] # [uint]
tuple = [1, "abc", 3]
"#);

assert_eq!(ARE_YOU_OK, true);
assert_eq!(ONE_TWO_THREE, 123usize);
assert_eq!(hello::ONE_TWO_THREE, 456isize);
assert_eq!(hello::ARRAY, [1, 2, 3]);
assert_eq!(hello::TUPLE, (1, "abc", 3));
```

Value types are necessary for generating Rust constant definitions. 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.

The above example will generate the following constants:

```rust
pub const ARE_YOU_OK: bool = true;
pub const ONE_TWO_THREE: usize = 123;

pub mod hello {
pub const ARRAY: &[usize] = &[1, 2, 3];
pub const ONE_TWO_THREE: isize = 456;
pub const TUPLE: (usize, &str, usize) = (1, "abc", 3);
}
```

You can also include the configuration file directly:

```rust,ignore
axconfig_gen_macros::include_configs!("/path/to/config.toml");
```
64 changes: 64 additions & 0 deletions axconfig-gen-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#![cfg_attr(feature = "nightly", feature(proc_macro_expand))]
#![doc = include_str!("../README.md")]

use proc_macro::{LexError, TokenStream};
use quote::{quote, ToTokens};
use syn::parse_macro_input;
use syn::{Error, LitStr};

use axconfig_gen::{Config, OutputFormat};

fn compiler_error<T: ToTokens>(tokens: T, msg: String) -> TokenStream {
Error::new_spanned(tokens, msg).to_compile_error().into()
}

/// Parses TOML config content and expands it into Rust code.
///
/// # Example
///
/// See the [crate-level documentation][crate].
#[proc_macro]
pub fn parse_configs(config_toml: TokenStream) -> TokenStream {
#[cfg(feature = "nightly")]
let config_toml = match config_toml.expand_expr() {
Ok(s) => s,
Err(e) => {
return Error::new(proc_macro2::Span::call_site(), e.to_string())
.to_compile_error()
.into()
}
};

let config_toml = parse_macro_input!(config_toml as LitStr).value();
let code = Config::from_toml(&config_toml).and_then(|cfg| cfg.dump(OutputFormat::Rust));
match code {
Ok(code) => code
.parse()
.unwrap_or_else(|e: LexError| compiler_error(config_toml, e.to_string())),
Err(e) => compiler_error(config_toml, e.to_string()),
}
}

/// Includes a TOML format config file and expands it into Rust code.
///
/// The given path should be an absolute path or a path relative to your
/// project's `Cargo.toml`.
///
/// # Example
///
/// See the [crate-level documentation][crate].
#[proc_macro]
pub fn include_configs(path: TokenStream) -> TokenStream {
let path = parse_macro_input!(path as LitStr);
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
let cfg_path = std::path::Path::new(&root).join(path.value());

let Ok(config_toml) = std::fs::read_to_string(&cfg_path) else {
return compiler_error(path, format!("Failed to read config file: {:?}", cfg_path));
};

quote! {
::axconfig_gen_macros::parse_configs!(#config_toml);
}
.into()
}
86 changes: 86 additions & 0 deletions axconfig-gen-macros/tests/example_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#[macro_use]
extern crate axconfig_gen_macros;

mod config {
include_configs!("../example-configs/defconfig.toml"); // root: CARGO_MANIFEST_DIR
}

#[cfg(feature = "nightly")]
mod config2 {
parse_configs!(include_str!("../../example-configs/defconfig.toml"));
}

mod config_expect {
include!("../../example-configs/output.rs");
}

macro_rules! mod_cmp {
($mod1:ident, $mod2:ident) => {
assert_eq!($mod1::ARCH, $mod2::ARCH);
assert_eq!($mod1::PLAT, $mod2::PLAT);
assert_eq!($mod1::SMP, $mod2::SMP);

assert_eq!($mod1::device::MMIO_REGIONS, $mod2::device::MMIO_REGIONS);
assert_eq!($mod1::device::PCI_BUS_END, $mod2::device::PCI_BUS_END);
assert_eq!($mod1::device::PCI_ECAM_BASE, $mod2::device::PCI_ECAM_BASE);
assert_eq!($mod1::device::PCI_RANGES, $mod2::device::PCI_RANGES);
assert_eq!(
$mod1::device::VIRTIO_MMIO_REGIONS,
$mod2::device::VIRTIO_MMIO_REGIONS
);

assert_eq!(
$mod1::kernel::TASK_STACK_SIZE,
$mod2::kernel::TASK_STACK_SIZE
);
assert_eq!($mod1::kernel::TICKS_PER_SEC, $mod2::kernel::TICKS_PER_SEC);

assert_eq!(
$mod1::platform::KERNEL_ASPACE_BASE,
$mod2::platform::KERNEL_ASPACE_BASE
);
assert_eq!(
$mod1::platform::KERNEL_ASPACE_SIZE,
$mod2::platform::KERNEL_ASPACE_SIZE
);
assert_eq!(
$mod1::platform::KERNEL_BASE_PADDR,
$mod2::platform::KERNEL_BASE_PADDR
);
assert_eq!(
$mod1::platform::KERNEL_BASE_VADDR,
$mod2::platform::KERNEL_BASE_VADDR
);
assert_eq!(
$mod1::platform::PHYS_BUS_OFFSET,
$mod2::platform::PHYS_BUS_OFFSET
);
assert_eq!(
$mod1::platform::PHYS_MEMORY_BASE,
$mod2::platform::PHYS_MEMORY_BASE
);
assert_eq!(
$mod1::platform::PHYS_MEMORY_SIZE,
$mod2::platform::PHYS_MEMORY_SIZE
);
assert_eq!(
$mod1::platform::PHYS_VIRT_OFFSET,
$mod2::platform::PHYS_VIRT_OFFSET
);
assert_eq!(
$mod1::platform::TIMER_FREQUENCY,
$mod2::platform::TIMER_FREQUENCY
);
};
}

#[test]
fn test_include_configs() {
mod_cmp!(config, config_expect);
}

#[cfg(feature = "nightly")]
#[test]
fn test_parse_configs() {
mod_cmp!(config2, config_expect);
}
16 changes: 16 additions & 0 deletions axconfig-gen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "axconfig-gen"
description = "A TOML-based configuration generation tool for ArceOS."
documentation = "https://docs.rs/axconfig-gen"
categories = ["config", "parsing", "parser-implementations"]
keywords.workspace = true
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
toml_edit = { version = "0.22" }
clap = { version = "4", features = ["derive"] }
Loading

0 comments on commit 8db0eb4

Please sign in to comment.