Skip to content

Commit 2a810d0

Browse files
Add support for Rust (#214)
* Add Rust as an alternative output language for DNNE - dnne-gen: Add -l flag to select output language (c99 default, rust) - dnne-analyzers: Add RustTypeAttribute and RustDeclCodeAttribute source-generated into consuming projects - platform.rs: Rust equivalent of platform.c (.NET Core only) * Add Rust Cargo crate generation and ImportingProcess.Rust test --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 56881df commit 2a810d0

File tree

26 files changed

+2210
-552
lines changed

26 files changed

+2210
-552
lines changed

.github/copilot-instructions.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copilot Instructions for DNNE
2+
3+
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`).
4+
5+
## Build & Test
6+
7+
```bash
8+
# Build the NuGet package (includes all components)
9+
dotnet build src/create_package.proj
10+
11+
# Run all unit tests
12+
dotnet test test/DNNE.UnitTests
13+
14+
# Run a single test by name
15+
dotnet test test/DNNE.UnitTests --filter "FullyQualifiedName~TestMethodName"
16+
17+
# Test with alternative compilers (useful for cross-compiler validation)
18+
dotnet test test/DNNE.UnitTests -p:BuildWithGCC=true
19+
dotnet test test/DNNE.UnitTests -p:BuildAsCPPWithMSVC=true
20+
dotnet test test/DNNE.UnitTests -p:BuildWithClangPP=true
21+
```
22+
23+
The test framework is **xUnit**. Tests target `net8.0` (and `net472` on Windows).
24+
25+
## Architecture
26+
27+
The project has a pipeline architecture where each component feeds into the next:
28+
29+
1. **dnne-analyzers** (`src/dnne-analyzers/`) — Roslyn source generator that emits the DNNE attribute types into consuming projects at compile time. Targets `netstandard2.0` and requires Roslyn 4.0+. Generated attributes include:
30+
- `ExportAttribute`, `C99DeclCodeAttribute`, `C99TypeAttribute` — for C99 output
31+
- `RustDeclCodeAttribute`, `RustTypeAttribute` — for Rust output
32+
33+
2. **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`:
34+
- `c99` (default) — generates C99 source with `DNNE_API` macros, C preprocessor platform guards, and lazy-init function pointer wrappers.
35+
- `rust` — generates Rust source with `pub unsafe fn` wrappers intended for Rust callers (no `extern "C"` / `#[no_mangle]`), `AtomicPtr` lazy initialization, `#[cfg(target_os)]` platform guards, and `core::ffi` types.
36+
37+
The `Generator` class uses language-specific type providers (`C99TypeProvider` / `RustTypeProvider`) and emitters (`EmitC99` / `EmitRust`). Attribute detection is unified through `TryGetLanguageTypeAttributeValue` and `TryGetLanguageDeclCodeAttributeValue`, 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.
38+
39+
3. **MSBuild integration** (`src/msbuild/`) — `DNNE.props` defines configurable properties; `DNNE.targets` wires up the build pipeline (run dnne-gen, then invoke the native compiler). `DNNE.BuildTasks/` contains custom MSBuild tasks with platform-specific compilation logic in `Windows.cs`, `Linux.cs`, and `macOS.cs`.
40+
41+
4. **Platform layer** (`src/platform/`) — Native source that bootstraps the .NET runtime via `nethost` and dispatches calls to managed exports.
42+
- `platform.c` / `dnne.h` — C implementation with platform-conditional code (`#ifdef DNNE_WINDOWS` etc.) for library loading, path resolution, error state preservation, and thread-safe runtime initialization via spinlock.
43+
- `platform_v4.cpp` — .NET Framework v4.x activation (C++ only).
44+
- `platform.rs` — Rust implementation targeting .NET (Core) only. Uses `std::sync::Mutex` for thread-safe initialization, platform-specific `sys` modules for Unix/Windows, and a UTF-8 public API (`*const u8`) with internal wide-string conversion on Windows. Expects a crate-root constant `DNNE_ASSEMBLY_NAME` (for example, generated into `lib.rs` by `DNNE.targets`).
45+
46+
5. **dnne-pkg** (`src/dnne-pkg/`) — Orchestrates NuGet package creation, bundling analyzers, gen tool, build tasks, and platform source into the published package.
47+
48+
## Key Conventions
49+
50+
- Exported methods must be `public static` and marked with `[UnmanagedCallersOnly]` (preferred) or `[DNNE.Export]` (experimental). The enclosing class's accessibility doesn't matter.
51+
- When using `[DNNE.Export]`, a companion `Delegate` type named `<MethodName>Delegate` must exist at the same scope.
52+
- Custom native type mappings use language-specific attributes:
53+
- **C99**: `[DNNE.C99Type("...")]` on parameters/return values and `[DNNE.C99DeclCode("...")]` on methods for struct definitions or `#include` directives.
54+
- **Rust**: `[DNNE.RustType("...")]` on parameters/return values and `[DNNE.RustDeclCode("...")]` on methods for type definitions or `use` statements.
55+
- The generated native binary is named `<AssemblyName>NE` by default (suffix controlled by `DnneNativeBinarySuffix` MSBuild property).
56+
- MSBuild properties controlling behavior are defined in `src/msbuild/DNNE.props` — this is the reference for all DNNE configuration options.
57+
- C# language version varies by component: `C# 11.0` for analyzers, `C# 9.0` for build tasks, default for other projects.
58+
- The DNNE attribute types are source-generated into consuming projects — DNNE provides no assembly reference.

.github/workflows/main.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ jobs:
2323
with:
2424
dotnet-version: '8.0.x'
2525
dotnet-quality: 'ga'
26+
- name: Setup Rust
27+
uses: actions-rust-lang/setup-rust-toolchain@v1
28+
with:
29+
toolchain: 'stable'
2630
- name: Build Product and Package
2731
run: dotnet build src/create_package.proj -c ${{ matrix.flavor }}
2832
- name: Unit Test Product
@@ -35,6 +39,9 @@ jobs:
3539
run: |
3640
dotnet clean test/DNNE.UnitTests -c ${{ matrix.flavor }}
3741
dotnet test test/DNNE.UnitTests -c ${{ matrix.flavor }} -p:BuildWithGPP=true
42+
- name: Build test.proj
43+
run: |
44+
dotnet build test/test.proj -c ${{ matrix.flavor }} -p:BuildPackage=false
3845
- name: Upload Build Logs
3946
if: failure()
4047
uses: actions/upload-artifact@v4
@@ -59,6 +66,10 @@ jobs:
5966
with:
6067
dotnet-version: '8.0.x'
6168
dotnet-quality: 'ga'
69+
- name: Setup Rust
70+
uses: actions-rust-lang/setup-rust-toolchain@v1
71+
with:
72+
toolchain: 'stable'
6273
- name: Build Product and Package
6374
run: dotnet build src\create_package.proj -c ${{ matrix.flavor }}
6475
- name: Build ExportingAssembly (.NET Core and .NET Framework)
@@ -71,6 +82,9 @@ jobs:
7182
run: |
7283
dotnet clean test\DNNE.UnitTests -c ${{ matrix.flavor }}
7384
dotnet test test\DNNE.UnitTests -c ${{ matrix.flavor }}
85+
- name: Build test.proj
86+
run: |
87+
dotnet build test\test.proj -c ${{ matrix.flavor }} -p:BuildPackage=false
7488
- name: Upload Build Logs
7589
if: failure()
7690
uses: actions/upload-artifact@v4
@@ -90,6 +104,10 @@ jobs:
90104
with:
91105
dotnet-version: '8.0.x'
92106
dotnet-quality: 'ga'
107+
- name: Setup Rust
108+
uses: actions-rust-lang/setup-rust-toolchain@v1
109+
with:
110+
toolchain: 'stable'
93111
- name: Build Product and Package
94112
run: dotnet build src/create_package.proj -c ${{ matrix.flavor }}
95113
- name: Unit Test Product
@@ -98,6 +116,9 @@ jobs:
98116
run: |
99117
dotnet clean test/DNNE.UnitTests -c ${{ matrix.flavor }}
100118
dotnet test test/DNNE.UnitTests -c ${{ matrix.flavor }} -p:BuildWithClangPP=true
119+
- name: Build test.proj
120+
run: |
121+
dotnet build test/test.proj -c ${{ matrix.flavor }} -p:BuildPackage=false
101122
- name: Upload Build Logs
102123
if: failure()
103124
uses: actions/upload-artifact@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
**/bin/
44
**/obj/
5+
**/target/
56
**/*.user
67
**/launchSettings.json
78

Directory.Build.props

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project>
2+
<PropertyGroup>
3+
<DnneTargetFramework>net8.0</DnneTargetFramework>
4+
<DnneVersion>2.0.8</DnneVersion>
5+
6+
<RepoRoot>$(MSBuildThisFileDirectory)/</RepoRoot>
7+
<SrcRoot>$(RepoRoot)src/</SrcRoot>
8+
</PropertyGroup>
9+
</Project>

0 commit comments

Comments
 (0)