Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
689582d
Initial rough crack at monolithic module consumption
DefaultRyan Mar 26, 2026
9a29dd1
Try import std in tests. Requires PlatformToolset=v145
DefaultRyan Mar 26, 2026
1652e6b
Exclude v145-specific project from batch builds.
DefaultRyan Mar 26, 2026
79d8003
Attempt detecting ability to import std inside winrt
DefaultRyan Mar 27, 2026
546491d
Test component project is building. Checkpoint with some documentatio…
DefaultRyan Mar 30, 2026
01f5f03
PR feedback: split includes into std and non-std, add base_module_mac…
DefaultRyan Mar 30, 2026
797fea7
Explicitly include <ratio> header in base_std_includes.h
DefaultRyan Mar 30, 2026
5cddeb3
Add base_std_includes.h to natvis project
DefaultRyan Mar 30, 2026
da640ca
PR feedback: Reduce code duplication in base_macros.h, extern linkage…
DefaultRyan Mar 31, 2026
f9b0871
Add base_module_macros.h to natvis pch
DefaultRyan Mar 31, 2026
21eb38c
Workaround access_token issue. More tests.
DefaultRyan Mar 31, 2026
3ea319b
Revert dual-build of await_adapter.cpp
DefaultRyan Mar 31, 2026
9c021d2
Fix some more test pch issues
DefaultRyan Mar 31, 2026
2ea8a9a
Update module limitation docs
DefaultRyan Mar 31, 2026
afe92af
Export impl namespace to get component authoring to work.
DefaultRyan Apr 1, 2026
8abb336
Enable VS2022 support!
DefaultRyan Apr 1, 2026
abae82c
For some reason, the project dependencies got clobbered
DefaultRyan Apr 1, 2026
92ea106
import std in components should be conditional
DefaultRyan Apr 1, 2026
c78fc14
Enable building module test projects
DefaultRyan Apr 1, 2026
22c67d8
Don't export impl from fast_forward
DefaultRyan Apr 1, 2026
9250b45
Fix stuff around pch inclusion and component files. Updated docs.
DefaultRyan Apr 1, 2026
eb56247
Add some Copilot instructions for future sessions.
DefaultRyan Apr 2, 2026
e410940
Merge branch 'master' into feature/modules_v1
DefaultRyan Apr 2, 2026
d82e886
Remove stale ixx component folding code.
DefaultRyan Apr 2, 2026
78c7344
Clean up some duplication in generated files.
DefaultRyan Apr 2, 2026
4854d8f
Merge branch 'master' into feature/modules_v1
DefaultRyan Apr 2, 2026
322a103
Add some #endif comments
DefaultRyan Apr 3, 2026
5c8939c
Add tests to ensure cpp23 with import std work.
DefaultRyan Apr 3, 2026
c49874f
Merge branch 'master' into feature/modules_v1
DefaultRyan Apr 3, 2026
851c745
Add nuget test projects. Replace cppwinrt -module with component #ifd…
DefaultRyan Apr 3, 2026
86c210f
Test modules in CI
DefaultRyan Apr 3, 2026
9ef6792
TestModuleComponent now has multiple namespaces to further check WINR…
DefaultRyan Apr 3, 2026
4ff8c7b
Explicitly generate list of module namespaces and use that for header…
DefaultRyan Apr 4, 2026
e25fb9a
WINRT_IMPL_SKIP_INCLUDES is finally gone
DefaultRyan Apr 4, 2026
a6eb07e
Define missinf WINRT_MODULE for test_cpp23_module project
DefaultRyan Apr 6, 2026
b0f6665
Add module namespace filtering
DefaultRyan Apr 6, 2026
272113b
Add module_filter to modules.instructions.md
DefaultRyan Apr 7, 2026
e87427c
(Checkpoint) inclusion with WINRT_MODULE works, but requires WINRT_MO…
DefaultRyan Apr 17, 2026
7bc1c11
Full and clean inclusion via auto-import. Cleanup of terminology and …
DefaultRyan Apr 17, 2026
7d2b8fe
Fir sln project dependencies and module project guids
DefaultRyan Apr 18, 2026
3542a4c
Enforcing include guards only on the module boundary. It works.
DefaultRyan Apr 18, 2026
31dd7db
Build cppwinrt23 test projects in solution
DefaultRyan Apr 18, 2026
703490e
PR feedback items
DefaultRyan Apr 18, 2026
2bd4342
Import-then-include works via extern "C++"
DefaultRyan Apr 20, 2026
f76820e
Revert accidentally added description file.
DefaultRyan Apr 20, 2026
ff34fd9
Merge branch 'master' into feature/modules_v1
DefaultRyan Apr 20, 2026
f6ecdb9
Cross-namespace dependencies in component authoring
DefaultRyan Apr 20, 2026
b83ea95
Merge branch 'feature/modules_v1' of https://github.com/microsoft/cpp…
DefaultRyan Apr 20, 2026
9873877
Test cross-namespace base class dependency guarding
DefaultRyan Apr 21, 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
17 changes: 15 additions & 2 deletions cppwinrt.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33829.357
# Visual Studio Version 18
VisualStudioVersion = 18.6.11620.209 main
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cppwinrt", "cppwinrt\cppwinrt.vcxproj", "{D613FB39-5035-4043-91E2-BAB323908AF4}"
ProjectSection(ProjectDependencies) = postProject
Expand Down Expand Up @@ -124,6 +124,12 @@ 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", "{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}"
ProjectSection(ProjectDependencies) = postProject
{A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}
{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 +417,12 @@ 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
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}.Debug|x64.ActiveCfg = Debug|x64
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}.Debug|x86.ActiveCfg = Debug|Win32
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}.Release|ARM64.ActiveCfg = Release|ARM64
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}.Release|x64.ActiveCfg = Release|x64
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE}.Release|x86.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -435,6 +447,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}
{B85004D5-2C57-4A53-BA7F-FE0B614EA1DE} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2783B8FD-EA3B-4D6B-9F81-662D289E02AA}
Expand Down
14 changes: 13 additions & 1 deletion cppwinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@ namespace cppwinrt

static void write_version_assert(writer& w)
{
// When WINRT_IMPL_SKIP_INCLUDES is defined, base.h is already available
// from the imported winrt module. However, macros don't cross module
// boundaries, so we include a lightweight header with just the macros.
auto format_guard = R"(#ifdef WINRT_IMPL_SKIP_INCLUDES
#include "winrt/base_macros.h"
#else
)";
auto format_end = R"(#endif
)";
w.write(format_guard);
w.write_root_include("base");
w.write(format_end);

auto format = R"(static_assert(winrt::check_version(CPPWINRT_VERSION, "%"), "Mismatched C++/WinRT headers.");
#define CPPWINRT_VERSION "%"
)";
Expand Down Expand Up @@ -137,7 +149,7 @@ namespace cppwinrt

if (found != c.namespaces().end() && has_projected_types(found->second))
{
w.write_root_include(parent);
w.write_root_include_guarded(parent);
}
else
{
Expand Down
17 changes: 16 additions & 1 deletion cppwinrt/component_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,14 @@ namespace cppwinrt

static void write_module_g_cpp(writer& w, std::vector<TypeDef> const& classes)
{
w.write_root_include("base");
if (settings.component_module)
{
w.write("import std;\nimport winrt;\n");
}
else
{
w.write_root_include("base");
}
auto format = R"(%
bool __stdcall %_can_unload_now() noexcept
{
Expand Down Expand Up @@ -397,6 +404,14 @@ catch (...) { return winrt::to_hresult(); }
return;
}

// In module mode, the constructor/static definitions are already exported
// from the winrt module (via the namespace header folded into winrt.ixx).
// Only the winrt_make_* factory function above is needed.
if (settings.component_module)
{
return;
}

auto wrap_type = wrap_type_namespace(w, type_namespace);

for (auto&&[factory_name, factory] : get_factories(w, type))
Expand Down
74 changes: 69 additions & 5 deletions cppwinrt/file_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,57 @@ namespace cppwinrt
w.flush_to_file(settings.output_folder + "winrt/base.h");
}

// Lightweight header containing only the preprocessor macros needed by
// generated namespace headers. Used when WINRT_IMPL_SKIP_INCLUDES is
// defined (i.e., consuming component headers after 'import winrt;').
// Macros don't cross module boundaries, so this provides the macros
// that base.h would normally supply.
static void write_base_macros_h()
{
writer w;
write_preamble(w);
w.write(R"(#pragma once

// This header provides the preprocessor macros needed by generated C++/WinRT
Comment thread
DefaultRyan marked this conversation as resolved.
Outdated
// headers when the full base.h is not #included (e.g., when using 'import winrt;').
// Macros are not exported across C++20 module boundaries, so this lightweight
// header must be included textually.

#ifndef CPPWINRT_VERSION
#define CPPWINRT_VERSION "%"
#endif

#ifndef WINRT_EXPORT
#define WINRT_EXPORT
#endif

#if defined(_MSC_VER)
#define WINRT_IMPL_EMPTY_BASES __declspec(empty_bases)
#else
#define WINRT_IMPL_EMPTY_BASES
#endif

#if defined(_MSC_VER)
#define WINRT_IMPL_NOVTABLE __declspec(novtable)
#else
#define WINRT_IMPL_NOVTABLE
#endif

#if defined(__clang__) && defined(__has_attribute)
#if __has_attribute(__lto_visibility_public__)
#define WINRT_IMPL_PUBLIC __attribute__((lto_visibility_public))
#else
#define WINRT_IMPL_PUBLIC
#endif
#else
#define WINRT_IMPL_PUBLIC
#endif

#define WINRT_IMPL_ABI_DECL WINRT_IMPL_NOVTABLE WINRT_IMPL_PUBLIC
)", CPPWINRT_VERSION_STRING);
w.flush_to_file(settings.output_folder + "winrt/base_macros.h");
}

static void write_fast_forward_h(std::vector<TypeDef> const& classes)
{
writer w;
Expand Down Expand Up @@ -139,7 +190,7 @@ namespace cppwinrt

for (auto&& depends : w.depends)
{
w.write_depends(depends.first, '0');
w.write_depends_guarded(depends.first, '0');
}

w.write_depends(w.type_namespace, '0');
Expand Down Expand Up @@ -169,7 +220,7 @@ namespace cppwinrt

for (auto&& depends : w.depends)
{
w.write_depends(depends.first, impl);
w.write_depends_guarded(depends.first, impl);
}

w.write_depends(w.type_namespace, '1');
Expand Down Expand Up @@ -224,7 +275,7 @@ namespace cppwinrt

for (auto&& depends : w.depends)
{
w.write_depends(depends.first, '2');
w.write_depends_guarded(depends.first, '2');
}

w.write_depends(w.type_namespace, '2');
Expand All @@ -250,9 +301,22 @@ namespace cppwinrt
write_preamble(w);
write_include_guard(w);

for (auto&& depends : w.depends)
if (settings.component_module)
{
// In module mode, both SDK and component types are available from
// 'import winrt;' (the component projection is folded into the
// winrt.ixx module). Only macros need a textual include since
// they don't cross module boundaries.
w.write("#include \"winrt/base_macros.h\"\n");
w.write("import std;\n");
w.write("import winrt;\n");
}
else
{
w.write_depends(depends.first);
for (auto&& depends : w.depends)
{
w.write_depends(depends.first);
}
}

auto filename = settings.output_folder + get_generated_component_filename(type) + ".g.h";
Expand Down
13 changes: 13 additions & 0 deletions cppwinrt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace cppwinrt
{ "fastabi", 0, 0 }, // Enable support for the Fast ABI
{ "ignore_velocity", 0, 0 }, // Ignore feature staging metadata and always include implementations
{ "synchronous", 0, 0 }, // Instructs cppwinrt to run on a single thread to avoid file system issues in batch builds
{ "module", 0, 0, {}, "Generate component files using 'import winrt;' instead of #include" },
};

static void print_usage(writer& w)
Expand Down Expand Up @@ -172,6 +173,13 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder
settings.component_lib = args.value("library", "winrt");
settings.component_opt = args.exists("optimize");
settings.component_ignore_velocity = args.exists("ignore_velocity");
settings.component_module = args.exists("module");

if (settings.component_module)
{
// Module mode disables the PCH include in generated files
settings.component_pch.clear();
}

if (settings.component_pch == ".")
{
Expand Down Expand Up @@ -345,7 +353,11 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder
writer ixx;
write_preamble(ixx);
ixx.write("module;\n");
// In the global module fragment, 'import' is not allowed.
// Suppress the 'import std;' path in base_includes so we get raw #includes.
ixx.write("#define WINRT_IMPL_GLOBAL_MODULE_FRAGMENT\n");
ixx.write(strings::base_includes);
ixx.write("#undef WINRT_IMPL_GLOBAL_MODULE_FRAGMENT\n");
ixx.write("\nexport module winrt;\n#define WINRT_EXPORT export\n\n");

for (auto&&[ns, members] : c.namespaces())
Expand All @@ -369,6 +381,7 @@ R"( local Local ^%WinDir^%\System32\WinMetadata folder
if (settings.base)
{
write_base_h();
write_base_macros_h();
ixx.flush_to_file(settings.output_folder + "winrt/winrt.ixx");
}

Expand Down
2 changes: 2 additions & 0 deletions cppwinrt/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ namespace cppwinrt

bool fastabi{};
std::map<winmd::reader::TypeDef, winmd::reader::TypeDef> fastabi_cache;

bool component_module{}; // Generate component files using 'import winrt;' instead of #include
};

extern settings_type settings;
Expand Down
25 changes: 25 additions & 0 deletions cppwinrt/type_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,19 @@ namespace cppwinrt
settings.brackets ? '>' : '\"');
}

void write_root_include_guarded(std::string_view const& include)
{
auto format = R"(#ifndef WINRT_IMPL_SKIP_INCLUDES
#include %winrt/%.h%
#endif
)";

write(format,
settings.brackets ? '<' : '\"',
include,
settings.brackets ? '>' : '\"');
}

void write_depends(std::string_view const& ns, char impl = 0)
{
if (impl)
Expand All @@ -576,6 +589,18 @@ namespace cppwinrt
}
}

void write_depends_guarded(std::string_view const& ns, char impl = 0)
{
if (impl)
{
write_root_include_guarded(write_temp("impl/%.%", ns, impl));
}
else
{
write_root_include_guarded(ns);
}
}

void save_header(char impl = 0)
{
auto filename{ settings.output_folder + "winrt/" };
Expand Down
Loading