Skip to content

Commit f9f3c39

Browse files
ssparachScottj1s
andauthored
[WinAppSDK CMake Support]: Implicitly choose deployment mode based on WindowsAppSDKSelfContained (#6490)
* cmake modifications * change * move appxmanifest generation to base and add property validation --------- Co-authored-by: Scott Jones <sjones@microsoft.com>
1 parent d4e1798 commit f9f3c39

1 file changed

Lines changed: 105 additions & 98 deletions

File tree

‎build/NuSpecs/microsoft.windowsappsdk.foundation-config.cmake‎

Lines changed: 105 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
This package defines the following CMake targets:
1111
1212
* Microsoft.WindowsAppSDK.Foundation - A target representing the C++/WinRT projection, headers and libraries of
13-
the Windows App SDK Foundation APIs. This does not include the necessary runtime components to use the
14-
Windows App SDK, and is intended to be used by library authors who want to consume the Windows App SDK
15-
Foundation APIs.
13+
the Windows App SDK Foundation APIs. This target does not have any deployment-mode-specific behavior,
14+
however it does forward-declare two variant targets (Foundation_Framework and Foundation_SelfContained) that the wasdk_link_component_variant() helper populates based on the consumer's WindowsAppSDKSelfContained property.
1615
1716
* Microsoft.WindowsAppSDK.Foundation_Framework - A target representing the Windows App SDK Foundation APIs for
1817
framework-dependent deployment scenarios. Auto-initialization behavior is controlled by target properties
@@ -51,7 +50,7 @@ if(CMAKE_VERSION VERSION_LESS 3.31)
5150
message(FATAL_ERROR "Microsoft.WindowsAppSDK.Foundation requires at least CMake 3.31, but CMake ${CMAKE_VERSION} is in use.")
5251
endif()
5352

54-
53+
find_package(Microsoft.WindowsAppSDK.Base CONFIG REQUIRED)
5554
find_package(Microsoft.WindowsAppSDK.InteractiveExperiences CONFIG REQUIRED)
5655

5756
block(SCOPE_FOR VARIABLES)
@@ -78,6 +77,17 @@ block(SCOPE_FOR VARIABLES)
7877
${PACKAGE_LOCATION}/include
7978
)
8079

80+
# Forward-declare deployment-mode variant targets adjacent to the basic target.
81+
# Populated further below; this declaration lets wasdk_link_component_variant() enforce
82+
# the contract that both variants exist at configure time.
83+
add_library(Microsoft.WindowsAppSDK.Foundation_Framework INTERFACE)
84+
add_library(Microsoft.WindowsAppSDK.Foundation_SelfContained INTERFACE)
85+
86+
# Basic target as variant selector
87+
# Resolves to _Framework or _SelfContained based on the consumer's WindowsAppSDKSelfContained
88+
# property at generation time.
89+
wasdk_link_component_variant(Foundation)
90+
8191
#[[====================================================================================================================
8292
Target: Microsoft.WindowsAppSDK.Foundation_DynamicDependencyBootstrap
8393
@@ -217,7 +227,7 @@ block(SCOPE_FOR VARIABLES)
217227
IMPORTED_LOCATION "${FRAMEWORK_DLLS}"
218228
)
219229

220-
add_library(Microsoft.WindowsAppSDK.Foundation_SelfContained INTERFACE)
230+
# _SelfContained interface target was forward-declared near the basic target above.
221231

222232
wasdk_transform_appxfragment(
223233
Microsoft.WindowsAppSDK.Foundation_SelfContained
@@ -271,7 +281,7 @@ block(SCOPE_FOR VARIABLES)
271281
272282
If the Runtime package is not found, _Framework is not available.
273283
====================================================================================================================]]#
274-
add_library(Microsoft.WindowsAppSDK.Foundation_Framework INTERFACE)
284+
# _Framework interface target was forward-declared near the basic target above.
275285

276286
target_link_libraries(Microsoft.WindowsAppSDK.Foundation_Framework
277287
INTERFACE
@@ -353,107 +363,104 @@ block(SCOPE_FOR VARIABLES)
353363
)
354364

355365
#[[====================================================================================================================
356-
wasdk_generate_appx_manifest()
357-
358-
Generates an AppxManifest.xml for packaged apps. For framework-dependent apps, automatically
359-
includes the <PackageDependency> entry for the Windows App Runtime Framework package.
360-
361-
wasdk_generate_appx_manifest(
362-
TARGET <target>
363-
OUTPUT <output-path>
364-
IDENTITY_NAME <name>
365-
IDENTITY_PUBLISHER <publisher>
366-
IDENTITY_VERSION <version>
367-
[DISPLAY_NAME <name>]
368-
[DESCRIPTION <description>]
369-
[LOGO <path>]
370-
)
371-
372-
The TARGET must already be defined via add_executable(). If the target links
373-
Microsoft.WindowsAppSDK.Foundation_Framework, a <PackageDependency> for the Windows App Runtime
374-
Framework package is automatically included. If the target links _SelfContained, no
375-
PackageDependency is added.
376-
====================================================================================================================]]#
377-
function(wasdk_generate_appx_manifest)
378-
set(ONE_VALUE_KEYWORDS TARGET OUTPUT IDENTITY_NAME IDENTITY_PUBLISHER IDENTITY_VERSION DISPLAY_NAME DESCRIPTION LOGO)
379-
cmake_parse_arguments(PARSE_ARGV 0 APPX "" "${ONE_VALUE_KEYWORDS}" "")
366+
Foundation auto-init configuration validation (deferred)
380367
381-
if(NOT APPX_TARGET)
382-
message(FATAL_ERROR "wasdk_generate_appx_manifest: TARGET is required")
383-
endif()
384-
if(NOT APPX_OUTPUT)
385-
set(APPX_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AppxManifest.xml")
386-
endif()
387-
if(NOT APPX_IDENTITY_NAME OR NOT APPX_IDENTITY_PUBLISHER OR NOT APPX_IDENTITY_VERSION)
388-
message(FATAL_ERROR "wasdk_generate_appx_manifest: IDENTITY_NAME, IDENTITY_PUBLISHER, and IDENTITY_VERSION are required")
389-
endif()
390-
if(NOT APPX_DISPLAY_NAME)
391-
set(APPX_DISPLAY_NAME "${APPX_IDENTITY_NAME}")
368+
Runs at end of consumer's directory configure. Walks targets that link
369+
any Microsoft.WindowsAppSDK* and validates 2 configuration rules:
370+
371+
Rule A: FW Unpackaged + WindowsAppSdkBootstrapInitialize=FALSE
372+
→ "The Windows App SDK Bootstrapper is required for framework-dependent unpackaged apps"
373+
374+
Rule B: SelfContained + downlevel SDK (< 19H1) + WindowsAppSdkUndockedRegFreeWinRTInitialize=FALSE
375+
→ "Undocked Reg-Free WinRT activation is required for self-contained apps targeting Windows versions prior to 10.0.18362.0"
376+
377+
Both produce configure-time FATAL_ERROR. This Foundation-side validator only fires when Foundation
378+
IS loaded.
379+
====================================================================================================================]]#
380+
function(_wasdk_foundation_validate_target target)
381+
get_target_property(_type ${target} TYPE)
382+
if(NOT _type STREQUAL "EXECUTABLE")
383+
return()
392384
endif()
393-
if(NOT APPX_DESCRIPTION)
394-
set(APPX_DESCRIPTION "${APPX_DISPLAY_NAME}")
385+
386+
get_target_property(_link_libs ${target} LINK_LIBRARIES)
387+
if(NOT _link_libs)
388+
return()
395389
endif()
396-
if(NOT APPX_LOGO)
397-
set(APPX_LOGO "Logo.png")
390+
set(_uses_wasdk FALSE)
391+
foreach(_lib IN LISTS _link_libs)
392+
if(_lib MATCHES "^Microsoft\\.WindowsAppSDK")
393+
set(_uses_wasdk TRUE)
394+
break()
395+
endif()
396+
endforeach()
397+
if(NOT _uses_wasdk)
398+
return()
398399
endif()
399400

400-
# Determine architecture
401-
wasdk_detect_platform()
402-
if(PLATFORM_IDENTIFIER STREQUAL "x64")
403-
set(_PROC_ARCH "x64")
404-
elseif(PLATFORM_IDENTIFIER STREQUAL "arm64")
405-
set(_PROC_ARCH "arm64")
406-
else()
407-
set(_PROC_ARCH "x64")
401+
get_target_property(_pkg_type ${target} WindowsPackageType)
402+
get_target_property(_sc ${target} WindowsAppSDKSelfContained)
403+
get_target_property(_bootstrap ${target} WindowsAppSdkBootstrapInitialize)
404+
get_target_property(_urf ${target} WindowsAppSdkUndockedRegFreeWinRTInitialize)
405+
406+
# Rule A: FW Unpackaged + BootstrapInit=FALSE → error
407+
if(_pkg_type STREQUAL "None" AND NOT _sc AND _bootstrap STREQUAL "FALSE")
408+
message(FATAL_ERROR
409+
"The Windows App SDK Bootstrapper is required for framework-dependent unpackaged apps. "
410+
"Target '${target}' has WindowsPackageType=\"None\" and "
411+
"WindowsAppSdkBootstrapInitialize=FALSE. Either set "
412+
"WindowsAppSdkBootstrapInitialize=TRUE, change the deployment model, or "
413+
"make the app self-contained.")
408414
endif()
409415

410-
# Build the PackageDependency section if framework info is available
411-
set(_PACKAGE_DEPENDENCY "")
412-
if(DEFINED WINDOWSAPPSDK_FRAMEWORK_PACKAGE_NAME)
413-
set(_PACKAGE_DEPENDENCY " <PackageDependency Name=\"${WINDOWSAPPSDK_FRAMEWORK_PACKAGE_NAME}\" MinVersion=\"${WINDOWSAPPSDK_FRAMEWORK_MIN_VERSION}\" Publisher=\"${WINDOWSAPPSDK_FRAMEWORK_PUBLISHER}\" />\n")
416+
# Rule B: SC + downlevel SDK + URFInit=FALSE → error
417+
if(_sc AND _urf STREQUAL "FALSE")
418+
# Detect the target's MINIMUM supported OS version.
419+
# CMAKE_SYSTEM_VERSION (set via -DCMAKE_SYSTEM_VERSION or by toolchain
420+
# file) is the canonical CMake variable for the target MIN OS version.
421+
# CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION is the BUILD SDK that VS
422+
# selects to compile against (often newer than the target min); not
423+
# what we want for this check. Falls back to env if neither set.
424+
set(_tpv "")
425+
if(NOT "${CMAKE_SYSTEM_VERSION}" STREQUAL "")
426+
set(_tpv "${CMAKE_SYSTEM_VERSION}")
427+
elseif(NOT "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" STREQUAL "")
428+
set(_tpv "${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}")
429+
elseif(DEFINED ENV{WindowsSDKVersion})
430+
string(REGEX REPLACE "^([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+).*" "\\1" _tpv "$ENV{WindowsSDKVersion}")
431+
endif()
432+
if(_tpv AND _tpv VERSION_LESS "10.0.18362.0")
433+
message(FATAL_ERROR
434+
"Undocked Reg-Free WinRT activation is required for self-contained apps targeting Windows versions prior to 10.0.18362.0. "
435+
"Target '${target}' is self-contained, targets SDK version ${_tpv}, and has "
436+
"WindowsAppSdkUndockedRegFreeWinRTInitialize=FALSE. Either remove the "
437+
"explicit FALSE override, raise the target SDK version, or switch to "
438+
"framework-dependent deployment.")
439+
endif()
414440
endif()
441+
endfunction()
415442

416-
# Generate the manifest
417-
file(WRITE "${APPX_OUTPUT}"
418-
"<?xml version=\"1.0\" encoding=\"utf-8\"?>
419-
<Package xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"
420-
xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"
421-
xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"
422-
IgnorableNamespaces=\"uap rescap\">
423-
<Identity Name=\"${APPX_IDENTITY_NAME}\"
424-
Version=\"${APPX_IDENTITY_VERSION}\"
425-
Publisher=\"${APPX_IDENTITY_PUBLISHER}\"
426-
ProcessorArchitecture=\"${_PROC_ARCH}\" />
427-
<Properties>
428-
<DisplayName>${APPX_DISPLAY_NAME}</DisplayName>
429-
<PublisherDisplayName>${APPX_IDENTITY_PUBLISHER}</PublisherDisplayName>
430-
<Description>${APPX_DESCRIPTION}</Description>
431-
<Logo>${APPX_LOGO}</Logo>
432-
</Properties>
433-
<Dependencies>
434-
<TargetDeviceFamily Name=\"Windows.Desktop\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.26100.0\" />
435-
${_PACKAGE_DEPENDENCY} </Dependencies>
436-
<Resources>
437-
<Resource Language=\"en-us\" />
438-
</Resources>
439-
<Applications>
440-
<Application Id=\"App\" Executable=\"${APPX_TARGET}.exe\" EntryPoint=\"Windows.FullTrustApplication\">
441-
<uap:VisualElements DisplayName=\"${APPX_DISPLAY_NAME}\" Description=\"${APPX_DESCRIPTION}\"
442-
BackgroundColor=\"transparent\" Square150x150Logo=\"${APPX_LOGO}\" Square44x44Logo=\"${APPX_LOGO}\">
443-
<uap:DefaultTile Wide310x150Logo=\"${APPX_LOGO}\" />
444-
</uap:VisualElements>
445-
</Application>
446-
</Applications>
447-
<Capabilities>
448-
<rescap:Capability Name=\"runFullTrust\" />
449-
</Capabilities>
450-
</Package>
451-
")
452-
453-
# Copy manifest + logo to output directory post-build
454-
add_custom_command(TARGET ${APPX_TARGET} POST_BUILD
455-
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${APPX_OUTPUT}" $<TARGET_FILE_DIR:${APPX_TARGET}>
456-
)
443+
function(_wasdk_foundation_validate_recursive _dir)
444+
get_directory_property(_targets DIRECTORY ${_dir} BUILDSYSTEM_TARGETS)
445+
foreach(_t IN LISTS _targets)
446+
_wasdk_foundation_validate_target(${_t})
447+
endforeach()
448+
449+
get_directory_property(_subdirs DIRECTORY ${_dir} SUBDIRECTORIES)
450+
foreach(_sub IN LISTS _subdirs)
451+
_wasdk_foundation_validate_recursive(${_sub})
452+
endforeach()
453+
endfunction()
454+
455+
function(_wasdk_foundation_validate)
456+
_wasdk_foundation_validate_recursive(${CMAKE_CURRENT_SOURCE_DIR})
457457
endfunction()
458458

459+
# Register DEFER hook; idempotent per directory.
460+
get_directory_property(_already_deferred _WASDK_FOUNDATION_VALIDATE_DEFERRED)
461+
if(NOT _already_deferred)
462+
set_property(DIRECTORY PROPERTY _WASDK_FOUNDATION_VALIDATE_DEFERRED TRUE)
463+
cmake_language(DEFER CALL _wasdk_foundation_validate)
464+
endif()
465+
459466
endblock()

0 commit comments

Comments
 (0)