DNNE (Dotnet Native Exports) enables .NET managed assemblies to expose native exports consumable by native code. It generates C or Rust source from assemblies marked with [UnmanagedCallersOnly] and compiles them into platform-specific native binaries (.dll, .so, .dylib).
# Build the NuGet package (includes all components)
dotnet build src/create_package.proj
# Run all unit tests
dotnet test test/DNNE.UnitTests
# Run a single test by name
dotnet test test/DNNE.UnitTests --filter "FullyQualifiedName~TestMethodName"
# Test with alternative compilers (useful for cross-compiler validation)
dotnet test test/DNNE.UnitTests -p:BuildWithGCC=true
dotnet test test/DNNE.UnitTests -p:BuildAsCPPWithMSVC=true
dotnet test test/DNNE.UnitTests -p:BuildWithClangPP=trueThe test framework is xUnit. Tests target net8.0 (and net472 on Windows).
The project has a pipeline architecture where each component feeds into the next:
-
dnne-analyzers (
src/dnne-analyzers/) — Roslyn source generator that emits the DNNE attribute types into consuming projects at compile time. Targetsnetstandard2.0and requires Roslyn 4.0+. Generated attributes include:ExportAttribute,C99DeclCodeAttribute,C99TypeAttribute— for C99 outputRustDeclCodeAttribute,RustTypeAttribute— for Rust output
-
dnne-gen (
src/dnne-gen/) — CLI tool (.NET 8.0) that reads a compiled managed assembly via reflection, finds methods marked with[UnmanagedCallersOnly]or[DNNE.Export], and generates native source code with the corresponding export signatures. Supports two output languages selected via-l:c99(default) — generates C99 source withDNNE_APImacros, C preprocessor platform guards, and lazy-init function pointer wrappers.rust— generates Rust source withpub unsafe fnwrappers intended for Rust callers (noextern "C"/#[no_mangle]),AtomicPtrlazy initialization,#[cfg(target_os)]platform guards, andcore::ffitypes.
The
Generatorclass uses language-specific type providers (C99TypeProvider/RustTypeProvider) and emitters (EmitC99/EmitRust). Attribute detection is unified throughTryGetLanguageTypeAttributeValueandTryGetLanguageDeclCodeAttributeValue, which select C99 or Rust attributes based on the output language. Exports with non-primitive value types that lack a type override are skipped in Rust mode. -
MSBuild integration (
src/msbuild/) —DNNE.propsdefines configurable properties;DNNE.targetswires up the build pipeline (run dnne-gen, then invoke the native compiler).DNNE.BuildTasks/contains custom MSBuild tasks with platform-specific compilation logic inWindows.cs,Linux.cs, andmacOS.cs. -
Platform layer (
src/platform/) — Native source that bootstraps the .NET runtime vianethostand dispatches calls to managed exports.platform.c/dnne.h— C implementation with platform-conditional code (#ifdef DNNE_WINDOWSetc.) for library loading, path resolution, error state preservation, and thread-safe runtime initialization via spinlock.platform_v4.cpp— .NET Framework v4.x activation (C++ only).platform.rs— Rust implementation targeting .NET (Core) only. Usesstd::sync::Mutexfor thread-safe initialization, platform-specificsysmodules for Unix/Windows, and a UTF-8 public API (*const u8) with internal wide-string conversion on Windows. Expects a crate-root constantDNNE_ASSEMBLY_NAME(for example, generated intolib.rsbyDNNE.targets).
-
dnne-pkg (
src/dnne-pkg/) — Orchestrates NuGet package creation, bundling analyzers, gen tool, build tasks, and platform source into the published package.
- Exported methods must be
public staticand marked with[UnmanagedCallersOnly](preferred) or[DNNE.Export](experimental). The enclosing class's accessibility doesn't matter. - When using
[DNNE.Export], a companionDelegatetype named<MethodName>Delegatemust exist at the same scope. - Custom native type mappings use language-specific attributes:
- C99:
[DNNE.C99Type("...")]on parameters/return values and[DNNE.C99DeclCode("...")]on methods for struct definitions or#includedirectives. - Rust:
[DNNE.RustType("...")]on parameters/return values and[DNNE.RustDeclCode("...")]on methods for type definitions orusestatements.
- C99:
- The generated native binary is named
<AssemblyName>NEby default (suffix controlled byDnneNativeBinarySuffixMSBuild property). - MSBuild properties controlling behavior are defined in
src/msbuild/DNNE.props— this is the reference for all DNNE configuration options. - C# language version varies by component:
C# 11.0for analyzers,C# 9.0for build tasks, default for other projects. - The DNNE attribute types are source-generated into consuming projects — DNNE provides no assembly reference.