Skip to content

Repair the Windows SwiftPM build #5068

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

Merged
merged 2 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
70 changes: 59 additions & 11 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ let platformsWithThreads: [Platform] = [
.linux,
.windows,
]
var dispatchIncludeFlags: [CSetting]

var dispatchIncludeFlags: [CSetting] = []
if let environmentPath = Context.environment["DISPATCH_INCLUDE_PATH"] {
dispatchIncludeFlags = [.unsafeFlags([
dispatchIncludeFlags.append(.unsafeFlags([
"-I\(environmentPath)",
"-I\(environmentPath)/Block"
])]
]))
} else {
dispatchIncludeFlags = [
dispatchIncludeFlags.append(
.unsafeFlags([
"-I/usr/lib/swift",
"-I/usr/lib/swift/Block"
], .when(platforms: [.linux, .android]))
]
)
if let sdkRoot = Context.environment["SDKROOT"] {
dispatchIncludeFlags.append(.unsafeFlags([
"-I\(sdkRoot)usr\\include",
Expand All @@ -35,10 +36,55 @@ if let environmentPath = Context.environment["DISPATCH_INCLUDE_PATH"] {
}
}

var libxmlIncludeFlags: [CSetting] = []
if let environmentPath = Context.environment["LIBXML_INCLUDE_PATH"] {
libxmlIncludeFlags = [
.unsafeFlags([
"-I\(environmentPath)"
]),
.define("LIBXML_STATIC")
]
}

var curlIncludeFlags: [CSetting] = []
if let environmentPath = Context.environment["CURL_INCLUDE_PATH"] {
curlIncludeFlags = [
.unsafeFlags([
"-I\(environmentPath)"
]),
.define("CURL_STATICLIB")
]
}

var curlLinkFlags: [LinkerSetting] = [
.linkedLibrary("libcurl.lib", .when(platforms: [.windows])),
.linkedLibrary("zlibstatic.lib", .when(platforms: [.windows]))
]
if let environmentPath = Context.environment["CURL_LIBRARY_PATH"] {
curlLinkFlags.append(.unsafeFlags([
"-L\(environmentPath)"
]))
}
if let environmentPath = Context.environment["ZLIB_LIBRARY_PATH"] {
curlLinkFlags.append(.unsafeFlags([
"-L\(environmentPath)"
]))
}

var libxmlLinkFlags: [LinkerSetting] = [
.linkedLibrary("libxml2s.lib", .when(platforms: [.windows]))
]
if let environmentPath = Context.environment["LIBXML2_LIBRARY_PATH"] {
libxmlLinkFlags.append(.unsafeFlags([
"-L\(environmentPath)"
]))
}

let coreFoundationBuildSettings: [CSetting] = [
.headerSearchPath("internalInclude"),
.define("DEBUG", .when(configuration: .debug)),
.define("CF_BUILDING_CF"),
.define("CF_WINDOWS_EXECUTABLE_INITIALIZER", .when(platforms: [.windows])), // Ensure __CFInitialize is run even when statically linked into an executable
.define("DEPLOYMENT_ENABLE_LIBDISPATCH", .when(platforms: platformsWithThreads)),
.define("DEPLOYMENT_RUNTIME_SWIFT"),
.define("HAVE_STRUCT_TIMESPEC"),
Expand Down Expand Up @@ -216,25 +262,27 @@ let package = Package(
name: "_CFXMLInterface",
dependencies: [
"CoreFoundation",
"Clibxml2",
.target(name: "Clibxml2", condition: .when(platforms: [.linux])),
],
path: "Sources/_CFXMLInterface",
exclude: [
"CMakeLists.txt"
],
cSettings: interfaceBuildSettings
cSettings: interfaceBuildSettings + libxmlIncludeFlags,
linkerSettings: libxmlLinkFlags
),
.target(
name: "_CFURLSessionInterface",
dependencies: [
"CoreFoundation",
"Clibcurl",
.target(name: "Clibcurl", condition: .when(platforms: [.linux])),
],
path: "Sources/_CFURLSessionInterface",
exclude: [
"CMakeLists.txt"
],
cSettings: interfaceBuildSettings
cSettings: interfaceBuildSettings + curlIncludeFlags,
linkerSettings: curlLinkFlags
),
.systemLibrary(
name: "Clibxml2",
Expand Down Expand Up @@ -292,8 +340,8 @@ let package = Package(
"Foundation",
"FoundationXML",
"FoundationNetworking",
.targetItem(name: "XCTest", condition: .when(platforms: [.linux])),
"xdgTestHelper"
"XCTest",
.target(name: "xdgTestHelper", condition: .when(platforms: [.linux]))
],
resources: [
.copy("Foundation/Resources")
Expand Down
10 changes: 10 additions & 0 deletions Sources/CoreFoundation/CFRuntime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,16 @@ static CFBundleRef RegisterCoreFoundationBundle(void) {
#define DLL_THREAD_DETACH 3
#define DLL_PROCESS_DETACH 0

#if CF_WINDOWS_EXECUTABLE_INITIALIZER
static void __CFWindowsExecutableInitializer(void) __attribute__ ((constructor)) __attribute__ ((used));

void __CFWindowsExecutableInitializer(void) {
static CFBundleRef cfBundle = NULL;
__CFInitialize();
cfBundle = RegisterCoreFoundationBundle();
}
#endif

int DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) {
static CFBundleRef cfBundle = NULL;
if (dwReason == DLL_PROCESS_ATTACH) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/_CFURLSessionInterface/CFURLSessionInterface.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
///
//===----------------------------------------------------------------------===//

#include "CFURLSessionInterface.h"
#include "CFInternal.h"
#include "CFURLSessionInterface.h"
#include "CFString.h"
#include <curl/curl.h>

Expand Down
10 changes: 9 additions & 1 deletion Tests/Foundation/TestBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,16 @@ internal func testBundleName() -> String {
return testBundle().infoDictionary!["CFBundleName"] as! String
}

internal func xdgTestHelperURL() -> URL {
internal func xdgTestHelperURL() throws -> URL {
#if os(Windows)
// Adding the xdgTestHelper as a dependency of TestFoundation causes its object files (including the main function) to be linked into the test runner executable as well
// While this works on Linux due to special linker functionality, this doesn't work on Windows and results in a collision between the two main symbols
// SwiftPM also cannot support depending on this executable (to ensure it is built) without also linking its objects into the test runner
// For those reasons, using the xdgTestHelper on Windows is currently unsupported and tests that rely on it must be skipped
throw XCTSkip("xdgTestHelper is not supported during testing on Windows")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this was usable when we were running the tests through CMake. I think that the issue is limited to SPM which doesn't build the libraries and tries to link the executable object library into another executable. Can we clarify this to be unsupported when running with SPM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is a limitation of SwiftPM that I've brought up to some of the SwiftPM folks - I see we have a few GitHub issues tracking it. SwiftPM attempts to link the object files from the executable into the test runner, and while this happens to work on Linux on Windows the two main functions have conflicting symbols and linking fails.

Can we clarify this to be unsupported when running with SPM?

Is there something that you'd like me to add in addition to the comment that I added here? I think that comment should explain why SwiftPM cannot support this but I'm happy to add any details that you think are missing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update the message of the XCTSkip to call out that this is unsupported with SPM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, added in 595a4e9

#else
testBundle().bundleURL.deletingLastPathComponent().appendingPathComponent("xdgTestHelper")
#endif
}


Expand Down
2 changes: 1 addition & 1 deletion Tests/Foundation/TestFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ class TestFileManager : XCTestCase {
environment[entry.key] = entry.value
}

let helper = xdgTestHelperURL()
let helper = try xdgTestHelperURL()
let (stdout, _) = try runTask([ helper.path, "--nspathfor", method, identifier ],
environment: environment)

Expand Down
2 changes: 1 addition & 1 deletion Tests/Foundation/TestHTTPCookieStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ class TestHTTPCookieStorage: XCTestCase {

// Test by setting the environmental variable
let task = Process()
task.executableURL = xdgTestHelperURL()
task.executableURL = try xdgTestHelperURL()
task.arguments = ["--xdgcheck"]
var environment = ProcessInfo.processInfo.environment
let testPath = NSHomeDirectory() + "/TestXDG"
Expand Down
Loading