Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a7d7c90
Add Rust as an alternative output language for DNNE
AaronRobinsonMSFT Feb 6, 2026
cc36213
Add Rust Cargo crate generation and ImportingProcess.Rust test
AaronRobinsonMSFT Feb 6, 2026
8999ba0
Update README to include Rust toolchain and examples for native exports
AaronRobinsonMSFT Feb 6, 2026
c8658c2
Apply suggestions from code review
AaronRobinsonMSFT Feb 7, 2026
4a95259
Enhance Rust crate generation by updating cleanup rules and adding cr…
AaronRobinsonMSFT Feb 7, 2026
b282bad
Apply suggestions from code review
AaronRobinsonMSFT Feb 7, 2026
d732288
Apply suggestions from code review
AaronRobinsonMSFT Feb 7, 2026
e3f4dec
Refactor project files to use variables for target framework and vers…
AaronRobinsonMSFT Feb 7, 2026
b5af00a
Build test.proj in CI
AaronRobinsonMSFT Feb 7, 2026
354d5b8
Fix linux path
AaronRobinsonMSFT Feb 7, 2026
a3c55bf
Fix Windows build
AaronRobinsonMSFT Feb 7, 2026
ca7a0fa
Update build configuration to conditionally disable package building …
AaronRobinsonMSFT Feb 8, 2026
6c3f841
Cargo release build.
AaronRobinsonMSFT Feb 8, 2026
3bc3204
Update abort handling in Rust platform module to use standard abort f…
AaronRobinsonMSFT Feb 8, 2026
3f1a1c2
Update README to clarify sample directory structure and Rust example …
AaronRobinsonMSFT Feb 8, 2026
c3253aa
Refactor various files
AaronRobinsonMSFT Feb 8, 2026
6830f9b
Review feedback
AaronRobinsonMSFT Feb 8, 2026
559d4cf
Incorrect argument to setup rust toolchain
AaronRobinsonMSFT Feb 8, 2026
6e09886
Remove Rust targeting on Windows.
AaronRobinsonMSFT Feb 8, 2026
6eed37c
Disable .NET 4.7.2 builds when using Rust.
AaronRobinsonMSFT Feb 8, 2026
b71a8a9
Build break.
AaronRobinsonMSFT Feb 8, 2026
48a3487
Update Rust export paths and add new input files for incremental builds
AaronRobinsonMSFT Feb 8, 2026
56a47db
Add safe identifier handling for Rust keywords in RustEmitter
AaronRobinsonMSFT Feb 8, 2026
9a878a9
Apply suggestions from code review
AaronRobinsonMSFT Feb 8, 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
58 changes: 58 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copilot Instructions for DNNE

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 & Test

```bash
# 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=true
```

The test framework is **xUnit**. Tests target `net8.0` (and `net472` on Windows).

## Architecture

The project has a pipeline architecture where each component feeds into the next:

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:
- `ExportAttribute`, `C99DeclCodeAttribute`, `C99TypeAttribute` — for C99 output
- `RustDeclCodeAttribute`, `RustTypeAttribute` — for Rust output

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`:
- `c99` (default) — generates C99 source with `DNNE_API` macros, C preprocessor platform guards, and lazy-init function pointer wrappers.
- `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.

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.

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`.

4. **Platform layer** (`src/platform/`) — Native source that bootstraps the .NET runtime via `nethost` and dispatches calls to managed exports.
- `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.
- `platform_v4.cpp` — .NET Framework v4.x activation (C++ only).
- `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`).

5. **dnne-pkg** (`src/dnne-pkg/`) — Orchestrates NuGet package creation, bundling analyzers, gen tool, build tasks, and platform source into the published package.

## Key Conventions

- Exported methods must be `public static` and marked with `[UnmanagedCallersOnly]` (preferred) or `[DNNE.Export]` (experimental). The enclosing class's accessibility doesn't matter.
- When using `[DNNE.Export]`, a companion `Delegate` type named `<MethodName>Delegate` must 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 `#include` directives.
- **Rust**: `[DNNE.RustType("...")]` on parameters/return values and `[DNNE.RustDeclCode("...")]` on methods for type definitions or `use` statements.
- The generated native binary is named `<AssemblyName>NE` by default (suffix controlled by `DnneNativeBinarySuffix` MSBuild 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.0` for analyzers, `C# 9.0` for build tasks, default for other projects.
- The DNNE attribute types are source-generated into consuming projects — DNNE provides no assembly reference.
21 changes: 21 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ jobs:
with:
dotnet-version: '8.0.x'
dotnet-quality: 'ga'
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 'stable'
- name: Build Product and Package
run: dotnet build src/create_package.proj -c ${{ matrix.flavor }}
- name: Unit Test Product
Expand All @@ -35,6 +39,9 @@ jobs:
run: |
dotnet clean test/DNNE.UnitTests -c ${{ matrix.flavor }}
dotnet test test/DNNE.UnitTests -c ${{ matrix.flavor }} -p:BuildWithGPP=true
- name: Build test.proj
run: |
dotnet build test/test.proj -c ${{ matrix.flavor }} -p:BuildPackage=false
- name: Upload Build Logs
if: failure()
uses: actions/upload-artifact@v4
Expand All @@ -59,6 +66,10 @@ jobs:
with:
dotnet-version: '8.0.x'
dotnet-quality: 'ga'
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 'stable'
- name: Build Product and Package
run: dotnet build src\create_package.proj -c ${{ matrix.flavor }}
- name: Build ExportingAssembly (.NET Core and .NET Framework)
Expand All @@ -71,6 +82,9 @@ jobs:
run: |
dotnet clean test\DNNE.UnitTests -c ${{ matrix.flavor }}
dotnet test test\DNNE.UnitTests -c ${{ matrix.flavor }}
- name: Build test.proj
run: |
dotnet build test\test.proj -c ${{ matrix.flavor }} -p:BuildPackage=false
- name: Upload Build Logs
if: failure()
uses: actions/upload-artifact@v4
Expand All @@ -90,6 +104,10 @@ jobs:
with:
dotnet-version: '8.0.x'
dotnet-quality: 'ga'
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: 'stable'
- name: Build Product and Package
run: dotnet build src/create_package.proj -c ${{ matrix.flavor }}
- name: Unit Test Product
Expand All @@ -98,6 +116,9 @@ jobs:
run: |
dotnet clean test/DNNE.UnitTests -c ${{ matrix.flavor }}
dotnet test test/DNNE.UnitTests -c ${{ matrix.flavor }} -p:BuildWithClangPP=true
- name: Build test.proj
run: |
dotnet build test/test.proj -c ${{ matrix.flavor }} -p:BuildPackage=false
- name: Upload Build Logs
if: failure()
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

**/bin/
**/obj/
**/target/
**/*.user
**/launchSettings.json

Expand Down
9 changes: 9 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<DnneTargetFramework>net8.0</DnneTargetFramework>
<DnneVersion>2.0.8</DnneVersion>

<RepoRoot>$(MSBuildThisFileDirectory)/</RepoRoot>
<SrcRoot>$(RepoRoot)src/</SrcRoot>
</PropertyGroup>
</Project>
Loading