Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5f7d2f5
Initial implementation of vs modules based on cppwinrtplus fork
DefaultRyan Apr 23, 2026
469c963
Rename WINRT_MODULE -> WINRT_IMPL_BUILD_MODULE; WINRT_CONSUME_MODULE …
DefaultRyan Apr 23, 2026
84da05a
Namespace modules are winrt.Namespace. Non-namespace modules are winr…
DefaultRyan Apr 23, 2026
ab72882
Bootstrap basic unit test
DefaultRyan Apr 24, 2026
464a75c
Fix coroutine export. A few more unit tests.
DefaultRyan Apr 24, 2026
b6d6186
module_include, module_exclude
DefaultRyan Apr 24, 2026
717754a
Rename test_module to test_cpp20_module
DefaultRyan Apr 24, 2026
d9ec817
Nuget test project with module include/exclude
DefaultRyan Apr 24, 2026
ca0090a
Refine generated files in component.
DefaultRyan Apr 24, 2026
1aceeed
Added a type deriving from DependencyObject
DefaultRyan Apr 24, 2026
05a845e
Basic module build/consume support
DefaultRyan Apr 24, 2026
6fffa1d
Prefix namespace ixx files with "winrt." for consistency with module …
DefaultRyan Apr 24, 2026
b8b9715
IFC needs better per-project scoping. Now it fully works end to end.
DefaultRyan Apr 25, 2026
25ce335
Minor fix to TestModuleComponent2
DefaultRyan Apr 25, 2026
ccfee65
Documentation and polish.
DefaultRyan Apr 25, 2026
f4073d3
Fix CI failures
DefaultRyan Apr 25, 2026
d5d1821
Try for better std hygiene
DefaultRyan Apr 25, 2026
4d91f75
Address some PR feedback
DefaultRyan Apr 25, 2026
79eb574
Better automation of AdditionalBMIDirectories
DefaultRyan Apr 25, 2026
b0d4656
Missed fallback definition of WINRT_IMPL_STD_EXPORT
DefaultRyan Apr 26, 2026
392a1c3
Wrap base.h and extern the handler pointers
DefaultRyan Apr 26, 2026
9207cf9
Uniform namespace filtering
DefaultRyan Apr 27, 2026
9a98166
Write trailing comments for #endif
DefaultRyan Apr 27, 2026
c4e1b06
Clarify language version requirement
DefaultRyan Apr 27, 2026
5889c9e
Add source_location test
DefaultRyan Apr 27, 2026
2c83d42
Refactor/cleanup some string writers
DefaultRyan Apr 27, 2026
cb62bc7
Add arm64 configs and replace bogus project guids with real guids.
DefaultRyan Apr 27, 2026
054fdf5
More cleanup of strings. Collected common macros into base_macros.h, …
DefaultRyan Apr 27, 2026
9322551
More strings cleanup. Emit a canonical winrt/base_macros.h
DefaultRyan Apr 28, 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/instructions/cppwinrt.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# C++/WinRT Codebase — Agent Instructions

## Repository Structure

- `cppwinrt/` — The cppwinrt.exe code generator (C++ source)
- `main.cpp` — CLI parsing, namespace iteration, SCC detection, .ixx orchestration
- `file_writers.h` — All file generation functions (headers, .ixx modules, component stubs)
- `code_writers.h` — Code-level writing utilities (guards, namespace wrappers, type writers)
- `type_writers.h` — Type formatting (ABI signatures, names, GUIDs)
- `component_writers.h` — Component authoring code generation
- `helpers.h` — Metadata reading helpers
- `settings.h` — Global settings populated from CLI args
- `text_writer.h` — Core text writer infrastructure
- `strings/` — String literal `.h` files embedded by the prebuild step. Changes require: delete prebuild.exe → rebuild solution
- `nuget/` — MSBuild targets, props, and NuGet packaging
- `Microsoft.Windows.CppWinRT.targets` — Main MSBuild integration (projections, module support)
- `test/` — Test projects
- `test/test_cpp20_module/` — Standalone module test (in main solution)
- `test/nuget/` — NuGet integration tests (multi-project module chain)
- `docs/` — Documentation
- `natvis/` — Visual Studio debug visualizer (includes strings/*.h in its pch.h — add new files there too)

## Build Process

- Use VS Developer Shell for correct toolset environment
- `cmake --build build --config Release --target cppwinrt` for cppwinrt.exe (or MSBuild: `msbuild cppwinrt\cppwinrt.vcxproj /p:Configuration=Release /p:Platform=x64`)
- NuGet tests: `msbuild test\nuget\NuGetTest.sln /p:Configuration=Release /p:Platform=x64`
- Module test projects require v145 toolset (VS 2026). Directory.Build.Props sets v143 by default — override with `<PlatformToolset>v145</PlatformToolset>` in Configuration PropertyGroup

## Key Patterns

### Prebuild Embedding
The `strings/*.h` files are embedded as string literals by the prebuild step. If you modify any `strings/*.h` file, you must delete `prebuild.exe` and rebuild the entire solution for changes to take effect.

### Module Guard Macros
- `WINRT_IMPL_BUILD_MODULE` — Defined in .ixx global fragment. Makes `WINRT_EXPORT` expand to `export extern "C++"` and suppresses `#include` of dependencies
- `WINRT_IMPORT_MODULE` — Defined by consumers who import modules. Makes namespace headers and base.h no-op (types come from module import)
- `WINRT_EXPORT` — Empty in header mode, `export extern "C++"` in module mode. Defined in `winrt/base_macros.h`
- `WINRT_IMPL_STD_EXPORT` — Empty in header mode, `extern "C++"` (without export) in module mode. Used for `namespace std` specializations

### Generated Header Structure
Each namespace produces four header files:
- `impl/<ns>.0.h` — Forward declarations, ABIs, GUIDs, categories
- `impl/<ns>.1.h` — Interface definitions
- `impl/<ns>.2.h` — Delegates, structs, class implementations
- `<ns>.h` — Public API surface (consume definitions, class wrappers, operators)

### Dependency Collection
When generating headers with `-modules`, writer.depends is inspected after each header to build a namespace dependency graph. This graph drives SCC detection and module import lists.

## Common Gotchas

- Module IFCs are NOT compatible across toolset versions — always clean rebuild when switching
- PCH and modules can coexist but PCH should NOT include winrt headers when using modules
- `/ifcSearchDir` works for the module dependency scanner to find IFCs, but cross-component modules may need explicit `/reference "name=path.ifc"` flags
- `import std;` requires `BuildStlModules=true`
- `strings/base_macros.h` is the single source of truth for shared macros (generated as `winrt/base_macros.h`). New macros go in `base_macros.h` only
- When adding, removing, or heavily refactoring `strings/*.h` files, always rebuild the natvis project (`natvis/cppwinrtvisualizer.sln`) to verify — it includes strings/*.h directly in its pch.h
40 changes: 40 additions & 0 deletions .github/instructions/modules.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# C++/WinRT Modules — Agent Instructions

## Module Architecture (v2 — Per-Namespace)

Each WinRT namespace gets its own C++20 named module (`winrt.<Namespace>`). Base infrastructure is in `winrt_base` and `winrt_numerics`.

### Code Generator Flow

1. `-modules` flag enables .ixx generation in cppwinrt.exe
2. `-module_include`/`-module_exclude` filter which namespaces get modules
3. Headers are generated with dependency tracking (deps_ptr parameter)
4. Tarjan's SCC algorithm detects cyclic namespace groups
5. Standalone namespaces get individual .ixx; cyclic groups get consolidated SCC owner + re-export stubs

### MSBuild Flow

1. `CppWinRTBuildModule=true` adds `-modules` to cppwinrt.exe invocations
2. `CppWinRTAddModuleInterfaces` discovers `$(GeneratedFilesDir)winrt\*.ixx` and adds to ClCompile
3. `CppWinRTConsumeModule` metadata on ProjectReference controls per-reference IFC sharing
4. `CppWinRTResolveModuleReferences` calls `CppWinRTGetModuleOutputs` on tagged references
5. Platform projection suppresses `-modules` when consuming pre-built IFCs

### Critical Invariants

- Module guards are unconditional in codegen — `-modules` controls .ixx generation and component codegen (module.g.cpp, stub .cpp)
- SCC owner is alphabetically first namespace in the cycle
- All .ixx filenames use `winrt` prefix: `winrt.Windows.Foundation.ixx`, `winrt_base.ixx`
- Shared macros live in `strings/base_module.h` → generates `winrt/macros.h`. `base_macros.h` includes it via `#include "winrt/macros.h"`

### Testing Changes

After modifying cppwinrt.exe code:
1. Rebuild cppwinrt.exe: `msbuild cppwinrt\cppwinrt.vcxproj /p:Configuration=Release /p:Platform=x64`
2. Run standalone test: build `test_cpp20_module` in main solution
3. Run NuGet tests: `msbuild test\nuget\NuGetTest.sln /p:Configuration=Release /p:Platform=x64`

After modifying targets:
1. Clean NuGet test obj dirs
2. Build with `/v:normal` and check "Module providers:" diagnostic messages
3. Inspect `.rsp` files in `obj/` to verify correct `-modules` flag placement
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,15 @@ jobs:
$target_platform = "${{ matrix.arch }}"
& "_build\$target_platform\$target_configuration\cppwinrt.exe" -in local -out _build\$target_platform\$target_configuration -verbose

- name: Remove module test projects on v143
if: matrix.toolchain.platform_toolset == 'v143'
run: |
# Module test projects require v145 toolset
mv test\nuget\NugetTest.sln test\nuget\NugetTest.sln.orig
Comment thread
dmachaj marked this conversation as resolved.
Get-Content test\nuget\NugetTest.sln.orig |
Where-Object { -not ($_ -match 'TestModule') } |
Set-Content test\nuget\NugetTest.sln

- name: Run nuget test
run: |
cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor "$env:msbuild_config_props" test\nuget\NugetTest.sln
Expand Down
14 changes: 14 additions & 0 deletions cppwinrt.sln
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_nocoro", "test\test_no
{D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cpp20_module", "test\test_cpp20_module\test_cpp20_module.vcxproj", "{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}"
ProjectSection(ProjectDependencies) = postProject
{D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D15C8430-A7CD-4616-BD84-243B26A9F1C2}"
ProjectSection(SolutionItems) = preProject
build_nuget.cmd = build_nuget.cmd
Expand Down Expand Up @@ -411,6 +416,14 @@ Global
{9E392830-805A-4AAF-932D-C493143EFACA}.Release|x64.Build.0 = Release|x64
{9E392830-805A-4AAF-932D-C493143EFACA}.Release|x86.ActiveCfg = Release|Win32
{9E392830-805A-4AAF-932D-C493143EFACA}.Release|x86.Build.0 = Release|Win32
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|ARM64.ActiveCfg = Debug|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x64.ActiveCfg = Debug|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x64.Build.0 = Debug|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x86.ActiveCfg = Debug|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|ARM64.ActiveCfg = Release|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x64.ActiveCfg = Release|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x64.Build.0 = Release|x64
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -435,6 +448,7 @@ Global
{5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0}
{D4C8F881-84D5-4A7B-8BDE-AB4E34A05374} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0}
{9E392830-805A-4AAF-932D-C493143EFACA} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0}
{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2783B8FD-EA3B-4D6B-9F81-662D289E02AA}
Expand Down
47 changes: 34 additions & 13 deletions cppwinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace cppwinrt
struct finish_with
{
writer& w;
void (*finisher)(writer&);
std::function<void(writer&)> finisher;

finish_with(writer& w, void (*finisher)(writer&)) : w(w), finisher(finisher) {}
finish_with(writer& w, std::function<void(writer&)> finisher) : w(w), finisher(std::move(finisher)) {}
finish_with(finish_with const&)= delete;
void operator=(finish_with const&) = delete;

Expand Down Expand Up @@ -35,6 +35,35 @@ namespace cppwinrt
}
}

static void write_endif(writer& w, std::string_view macro = {})
{
if (macro.empty())
{
w.write("#endif\n");
}
else
{
w.write("#endif // %\n", macro);
}
}

// When modules are enabled, wraps a block of #include directives in
// #ifndef WINRT_IMPL_BUILD_MODULE ... #endif so that in module builds (where
// WINRT_IMPL_BUILD_MODULE is defined in the global module fragment), textual
// includes are suppressed — dependencies come via import instead.
[[nodiscard]] static finish_with wrap_module_aware_includes_guard(writer& w, bool modules_enabled)
{
if (modules_enabled)
{
w.write("#ifndef WINRT_IMPL_BUILD_MODULE\n");
return { w, [](writer& w) { write_endif(w, "WINRT_IMPL_BUILD_MODULE"); } };
}
else
{
return { w, write_nothing };
}
}

static void write_version_assert(writer& w)
{
w.write_root_include("base");
Expand All @@ -52,14 +81,6 @@ namespace cppwinrt
w.write(format);
}

static void write_endif(writer& w)
{
auto format = R"(#endif
)";

w.write(format);
}

static void write_close_file_guard(writer& w)
{
write_endif(w);
Expand Down Expand Up @@ -105,7 +126,7 @@ namespace cppwinrt

w.write(format);

return { w, write_endif };
return { w, [](writer& w) { write_endif(w, "WINRT_LEAN_AND_MEAN"); } };
}
else
{
Expand All @@ -120,7 +141,7 @@ namespace cppwinrt

w.write(format, macro);

return { w, write_endif };
return { w, [macro = std::string(macro)](writer& w) { write_endif(w, macro); } };
}

static void write_parent_depends(writer& w, cache const& c, std::string_view const& type_namespace)
Expand Down Expand Up @@ -166,7 +187,7 @@ namespace cppwinrt

[[nodiscard]] static finish_with wrap_impl_namespace(writer& w)
{
auto format = R"(namespace winrt::impl
auto format = R"(WINRT_EXPORT namespace winrt::impl
{
)";

Expand Down
5 changes: 4 additions & 1 deletion cppwinrt/component_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ namespace cppwinrt

static void write_module_g_cpp(writer& w, std::vector<TypeDef> const& classes)
{
w.write_root_include("base");
if (!settings.modules)
{
w.write_root_include("base");
}
auto format = R"(%
bool __stdcall %_can_unload_now() noexcept
{
Expand Down
Loading