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
15 changes: 7 additions & 8 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 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
Expand Down Expand Up @@ -722,13 +722,12 @@ void importer::getNormalInvocationArguments(
1})); // b
}
} else {
// Ideally we should turn this on for all Glibc targets that are actually
// using Glibc or a libc that respects that flag. This will cause some
// source breakage however (specifically with strerror_r()) on Linux
// without a workaround.
if (triple.isOSFuchsia() || triple.isAndroid() || triple.isMusl()) {
// Many of the modern libc features are hidden behind feature macros like
// _GNU_SOURCE or _XOPEN_SOURCE.
// Many of the modern libc features are hidden behind feature macros like
// _GNU_SOURCE or _XOPEN_SOURCE. Clang already predefines _GNU_SOURCE for
// these targets when the language is C++, so we define it unconditionally
// to minimize libc differences with and without C++ interop.
if (triple.isOSFuchsia() || triple.isAndroid() || triple.isMusl() ||
triple.isOSGlibc()) {
llvm::append_values(invocationArgStrs, "-D_GNU_SOURCE");
}

Expand Down
1 change: 0 additions & 1 deletion stdlib/private/SwiftReflectionTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ if (SWIFT_INCLUDE_TESTS AND SWIFT_BUILD_DYNAMIC_STDLIB)
add_swift_target_library(swiftSwiftReflectionTest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
SwiftReflectionTest.swift
SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
SWIFT_COMPILE_FLAGS_LINUX -Xcc -D_GNU_SOURCE
SWIFT_MODULE_DEPENDS_OSX ${swift_reflection_test_darwin_dependencies}
SWIFT_MODULE_DEPENDS_IOS ${swift_reflection_test_darwin_dependencies}
SWIFT_MODULE_DEPENDS_TVOS ${swift_reflection_test_darwin_dependencies}
Expand Down
32 changes: 32 additions & 0 deletions stdlib/public/Platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,38 @@ foreach(sdk ${SWIFT_SDKS})
endif()
endforeach()
endforeach()

# Put the API notes file in place.
if("LINUX" IN_LIST SWIFT_SDKS OR "FREEBSD" IN_LIST SWIFT_SDKS OR
"OPENBSD" IN_LIST SWIFT_SDKS OR "CYGWIN" IN_LIST SWIFT_SDKS OR
"HAIKU" IN_LIST SWIFT_SDKS)
set(glibc_apinotes_source "SwiftGlibc.apinotes")
add_custom_command_target(
copy_glibc_apinotes_resource
COMMAND
"${CMAKE_COMMAND}" "-E" "make_directory" ${SWIFTLIB_DIR}/apinotes ${SWIFTSTATICLIB_DIR}/apinotes
COMMAND
"${CMAKE_COMMAND}" "-E" "copy_if_different"
"${CMAKE_CURRENT_SOURCE_DIR}/${glibc_apinotes_source}" ${SWIFTLIB_DIR}/apinotes
COMMAND
"${CMAKE_COMMAND}" "-E" "copy_if_different"
"${CMAKE_CURRENT_SOURCE_DIR}/${glibc_apinotes_source}" ${SWIFTSTATICLIB_DIR}/apinotes
OUTPUT
${SWIFTLIB_DIR}/apinotes/${glibc_apinotes_source}
${SWIFTSTATICLIB_DIR}/apinotes/${glibc_apinotes_source}
COMMENT "Copying SwiftGlibc API notes to resource directories")

list(APPEND glibc_modulemap_target_list ${copy_glibc_apinotes_resource})
swift_install_in_component(FILES "${glibc_apinotes_source}"
DESTINATION "lib/swift/apinotes"
COMPONENT sdk-overlay)
if(SWIFT_BUILD_STATIC_STDLIB)
swift_install_in_component(FILES "${glibc_apinotes_source}"
DESTINATION "lib/swift_static/apinotes"
COMPONENT sdk-overlay)
endif()
endif()

add_custom_target(glibc_modulemap DEPENDS ${glibc_modulemap_target_list})
set_property(TARGET glibc_modulemap PROPERTY FOLDER "Miscellaneous")
add_dependencies(sdk-overlay glibc_modulemap)
Expand Down
89 changes: 88 additions & 1 deletion stdlib/public/Platform/Glibc.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 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
Expand Down Expand Up @@ -83,3 +83,90 @@ nonisolated(unsafe) public var stderr: UnsafeMutablePointer<FILE>! {
_swift_stdlib_stderr().assumingMemoryBound(to: FILE.self)
}
#endif

#if os(Linux)
// Glibc spells 'fd_set''s property as 'fds_bits' when '_XOPEN_SOURCE' is
// defined, or as '__fds_bits' otherwise. Allow code written before we defined
// '_GNU_SOURCE' to refer to the latter.
%{
import textwrap
# <sys/select.h> declares the bit set as '__fd_mask fds_bits[1024 / N]',
# where '__fd_mask' is a 'long' and N is the bit width of 'long'.
FD_SET_BITS = 1024
widths = [(bits, FD_SET_BITS // bits) for bits in (64, 32)]
}%
// We must qualify which version of `fd_set` is being extended.
extension SwiftGlibc.fd_set {
% for bits, count in widths:
#if _pointerBitWidth(_${bits})
@available(swift, deprecated: 7, renamed: "fds_bits")
@export(implementation)
public var __fds_bits: (
${textwrap.fill(', '.join(['Int'] * count), width = 74,
initial_indent = ' ', subsequent_indent = ' ')}
) {
get { fds_bits }
set { fds_bits = newValue }
}
#endif
% end
}
#endif // os(Linux)

#if os(Linux)
// The first parameter of these functions is 'int' in Glibc, unless
// '__USE_GNU' is defined, when it is an enumeration instead. Swift can't
// promote the int parameters to the enumeration, so we add overloads
// with the type signature expected by existing callers.
@_alwaysEmitIntoClient
public func getitimer(
_ which: CInt, _ value: UnsafeMutablePointer<itimerval>!
) -> CInt {
SwiftGlibc.getitimer(
__itimer_which(rawValue: UInt32(bitPattern: which)), value)
}

@_alwaysEmitIntoClient
public func setitimer(
_ which: CInt, _ new: UnsafePointer<itimerval>!,
_ old: UnsafeMutablePointer<itimerval>!
) -> CInt {
SwiftGlibc.setitimer(
__itimer_which(rawValue: UInt32(bitPattern: which)), new, old)
}

@_alwaysEmitIntoClient
public func getpriority(_ which: CInt, _ who: id_t) -> CInt {
SwiftGlibc.getpriority(
__priority_which(rawValue: UInt32(bitPattern: which)), who)
}

@_alwaysEmitIntoClient
public func setpriority(_ which: CInt, _ who: id_t, _ priority: CInt) -> CInt {
SwiftGlibc.setpriority(
__priority_which(rawValue: UInt32(bitPattern: which)), who, priority)
}

@_alwaysEmitIntoClient
public func getrlimit(
_ resource: CInt, _ rlimits: UnsafeMutablePointer<rlimit>
) -> CInt {
SwiftGlibc.getrlimit(
__rlimit_resource(rawValue: UInt32(bitPattern: resource)), rlimits)
}

@_alwaysEmitIntoClient
public func setrlimit(
_ resource: CInt, _ rlimits: UnsafePointer<rlimit>
) -> CInt {
SwiftGlibc.setrlimit(
__rlimit_resource(rawValue: UInt32(bitPattern: resource)), rlimits)
}

@_alwaysEmitIntoClient
public func getrusage(
_ who: CInt, _ usage: UnsafeMutablePointer<rusage>!
) -> CInt {
SwiftGlibc.getrusage(__rusage_who(rawValue: who), usage)
}
#endif // os(Linux)
38 changes: 38 additions & 0 deletions stdlib/public/Platform/SwiftGlibc.apinotes
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
Name: SwiftGlibc
Functions:
# Clang's ABI passes a transparent union as its first member. The following
# rewrites the union parameter type as the first member type, so that
# these functions import as they did before we defined '_GNU_SOURCE'.
- Name: accept
Parameters:
- Position: 1
Type: "struct sockaddr * _Null_unspecified"
- Name: accept4
Parameters:
- Position: 1
Type: "struct sockaddr * _Null_unspecified"
- Name: bind
Parameters:
- Position: 1
Type: "const struct sockaddr * _Null_unspecified"
- Name: connect
Parameters:
- Position: 1
Type: "const struct sockaddr * _Null_unspecified"
- Name: getpeername
Parameters:
- Position: 1
Type: "struct sockaddr * _Null_unspecified"
- Name: getsockname
Parameters:
- Position: 1
Type: "struct sockaddr * _Null_unspecified"
- Name: recvfrom
Parameters:
- Position: 4
Type: "struct sockaddr * _Null_unspecified"
- Name: sendto
Parameters:
- Position: 4
Type: "const struct sockaddr * _Null_unspecified"
42 changes: 0 additions & 42 deletions stdlib/public/RuntimeModule/modules/OS/Linux.h

This file was deleted.

14 changes: 14 additions & 0 deletions test/ClangImporter/linux-sdk-macros.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Platforms whose libc hides modern interfaces behind feature macros get
// _GNU_SOURCE defined for the importer.
// RUN: %swift -target x86_64-unknown-linux-gnu -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE
// RUN: %swift -target x86_64-swift-linux-musl -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE
// RUN: %swift -target aarch64-unknown-linux-android28 -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE

// Clang predefines _GNU_SOURCE for C++, so the two modes agree.
// RUN: %swift -target x86_64-unknown-linux-gnu -typecheck %s -parse-stdlib -cxx-interoperability-mode=default -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE

// Platforms that don't use glibc shouldn't be affected.
// RUN: %swift -target x86_64-unknown-freebsd -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-NO-GNU-SOURCE

// CHECK-GNU-SOURCE: -D_GNU_SOURCE
// CHECK-NO-GNU-SOURCE-NOT: -D_GNU_SOURCE
60 changes: 60 additions & 0 deletions validation-test/stdlib/Glibc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,64 @@ GlibcIoctlConstants.test("tty ioctl constants availability") {
let aConstant = TIOCSTI
}


// Test spellings introduced when '_GNU_SOURCE' is defined.
var GlibcGNUSource = TestSuite("GlibcGNUSource")

GlibcGNUSource.test("accept4 address parameter") {
let accept4Signature: (
CInt, UnsafeMutablePointer<sockaddr>?, UnsafeMutablePointer<socklen_t>?, CInt
) -> CInt
accept4Signature = accept4
}

GlibcGNUSource.test("getrlimit accepts CInt") {
var rl = rlimit()

expectEqual(0, getrlimit(__rlimit_resource_t(RLIMIT_CORE.rawValue), &rl))
// use the compatibility overload:
expectEqual(0, getrlimit(CInt(RLIMIT_CORE.rawValue), &rl))
}

GlibcGNUSource.test("getrusage accepts a negative CInt") {
var ru = rusage()

expectEqual(0, getrusage(__rusage_who_t(RUSAGE_CHILDREN.rawValue), &ru))
// use the compatibility overload:
expectEqual(0, getrusage(CInt(RUSAGE_CHILDREN.rawValue), &ru))
}

GlibcGNUSource.test("getpriority accepts CInt") {
errno = 0
_ = getpriority(__priority_which_t(PRIO_PROCESS.rawValue), 0)
expectEqual(0, errno)

// use the compatibility overload:
errno = 0
_ = getpriority(CInt(PRIO_PROCESS.rawValue), 0)
expectEqual(0, errno)
}

GlibcGNUSource.test("getitimer accepts CInt") {
var itv = itimerval()

expectEqual(0, getitimer(__itimer_which_t(ITIMER_REAL.rawValue), &itv))
// use the compatibility overload:
expectEqual(0, getitimer(CInt(ITIMER_REAL.rawValue), &itv))
}

GlibcGNUSource.test("fd_set __fds_bits") {
var set = SwiftGlibc.fd_set()
// .fds_bits is the property name when _GNU_SOURCE is defined
set.fds_bits.3 = 42

// .__fds_bits is the property name without _GNU_SOURCE,
// made available with an extension in the Glibc overlay.
expectTrue(type(of: set.__fds_bits) == type(of: set.fds_bits))
expectEqual(42, set.__fds_bits.3)

set.__fds_bits.7 = 99
expectEqual(99, set.fds_bits.7)
}

runAllTests()
Loading