microsoft / microsoft/winappCli

[Feature] Add CMAKE vcpkg

Open
#297 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

cli enhancement
Dominant language
C#
Stars
1.3k
Forks
80
Avg merge
3d 6h
Merged PRs (30d)
51

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

1. Reduce C++ CMakeLists.txt from ~80 lines of boilerplate to ~15 lines
2. Package resolution from `winapp.yaml` works automatically in CMake
3. MSIX packaging is a build-system step (CMake custom target)
4. Zero-friction first-time setup: `vcpkg install winapp` gets everything needed

---

## Developer Experience: Before & After

Before (current — 79 lines in sample CMakeLists.txt)

```cmake
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 $<$:winapp> $<$:create-debug-identity> ...
)
```

### After (with vcpkg port)

```cmake
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`

```powershell
# 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

```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.json` with `winapp` dependency
- Creates `vcpkg-configuration.json` pointing to the winapp repo as a vcpkg registry
- Parses `CMakeLists.txt` to find the target name (from `add_executable`)
- Prompts the user to auto-edit `CMakeLists.txt` with 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:**
```json
{
"dependencies": ["winapp"]
}
```

**vcpkg-configuration.json:**
```json
{
"registries": [
{
"kind": "git",
"repository": "https://github.com/microsoft/winappCli",
"baseline": "",
"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

1. Downloads the winapp CLI binary (architecture-appropriate) from GitHub releases
2. Installs it to `${CURRENT_PACKAGES_DIR}/tools/winapp/winapp.exe`
3. Installs `winapp-config.cmake` and `winapp-functions.cmake` to the CMake package config directory
4. No C++ libraries are compiled — this is a "tool + CMake modules" port

### What `find_package(winapp)` does

1. Locates the winapp CLI (vcpkg tools dir → system PATH → auto-download fallback)
2. Sets `WINAPP_CLI` cache variable pointing to the winapp executable
3. Runs `winapp restore --output-dir ${CMAKE_BINARY_DIR}/_winapp` if headers don't exist — restores ALL packages to the build tree
4. Creates imported targets pointing at `${CMAKE_BINARY_DIR}/_winapp/include` and `_winapp/lib`
5. Loads `winapp-functions.cmake` to make helper functions available

> **Note:** Certificate generation is not automatic — developers run `winapp cert generate` manually 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()`

Adds a post-build command that applies debug identity (sparse package) in Debug configuration.

```cmake
winapp_debug_identity(cpp-app)
```

#### `winapp_package( [TARGETS ...] [CERT ] [OUTPUT_DIR ] [MANIFEST ])`

Creates a custom build target `package-` that builds an MSIX package.

**Single-target (most common):**
```cmake
winapp_package(cpp-app)
# cmake --build build --target package-cpp-app --config Release
```

**Multi-target (app + DLLs):**
```cmake
winapp_package(myapp
TARGETS myapp helper plugin
CERT devcert.pfx
)
# Collects myapp.exe + helper.dll + plugin.dll into a single MSIX
```

Parameters:
- `TARGETS` — CMake targets to include in the MSIX (default: `` 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( )` *(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.

```cmake
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 `winapp` port 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 CLI
- `WINAPP_DOWNLOAD_VERSION` — exact version to auto-download if needed (e.g., `0.12.0`)

`find_package(winapp)` resolution order:
1. **vcpkg tools dir** → version pinned by portfile (SHA512 verified), always used
2. **System PATH** → runs `winapp --version`, accepts if `>= WINAPP_MIN_CLI_VERSION`
3. **Auto-download** → downloads `WINAPP_DOWNLOAD_VERSION` from 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-dir` CLI 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 portfile
- `ports/winapp/vcpkg.json` — vcpkg manifest
- `ports/winapp/winapp-config.cmake` — CMake find_package config
- `ports/winapp/winapp-functions.cmake` — CMake helper functions

### CLI changes
- Enhance `restore` to copy artifacts from ALL packages in winapp.yaml
- Add `--output-dir` flag to `restore` command
- Add `--skip-restore` flag to `init` command
- CMake auto-detection in `init` (parse target, generate vcpkg files, offer to edit CMakeLists.txt)
- Add `outputDir` field support in winapp.yaml parsing
- (Optional) Add `resolve-package-path` command for advanced use cases

### Docs & samples
- `samples/cpp-app/CMakeLists.txt` — Simplify to use `find_package(winapp)`
- `samples/cpp-app/vcpkg.json` — Add vcpkg manifest to sample
- `docs/guides/cpp.md` — Update guide with vcpkg workflow
- `docs/cmake.md` - Documenting the cmake functions and usage

---

## Verification

1. **Restore test**: `winapp restore` copies headers from ALL packages (WIL, CppWinRT, etc.) into `.winapp/include`
2. **Integration test**: `vcpkg install winapp` succeeds on x64 and ARM64
3. **End-to-end**: Clone sample → `cmake -B build -DCMAKE_TOOLCHAIN_FILE=` → build → verify debug identity works
4. **Packaging test**: `cmake --build build --target package-cpp-app --config Release` produces valid MSIX
5. **Fallback test**: Without vcpkg, `find_package(winapp)` auto-downloads CLI and still works
6. **Init test**: `winapp init` in a CMake project creates vcpkg files and offers to edit CMakeLists.txt

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing restore and init commands, then the proposed ports/winapp/portfile.cmake, winapp-config.cmake, and winapp-functions.cmake files. Compare the samples/cpp-app/CMakeLists.txt workflow with the documented CMake API and verification steps. Done means the port, CLI changes, sample, and documentation support restore, package integration, debug identity, and MSIX targets as specified.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, cpp
Domain
build-system, cli, devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.