Skip to content

Commit

Permalink
Encapsulate the WinRTComponent build (#316)
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle committed Sep 22, 2024
1 parent a1481f3 commit 025ea5d
Show file tree
Hide file tree
Showing 89 changed files with 110 additions and 112 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
working-directory: Generator/InteropTests
shell: pwsh
run: |
& .\Build-WinRTComponentProjection.ps1 -SwiftWinRT "$Env:GITHUB_WORKSPACE\Generator\.build\debug\SwiftWinRT.exe"
& .\SPMPrebuild.ps1 -SwiftWinRT "$Env:GITHUB_WORKSPACE\Generator\.build\debug\SwiftWinRT.exe"
- name: Build InteropTests
working-directory: Generator/InteropTests
Expand Down
2 changes: 1 addition & 1 deletion Building.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ Generator> swift build --build-tests
Generator> swift test --skip-build
```

The `/InteropTests` subdirectory requires building `WinRTComponent.winmd` and `WinRTComponent.dll`, which SPM cannot do. A helper script, `Build-WinRTComponentProjection.ps1`, will do that using CMake, after which the package can be built and tested normally.
The `/Generator/InteropTests` subdirectory requires building `WinRTComponent.winmd` and `WinRTComponent.dll`, which SPM cannot do. A helper script, `SPMPrebuild.ps1`, will do that using CMake, after which the package can be built and tested normally.
1 change: 0 additions & 1 deletion Generator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# This directory can be built in two modes: standalone or as a subdirectory.
message(STATUS "CMAKE_PROJECT_NAME: ${CMAKE_PROJECT_NAME}")
if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
# Standalone mode. Only build the code generator.
# We can't build InteropTests in this mode because it depends on SwiftWinRT.exe
Expand Down
3 changes: 0 additions & 3 deletions Generator/InteropTests/.gitignore

This file was deleted.

47 changes: 8 additions & 39 deletions Generator/InteropTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,16 @@ endif()
if (NOT DEFINED SWIFTWINRT_EXE)
message(FATAL_ERROR "SWIFTWINRT_EXE must be defined")
endif()
cmake_path(ABSOLUTE_PATH SWIFTWINRT_EXE NORMALIZE OUTPUT_VARIABLE SWIFTWINRT_EXE)

set(REPO_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../..")

# Generating projection sources will depend on WinRTComponent.winmd, so build it now
include(WinRTComponent/GenerateWinMD.cmake)
set(WINRTCOMPONENT_WINMD "${CMAKE_CURRENT_BINARY_DIR}/WinRTComponent.winmd")
generate_winrtcomponent_winmd(
IDL "${CMAKE_CURRENT_SOURCE_DIR}/WinRTComponent/WinRTComponent.idl"
WINMD "${WINRTCOMPONENT_WINMD}")

# Generate Swift projection
message(STATUS "Generating Swift projection for WinRTComponent...")
include(GenerateProjection.cmake)
generate_projection(
SWIFTWINRT_EXE "${SWIFTWINRT_EXE}"
WINRTCOMPONENT_WINMD "${WINRTCOMPONENT_WINMD}"
PROJECTION_JSON "${CMAKE_CURRENT_SOURCE_DIR}/projection.json"
PROJECTION_DIR "${CMAKE_CURRENT_BINARY_DIR}/Projection/Sources"
SPM_SUPPORT_PACKAGE_DIR "${REPO_ROOT}")

# Define WinRTComponent build (requires the cl.exe compiler)
include(ExternalProject)
message(STATUS "Building WinRTComponent...")
ExternalProject_Add(WinRTComponent
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/WinRTComponent"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/WinRTComponent"
CMAKE_ARGS
-D "WINRTCOMPONENT_WINMD=${WINRTCOMPONENT_WINMD}"
-D "CMAKE_CXX_COMPILER=cl.exe"
-D "CMAKE_CXX_FLAGS=/std:c++latest /W4 /EHsc"
INSTALL_COMMAND ""
TEST_COMMAND "")
cmake_path(ABSOLUTE_PATH SWIFTWINRT_EXE NORMALIZE)

# Build the support module if not already the case (by root CMakeLists.txt)
if(NOT TARGET WindowsRuntime)
add_subdirectory("${REPO_ROOT}/Support/Sources" "${CMAKE_CURRENT_BINARY_DIR}/Support")
execute_process(
COMMAND git.exe -C "${CMAKE_CURRENT_SOURCE_DIR}" rev-parse --path-format=absolute --show-toplevel
OUTPUT_VARIABLE REPO_ROOT
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
add_subdirectory("${REPO_ROOT}/Sources" "${CMAKE_CURRENT_BINARY_DIR}/Support")
endif()

# Build the Swift projection
# TODO(https://github.com/tristanlabelle/swift-winrt/issues/302): Fix linking issues
# add_subdirectory(
# ${CMAKE_CURRENT_BINARY_DIR}/Projection/Sources
# ${CMAKE_CURRENT_BINARY_DIR}/Projection/Build)
add_subdirectory(WinRTComponent)
15 changes: 8 additions & 7 deletions Generator/InteropTests/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@ let package = Package(
name: "InteropTests",
dependencies: [
.package(name: "Support", path: "../.."),
.package(path: "Generated"),
.package(name: "Projection", path: "WinRTComponent/Projection"),
],
targets: [
.testTarget(
name: "Tests",
dependencies: [
.product(name: "WindowsRuntime", package: "Support"),
.product(name: "UWP", package: "Generated"),
.product(name: "WinRTComponent", package: "Generated"),
.product(name: "UWP", package: "Projection"),
.product(name: "WinRTComponent", package: "Projection"),
],
path: "Tests",
// Workaround for SPM library support limitations causing "LNK4217: locally defined symbol imported" spew
linkerSettings: [ .unsafeFlags([
"-Xlinker", "-ignore:4217",
"-Xlinker", "/manifestinput:Generated/WinRTComponent.manifest",
"-Xlinker", "/manifest:embed"
// Embed the WinRT component manifest to locate activation factories
"-Xlinker", "/manifestinput:WinRTComponent/Projection/WinRTComponent.manifest",
"-Xlinker", "/manifest:embed",
// Workaround for SPM library support limitations causing "LNK4217: locally defined symbol imported" spew
"-Xlinker", "-ignore:4217"
]) ])
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,17 @@ if (-not $SwiftWinRT) {
if ($LASTEXITCODE -ne 0) { throw "Failed to build SwiftWinRT.exe" }
$SwiftWinRT = "$GeneratorProjectDir\.build\$SwiftConfiguration\SwiftWinRT.exe"
}
else {
$SwiftWinRT = [IO.Path]::GetFullPath($SwiftWinRT)
}

Write-Host -ForegroundColor Cyan "Building WinRTComponent.dll & winmd..."
$CMakePreset = "debug"
$CMakePreset = "debug" # Tests are always built in debug mode
Push-Location "$PSScriptRoot\WinRTComponent"
& cmake.exe --preset $CMakePreset
& cmake.exe --build --preset $CMakePreset
& cmake.exe --preset $CMakePreset -D "SWIFTWINRT_EXE=$SwiftWinRT" -D "PROJECTION_DIR=$(Get-Location)\Projection"
& cmake.exe --build --preset $CMakePreset --target WinRTComponent
$WinRTComponentBinDir = "$(Get-Location)\build\$CMakePreset\Dll"
Pop-Location
$WinRTComponentBinDir = "$PSScriptRoot\WinRTComponent\build\$CMakePreset"

Write-Host -ForegroundColor Cyan "Generating Swift projection for WinRT component..."
& cmake.exe `
-D "SWIFTWINRT_EXE=$SwiftWinRT" `
-D "WINRTCOMPONENT_WINMD=$WinRTComponentBinDir\WinRTComponent.winmd" `
-D "PROJECTION_JSON=$PSScriptRoot\projection.json" `
-D "PROJECTION_DIR=$PSScriptRoot\Generated" `
-D "SPM_SUPPORT_PACKAGE_DIR=$PSScriptRoot\..\.." `
-P "$PSScriptRoot\GenerateProjection.cmake"

Write-Host -ForegroundColor Cyan "Copying the WinRT component dll next to the test..."
$SwiftTestPackageDir = $PSScriptRoot
Expand Down
76 changes: 43 additions & 33 deletions Generator/InteropTests/WinRTComponent/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,38 +1,48 @@
cmake_minimum_required(VERSION 3.21.0)
# Support using this directory as a standalone project or as a subdirectory.
if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
cmake_minimum_required(VERSION 3.21.0)
project(WinRTComponent LANGUAGES CXX)
endif()

project(WinRTComponent LANGUAGES CXX)
if (NOT DEFINED SWIFTWINRT_EXE)
message(FATAL_ERROR "SWIFTWINRT_EXE must be defined")
endif()
cmake_path(ABSOLUTE_PATH SWIFTWINRT_EXE NORMALIZE)

# Generate the WinRTComponent.winmd file if not previously done
if("${WINRTCOMPONENT_WINMD}" STREQUAL "")
set(WINRTCOMPONENT_WINMD "${CMAKE_CURRENT_BINARY_DIR}/WinRTComponent.winmd")
include(GenerateWinMD.cmake)
generate_winrtcomponent_winmd(
IDL "${CMAKE_CURRENT_SOURCE_DIR}/WinRTComponent.idl"
WINMD "${WINRTCOMPONENT_WINMD}")
if(NOT DEFINED PROJECTION_DIR)
set(PROJECTION_DIR "${CMAKE_CURRENT_BINARY_DIR}/Projection/Sources")
endif()

# Generate the C++/WinRT boilerplate
message(STATUS "Generating C++/WinRT boilerplate for WinRTComponent...")
set(CPPWINRT_EXE_NATIVE "$ENV{WindowsSdkVerBinPath}$ENV{VSCMD_ARG_HOST_ARCH}\\cppwinrt.exe")
cmake_path(CONVERT "${WINRTCOMPONENT_WINMD}" TO_NATIVE_PATH_LIST WINRTCOMPONENT_WINMD_NATIVE)
string(REPLACE "\\" "" WINSDK_VERSION "$ENV{WindowsSDKVersion}")
set(CPPWINRT_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/CppWinRT")
cmake_path(CONVERT "${CPPWINRT_GENERATED_DIR}" TO_NATIVE_PATH_LIST CPPWINRT_GENERATED_DIR_NATIVE)
execute_process(
COMMAND "${CPPWINRT_EXE_NATIVE}"
-input "${WINRTCOMPONENT_WINMD_NATIVE}"
-reference "${WINSDK_VERSION}"
-component "${CPPWINRT_GENERATED_DIR_NATIVE}" -overwrite -optimize
-output "${CPPWINRT_GENERATED_DIR_NATIVE}"
COMMAND_ERROR_IS_FATAL ANY)
# Generate the WinRTComponent.winmd file
set(WINRTCOMPONENT_WINMD "${CMAKE_CURRENT_BINARY_DIR}/WinRTComponent.winmd")
include(GenerateWinMD.cmake)
generate_winrtcomponent_winmd(
IDL "${CMAKE_CURRENT_SOURCE_DIR}/IDL/WinRTComponent.idl"
WINMD "${WINRTCOMPONENT_WINMD}")

# Generate Swift projection
message(STATUS "Generating Swift projection for WinRTComponent...")
include(GenerateProjection.cmake)
generate_projection(
SWIFTWINRT_EXE "${SWIFTWINRT_EXE}"
WINRTCOMPONENT_WINMD "${WINRTCOMPONENT_WINMD}"
PROJECTION_JSON "${CMAKE_CURRENT_SOURCE_DIR}/projection.json"
PROJECTION_DIR "${PROJECTION_DIR}")

# Define the dll build (requires cl.exe)
include(ExternalProject)
ExternalProject_Add(WinRTComponent
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Dll"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/Dll"
CMAKE_ARGS
-D "WINRTCOMPONENT_WINMD=${WINRTCOMPONENT_WINMD}"
-D "CMAKE_CXX_COMPILER=cl.exe"
-D "CMAKE_CXX_FLAGS=/std:c++latest /W4 /EHsc"
INSTALL_COMMAND ""
TEST_COMMAND "")

# Build WinRTComponent.dll
file(GLOB SOURCES "*.cpp")
add_library(WinRTComponent SHARED
${SOURCES}
"${CPPWINRT_GENERATED_DIR}/module.g.cpp" # Other .g.cpp files are #included by our .cpp files
WinRTComponent.def)
target_include_directories(WinRTComponent PRIVATE
"${CMAKE_CURRENT_SOURCES_DIR}"
"${CPPWINRT_GENERATED_DIR}")
target_precompile_headers(WinRTComponent PRIVATE pch.h)
# Define the projection build
# TODO(https://github.com/tristanlabelle/swift-winrt/issues/302): Fix linking issues
# add_subdirectory(
# ${CMAKE_CURRENT_BINARY_DIR}/Projection/Sources
# ${CMAKE_CURRENT_BINARY_DIR}/Projection/Build)
33 changes: 33 additions & 0 deletions Generator/InteropTests/WinRTComponent/Dll/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
if(NOT DEFINED WINRTCOMPONENT_WINMD)
message(FATAL_ERROR "WINRTCOMPONENT_WINMD must be defined")
endif()

# Generate the C++/WinRT boilerplate
message(STATUS "Generating C++/WinRT boilerplate for WinRTComponent...")
set(CPPWINRT_EXE_NATIVE "$ENV{WindowsSdkVerBinPath}$ENV{VSCMD_ARG_HOST_ARCH}\\cppwinrt.exe")
if(NOT EXISTS "${CPPWINRT_EXE_NATIVE}")
message(FATAL_ERROR "Could not find cppwinrt.exe at ${CPPWINRT_EXE_NATIVE}")
endif()

cmake_path(CONVERT "${WINRTCOMPONENT_WINMD}" TO_NATIVE_PATH_LIST WINRTCOMPONENT_WINMD_NATIVE)
string(REPLACE "\\" "" WINSDK_VERSION "$ENV{WindowsSDKVersion}")
set(CPPWINRT_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/CppWinRT")
cmake_path(CONVERT "${CPPWINRT_GENERATED_DIR}" TO_NATIVE_PATH_LIST CPPWINRT_GENERATED_DIR_NATIVE)
execute_process(
COMMAND "${CPPWINRT_EXE_NATIVE}"
-input "${WINRTCOMPONENT_WINMD_NATIVE}"
-reference "${WINSDK_VERSION}"
-component "${CPPWINRT_GENERATED_DIR_NATIVE}" -overwrite -optimize
-output "${CPPWINRT_GENERATED_DIR_NATIVE}"
COMMAND_ERROR_IS_FATAL ANY)

# Build WinRTComponent.dll
file(GLOB SOURCES "*.cpp")
add_library(WinRTComponent SHARED
${SOURCES}
"${CPPWINRT_GENERATED_DIR}/module.g.cpp" # Other .g.cpp files are #included by our .cpp files
WinRTComponent.def)
target_include_directories(WinRTComponent PRIVATE
"${CMAKE_CURRENT_SOURCES_DIR}"
"${CPPWINRT_GENERATED_DIR}")
target_precompile_headers(WinRTComponent PRIVATE pch.h)
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Invokes SwiftWinRT to generate a Swift projection for a WinRT component
# This file can be called as a CMake script.
function(generate_projection)
cmake_parse_arguments("ARG" "" "SWIFTWINRT_EXE;WINRTCOMPONENT_WINMD;PROJECTION_JSON;PROJECTION_DIR;SPM_SUPPORT_PACKAGE_DIR" "" ${ARGN})
cmake_parse_arguments("ARG" "" "SWIFTWINRT_EXE;WINRTCOMPONENT_WINMD;PROJECTION_JSON;PROJECTION_DIR" "" ${ARGN})

if ("${ARG_SWIFTWINRT_EXE}" STREQUAL "")
message(FATAL_ERROR "SWIFTWINRT_EXE argument is required")
Expand All @@ -19,17 +19,17 @@ function(generate_projection)
message(FATAL_ERROR "PROJECTION_DIR argument is required")
endif()

if("${ARG_SPM_SUPPORT_PACKAGE_DIR}" STREQUAL "")
message(FATAL_ERROR "SPM_SUPPORT_PACKAGE_DIR argument is required")
endif()

# SPM won't handle an absolute path with a ".." component.
cmake_path(ABSOLUTE_PATH ARG_SPM_SUPPORT_PACKAGE_DIR NORMALIZE)
# Determine the support package directory
execute_process(
COMMAND git.exe -C "${CMAKE_CURRENT_SOURCE_DIR}" rev-parse --path-format=absolute --show-toplevel
OUTPUT_VARIABLE REPO_ROOT
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)

cmake_path(CONVERT "${ARG_PROJECTION_JSON}" TO_NATIVE_PATH_LIST PROJECTION_JSON_NATIVE)
string(REPLACE "\\" "" WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}") # Remove trailing slash
cmake_path(CONVERT "${ARG_WINRTCOMPONENT_WINMD}" TO_NATIVE_PATH_LIST WINRTCOMPONENT_WINMD_NATIVE)
cmake_path(CONVERT "${ARG_SPM_SUPPORT_PACKAGE_DIR}" TO_NATIVE_PATH_LIST SPM_SUPPORT_PACKAGE_DIR_NATIVE)
cmake_path(CONVERT "${REPO_ROOT}" TO_NATIVE_PATH_LIST SPM_SUPPORT_PACKAGE_DIR_NATIVE)
cmake_path(CONVERT "${ARG_PROJECTION_DIR}" TO_NATIVE_PATH_LIST PROJECTION_DIR_NATIVE)
execute_process(
COMMAND "${ARG_SWIFTWINRT_EXE}"
Expand All @@ -50,6 +50,5 @@ if(CMAKE_SCRIPT_MODE_FILE AND NOT CMAKE_PARENT_LIST_FILE)
SWIFTWINRT_EXE "${SWIFTWINRT_EXE}"
WINRTCOMPONENT_WINMD "${WINRTCOMPONENT_WINMD}"
PROJECTION_JSON "${PROJECTION_JSON}"
PROJECTION_DIR "${PROJECTION_DIR}"
SPM_SUPPORT_PACKAGE_DIR "${SPM_SUPPORT_PACKAGE_DIR}")
PROJECTION_DIR "${PROJECTION_DIR}")
endif()
4 changes: 0 additions & 4 deletions Generator/InteropTests/WinRTComponent/packages.config

This file was deleted.

File renamed without changes.

0 comments on commit 025ea5d

Please sign in to comment.