diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 75abc440a..ba36f8995 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -108,3 +108,160 @@ jobs:
${{ runner.temp }}/${{ env.BASELINE_FILE }}
${{ runner.temp }}/${{ env.COMPARE_FILE }}
${{ runner.temp }}/${{ env.COMPARE_OUT_FILE }}
+ crossrepo:
+ name: Cross-Repo Validation
+ runs-on: ubuntu-latest
+ container:
+ image: swift:6.3-jammy
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v7
+ - name: Install apt dependencies
+ run: apt-get -qq update && apt-get -qq -y install curl ninja-build
+ - name: Install CMake 3.30.2
+ run: |
+ ARCH=`uname -m`
+ curl -sL https://github.com/Kitware/CMake/releases/download/v3.30.2/cmake-3.30.2-linux-$ARCH.tar.gz -O
+ tar xzf cmake-3.30.2-linux-$ARCH.tar.gz
+ export PATH="`pwd`/cmake-3.30.2-linux-$ARCH/bin:$PATH"
+ echo "PATH=$PATH" >> $GITHUB_ENV
+ cmake --version
+ - name: Compare CMakeLists.txt against swiftlang/swift/Runtimes/Supplemental
+ id: compare
+ env:
+ UPSTREAM_DIR: https://raw.githubusercontent.com/swiftlang/swift/refs/heads/main/Runtimes/Supplemental/StringProcessing
+ MODULES: _StringProcessing _RegexParser
+ shell: bash
+ run: |
+ set -u
+
+ # `-f` so a file moving upstream reports a fetch error rather than
+ # diffing against a 404 page.
+ for module in $MODULES; do
+ if ! curl -fsSL "$UPSTREAM_DIR/$module/CMakeLists.txt" \
+ -o "upstream-$module-CMakeLists.txt"; then
+ echo "result=fetch-failed" >> "$GITHUB_OUTPUT"
+ echo "Could not fetch $module/CMakeLists.txt from swiftlang/swift."
+ exit 1
+ fi
+ done
+
+ : > "$RUNNER_TEMP/cmake-diff.txt"
+ status=0
+ for module in $MODULES; do
+ if ! diff -u \
+ --label "swiftlang/swift: Runtimes/Supplemental/StringProcessing/$module/CMakeLists.txt" \
+ --label "this repo: Sources/$module/CMakeLists.txt" \
+ "upstream-$module-CMakeLists.txt" \
+ "Sources/$module/CMakeLists.txt" \
+ >> "$RUNNER_TEMP/cmake-diff.txt"; then
+ status=1
+ fi
+ done
+
+ if [ "$status" -eq 0 ]; then
+ echo "result=match" >> "$GITHUB_OUTPUT"
+ echo "Both CMakeLists.txt files match swiftlang/swift."
+ else
+ echo "result=differs" >> "$GITHUB_OUTPUT"
+ cat "$RUNNER_TEMP/cmake-diff.txt"
+ fi
+ exit "$status"
+ - name: Configure CMake
+ id: configure
+ if: ${{ !cancelled() }}
+ run: cmake -G Ninja -S Sources -B build -DCMAKE_BUILD_TYPE=Release
+ - name: Build
+ id: build
+ if: ${{ !cancelled() }}
+ run: cmake --build build
+ - name: Verdict
+ if: ${{ always() }}
+ env:
+ COMPARE_OUTCOME: ${{ steps.compare.outcome }}
+ COMPARE_RESULT: ${{ steps.compare.outputs.result }}
+ CONFIGURE_OUTCOME: ${{ steps.configure.outcome }}
+ BUILD_OUTCOME: ${{ steps.build.outcome }}
+ shell: bash
+ run: |
+ set -u
+
+ # Report to the step log and to the job summary page.
+ emit() { printf '%s\n' "$@" | tee -a "$GITHUB_STEP_SUMMARY"; }
+
+ failed=0
+
+ # --- the CMake build ---
+ if [ "$CONFIGURE_OUTCOME" = failure ] || [ "$BUILD_OUTCOME" = failure ]; then
+ failed=1
+ if [ "$CONFIGURE_OUTCOME" = failure ]; then
+ stage='CMake configuration'
+ else
+ stage='CMake build'
+ fi
+ emit \
+ "## :x: $stage failed" \
+ '' \
+ 'The CMake build of `_RegexParser` and `_StringProcessing` is broken.' \
+ "See the \"$stage\" step above for the output." \
+ ''
+ fi
+
+ # --- the comparison against swiftlang/swift ---
+ case "${COMPARE_RESULT:-}" in
+ match)
+ ;;
+ differs)
+ failed=1
+ emit \
+ '## :warning: `CMakeLists.txt` differs from swiftlang/swift' \
+ '' \
+ 'Diff to apply upstream
' \
+ '' \
+ '```diff'
+ tee -a "$GITHUB_STEP_SUMMARY" < "$RUNNER_TEMP/cmake-diff.txt"
+ emit '```' '' ' ' ''
+ ;;
+ fetch-failed)
+ failed=1
+ emit \
+ '## :x: Could not fetch the upstream `CMakeLists.txt` files'
+ ;;
+ *)
+ failed=1
+ emit \
+ "## :x: Comparison did not complete (outcome: ${COMPARE_OUTCOME:-unknown})"
+ ;;
+ esac
+
+ # --- all clear ---
+ if [ "$failed" -eq 0 ]; then
+ emit \
+ '## :white_check_mark: Cross-repo validation passed'
+ fi
+
+ exit "$failed"
+ required:
+ name: Required
+ runs-on: ubuntu-latest
+ needs:
+ - tests
+ - soundness
+ - bench
+ if: always() # Runs even if previous jobs fail so it can evaluate them
+ steps:
+ - name: Install required tools
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends coreutils jq
+ - name: Check upstream job statuses
+ env:
+ NEEDS_JSON: ${{ toJSON(needs) }}
+ run: |
+ failed=$(printf '%s' "$NEEDS_JSON" | jq -r 'to_entries | map(select(.value.result != "success")) | .[] | "\(.key): \(.value.result)"')
+ if [ -n "$failed" ]; then
+ echo "The following required jobs did not succeed:"
+ printf '%s\n' "$failed"
+ exit 1
+ fi
+ echo "All required jobs succeeded."
diff --git a/Package.swift b/Package.swift
index 1ca2a7400..97186bc61 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,6 +1,4 @@
// swift-tools-version:6.0
-// The swift-tools-version declares the minimum version of Swift required to build this package.
-
import PackageDescription
let availabilityDefinition = PackageDescription.SwiftSetting.unsafeFlags([
@@ -46,7 +44,6 @@ let publicStdlibSettings: [PackageDescription.SwiftSetting] = [
let package = Package(
name: "swift-experimental-string-processing",
products: [
- // Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "_StringProcessing",
targets: ["_StringProcessing"]),
@@ -69,11 +66,10 @@ let package = Package(
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
],
targets: [
- // Targets are the basic building blocks of a package. A target can define a module or a test suite.
- // Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "_RegexParser",
dependencies: [],
+ exclude: ["CMakeLists.txt"],
packageAccess: false,
swiftSettings: privateStdlibSettings),
.testTarget(
@@ -90,6 +86,7 @@ let package = Package(
"_RegexParser",
"_CUnicode",
],
+ exclude: ["CMakeLists.txt", "_CUnicode"],
packageAccess: false,
swiftSettings: publicStdlibSettings),
.target(
diff --git a/Sources/CMakeLists.txt b/Sources/CMakeLists.txt
new file mode 100644
index 000000000..03199afb0
--- /dev/null
+++ b/Sources/CMakeLists.txt
@@ -0,0 +1,92 @@
+#===-----------------------------------------------------------------------===#
+#
+# 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
+#
+#===-----------------------------------------------------------------------===#
+
+# NOTE: This build configuration is only to validate that the settings
+# match between the swiftlang/swift project and this one, not for
+# producing installable libraries.
+
+cmake_minimum_required(VERSION 3.26)
+
+project(SwiftStringProcessing
+ VERSION 1.0.0
+ LANGUAGES C Swift)
+
+include(GNUInstallDirs)
+
+set(CMAKE_Swift_LANGUAGE_VERSION 5)
+set(CMAKE_C_STANDARD 99)
+
+option(BUILD_SHARED_LIBS "Build shared libraries" YES)
+
+set(SwiftStringProcessing_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/swift"
+ CACHE STRING "Destination for the built libraries")
+
+#===-----------------------------------------------------------------------===#
+# Compiler flags
+#===-----------------------------------------------------------------------===#
+
+add_compile_options(
+ "$<$:SHELL:-Xfrontend -disable-implicit-concurrency-module-import>"
+ "$<$:SHELL:-Xfrontend -disable-implicit-string-processing-module-import>")
+
+foreach(definition
+ "SwiftStdlib 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0"
+ "SwiftStdlib 5.8:macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4"
+ "SwiftStdlib 5.9:macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0"
+ "SwiftStdlib 6.0:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"
+ "SwiftStdlib 6.1:macOS 15.4, iOS 18.4, watchOS 11.4, tvOS 18.4, visionOS 2.4"
+ "SwiftStdlib 6.2:macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0"
+ "SwiftStdlib 6.3:macOS 26.4, iOS 26.4, watchOS 26.4, tvOS 26.4, visionOS 26.4"
+ "SwiftStdlib 6.4:macOS 27.0, iOS 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0"
+ "SwiftStdlib 6.5:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, visionOS 9999")
+ add_compile_options(
+ "$<$:SHELL:-Xfrontend -define-availability -Xfrontend \"${definition}\">")
+endforeach()
+
+add_compile_options(
+ "$<$:SHELL:-enable-upcoming-feature MemberImportVisibility>")
+
+#===-----------------------------------------------------------------------===#
+# Shims for the runtime build
+#===-----------------------------------------------------------------------===#
+#
+# The module files call into helpers that only exist in the Swift runtime's
+# build. Define just enough here for them to resolve. Each shim below is a no-op,
+# so this configuration builds and links the two modules but does *not* produce
+# an installable stdlib component.
+
+# In the runtime build `swiftCore` is a real target. Standalone, the stdlib
+# comes from the toolchain and needs no explicit link.
+if(NOT TARGET swiftCore)
+ add_library(swiftCore INTERFACE)
+endif()
+
+# Library evolution and `.swiftinterface` emission.
+function(emit_swift_interface target)
+endfunction()
+
+function(install_swift_interface target)
+endfunction()
+
+# Darwin packaging metadata, irrelevant to a standalone build.
+function(generate_plist name version target)
+endfunction()
+
+function(embed_manifest target)
+endfunction()
+
+#===-----------------------------------------------------------------------===#
+# Modules
+#===-----------------------------------------------------------------------===#
+
+# Order matters: _StringProcessing links _RegexParser.
+add_subdirectory(_RegexParser)
+add_subdirectory(_StringProcessing)
diff --git a/Sources/_RegexParser/CMakeLists.txt b/Sources/_RegexParser/CMakeLists.txt
new file mode 100644
index 000000000..a3bad7850
--- /dev/null
+++ b/Sources/_RegexParser/CMakeLists.txt
@@ -0,0 +1,49 @@
+add_library(swift_RegexParser
+ Regex/TreeProtocols.swift
+ Regex/Parse/DelimiterLexing.swift
+ Regex/Parse/Source.swift
+ Regex/Parse/CharacterPropertyClassification.swift
+ Regex/Parse/LexicalAnalysis.swift
+ Regex/Parse/SyntaxOptions.swift
+ Regex/Parse/CaptureList.swift
+ Regex/Parse/CompilerInterface.swift
+ Regex/Parse/CaptureStructure.swift
+ Regex/Parse/Parse.swift
+ Regex/Parse/SourceLocation.swift
+ Regex/Parse/Diagnostics.swift
+ Regex/Parse/Sema.swift
+ Regex/Printing/RenderRanges.swift
+ Regex/Printing/PrettyPrinter.swift
+ Regex/Printing/PrintAsCanonical.swift
+ Regex/Printing/DumpAST.swift
+ Regex/AST/ASTAction.swift
+ Regex/AST/AST.swift
+ Regex/AST/CustomCharClass.swift
+ Regex/AST/Quantification.swift
+ Regex/AST/Conditional.swift
+ Regex/AST/MatchingOptions.swift
+ Regex/AST/ASTProtocols.swift
+ Regex/AST/Group.swift
+ Regex/AST/Atom.swift
+ Utility/TypeConstruction.swift
+ Utility/Misc.swift
+ Utility/AllScalars.swift
+ Utility/Errors.swift
+ Utility/MissingUnicode.swift)
+
+target_link_libraries(swift_RegexParser PRIVATE
+ swiftCore
+ $<$:swiftSwiftDirectRuntime>)
+
+set_target_properties(swift_RegexParser PROPERTIES
+ Swift_MODULE_NAME _RegexParser)
+
+install(TARGETS swift_RegexParser
+ ARCHIVE DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+emit_swift_interface(swift_RegexParser)
+install_swift_interface(swift_RegexParser)
+
+generate_plist(swiftRegexParser "${CMAKE_PROJECT_VERSION}" swift_RegexParser)
+embed_manifest(swift_RegexParser)
diff --git a/Sources/_StringProcessing/CMakeLists.txt b/Sources/_StringProcessing/CMakeLists.txt
new file mode 100644
index 000000000..56914d90b
--- /dev/null
+++ b/Sources/_StringProcessing/CMakeLists.txt
@@ -0,0 +1,96 @@
+add_library(swift_StringProcessing
+ _CUnicode/UnicodeData.c
+ _CUnicode/UnicodeScalarProps.c
+ _CharacterClassModel.swift
+ Executor.swift
+ Capture.swift
+ Unicode/NumberParsing.swift
+ Unicode/ASCII.swift
+ Unicode/CharacterProps.swift
+ Unicode/Transcoding.swift
+ Unicode/ScalarProps.swift
+ Unicode/Comparison.swift
+ Unicode/NecessaryEvils.swift
+ Unicode/Validation.swift
+ Unicode/UCD.swift
+ Unicode/NFC.swift
+ Unicode/Encodings.swift
+ Unicode/Formatting.swift
+ Unicode/WordBreaking.swift
+ Unicode/Decoding.swift
+ Unicode/CaseConversion.swift
+ Algorithms/Matching/FirstMatch.swift
+ Algorithms/Matching/MatchingCollectionSearcher.swift
+ Algorithms/Matching/MatchResult.swift
+ Algorithms/Matching/MatchReplace.swift
+ Algorithms/Matching/Matches.swift
+ Algorithms/Algorithms/FirstRange.swift
+ Algorithms/Algorithms/Contains.swift
+ Algorithms/Algorithms/Split.swift
+ Algorithms/Algorithms/Trim.swift
+ Algorithms/Algorithms/Ranges.swift
+ Algorithms/Algorithms/StartsWith.swift
+ Algorithms/Algorithms/Replace.swift
+ Algorithms/Algorithms/SubstringSearcher.swift
+ Algorithms/Searchers/CollectionSearcher.swift
+ Algorithms/Searchers/ZSearcher.swift
+ Algorithms/Consumers/CollectionConsumer.swift
+ Algorithms/Consumers/FixedPatternConsumer.swift
+ Regex/Options.swift
+ Regex/Match.swift
+ Regex/CustomComponents.swift
+ Regex/Core.swift
+ Regex/ASTConversion.swift
+ Regex/DSLTree.swift
+ Regex/DSLList.swift
+ Regex/AnyRegexOutput.swift
+ LiteralPrinter.swift
+ MatchingOptions.swift
+ Compiler.swift
+ ConsumerInterface.swift
+ PrintAsPattern.swift
+ Engine/MEBuiltins.swift
+ Engine/Processor.swift
+ Engine/MEProgram.swift
+ Engine/MEQuantify.swift
+ Engine/InstPayload.swift
+ Engine/Backtracking.swift
+ Engine/Instruction.swift
+ Engine/Registers.swift
+ Engine/MEBuilder.swift
+ Engine/Structuralize.swift
+ Engine/Tracing.swift
+ Engine/Metrics.swift
+ Engine/MECapture.swift
+ ByteCodeGen.swift
+ ByteCodeGen+DSLList.swift
+ Optimizations/AutoPossessification.swift
+ Utility/AsciiBitset.swift
+ Utility/AtomicCounter.swift
+ Utility/Traced.swift
+ Utility/TypedIndex.swift
+ Utility/Misc.swift
+ Utility/TypedInt.swift
+ Utility/Protocols.swift
+ Utility/TypeVerification.swift
+ Utility/RegexFactory.swift
+ Utility/UndoableArray.swift
+ Utility/ASTBuilder.swift)
+
+set_target_properties(swift_StringProcessing PROPERTIES
+ Swift_MODULE_NAME _StringProcessing)
+
+target_link_libraries(swift_StringProcessing PRIVATE
+ swift_RegexParser
+ swiftCore
+ $<$:swiftSwiftDirectRuntime>)
+
+install(TARGETS swift_StringProcessing
+ ARCHIVE DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${SwiftStringProcessing_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+emit_swift_interface(swift_StringProcessing)
+install_swift_interface(swift_StringProcessing)
+
+generate_plist(swiftStringProcessing "${CMAKE_PROJECT_VERSION}" swift_StringProcessing)
+embed_manifest(swift_StringProcessing)
diff --git a/Sources/_StringProcessing/_CUnicode b/Sources/_StringProcessing/_CUnicode
new file mode 120000
index 000000000..efcc8b167
--- /dev/null
+++ b/Sources/_StringProcessing/_CUnicode
@@ -0,0 +1 @@
+../_CUnicode
\ No newline at end of file