microsoft / cppwinrt

C++/WinRT
MIT License
1.64k stars 238 forks source link

XAML controls created in subdirectories will not compile #1168

Closed haydenmc closed 2 years ago

haydenmc commented 2 years ago

Suspect this is related to https://github.com/microsoft/microsoft-ui-xaml/issues/5186, but I think the root cause is a cppwinrt generated header file.

Repro steps:

1.) Create a new "Blank App (C++/WinRT)" UWP project 2.) Select "show all files" in Visual Studio Solution Explorer so you can create a real, non-virtual folder

show all files icon

3.) Create new directory "Controls" 4.) Create a new item "Blank User Control (C++/WinRT)" inside of the Controls folder 5.) Build

Expect: Successful build

Actual: Build fails

1>XamlTypeInfo.Impl.g.cpp
1>[...]\UWPScratch\UWPScratch\Controls\BlankUserControl.cpp(14,9): error C3861: 'InitializeComponent': identifier not found
1>[...]\UWPScratch\UWPScratch\Controls\BlankUserControl.cpp(29,9): error C3861: 'Button': identifier not found
1>C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\XamlCompiler\Microsoft.Windows.UI.Xaml.Common.targets(486,5): error MSB4181: The "CompileXaml" task returned false but did not log an error.
1>Done building project "UWPScratch.vcxproj" -- FAILED.

I found BlankUserControl.g.h contains the following lines (presumably generated here):

#if defined(WINRT_FORCE_INCLUDE_BLANKUSERCONTROL_XAML_G_H) || __has_include("BlankUserControl.xaml.g.h")
#include "BlankUserControl.xaml.g.h"
#else

namespace winrt::UWPScratch::implementation
{
    template <typename D, typename... I>
    using BlankUserControlT = BlankUserControl_base<D, I...>;
}

#endif

Since BlankUserControl.xaml.g.h is not in the include path (Controls/BlankUserControl.xaml.g.h is), the XAML generated header file gets skipped along with the necessary definitions.

Workaround: Adding $(GeneratedFilesDir)Controls to the include paths allows the build to succeed but is less than ideal as it pollutes the root include directory with files from the Controls subdirectory.

kennykerr commented 2 years ago

The Xaml teams owns the build support for such things so if you already have an issue open on their repo that should suffice. This repo's issues is squarely for reporting issues with the cppwinrt.exe compiler itself.

haydenmc commented 2 years ago

Ah, maybe I'm mistaken, but the lines associated with what I think is the root cause are generated by cppwinrt/cppwinrt/component_writers.h: https://github.com/microsoft/cppwinrt/blob/4f0be70254efab15530feaf5fb4894ff56fe8617/cppwinrt/component_writers.h#L865

Should I migrate this issue to the XAML GitHub?

kennykerr commented 2 years ago

Sure, but how that is backed by code provided by the Xaml compiler is theirs to own. Feel free to transfer the issue.

kennykerr commented 2 years ago

That is essentially an extensibility hook for them.

haydenmc commented 2 years ago

Done - I think the fix will need to occur in cppwinrt/cppwinrt/component_writers.h though because the root cause appears to be the __has_include pointing to an incorrect path preventing the XAML header from getting loaded in the first place.

sylveon commented 2 years ago

This issue is caused because in cppwinrt (and XAML in general), it is expected for subfolder to represents namespaces. If the original file was declared to be in the UWPScratch.Controls namespace, everything would have built fine. Additionally, the suggested fix of adjusting the path in cppwinrt-generated code would mean existing valid code using subfolders for namespaces would break, because now their include path is incorrect.

haydenmc commented 2 years ago

Ah ha! PEBKAC. Good to know, thanks - I will adjust the namespace.

haydenmc commented 2 years ago

Indeed adding the namespace fixed the issue - I had to make sure this project property was set as well:

<CppWinRTUsePrefixes>false</CppWinRTUsePrefixes>

I had to prefix the include path for the generated files as well:

#include <Controls/HorizontalGameListControl.g.cpp>

Thank you both! Hopefully this helps others who run into this as well. :)