Closed carlsound closed 1 year ago
Since WinUI is a Windows only concept, what's the benefit of supporting CMake? The different between CMake and MSBuild is much more significant than .NET side. I think it's much harder than supporting .NET Core SDK.
CMake simplifies generating a Visual Studio solution for a 3rd-party C++ SDK I use. Being able to use WinUI 3.0 with that work flow would be beneficial.
@huoyaoyuan CMake is a very popular C++ build tool and MSBuild for C++ is horrific to deal with.
Following on from #4127
Frame.Navigate()
takes a xaml_typename
.
winrt::xaml_typename
for it, Frame.Navigate()
eventually wants an IXamlType
. This is impractical without a xaml compilerFrame.Navigate(Page)
, allowing me to pass in an instance instead of a type--
As for 'why cmake?':
I'm looking at making cmake invoke msbuild just for xaml generation
It looks like MarkupCompilePass2 depends on a ClCompile task being used for the app, so this doesn't seem workable. Could just be I don't understand how it's meant to work - it doesn't appear that the related targets fully describe their dependencies. For example, if I delete all generated files/build artifacts (e.g. git clean -ffdx
), msbuild App1.vcxproj -target:MarkupCompilePass2
fails with XamlCompiler error WMC1007: Cannot resolve metadata for WinUI types. Please ensure that Nuget Restore was successful.
- but the step completes fine if I skip the -target
argument.
Are you able to make cmake use the msbuild/Visual Studio generators? Would make things a lot easier, most likely.
I believe msbuild App1.vcxproj -target:MarkupCompilePass2
would work if you run a nuget restore
or msbuild App1.vcxproj -target:Restore
beforehand (since git clean removes the installed nuget packages, just like how e.g. you'd have to run npm i
after a git clean
to restore NPM packages)
Are you able to make cmake use the msbuild/Visual Studio generators? Would make things a lot easier, most likely.
In general, this still needs the targets to be defined in cmake, it just uses msbuild as an execution engine - or do you have something else in mind? I am using these generators (they're the default)
I believe msbuild App1.vcxproj -target:MarkupCompilePass2 would work if you run a nuget restore or msbuild App1.vcxproj -target:Restore beforehand
Even with the packages installed, it still fails - and the Restore target also fails, even though they're present:
msbuild App1.vcxproj -target:Clean;Restore;MarkupCompilePass2
Determining projects to restore...
Restore:
Nothing to do. None of the projects specified contain packages to restore.
... and then it fails the same way. I'm using the winui3 app template, and it seems the packages are installed a level higher than the vcxproj, but are referenced by it.
I resurrected my attempt at building only the xaml + idl stuff with msbuild, and have made some progress; I'll try to update my demo project later this week. Still a WIP, but just for reference in case my drive dies or something, this seems to be the magic set of targets needed to make this work:
ResolveReferences;_Midl;BuildGenerateSources;
MarkupCompilePass1;CppWinRTMakeProjections;
ComputeReferenceCLInput;MarkupCompilePass2
This at least from a cursory look gives me all the .g.h, .g.cpp, and .xbf files I expect.
Right now this gets me a partial build in my real app; next up:
As an aside, cmake has some xaml and IDL support - but for modern stuff, it's limited to c++/cx, not C++/winrt; there's some legacy IDL support for standard C++.
As an aside, cmake has some xaml and IDL support - but for modern stuff, it's limited to c++/cx, not C++/winrt; there's some legacy IDL support for standard C++.
@fredemmott - If you have a well-defined list of asks for CMake support, I can contribute this to upstream CMake. No promises though :)
Thanks; the first thing would be support for IDL files -> MIDL sections in the vcxproj; with how cppwinrt and the xaml build tasks are linked, custom targets don’t work for this. I can’t give an accurate complete list as the lack of IDL support really blocks digging into it further.
the ideal end result would be:
add_executable(
MyApp
WIN32
app.xaml
app.idl
app.xaml.cpp
window.xaml
window.idl
window.xaml.cpp
page.xaml
page.idl
page.xaml.cpp
)
Then:
AIUI the existing VS_GLOBAL_foo property should be usable for setting the windows app sdk, bootstrap, packed/unpackaged, self contained options.
Nice to have: integrate self contained deployment with install(RUNTIME_DEPENDENCY_SET) and $
VS_PACKAGE_REFERENCES
Microsoft's docs say "C++ and JavaScript project types are unsupported."; I thought I read recently that this had changed, but I'm unable to find a source or test right now.
VS_PACKAGE_REFERENCES
Microsoft's docs say "C++ and JavaScript project types are unsupported."; I thought I read recently that this had changed, but I'm unable to find a source or test right now.
https://github.com/microsoft/react-native-windows/issues/8171 indicates it's supported now
@bhardwajs ignore me for now; some of these may have been fixed with CMake 3.23 which came out in April; I was still on 3.22. Will experiment and report back :)
@bhardwajs ignore me for now; some of these may have been fixed with CMake 3.23 which came out in April; I was still on 3.22. Will experiment and report back :)
Sounds good. Thanks!
This is much closer to working out the box with the changes in CMake 3.23; the next problem is that the msbuild targets/props and cmake seem to disagree on how ProjectDir, OutDir, and IntDir relate:
Example config (full project]:
add_executable(
DemoApp
WIN32
app.manifest
App.xaml.cpp App.xaml App.idl
MainWindow.xaml.cpp MainWindow.xaml MainWindow.idl
)
set_property(
SOURCE App.xaml
PROPERTY VS_XAML_TYPE
"ApplicationDefinition"
)
set_property(
TARGET DemoApp
PROPERTY VS_PACKAGE_REFERENCES
"Microsoft.Windows.CppWinRT_2.0.220608.4"
"Microsoft.WindowsAppSDK_1.1.1"
"Microsoft.Windows.SDK.BuildTools_10.0.22621.1"
)
set_target_properties(
DemoApp
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
VS_GLOBAL_RootNamespace DemoApp
VS_GLOBAL_AppContainerApplication false
VS_GLOBAL_AppxPackage false
VS_GLOBAL_CppWinRTEnabled true
VS_GLOBAL_CppWinRTOptimized true
VS_GLOBAL_CppWinRTRootNamespaceAutoMerge true
VS_GLOBAL_UseWinUI true
VS_GLOBAL_ApplicationType "Windows Store"
VS_GLOBAL_WindowsPackageType None
VS_GLOBAL_EnablePreviewMsixTooling true
# Skip the "ResolveNuGetPackageAssets" target as it fails with an empty sequence error
# Not certain why - I *think* that none of the packages we use have assets
VS_GLOBAL_ResolveNuGetPackages false
)
This seems to set up things where C++/WinRT runs, knows of the xaml files etc; it fail with:
"c:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\DemoApp.vcxproj" (default target) (1) ->
(Midl target) ->
midlrt : error MIDL2212: [msg]error while writing to file [context]App.winmd (HRESULT:0x80070003 - The system cannot
find the path specified. ) [c:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\DemoApp.vcxproj]
2 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.83
If I look at the msbuild binlogs, I see that:
build\src\DemoApp.dir\Debug\DemoApp.dir\Debug\Unmerged
- note that DemoApp.dir\Debug
is incorrectly repeatedC:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\midl.exe /metadata_dir "C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0\windows.foundation.foundationcontract\4.0.0.0" /winrt /W1 /nologo /char signed /env x64 /out"c:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\/DemoApp.dir\Debug\\" /winmd "DemoApp.dir\Debug\Unmerged\App.winmd" /h "App.h" /dlldata "nul" /iid "App_i.c" /proxy "App_p.c" /notlb /client none /server none /enum_class /ns_prefix /target "NT60" /nomidl @"c:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\DemoApp.dir\Debug\DemoApp.vcxproj.midlrt.rsp" "C:\Users\Fred\code\cmake-cpp-winrt-winui3\src\App.idl"
If I replace /winmd "DemoApp.dir\Debug\Unmerged\App.winmd"
with /winmd "DemoApp.dir\Debug\DemoApp.dir\Debug\Unmerged\App.winmd"
I can run midl.exe by hand without error, but later steps fail with similar confusion.
The cmake-generated vcxproj looks like IntDir/OutDir are set reasonably - full generated vcxproj here: https://gist.github.com/fredemmott/25f257f9504da747b896abf1d0ffea8c
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">C:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\Debug\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">DemoApp.dir\Debug\</IntDir>
I've not been able to track down precisely where this is doubling up; it could be C++/winrt's msbuild files, the Midl code in Microsoft.CppBuild.targets, or maybe cmake's generating a subtly invalid vcxproj?
Is there a dependency on Directory.Build.Props
somewhere and are you doing out-of-source build (recommended) in CMake? If so, there is the issue of CMake not working very well with Directory.Build.Props
.
c:\users\fred\code\cmake-cpp-winrt-winui
, the build is in the build
subfolder of thatDirectory.Build.Props
in the generated vcxproj, or in my cmake code_DirectoryBuildPropsFile = Directory.Build.props
in the binlog but no other mention of itwsl find . -iname directory.build.props
finds nothingCompared to a working project started from the winui template, saw that <Midl><OutputDirectory>
is not specified; if I remove <OutputDirectory>$(ProjectDir)/$(IntDir)</OutputDirectory>
manually from the vcxproj, I can proceed past this step.
Next up:
"c:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\DemoApp.vcxproj" (default target) (1) ->
(CppWinRTMakePlatformProjection target) ->
C:\Users\Fred\.nuget\packages\microsoft.windows.cppwinrt\2.0.220608.4\build\native\Microsoft.Windows.CppWinRT.targets
(661,9): error MSB3073: The command ""C:\Users\Fred\.nuget\packages\microsoft.windows.cppwinrt\2.0.220608.4\build\nativ
e\"cppwinrt @"DemoApp.dir\Debug\DemoApp.vcxproj.cppwinrt_plat.rsp"" exited with code 9009. [c:\Users\Fred\code\cmake-cp
p-winrt-winui3\build\src\DemoApp.vcxproj]
not seeing an obvious cause for that yet, suspect it might be related to VS_GLOBAL_ResolveNuGetPackages false
Okay, got that one:
VS_GLOBAL_CppWinRTEnabled true
I need to remove this from CMake: it is mutually exclusive with CppWinRTPackaged
, which I want instead, and is automatically set if !CppWinRTEnabled.
<Midl>
<OutputDirectory>
This needs removing and I think it needs changes to CMake - https://gitlab.kitware.com/cmake/cmake/-/issues/21668 might be one approach (an additional target property) might be one approach
Next problems appear to be "my C++ code is invalid" rather than build system so I'll poke some more and hopefully it's just the midl/outputdirectory change that's needed
[build] C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(502,5):
warning MSB8021: The value 'MultiByte' of the variable 'CharacterSet' is incompatible
with the value 'true' of the variable 'WindowsAppContainer'.
Doesn't block the build, but it's not overridable and I'm guessing it's incompatible because it may cause issues. The generator sets it depending on the target type.
Here we go: https://github.com/fredemmott/cmake-cpp-winrt-winui3
It's undocumented, but basically just needs... a lot of digging through msbuild and cmake stuff to figure out what properties etc are needed.
Requires CMake 3.23 (April 2022) and Windows App SDK 1.1 (June 2022) - i.e. this has only became possible in the last ~ 2 weeks.
pch.h
in this set up - target_precompile_headers()
gets me the error in the next sectionI'm not planning on looking at these; I think I'm done on cmake/msbuild yak shaving for now, and this unblocks me cleaning up my real app (which currently has cmake shell out to msbuild)
[build] Generated Files\XamlTypeInfo.Impl.g.cpp(631,1): fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "C:/Users/Fred/code/cmake-cpp-winrt-winui3/build/src/CMakeFiles/DemoApp.dir/Debug/cmake_pch.hxx"' to your source? [C:\Users\Fred\code\cmake-cpp-winrt-winui3\build\src\DemoApp.vcxproj]
ResolveNuGetPackages
I also have this problem with pure msbuild in the unpackaged app template if I replace packages.config and the target import with:
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CppWinRT" Version="2.0.220608.4" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.1" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.1" />
<PackageReference Include="Microsoft.Windows.ImplementationLibrary" Version="1.0.220201.1" />
</ItemGroup>
cppwinrt has had work to enable being used with a PackageReference, but I'm not sure about the other NuGet packages here.
However, WIL can be brought in using vcpkg instead of NuGet, and the SDK build tools NuGet can be entirely ignored as long as the Windows SDK build 22621 is installed locally.
Yeah, a few issues to work around; sadly several of the core “ modern Microsoft projects - including the windows app sdk - are only supported via nuget; externalprojevt_add and directory props/targets may be a more reliable to deal with them.
vcpkg can be handy, but AIUI it’s primarily intended for each system to have a active version of a dependency for all projects they want to build, instead of projects being able to specify version constraints that the package manager satisfies just for that project?
it feels like there’s really two C++ package management camps at microsoft: nuget is the future, and vcpkg is the future. Neither had complete support across tooling.
https://developercommunity.visualstudio.com/t/use-packagereference-in-vcxproj/351636 Seems the most detailed resource about vcxproj project references - it’s definitely not “supported now” as I thought :(
it feels like there’s really two C++ package management camps at microsoft: nuget is the future, and vcpkg is the future. Neither had complete support across tooling.
What I'm seeing is WinDiv prefers NuGet, while DevDiv prefers vcpkg. But since DevDiv owns NuGet, and for them the future of C++ package management is vcpkg, they do not seem to be making efforts towards making NuGet better to publish for and consume from C++
These props fix the package references - again specifying them via VS_GLOBAL does not appear to work:
<Project>
<PropertyGroup>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
<NuGetTargetMoniker>native,Version=v0.0</NuGetTargetMoniker>
</PropertyGroup>
</Project>
In my real app, I also need win-x64
(not win10) as a runtime identifier; it looks like this might be added when I add a .rc file to my sources.
My real app now seems to basically be working with this :)
Extra things:
DependentUpon
set correctly via the VS_SETTINGS source property. If you consistently name your files and stick to one idl/xaml/h file, https://github.com/fredemmott/OpenKneeboard/blob/0acc0568973194eeb206ec22bfab11bcf4c5d885/src/app/app-winui3/winui3-boilerplate.cmake#L38-L72 does the trick but is a bit painfulVS_PACKAGE_REFERENCES
on an INTERFACE
library, these would propagate to dependencies, but they don't. Without that, I'm using a package reference for the winui executable, but https://github.com/fredemmott/OpenKneeboard/blob/0acc0568973194eeb206ec22bfab11bcf4c5d885/third-party/cppwinrt.cmake for the other stuff. To avoid issues,versions must exactly match, and must specify -in ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}
instead of -in sdk
or -in local
Combined with https://github.com/microsoft/microsoft-ui-xaml/issues/3863 there's lots of small improvements that could be made to CMake to make things a little better, but making winui3 with c++/winrt actually 'good' would require big changes across multiple MS projects - either:
<PackageReference>
in MSBuild for C++ projects.
@bhardwajs one more thing: Not-self-contained builds depend on msbuild rules adding the bootstrapping libraries to <Link><AdditionalDependencies>
, and this doesn't work in cmake (I've stopped using self-contained mode because of #7281)
CMake's MSBuild generator completely overrides AdditionalDependencies
, removing these; the normal thing in msbuild outside of cmake seems to be to set it to <AdditionalDependencies>foo.lib;%(AdditionalDependencies)</AdditionalDependencies>
instead of cmake's approach of <AdditionalDependencies>foo.lib</AdditionalDependencies>
For now, I'm working around this by adding %(AdditionalDependencies)
to CMAKE_CXX_STANDARD_LIBRARIES
. This feels brittle, and interferes with standard toolchain configuration. I also tried an IMPORTED INTERFACE library, however the msbuild generators appear to ignore SUFFIX and IMPORTED_SUFFIX, and always append ".lib" - it would also feel pretty brittle that %(AdditionalDependencies)
works as an IMPORTED_LIBNAME
I suggest modifying cmake to do one out of:
%(AdditionalDependencies)
into the dependencies of msbuild projectsVS_PACKAGE_REFERENCE
(i.e. when it's likely that there's a nuget package that's trying to add library dependencies)%(AdditionalDependencies)
is included; possibly default to on, or on if VS_PACKAGE_REFERENCE is presentOf course, it would be better if WinUI3 and the Windows App SDK supported and documented systems other than msbuild :p
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 5 days.
Will it be possible to use CMake to generate a Visual Studio solution for using C++/WinRT XAML UI's for Win32 desktop applications with 3.0?