2 releases

0.1.1 Aug 6, 2023
0.1.0 Aug 5, 2023

#211 in Procedural macros


Used in nucomcore

MIT license

82KB
2K SLoC

nuidl

nuidl is the IDL compiler for Nucom. It is available as both a command-line tool and a Rust macro crate.

See Nucom repository or the nucomcore crate (lib.rs crates.io) for more information about Nucom.

CLI

Use with Nix

Run nix shell git+https://git.dblsaiko.net/nucom to enter a shell with nuidl.

Use with Cargo

Run cargo install nuidl to install.

Syntax

IDL source generator

Usage: nuidl [OPTIONS] <--header|--c-guid|--rust> <FILE>...

Arguments:
  <FILE>...  Specify input IDL files

Options:
  -I, --include <DIR>  Add to include path
  -c, --header         Generate C/C++ header files
  -g, --c-guid         Generate C GUID source for interfaces
  -r, --rust           Generate Rust module
  -o, --output <DIR>   Specify output directory [default: .]
  -w, --win32          Generate headers compatible with the Win32 C/C++ API instead of nucom
  -h, --help           Print help
  -V, --version        Print version

Notes:

  • The files generated depend on the nucomcore library for both Rust and C/C++.
  • The --win32 option is currently not implemented.

Macro crate

nuidl can also be used to directly generate Rust modules from IDL files using procedural macros, without having to save the generated code to a file first.

Add nuidl and nucomcore to your project.

[build-dependencies]
nuidl = "0.1.0"

[dependencies]
nucomcore = "0.1.0"
nuidl = "0.1.0"

Since the IDL compiler needs to access IDL files you import in your IDLs such as iunknown.idl, it needs to know the include path list to find those at. When compiling with Cargo, this can be discovered automatically, you just need to run the code for this in build.rs. Add the following code into the file:

fn main() {
    nuidl::cargo::use_cargo_deps();
}

In the end, this passes the include path to nuidl with println!("cargo:rustc-env=NUIDL_INCLUDE_PATH={}", paths);, so you can also pass those manually like that if you wish (then, you don't need nuidl as a build dependency).

After that, put your IDL files into the include directory in your crate root (it is recommended to put it in a subdirectory to avoid path conflicts, e.g. Nucom IDLs are in include/nucom). The include path for your crate can be changed in Cargo.toml using the package.metadata.nucom.include key. Paths specified here are relative to the crate root directory.

[package.metadata.nucom]
include = ["other/path"]

Make an idl module in your crate with the following contents:

use nuidl::idl_mod;

// Import everything from each crate's idl module that you want to use. idl_mod!
// generated code will use these.
// You'll probably want nucomcore at the very least.

#[allow(unused_imports)]
use nucomcore::idl::*;

// List your crate's IDL files here, relative to the include directory.
// If your include path is unchanged, this will look in
// include/path/to/foo.idl.

idl_mod!(pub mod "path/to/foo.idl");
idl_mod!(pub mod "path/to/bar.idl");

// If your IDLs have any include statements (not import) referring to C headers,
// they must also be provided as Rust code. For example, a C header baz.h with
// the following contents,
//
//     #include <nucom/iunknown.h>
//     
//     extern IUnknown *make_baz(void);
//
// might be represented as the following module:

pub mod baz {
    // The scope module helps to emulate C #include behavior, where including
    // a file also brings all the type that file itself includes into scope.
    // This is an ugly hack and I might replace it with something else in the
    // future. Ideally, everything should be written as IDL definitions anyway,
    // so once the IDL compiler is sufficiently done maybe this can go away.
    //
    // Any declarations visible from the C header (here, IUnknown and make_baz)
    // must also be publicly accessible through the scope module.
    #[doc(hidden)]
    pub mod scope {
        // NB: this does not re-export private items from super, like ComPtr.
        pub use super::*;
        pub use nucomcore::IUnknown;
    }
    
    use nucomcore::{ComPtr, IUnknown};
    
    extern "C" {
        // ComPtr<T> has the same representation as a T* pointer in C.
        pub fn make_baz() -> ComPtr<IUnknown>;
    }
}

Dependencies

~5–8MB
~140K SLoC