Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encapsulate the WinRTComponent build #316

Merged
merged 9 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
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.

Loading