Skip to content
Draft
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
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ endif()
project(Foundation
LANGUAGES C Swift)

include(CTest)

option(FOUNDATION_SWIFTPM_DEPS "build Windows SwiftPM dependencies via CMake" NO)
if(FOUNDATION_SWIFTPM_DEPS)
include(WindowsSwiftPMDependencies)
Expand Down Expand Up @@ -249,4 +251,7 @@ include(GNUInstallDirs)
include(FoundationSwiftSupport)

add_subdirectory(Sources)
if(BUILD_TESTING)
add_subdirectory(Tests)
endif()
add_subdirectory(cmake/modules)
12 changes: 11 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,11 @@ let package = Package(
path: "Sources/CoreFoundation",
exclude: [
"BlockRuntime",
"CMakeLists.txt"
"CMakeLists.txt",
// SwiftPM does not support mixed C and Swift targets. The
// CoreFoundation Swift overlay is built by CMake as part of
// the toolchain build.
"CoreFoundation.swift"
],
cSettings: coreFoundationBuildSettings,
linkerSettings: [.linkedLibrary("log", .when(platforms: [.android]))]
Expand Down Expand Up @@ -339,6 +343,12 @@ let package = Package(
"Testing",
.target(name: "xdgTestHelper", condition: .when(platforms: [.linux, .android, .windows]))
],
exclude: [
"CMakeLists.txt",
// The CoreFoundation Swift overlay is built and tested by
// CMake because SwiftPM cannot build mixed C/Swift targets.
"CoreFoundation"
],
resources: [
.copy("Foundation/Resources")
],
Expand Down
41 changes: 41 additions & 0 deletions Sources/CoreFoundation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,32 @@ target_link_libraries(CoreFoundation
_FoundationICU
dispatch)

if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_library(CoreFoundationOverlay OBJECT
CoreFoundation.swift)
# The Clang importer looks specifically for the compiler-known protocol
# CoreFoundation._CFObject. The CMake target may have a distinct name, but
# the emitted Swift module must remain CoreFoundation for imported CF
# reference types to receive their synthesized conformance.
set_target_properties(CoreFoundationOverlay PROPERTIES
Swift_MODULE_NAME CoreFoundation)
target_include_directories(CoreFoundationOverlay INTERFACE
include)
target_compile_options(CoreFoundationOverlay
PRIVATE
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-swift-version 6>"
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -module-link-name -Xfrontend Foundation>"
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -fmodule-map-file=${CMAKE_CURRENT_SOURCE_DIR}/include/module.modulemap>"
INTERFACE
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -fmodule-map-file=${CMAKE_CURRENT_SOURCE_DIR}/include/module.modulemap>")
set_property(GLOBAL APPEND PROPERTY Foundation_EXPORTS
CoreFoundationOverlay)
if(NOT BUILD_SHARED_LIBS)
target_compile_options(CoreFoundationOverlay PRIVATE
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -public-autolink-library -Xfrontend CoreFoundation>")
endif()
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "WASI")
# On WASI, we use vendored BlocksRuntime instead of the one from libdispatch
add_subdirectory(BlockRuntime)
Expand All @@ -146,6 +172,21 @@ install(DIRECTORY
DESTINATION
lib/${swift}/CoreFoundation)

if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin")
install(FILES
$<TARGET_PROPERTY:CoreFoundationOverlay,Swift_MODULE_DIRECTORY>/CoreFoundation.swiftdoc
DESTINATION
lib/${swift}/${SWIFT_SYSTEM_NAME}/CoreFoundation.swiftmodule
RENAME
${SwiftFoundation_MODULE_TRIPLE}.swiftdoc)
install(FILES
$<TARGET_PROPERTY:CoreFoundationOverlay,Swift_MODULE_DIRECTORY>/CoreFoundation.swiftmodule
DESTINATION
lib/${swift}/${SWIFT_SYSTEM_NAME}/CoreFoundation.swiftmodule
RENAME
${SwiftFoundation_MODULE_TRIPLE}.swiftmodule)
endif()

if(NOT BUILD_SHARED_LIBS)
install(TARGETS CoreFoundation
ARCHIVE DESTINATION lib/swift_static/${SWIFT_SYSTEM_NAME}
Expand Down
29 changes: 29 additions & 0 deletions Sources/CoreFoundation/CoreFoundation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

@_exported import CoreFoundation

// The Clang importer synthesizes conformance to the compiler-known
// CoreFoundation._CFObject protocol for declarations it recognizes as Core
// Foundation reference types. Both the module and protocol names are required;
// the default witnesses below then satisfy Hashable for those imported types.
public protocol _CFObject: AnyObject, Hashable {}

extension _CFObject {
public var hashValue: Int {
Int(bitPattern: CFHash(self))
}

public func hash(into hasher: inout Hasher) {
hasher.combine(hashValue)
}

public static func == (lhs: Self, rhs: Self) -> Bool {
CFEqual(lhs, rhs)
}
}
5 changes: 5 additions & 0 deletions Sources/Foundation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ target_link_libraries(Foundation
FoundationEssentials
FoundationInternationalization)

if(TARGET CoreFoundationOverlay)
target_link_libraries(Foundation PRIVATE
CoreFoundationOverlay)
endif()

if(NOT BUILD_SHARED_LIBS)
target_compile_options(Foundation PRIVATE
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -public-autolink-library -Xfrontend CoreFoundation>")
Expand Down
33 changes: 33 additions & 0 deletions Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift open source project
##
## Copyright (c) 2026 Apple Inc. and the Swift project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.md for the list of Swift project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##

if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_executable(CoreFoundationHashableTests
CoreFoundation/TestCFObjectHashable.swift)

target_compile_options(CoreFoundationHashableTests PRIVATE
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-parse-as-library -swift-version 6>"
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -fmodule-map-file=${CMAKE_SOURCE_DIR}/Sources/CoreFoundation/include/module.modulemap>")
target_include_directories(CoreFoundationHashableTests PRIVATE
${CMAKE_SOURCE_DIR}/Sources/CoreFoundation/include)

target_link_libraries(CoreFoundationHashableTests PRIVATE
Foundation)

add_test(NAME CoreFoundation.Hashable
COMMAND CoreFoundationHashableTests)
set_property(TEST CoreFoundation.Hashable PROPERTY
ENVIRONMENT_MODIFICATION
"LD_LIBRARY_PATH=path_list_prepend:$<TARGET_FILE_DIR:Foundation>")
endif()
33 changes: 33 additions & 0 deletions Tests/CoreFoundation/TestCFObjectHashable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

import CoreFoundation

private func requireHashable<T: Hashable>(_: T.Type) {}

@main
struct TestCFObjectHashable {
static func main() {
requireHashable(CFDictionary.self)
requireHashable(CFMutableDictionary.self)
requireHashable(CFArray.self)
requireHashable(CFMutableArray.self)
requireHashable(CFSet.self)
requireHashable(CFMutableSet.self)
requireHashable(CFString.self)
requireHashable(CFMutableString.self)
requireHashable(CFData.self)
requireHashable(CFMutableData.self)

let lhs = CFDictionaryCreate(nil, nil, nil, 0, nil, nil)!
let rhs = CFDictionaryCreate(nil, nil, nil, 0, nil, nil)!

precondition(lhs == rhs)
precondition(Set([lhs, rhs]).count == 1)
}
}