- Notifications
You must be signed in to change notification settings - Fork 38
Description
Spec: CMake / vcpkg Integration for winapp CLI
C++ developers using winapp CLI with CMake currently face significant friction. The existing approach requires 60-80 lines of boilerplate CMake code to: download the CLI, restore SDK headers, resolve package paths, and apply debug identity. winapp should be a vcpkg port that provides native CMake integration, replacing manual execute_process() calls with proper CMake functions and imported targets.
Goals
- Reduce C++ CMakeLists.txt from ~80 lines of boilerplate to ~15 lines
- Package resolution from
winapp.yamlworks automatically in CMake - MSIX packaging is a build-system step (CMake custom target)
- Zero-friction first-time setup:
vcpkg install winappgets everything needed
Developer Experience: Before & After
Before (current — 79 lines in sample CMakeLists.txt)
cmake_minimum_required(VERSION 3.20) project(cpp-app) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # 30 lines: download winapp CLI if not in PATH find_program(WINAPP_CLI winapp) if(NOT WINAPP_CLI) # ... download logic, architecture detection, zip extraction ... endif() # 10 lines: restore headers if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include") execute_process(COMMAND "${WINAPP_CLI}" restore ...) endif() add_executable(cpp-app main.cpp) target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib) target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include) # Manually hardcode WIL path set(WIL_PACKAGE_DIR "$ENV{UserProfile}/.winapp/packages/Microsoft.Windows.ImplementationLibrary.1.0.260126.7") target_include_directories(cpp-app PRIVATE ${WIL_PACKAGE_DIR}/include/) # 8 lines: post-build debug identity add_custom_command(TARGET cpp-app POST_BUILD COMMAND $<$<CONFIG:Debug>:winapp> $<$<CONFIG:Debug>:create-debug-identity> ... )After (with vcpkg port)
cmake_minimum_required(VERSION 3.20) project(cpp-app) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(winapp REQUIRED) add_executable(cpp-app main.cpp) # Links WindowsApp.lib + OneCoreUap.lib, sets include dirs for # all packages from winapp.yaml (WinAppSDK, WIL, etc.) target_link_libraries(cpp-app PRIVATE winapp::sdk) # Post-build: apply debug identity in Debug config winapp_debug_identity(cpp-app) # Build target: create MSIX package (cmake --build build --target package-cpp-app) winapp_package(cpp-app)From ~80 lines to ~15 lines, with automatic package resolution and MSIX packaging as a proper build target.
Component 0: CLI Changes
winapp restore copies ALL package artifacts
Currently winapp restore copies Windows App SDK headers to .winapp/include and libs to .winapp/lib, but does NOT process other packages in winapp.yaml (e.g., WIL). Developers must manually locate and reference those packages.
Change: winapp restore processes ALL packages in winapp.yaml and copies their include headers and libraries into the unified .winapp/ directories:
.winapp/ ├── include/ │ ├── winrt/ # from Windows App SDK (existing) │ ├── wil/ # from WIL (NEW — auto-copied) │ ├── MddBootstrap.h # from Windows App SDK (existing) │ └── ... ├── lib/ │ └── ... └── bin/ └── ... A single target_include_directories(myapp PRIVATE .winapp/include) gives access to ALL packages — no per-package path resolution needed.
New --output-dir flag on winapp restore
# Current behavior (unchanged) winapp restore # → .winapp/ in cwd # New: explicit output directory winapp restore --output-dir build/_winapp # → build/_winapp/The CMake module uses this to place SDK artifacts in the build tree (following CMake conventions that generated files belong in CMAKE_BINARY_DIR, not the source tree).
New outputDir field in winapp.yaml
outputDir: build/.winapp # optional, default: .winapp packages: - name: Microsoft.Windows.CppWinRT version: 2.0.250303.1 # ...Precedence: --output-dir CLI flag > outputDir in yaml > default .winapp/
Updated winapp init flow for CMake projects
Currently winapp init creates config files AND restores SDKs. These should be separable. When CMakeLists.txt exists, winapp init automatically:
- Skips SDK restore (CMake will handle it via
find_package) - Creates
vcpkg.jsonwithwinappdependency - Creates
vcpkg-configuration.jsonpointing to the winapp repo as a vcpkg registry - Parses
CMakeLists.txtto find the target name (fromadd_executable) - Prompts the user to auto-edit
CMakeLists.txtwith the integration lines
Example output:
> winapp init ✓ Created appxmanifest.xml ✓ Created Assets/ ✓ Created winapp.yaml ✓ Created vcpkg.json ✓ Created vcpkg-configuration.json ✓ Updated .gitignore CMake project detected. Found target: cpp-app Would you like to update CMakeLists.txt with winapp integration? [Y/n] y ✓ Added find_package(winapp REQUIRED) ✓ Added target_link_libraries(cpp-app PRIVATE winapp::sdk) ✓ Added winapp_debug_identity(cpp-app) Configure with: cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake If the user declines, the lines to add are printed instead for manual editing.
Generated vcpkg files
vcpkg.json:
{ "dependencies": ["winapp"] }vcpkg-configuration.json:
{ "registries": [ { "kind": "git", "repository": "https://github.com/microsoft/winappCli", "baseline": "<latest-commit-hash>", "packages": ["winapp"] } ] }A --skip-restore flag is also available for explicit control: winapp init --skip-restore
Component 1: vcpkg Port
Port structure
ports/winapp/ ├── portfile.cmake # Downloads winapp CLI + installs CMake modules ├── vcpkg.json # Port manifest ├── winapp-config.cmake # find_package() entry point └── winapp-functions.cmake # CMake helper functions portfile.cmake behavior
- Downloads the winapp CLI binary (architecture-appropriate) from GitHub releases
- Installs it to
${CURRENT_PACKAGES_DIR}/tools/winapp/winapp.exe - Installs
winapp-config.cmakeandwinapp-functions.cmaketo the CMake package config directory - No C++ libraries are compiled — this is a "tool + CMake modules" port
What find_package(winapp) does
- Locates the winapp CLI (vcpkg tools dir → system PATH → auto-download fallback)
- Sets
WINAPP_CLIcache variable pointing to the winapp executable - Runs
winapp restore --output-dir ${CMAKE_BINARY_DIR}/_winappif headers don't exist — restores ALL packages to the build tree - Creates imported targets pointing at
${CMAKE_BINARY_DIR}/_winapp/includeand_winapp/lib - Loads
winapp-functions.cmaketo make helper functions available
Note: Certificate generation is not automatic — developers run
winapp cert generatemanually when needed.
Component 2: CMake API
Imported Targets
| Target | What it provides |
|---|---|
winapp::sdk | Links WindowsApp.lib + OneCoreUap.lib, adds _winapp/include to include dirs for all packages from winapp.yaml, includes Windows App SDK libraries and bootstrap headers |
Functions
winapp_debug_identity(<target>)
Adds a post-build command that applies debug identity (sparse package) in Debug configuration.
winapp_debug_identity(cpp-app)winapp_package(<name> [TARGETS ...] [CERT <path>] [OUTPUT_DIR <dir>] [MANIFEST <path>])
Creates a custom build target package-<name> that builds an MSIX package.
Single-target (most common):
winapp_package(cpp-app) # cmake --build build --target package-cpp-app --config ReleaseMulti-target (app + DLLs):
winapp_package(myapp TARGETS myapp helper plugin CERT devcert.pfx ) # Collects myapp.exe + helper.dll + plugin.dll into a single MSIXParameters:
TARGETS— CMake targets to include in the MSIX (default:<name>only)CERT— Path to signing certificate (optional; if omitted, MSIX is unsigned)OUTPUT_DIR— Where to place the MSIX (default:${CMAKE_BINARY_DIR}/msix)MANIFEST— Path to appxmanifest.xml (default:${CMAKE_CURRENT_SOURCE_DIR}/appxmanifest.xml)
winapp_resolve_package(<package_name> <output_variable>) (optional / advanced)
Resolves a raw package path from winapp.yaml into a CMake variable. Rarely needed since winapp restore copies all headers into the unified include directory.
winapp_resolve_package(Microsoft.Windows.ImplementationLibrary WIL_DIR)winapp_restore()
Restores packages from winapp.yaml. Called automatically by find_package(winapp) but can be invoked explicitly.
Component 3: Enhanced winapp restore
Primary solution: winapp restore copies all package artifacts
#292 is primarily solved by making winapp restore process ALL packages — not just Windows App SDK. After restore, .winapp/include contains headers from every package, so a single include path covers everything.
Design Decisions
winapp.yaml stays as source of truth
- vcpkg.json is only for declaring the
winappport dependency itself - Package versions (WindowsAppSDK, CppWinRT, WIL, etc.) remain in
winapp.yaml - Consistent across C++, Rust, Electron, and other ecosystems
CLI version pinning and auto-download fallback
The CMake module defines version constraints:
WINAPP_MIN_CLI_VERSION— minimum acceptable version (e.g.,0.10.0) for system-installed CLIWINAPP_DOWNLOAD_VERSION— exact version to auto-download if needed (e.g.,0.12.0)
find_package(winapp) resolution order:
- vcpkg tools dir → version pinned by portfile (SHA512 verified), always used
- System PATH → runs
winapp --version, accepts if>= WINAPP_MIN_CLI_VERSION - Auto-download → downloads
WINAPP_DOWNLOAD_VERSIONfrom GitHub releases (if PATH version too old or not found)
Generated artifacts go in build tree
- SDK headers, libs, and binaries are restored to
${CMAKE_BINARY_DIR}/_winapp/ - Source tree stays clean — no
.winapp/directory - Non-CMake workflows are unaffected — default remains
.winapp/in project root --output-dirCLI flag overrides everything (used by CMake module)
Port distribution
- Overlay port first (in winapp CLI repo under
ports/), then submit to official vcpkg registry once stable - Independent port versioning — portfile pins specific CLI version via download URL + SHA512 hash
Files to Create / Modify
New files
ports/winapp/portfile.cmake— vcpkg portfileports/winapp/vcpkg.json— vcpkg manifestports/winapp/winapp-config.cmake— CMake find_package configports/winapp/winapp-functions.cmake— CMake helper functions
CLI changes
- Enhance
restoreto copy artifacts from ALL packages in winapp.yaml - Add
--output-dirflag torestorecommand - Add
--skip-restoreflag toinitcommand - CMake auto-detection in
init(parse target, generate vcpkg files, offer to edit CMakeLists.txt) - Add
outputDirfield support in winapp.yaml parsing - (Optional) Add
resolve-package-pathcommand for advanced use cases
Docs & samples
samples/cpp-app/CMakeLists.txt— Simplify to usefind_package(winapp)samples/cpp-app/vcpkg.json— Add vcpkg manifest to sampledocs/guides/cpp.md— Update guide with vcpkg workflowdocs/cmake.md- Documenting the cmake functions and usage
Verification
- Restore test:
winapp restorecopies headers from ALL packages (WIL, CppWinRT, etc.) into.winapp/include - Integration test:
vcpkg install winappsucceeds on x64 and ARM64 - End-to-end: Clone sample →
cmake -B build -DCMAKE_TOOLCHAIN_FILE=<vcpkg>→ build → verify debug identity works - Packaging test:
cmake --build build --target package-cpp-app --config Releaseproduces valid MSIX - Fallback test: Without vcpkg,
find_package(winapp)auto-downloads CLI and still works - Init test:
winapp initin a CMake project creates vcpkg files and offers to edit CMakeLists.txt