Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4eb6489
add tool_header2rdl: C/C++ header to RDL converter
Copilot Apr 7, 2026
c567c9a
address code review: safe pointee unwrap, better unnamed identifiers
Copilot Apr 7, 2026
e03fc8c
Merge branch 'master' into copilot/parse-win32-headers-to-rdl
kennykerr Apr 7, 2026
f002771
Merge branch 'master' into copilot/parse-win32-headers-to-rdl
kennykerr Apr 7, 2026
7c022d2
Merge branch 'master' into copilot/parse-win32-headers-to-rdl
kennykerr Apr 7, 2026
e899265
plan: replace CLI-args API with builder pattern
Copilot Apr 7, 2026
7005f8b
replace args-based API with Converter builder pattern; update tests
Copilot Apr 7, 2026
432d5a1
address review: rename idx, unify unnamed identifiers to param_/field_
Copilot Apr 7, 2026
b54a62a
fix build: use clang runtime feature so libclang isn't required at co…
Copilot Apr 7, 2026
38cec2e
fix clippy: use is_some_and instead of map_or(false, ...)
Copilot Apr 7, 2026
4d4b7de
fix test: exclude test_header2rdl from stable arm64 CI (no libclang o…
Copilot Apr 7, 2026
c6198ff
fix: exclude test_header2rdl from all Windows CI test runs (libclang …
Copilot Apr 7, 2026
5115741
Merge branch 'master' into copilot/parse-win32-headers-to-rdl
kennykerr Apr 7, 2026
f347f6a
fix: install LLVM via KyleMayes/install-llvm-action@v2 and set LIBCLA…
Copilot Apr 7, 2026
1a68cb9
ci: fix double space in test.yml Test run step (triggers CI re-run wi…
Copilot Apr 7, 2026
0bfb4e9
fix: exclude test_header2rdl from i686 CI (64-bit libclang.dll not lo…
Copilot Apr 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,25 @@ jobs:
host: x86_64-pc-windows-msvc
target: x86_64-pc-windows-msvc
runner: windows-2025
extra_excludes: ""
- name: nightly i686
version: nightly
host: x86_64-pc-windows-msvc
target: i686-pc-windows-msvc
runner: windows-2025
extra_excludes: "--exclude test_header2rdl"
- name: nightly gnu
version: nightly
host: x86_64-pc-windows-gnu
target: x86_64-pc-windows-gnu
runner: windows-2025
extra_excludes: ""
- name: stable arm64
version: stable
host: aarch64-pc-windows-msvc
target: aarch64-pc-windows-msvc
runner: windows-11-arm
extra_excludes: ""

runs-on: ${{ matrix.runner }}

Expand All @@ -65,8 +69,15 @@ jobs:
uses: ./.github/actions/fix-environment
with:
target: ${{ matrix.target }}
- name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v2
with:
version: "18"
- name: Set LIBCLANG_PATH
run: echo "LIBCLANG_PATH=${{ env.LLVM_PATH }}/bin" >> "$GITHUB_ENV"
shell: bash
- name: Test
run: cargo test --all --target ${{ matrix.target }} --exclude windows_aarch64_gnullvm --exclude windows_aarch64_msvc --exclude windows_i686_gnu --exclude windows_i686_gnullvm --exclude windows_i686_msvc --exclude windows_x86_64_gnu --exclude windows_x86_64_gnullvm --exclude windows_x86_64_msvc
run: cargo test --all --target ${{ matrix.target }} --exclude windows_aarch64_gnullvm --exclude windows_aarch64_msvc --exclude windows_i686_gnu --exclude windows_i686_gnullvm --exclude windows_i686_msvc --exclude windows_x86_64_gnu --exclude windows_x86_64_gnullvm --exclude windows_x86_64_msvc ${{ matrix.extra_excludes }}
- name: Check diff
shell: bash
run: |
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"crates/tests/misc/*",
"crates/tests/winrt/*",
"crates/tests/libs/*",
"crates/tests/tools/*",
"crates/tools/*",
]
exclude = [
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/rdl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![doc = include_str!("../readme.md")]

mod error;
mod formatter;
pub mod formatter;
mod reader;
mod writer;

Expand Down
15 changes: 15 additions & 0 deletions crates/tests/tools/header2rdl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "test_header2rdl"
version = "0.0.0"
edition = "2021"
publish = false

[lib]
doc = false
doctest = false

[dependencies]
tool_header2rdl = { path = "../../../tools/header2rdl" }

[lints]
workspace = true
1 change: 1 addition & 0 deletions crates/tests/tools/header2rdl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Integration tests are in tests/
72 changes: 72 additions & 0 deletions crates/tests/tools/header2rdl/tests/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/// For each `tests/*.h` file, run the `tool_header2rdl` converter and compare
/// the output against the matching `tests/*.rdl` golden file.
///
/// A `tests/<name>.h.args` sidecar file may supply extra options
/// (whitespace-separated): the only currently recognised token is `--cpp`.
/// All tests use `namespace("Test")` by default.
///
/// The `.rdl` golden files are stored in git. If the tool output changes, the
/// test fails. Set `UPDATE_EXPECT=1` to overwrite them with the current output
/// and commit the diff.
#[test]
fn convert() {
let tests_dir = std::path::Path::new("tests");
let update = std::env::var("UPDATE_EXPECT").is_ok();

let mut headers: Vec<_> = std::fs::read_dir(tests_dir)
.expect("tests directory not found – run tests from the crate root")
.flatten()
.filter_map(|e| {
let p = e.path();
if p.extension().is_some_and(|ext| ext == "h") {
Some(p)
} else {
None
}
})
.collect();
headers.sort();

for h_path in &headers {
let stem = h_path.file_stem().unwrap().to_string_lossy();

let mut c = tool_header2rdl::converter();
c.input(h_path.to_str().unwrap()).namespace("Test");

// Load optional sidecar options from `<name>.h.args`.
let sidecar = h_path.with_extension("h.args");
if sidecar.exists() {
let content = std::fs::read_to_string(&sidecar)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", sidecar.display()));
for token in content.split_whitespace() {
match token {
"--cpp" => {
c.cpp(true);
}
other => panic!(
"unrecognised sidecar token `{other}` in {}",
sidecar.display()
),
}
}
}

let actual = c
.convert()
.unwrap_or_else(|e| panic!("convert failed for {stem}.h: {e}"));

let rdl_path = tests_dir.join(format!("{stem}.rdl"));

if update || !rdl_path.exists() {
std::fs::write(&rdl_path, &actual)
.unwrap_or_else(|e| panic!("failed to write {}: {e}", rdl_path.display()));
} else {
let expected = std::fs::read_to_string(&rdl_path)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", rdl_path.display()));
assert_eq!(
actual, expected,
"\noutput changed for {stem}.h – rerun with UPDATE_EXPECT=1 to refresh\n"
);
}
}
}
12 changes: 12 additions & 0 deletions crates/tests/tools/header2rdl/tests/enum.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
typedef enum _STATUS {
STATUS_IDLE = 0,
STATUS_RUNNING = 1,
STATUS_ERROR = -1,
} STATUS;

typedef enum _FLAGS {
FLAG_NONE = 0,
FLAG_READ = 1,
FLAG_WRITE = 2,
FLAG_EXECUTE = 4,
} FLAGS;
16 changes: 16 additions & 0 deletions crates/tests/tools/header2rdl/tests/enum.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[win32]
mod Test {
#[repr(i32)]
enum STATUS {
STATUS_IDLE = 0,
STATUS_RUNNING = 1,
STATUS_ERROR = -1,
}
#[repr(i32)]
enum FLAGS {
FLAG_NONE = 0,
FLAG_READ = 1,
FLAG_WRITE = 2,
FLAG_EXECUTE = 4,
}
}
8 changes: 8 additions & 0 deletions crates/tests/tools/header2rdl/tests/fn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
typedef long HRESULT;
typedef unsigned int DWORD;
typedef void* HANDLE;

int Add(int a, int b);
HRESULT CreateHandle(DWORD flags, HANDLE* out);
void CloseHandle(HANDLE handle);
DWORD GetCount(const void* data, DWORD length);
7 changes: 7 additions & 0 deletions crates/tests/tools/header2rdl/tests/fn.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[win32]
mod Test {
fn Add(a: i32, b: i32) -> i32;
fn CreateHandle(flags: DWORD, out: *mut HANDLE) -> HRESULT;
fn CloseHandle(handle: HANDLE);
fn GetCount(data: *const u8, length: DWORD) -> DWORD;
}
14 changes: 14 additions & 0 deletions crates/tests/tools/header2rdl/tests/interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
struct IUnknown {
virtual long QueryInterface(int riid, void** ppv) = 0;
virtual unsigned int AddRef() = 0;
virtual unsigned int Release() = 0;
};

struct IWidget : IUnknown {
virtual long GetId(int* id) = 0;
virtual long SetName(const char* name) = 0;
};

struct IWidgetFactory : IUnknown {
virtual long CreateWidget(const char* name, IWidget** out) = 0;
};
1 change: 1 addition & 0 deletions crates/tests/tools/header2rdl/tests/interface.h.args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--cpp
14 changes: 14 additions & 0 deletions crates/tests/tools/header2rdl/tests/interface.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[win32]
mod Test {
#[no_guid]
interface IUnknown {}
#[no_guid]
interface IWidget: IUnknown {
fn GetId(&self, id: *mut i32) -> i32;
fn SetName(&self, name: *const i8) -> i32;
}
#[no_guid]
interface IWidgetFactory: IUnknown {
fn CreateWidget(&self, name: *const i8, out: *mut *mut IWidget) -> i32;
}
}
19 changes: 19 additions & 0 deletions crates/tests/tools/header2rdl/tests/struct.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
typedef struct _POINT {
int X;
int Y;
} POINT;

typedef struct _RECT {
int left;
int top;
int right;
int bottom;
} RECT;

typedef union _LARGE_INTEGER {
struct {
unsigned int LowPart;
int HighPart;
} u;
long long QuadPart;
} LARGE_INTEGER;
17 changes: 17 additions & 0 deletions crates/tests/tools/header2rdl/tests/struct.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#[win32]
mod Test {
struct POINT {
X: i32,
Y: i32,
}
struct RECT {
left: i32,
top: i32,
right: i32,
bottom: i32,
}
union LARGE_INTEGER {
u: *mut u8,
QuadPart: i64,
}
}
26 changes: 26 additions & 0 deletions crates/tools/header2rdl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "tool_header2rdl"
version = "0.0.0"
edition = "2021"
publish = false

# This tool requires libclang at runtime.
# On Ubuntu, set LIBCLANG_PATH=/usr/lib/llvm-18/lib (or whichever LLVM version
# is installed) before running `cargo run` or the tests.

[lib]
name = "tool_header2rdl"
path = "src/lib.rs"
doc = false
doctest = false

[[bin]]
name = "tool_header2rdl"
path = "src/main.rs"

[dependencies]
clang = { version = "2.0.0", features = ["runtime"] }
windows-rdl = { workspace = true }

[lints]
workspace = true
Loading
Loading