├── Tests ├── TSCBasicTests │ ├── Inputs │ │ ├── empty_file │ │ └── regular_text_file │ ├── processInputs │ │ ├── exit4 │ │ ├── blocking │ │ ├── in-to-out │ │ ├── blocking-ignore-sigint │ │ ├── print-pid │ │ ├── simple-stdout-stderr │ │ ├── subprocess │ │ ├── long-stdout-stderr │ │ └── deadlock-if-blocking-io │ ├── CacheableSequenceTests.swift │ ├── SendableTests.swift │ ├── TupleTests.swift │ ├── CStringArrayTests.swift │ ├── CollectionAlgorithmsTests.swift │ ├── DictionaryExtensionsTests.swift │ ├── CollectionExtensionsTests.swift │ ├── OrderedDictionaryTests.swift │ ├── RegExTests.swift │ ├── AwaitTests.swift │ ├── LazyCacheTests.swift │ ├── EditDistanceTests.swift │ ├── KeyedPairTests.swift │ ├── DeltaAlgorithmTests.swift │ ├── ObjectIdentifierProtocolTests.swift │ ├── DictionaryLiteralExtensionsTests.swift │ ├── TerminalControllerTests.swift │ ├── Base64URLTests.swift │ ├── StringConversionsTests.swift │ ├── ThreadTests.swift │ ├── POSIXTests.swift │ ├── OrderedSetTests.swift │ ├── ResultTests.swift │ ├── PathShimTests.swift │ ├── ProcessEnvTests.swift │ ├── SHA256Tests.swift │ ├── ByteStringTests.swift │ ├── ConditionTests.swift │ ├── ProcessSetTests.swift │ ├── miscTests.swift │ ├── SortedArrayTests.swift │ ├── JSONTests.swift │ └── DiagnosticsEngineTests.swift ├── TSCUtilityTests │ ├── Inputs │ │ ├── invalid_archive.zip │ │ ├── clang.dia │ │ ├── archive.zip │ │ ├── multiblock.dia │ │ ├── no-location.dia │ │ ├── serialized.dia │ │ ├── category-url.dia │ │ └── string_init_ambig.dia │ ├── pkgconfigInputs │ │ ├── empty_cflags.pc │ │ ├── failure_case.pc │ │ ├── deps_variable.pc │ │ ├── escaped_spaces.pc │ │ ├── quotes_failure.pc │ │ ├── harfbuzz.pc │ │ ├── libffi.pc │ │ ├── freetype2.pc │ │ ├── gobject-2.0.pc │ │ └── gtk+-3.0.pc │ ├── SendableTests.swift │ ├── ContextTests.swift │ ├── HexTests.swift │ ├── CollectionTests.swift │ ├── TracingTests.swift │ ├── InterruptHandlerTests.swift │ ├── PlatformTests.swift │ ├── miscTests.swift │ ├── StringConversionTests.swift │ ├── DiagnosticsUtilityTests.swift │ ├── TripleTests.swift │ ├── StringTests.swift │ └── PolymorphicCodableTests.swift ├── TSCBasicPerformanceTests │ ├── StringConversionsPerfTests.swift │ ├── SHA256PerfTests.swift │ ├── ByteStringPerfTests.swift │ ├── PathPerfTests.swift │ ├── SortedArrayPerfTests.swift │ └── SynchronizedQueuePerfTests.swift └── TSCTestSupportTests │ └── TestSupportTests.swift ├── cmake └── modules │ ├── TSCConfig.cmake.in │ └── CMakeLists.txt ├── Sources ├── CMakeLists.txt ├── TSCclibc │ ├── include │ │ ├── strerror.h │ │ ├── module.modulemap │ │ ├── TSCclibc.h │ │ └── process.h │ ├── strerror.c │ ├── libc.c │ ├── CMakeLists.txt │ └── process.c ├── TSCBasic │ ├── README.md │ ├── Closable.swift │ ├── Tuple.swift │ ├── ObjectIdentifierProtocol.swift │ ├── CStringArray.swift │ ├── DictionaryExtensions.swift │ ├── CollectionExtensions.swift │ ├── CollectionAlgorithms.swift │ ├── DictionaryLiteralExtensions.swift │ ├── Await.swift │ ├── Result.swift │ ├── Condition.swift │ ├── KeyedPair.swift │ ├── FileInfo.swift │ ├── CMakeLists.txt │ ├── RegEx.swift │ ├── CacheableSequence.swift │ ├── LazyCache.swift │ ├── SynchronizedQueue.swift │ ├── CodableResult.swift │ ├── EditDistance.swift │ ├── Thread.swift │ └── StringConversions.swift ├── TSCLibc │ ├── CMakeLists.txt │ └── libc.swift ├── TSCUtility │ ├── OSLog.swift │ ├── Array+Extensions.swift │ ├── Verbosity.swift │ ├── CollectionExtensions.swift │ ├── BuildFlags.swift │ ├── URL.swift │ ├── CMakeLists.txt │ ├── PolymorphicCodable.swift │ ├── misc.swift │ ├── Hex.swift │ ├── Archiver.swift │ ├── Versioning.swift │ ├── Bits.swift │ └── Git.swift └── TSCTestSupport │ ├── XCTestCasePerf.swift │ ├── JSONExtensions.swift │ ├── PseudoTerminal.swift │ └── DiagnosticsEngine.swift ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── automerge.yml │ └── pull_request.yml ├── Utilities ├── ci.xcworkspace │ └── contents.xcworkspacedata └── import ├── .swiftformat ├── CODEOWNERS ├── README.md ├── CMakeLists.txt └── Package.swift /Tests/TSCBasicTests/Inputs/empty_file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/invalid_archive.zip: -------------------------------------------------------------------------------- 1 | not an archive -------------------------------------------------------------------------------- /cmake/modules/TSCConfig.cmake.in: -------------------------------------------------------------------------------- 1 | include(@TSC_EXPORTS_FILE@) 2 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/exit4: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exit 4 4 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/Inputs/regular_text_file: -------------------------------------------------------------------------------- 1 | Hello world 2 | It is a regular text file. 3 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/blocking: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | touch $1 4 | while true; do sleep 1; done 5 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/in-to-out: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | sys.stdout.write(sys.stdin.readline()) 6 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/blocking-ignore-sigint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | trap '' INT 3 | set -e 4 | touch $1 5 | while true; do sleep 1; done 6 | -------------------------------------------------------------------------------- /Sources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(TSCclibc) 2 | add_subdirectory(TSCLibc) 3 | add_subdirectory(TSCBasic) 4 | add_subdirectory(TSCUtility) 5 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/clang.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/clang.dia -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .swiftpm 7 | build 8 | .vscode 9 | Package.resolved 10 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/archive.zip -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/print-pid: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | trap 'exit 1' INT 3 | set -e 4 | printf $$ > $1 5 | touch $2 6 | while true; do sleep 1; done 7 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/multiblock.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/multiblock.dia -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/no-location.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/no-location.dia -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/serialized.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/serialized.dia -------------------------------------------------------------------------------- /Sources/TSCclibc/include/strerror.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _WIN32 4 | extern int tsc_strerror_r(int errnum, char *buf, size_t buflen); 5 | #endif 6 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/category-url.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/category-url.dia -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/Inputs/string_init_ambig.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftlang/swift-tools-support-core/HEAD/Tests/TSCUtilityTests/Inputs/string_init_ambig.dia -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/empty_cflags.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/bin 2 | exec_prefix=${prefix} 3 | 4 | #some comment 5 | 6 | Requires: gdk-3.0 atk 7 | Libs:-L${prefix} -lgtk-3 8 | Cflags: 9 | -------------------------------------------------------------------------------- /Sources/TSCclibc/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module TSCclibc { 2 | header "TSCclibc.h" 3 | header "indexstore_functions.h" 4 | header "process.h" 5 | header "strerror.h" 6 | export * 7 | } 8 | -------------------------------------------------------------------------------- /Utilities/ci.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/failure_case.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/bin 2 | exec_prefix=${prefix} 3 | 4 | #some comment 5 | 6 | Requires: gdk-3.0 >= 1.0.0 7 | Libs: -L${prefix} -lgtk-3 ${my_dep} 8 | Cflags: -I 9 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/simple-stdout-stderr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | sys.stderr.write("simple error\n") 6 | sys.stderr.flush() 7 | sys.stdout.write("simple output\n") 8 | sys.stdout.flush() 9 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/deps_variable.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/bin 2 | exec_prefix=${prefix} 3 | my_dep=atk 4 | #some comment 5 | 6 | Requires: gdk-3.0 >= 1.0.0 ${my_dep} 7 | Libs: -L${prefix} -lgtk-3 8 | Cflags: -I 9 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # file options 2 | 3 | --swiftversion 5.0 4 | --exclude .build 5 | 6 | # format options 7 | 8 | --self insert 9 | --patternlet inline 10 | --stripunusedargs unnamed-only 11 | --ifdef no-indent 12 | 13 | # rules 14 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/subprocess: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | PID=$$ 4 | while true; do sleep 1; done & 5 | CHILD_PID=$! 6 | printf "{\"parent\": $PID, \"child\": $CHILD_PID}" >> $1 7 | touch $2 8 | while true; do sleep 1; done 9 | -------------------------------------------------------------------------------- /Sources/TSCclibc/strerror.c: -------------------------------------------------------------------------------- 1 | #undef _GNU_SOURCE 2 | #include "strerror.h" 3 | #include 4 | 5 | #ifndef _WIN32 6 | int tsc_strerror_r(int errnum, char *buf, size_t buflen) { 7 | return strerror_r(errnum, buf, buflen); 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/long-stdout-stderr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | for i in range(0,16 * 1024): 6 | sys.stdout.write("1") 7 | sys.stdout.flush() 8 | sys.stderr.write("2") 9 | sys.stderr.flush() 10 | -------------------------------------------------------------------------------- /cmake/modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TSC_EXPORTS_FILE ${CMAKE_CURRENT_BINARY_DIR}/TSCExports.cmake) 2 | configure_file(TSCConfig.cmake.in 3 | ${CMAKE_CURRENT_BINARY_DIR}/TSCConfig.cmake) 4 | 5 | get_property(TSC_EXPORTS GLOBAL PROPERTY TSC_EXPORTS) 6 | export(TARGETS ${TSC_EXPORTS} FILE ${TSC_EXPORTS_FILE}) 7 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/processInputs/deadlock-if-blocking-io: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | kb = 1024 6 | 7 | for i in range(0,16 * kb): 8 | sys.stdout.write("1") 9 | sys.stdout.flush() 10 | 11 | for i in range(0,16 * kb): 12 | sys.stderr.write("2") 13 | sys.stderr.flush() 14 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/escaped_spaces.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/bin 2 | exec_prefix=${prefix} 3 | my_dep=atk 4 | #some comment 5 | 6 | Requires: gdk-3.0 >= 1.0.0 ${my_dep} 7 | Libs: -L"${prefix}" -l"gtk 3" -wantareal\\here -one\\ -two 8 | Cflags: -I/usr/local/Wine\ Cellar/gtk+3/3.18.9/include/gtk-3.0 -I/after/extra/spaces 9 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/quotes_failure.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/bin 2 | exec_prefix=${prefix} 3 | my_dep=atk 4 | #some comment 5 | 6 | Requires: gdk-3.0 >= 1.0.0 ${my_dep} 7 | Libs: -L"${prefix}" -l"gt"k3" -wantareal\\here -one\\ -two 8 | Cflags: -I/usr/local/Wine\ Cellar/gtk+3/3.18.9/include/gtk-3.0 -I/after/extra/spaces 9 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/harfbuzz.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr 2 | libdir=${prefix}/lib 3 | includedir=${prefix}/include 4 | 5 | Name: harfbuzz 6 | Description: HarfBuzz text shaping library 7 | Version: 2.7.4 8 | Requires.private: freetype2 9 | Libs: -L${libdir} -lharfbuzz 10 | Libs.private: -pthread -lm 11 | Cflags: -I${includedir}/harfbuzz 12 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/libffi.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/Cellar/libffi/3.3 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/lib 4 | toolexeclibdir=${libdir} 5 | includedir=${prefix}/include 6 | 7 | Name: libffi 8 | Description: Library supporting Foreign Function Interfaces 9 | Version: 3.3 10 | Libs: -L${toolexeclibdir} -lffi 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/freetype2.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr 2 | exec_prefix=/usr 3 | libdir=/usr/lib 4 | includedir=/usr/include 5 | 6 | Name: FreeType 2 7 | URL: https://freetype.org 8 | Description: A free, high-quality, and portable font engine. 9 | Version: 23.4.17 10 | Requires: 11 | Requires.private: harfbuzz 12 | Libs: -L${libdir} -lfreetype 13 | Libs.private: -lbz2 14 | Cflags: -I${includedir}/freetype2 15 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a case-sensitive file pattern followed by one or more owners. 3 | # Order is important. The last matching pattern has the most precedence. 4 | # More information: https://docs.github.com/en/articles/about-code-owners 5 | # 6 | # Please mirror the repository's file hierarchy in case-sensitive lexicographic 7 | # order. 8 | 9 | * @jakepetroules @owenv 10 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/gobject-2.0.pc: -------------------------------------------------------------------------------- 1 | prefix=/usr/local/Cellar/glib/2.64.3 2 | libdir=${prefix}/lib 3 | includedir=${prefix}/include 4 | 5 | Name: GObject 6 | Description: GLib Type, Object, Parameter and Signal Library 7 | Version: 2.64.3 8 | # Requires: glib-2.0 9 | Requires.private: /usr/local/opt/libffi/lib/pkgconfig/libffi.pc >= 3.0.0 10 | Libs: -L${libdir} -lgobject-2.0 11 | Libs.private: -lintl 12 | Cflags:-I${includedir} 13 | -------------------------------------------------------------------------------- /Sources/TSCclibc/libc.c: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | // Stub method to avoid no debug symbol warning from compiler. 12 | int tools_support_core_clibc_anchor() { 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /Sources/TSCBasic/README.md: -------------------------------------------------------------------------------- 1 | # TSCBasic Library 2 | 3 | This library contains the basic support facilities for build system related 4 | projects like `swiftpm`. It is layered on `TSCLibc` and `Foundation`, and 5 | defines the shared data structures, utilities, and algorithms for the rest 6 | of the package manager. 7 | 8 | This library is also intended to contain the bulk of the cross-platform 9 | compatibility logic not present in lower layers, so that higher level libraries 10 | can be written in as platform agnostic a manner as possible. 11 | -------------------------------------------------------------------------------- /Sources/TSCclibc/include/TSCclibc.h: -------------------------------------------------------------------------------- 1 | #if defined(__linux__) 2 | #include 3 | #endif 4 | 5 | #define STR_EXPAND(VALUE) #VALUE 6 | #define STR(VALUE) STR_EXPAND(VALUE) 7 | 8 | static inline const char* SPM_VendorNameString() { 9 | #ifdef SPM_VENDOR_NAME 10 | return STR(SPM_VENDOR_NAME); 11 | #else 12 | return ""; 13 | #endif 14 | } 15 | static inline const char* SPM_BuildIdentifierString() { 16 | #ifdef SPM_BUILD_IDENT 17 | return STR(SPM_BUILD_IDENT); 18 | #else 19 | return ""; 20 | #endif 21 | } 22 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/pkgconfigInputs/gtk+-3.0.pc: -------------------------------------------------------------------------------- 1 | prefix = /usr/local/Cellar/gtk+3/3.18.9 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/lib 4 | includedir=${prefix}/include 5 | targets=quartz 6 | #some comment 7 | gtk_binary_version=3.0.0 8 | gtk_host=x86_64-apple-darwin15.3.0 9 | 10 | Name: GTK+ 11 | Description: GTK+ Graphical UI Library 12 | Version: 3.18.9 13 | Requires: gdk-3.0,atk >= 2.15.1 cairo >= 1.14.0 cairo-gobject >= 1.14.0 gdk-pixbuf-2.0 >= 2.30.0 gio-2.0 >= 2.45.8 #some random comment 14 | Requires.private: atk epoxy >= 1.0 gio-unix-2.0 >= 2.45.8 15 | Libs: -L${libdir} -lgtk-3 16 | Cflags: -I${includedir}/gtk-3.0 17 | -------------------------------------------------------------------------------- /Sources/TSCLibc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This source file is part of the Swift.org open source project 2 | # 3 | # Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 4 | # Licensed under Apache License v2.0 with Runtime Library Exception 5 | # 6 | # See http://swift.org/LICENSE.txt for license information 7 | # See http://swift.org/CONTRIBUTORS.txt for Swift project authors 8 | 9 | add_library(TSCLibc STATIC 10 | libc.swift) 11 | 12 | # NOTE(compnerd) workaround for CMake not setting up include flags yet 13 | set_target_properties(TSCLibc PROPERTIES 14 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 15 | 16 | set_property(GLOBAL APPEND PROPERTY TSC_EXPORTS TSCLibc) 17 | -------------------------------------------------------------------------------- /Utilities/import: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | # This source file is part of the Swift.org open source project 6 | # 7 | # Copyright (c) 2019 Apple Inc. and the Swift project authors 8 | # Licensed under Apache License v2.0 with Runtime Library Exception 9 | # 10 | # See http://swift.org/LICENSE.txt for license information 11 | # See http://swift.org/CONTRIBUTORS.txt for Swift project authors 12 | 13 | __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 14 | 15 | SRCROOT="`cd "${__dir}/..";pwd`" 16 | echo "SRCROOT is $SRCROOT" 17 | 18 | IMPORT_DIR="`cd "${SRCROOT}/../swiftpm/swift-tools-support-core";pwd`" 19 | 20 | echo Copying from $IMPORT_DIR 21 | cp -r ${IMPORT_DIR}/* $SRCROOT 22 | -------------------------------------------------------------------------------- /Sources/TSCBasic/Closable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Closable entity is one that manages underlying resources and needs to be closed for cleanup 12 | /// The intent of this method is for the sole owner of the reference/handle of the resource to close it completely, compared to releasing a shared resource. 13 | public protocol Closable { 14 | func close() throws 15 | } 16 | -------------------------------------------------------------------------------- /Sources/TSCUtility/OSLog.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCBasic 12 | 13 | extension OSLog { 14 | /// Log for SwiftPM. 15 | public static let swiftpm = OSLog(subsystem: "org.swift.swiftpm", category: "default") 16 | } 17 | 18 | public enum SignpostName { 19 | /// SignPost name for package resolution. 20 | public static let resolution: StaticString = "resolution" 21 | } 22 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/CacheableSequenceTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2018 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class CacheableSequenceTests: XCTestCase { 16 | func testBasics() throws { 17 | let s = sequence(first: 0, next: { i in 18 | return i < 5 ? i + 1 : nil 19 | }) 20 | let s2 = CacheableSequence(s) 21 | XCTAssertEqual(Array(s2), Array(s2)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/SendableTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2023 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import _Concurrency 13 | 14 | import TSCBasic 15 | 16 | final class SendableTests: XCTestCase { 17 | #if compiler(>=5.5.2) 18 | func testByteStringIsSendable() { 19 | self.sendableBlackhole(ByteString()) 20 | } 21 | 22 | // MARK: - Utilities 23 | private func sendableBlackhole(_ value: T) {} 24 | #endif 25 | } 26 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/TupleTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class TupleTests: XCTestCase { 16 | 17 | func testBasics() throws { 18 | XCTAssertTrue([("A", "A")] == [("A", "A")]) 19 | XCTAssertFalse([("A", "A")] == [("A", "B")]) 20 | 21 | XCTAssertTrue([("A", 1)] == [("A", 1)]) 22 | XCTAssertFalse([("A", 1)] == [("A", 2)]) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/SendableTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2023 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import _Concurrency 13 | 14 | import TSCUtility 15 | 16 | final class SendableTests: XCTestCase { 17 | #if compiler(>=5.5.2) 18 | func testByteContextIsSendable() { 19 | self.sendableBlackhole(Context()) 20 | } 21 | 22 | // MARK: - Utilities 23 | private func sendableBlackhole(_ value: T) {} 24 | #endif 25 | } 26 | -------------------------------------------------------------------------------- /Sources/TSCBasic/Tuple.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Returns true if these arrays of tuple contains the same elements. 12 | public func ==( 13 | lhs: [(A, B)], rhs: [(A, B)] 14 | ) -> Bool { 15 | guard lhs.count == rhs.count else { return false } 16 | for (idx, lElement) in lhs.enumerated() { 17 | guard lElement == rhs[idx] else { 18 | return false 19 | } 20 | } 21 | return true 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | swift-tools-support-core 2 | ========================= 3 | 4 | Contains common infrastructural code for both [SwiftPM](https://github.com/swiftlang/swift-package-manager) 5 | and [llbuild](https://github.com/apple/swift-llbuild). 6 | 7 | ## ⚠️ This package is deprecated 8 | 9 | As this package with time has become a collection of unrelated utilities, that made it much harder to version. 10 | Primary users of TSC such as SwiftPM and Swift Driver came up with specialized alternatives to APIs provided 11 | in TSC. Moving forward, we don't recommend adding TSC as a dependency to your project. More and more types 12 | and functions here will be deprecated, with minimal modifications to ease the migration off TSC. 13 | 14 | License 15 | ------- 16 | 17 | See http://swift.org/LICENSE.txt for license information. 18 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/CStringArrayTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | 14 | class CStringArrayTests: XCTestCase { 15 | func testInitialization() { 16 | let array = CStringArray(["hello", "world"]) 17 | XCTAssertEqual(array.cArray.count, 3) 18 | XCTAssertEqual(String(cString: array.cArray[0]!), "hello") 19 | XCTAssertEqual(String(cString: array.cArray[1]!), "world") 20 | XCTAssertNil(array.cArray[2]) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/CollectionAlgorithmsTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class CollectionAlgorithmsTests: XCTestCase { 16 | func testFindDuplicates() { 17 | XCTAssertEqual(Set([1, 2, 3, 2, 1].spm_findDuplicates()), [1, 2]) 18 | XCTAssertEqual(Set([1, 2, 3, 2, 1, 2].spm_findDuplicates()), [1, 2]) 19 | XCTAssertEqual(["foo", "bar"].spm_findDuplicates(), []) 20 | XCTAssertEqual(["foo", "Foo"].spm_findDuplicates(), []) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/TSCclibc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This source file is part of the Swift.org open source project 2 | # 3 | # Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 4 | # Licensed under Apache License v2.0 with Runtime Library Exception 5 | # 6 | # See http://swift.org/LICENSE.txt for license information 7 | # See http://swift.org/CONTRIBUTORS.txt for Swift project authors 8 | 9 | add_library(TSCclibc STATIC 10 | libc.c process.c strerror.c) 11 | target_include_directories(TSCclibc PUBLIC 12 | include) 13 | target_compile_definitions(TSCclibc PRIVATE 14 | "$<$:_GNU_SOURCE>") 15 | set_target_properties(TSCclibc PROPERTIES POSITION_INDEPENDENT_CODE YES) 16 | 17 | if(NOT BUILD_SHARED_LIBS) 18 | install(TARGETS TSCclibc 19 | ARCHIVE DESTINATION lib) 20 | endif() 21 | 22 | set_property(GLOBAL APPEND PROPERTY TSC_EXPORTS TSCclibc) 23 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/ContextTests.swift: -------------------------------------------------------------------------------- 1 | // This source file is part of the Swift.org open source project 2 | // 3 | // Copyright (c) 2020 Apple Inc. and the Swift project authors 4 | // Licensed under Apache License v2.0 with Runtime Library Exception 5 | // 6 | // See http://swift.org/LICENSE.txt for license information 7 | // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 | 9 | import XCTest 10 | 11 | import TSCUtility 12 | 13 | struct SomeType { 14 | var name: String 15 | } 16 | 17 | class ContextTests: XCTestCase { 18 | func testBasics() { 19 | var ctx = Context() 20 | ctx.set(SomeType(name: "test")) 21 | XCTAssertEqual(ctx.get(SomeType.self).name, "test") 22 | 23 | ctx.set(SomeType(name: "optional")) 24 | XCTAssertEqual(ctx.getOptional(SomeType.self)?.name, "optional") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/DictionaryExtensionsTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | 14 | class DictionaryExtensionTests: XCTestCase { 15 | 16 | func testBasics() { 17 | XCTAssertEqual(["foo": "1", "bar": "2", "baz": "f"].spm_flatMapValues({ Int($0) }), ["foo": 1, "bar": 2]) 18 | } 19 | 20 | func testCreateDictionary() { 21 | XCTAssertEqual([("foo", 1), ("bar", 2)].spm_createDictionary({ $0 }), ["foo": 1, "bar": 2]) 22 | XCTAssertEqual(["foo", "bar"].spm_createDictionary({ ($0[$0.startIndex], $0) }), ["f": "foo", "b": "bar"]) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/TSCUtility/Array+Extensions.swift: -------------------------------------------------------------------------------- 1 | // This source file is part of the Swift.org open source project 2 | // 3 | // Copyright (c) 2020 Apple Inc. and the Swift project authors 4 | // Licensed under Apache License v2.0 with Runtime Library Exception 5 | // 6 | // See http://swift.org/LICENSE.txt for license information 7 | // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 | 9 | 10 | extension Array { 11 | /// Make several slices out of a given array. 12 | /// - Returns: 13 | /// An array of slices of `maxStride` elements each. 14 | @inlinable 15 | public func tsc_sliceBy(maxStride: Int) -> [ArraySlice] { 16 | let elementsCount = self.count 17 | let groupsCount = (elementsCount + maxStride - 1) / maxStride 18 | return (0.. Bool { 24 | return ObjectIdentifier(lhs) == ObjectIdentifier(rhs) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/RegExTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | 14 | class RegExTests: XCTestCase { 15 | 16 | func testErrors() { 17 | // https://bugs.swift.org/browse/SR-5557 18 | #if canImport(Darwin) 19 | XCTAssertThrowsError(try RegEx(pattern: "(")) 20 | #endif 21 | } 22 | 23 | func testMatchGroups() throws { 24 | try XCTAssert(RegEx(pattern: "([a-z]+)([0-9]+)").matchGroups(in: "foo1 bar2 baz3") == [["foo", "1"], ["bar", "2"], ["baz", "3"]]) 25 | try XCTAssert(RegEx(pattern: "[0-9]+").matchGroups(in: "foo bar baz") == []) 26 | try XCTAssert(RegEx(pattern: "[0-9]+").matchGroups(in: "1") == [[]]) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/TSCBasicPerformanceTests/SHA256PerfTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | import TSCTestSupport 15 | 16 | class SHA256PerfTests: XCTestCasePerf { 17 | func test20MBDigest_X1000() { 18 | #if canImport(Darwin) 19 | let sha256 = SHA256() 20 | let byte = "f" 21 | let stream = BufferedOutputByteStream() 22 | for _ in 0..<20000 { 23 | stream.send(byte) 24 | } 25 | measure { 26 | for _ in 0..<1000 { 27 | XCTAssertEqual(sha256.hash(stream.bytes).hexadecimalRepresentation, "23d00697ba26b4140869bab958431251e7e41982794d41b605b6a1d5dee56abf") 28 | } 29 | } 30 | #endif 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | if(POLICY CMP0091) 3 | cmake_policy(SET CMP0091 NEW) 4 | endif() 5 | 6 | cmake_minimum_required(VERSION 3.19) 7 | 8 | project(SwiftTSC LANGUAGES C Swift) 9 | 10 | set(CMAKE_Swift_LANGUAGE_VERSION 5) 11 | set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift) 12 | # TODO: lift this restriction once we have a split Swift runtime build which can 13 | # be built with a debug C runtime. 14 | set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY MultiThreadedDLL) 15 | 16 | # TODO: lift this restriction once we have a split Swift runtime build which can 17 | # be built with a debug C runtime. 18 | set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL) 19 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 20 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 21 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 22 | 23 | set(CMAKE_DISABLE_IN_SOURCE_BUILD YES) 24 | 25 | option(BUILD_SHARED_LIBS "Build shared libraries by default" YES) 26 | 27 | find_package(dispatch QUIET) 28 | find_package(Foundation QUIET) 29 | find_package(Threads QUIET) 30 | 31 | add_subdirectory(Sources) 32 | add_subdirectory(cmake/modules) 33 | -------------------------------------------------------------------------------- /Sources/TSCclibc/include/process.h: -------------------------------------------------------------------------------- 1 | #if !defined(_WIN32) 2 | 3 | #include 4 | #include 5 | 6 | #ifdef TSC_API_UNAVAILABLE_DEFINED 7 | #error TSC_API_UNAVAILABLE_DEFINED already defined 8 | #endif 9 | 10 | #ifndef __API_UNAVAILABLE 11 | #define __API_UNAVAILABLE(...) 12 | #define TSC_API_UNAVAILABLE_DEFINED 13 | #endif 14 | 15 | // Wrapper method for posix_spawn_file_actions_addchdir_np that fails on Linux versions that do not have this method available. 16 | int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restrict file_actions, const char *restrict path) __API_UNAVAILABLE(ios, tvos, watchos, visionos); 17 | 18 | // Runtime check for the availability of posix_spawn_file_actions_addchdir_np. Returns 0 if the method is available, -1 if not. 19 | bool SPM_posix_spawn_file_actions_addchdir_np_supported(); 20 | 21 | int SPM_posix_spawnp(pid_t *pid, const char *file, const posix_spawn_file_actions_t *actions, const posix_spawnattr_t *attr, char *const argv[], char *const env[]); 22 | 23 | #ifdef TSC_API_UNAVAILABLE_DEFINED 24 | #undef TSC_API_UNAVAILABLE_DEFINED 25 | #undef __API_UNAVAILABLE 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /Sources/TSCTestSupport/XCTestCasePerf.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | 14 | /// A helper class to write performance tests for SwiftPM. 15 | /// 16 | /// Subclasses of this will always build the tests but only run 17 | /// run the tests when `TSC_ENABLE_PERF_TESTS` is present in the environment. 18 | /// This is useful because we always want to be able to compile the perf tests 19 | /// even if they are not run locally. 20 | open class XCTestCasePerf: XCTestCase { 21 | #if canImport(Darwin) 22 | override open class var defaultTestSuite: XCTestSuite { 23 | if ProcessEnv.vars.keys.contains("TSC_ENABLE_PERF_TESTS") { 24 | return super.defaultTestSuite 25 | } 26 | return XCTestSuite(name: String(describing: type(of: self))) 27 | } 28 | #endif 29 | } 30 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/HexTests.swift: -------------------------------------------------------------------------------- 1 | // This source file is part of the Swift.org open source project 2 | // 3 | // Copyright (c) 2020 Apple Inc. and the Swift project authors 4 | // Licensed under Apache License v2.0 with Runtime Library Exception 5 | // 6 | // See http://swift.org/LICENSE.txt for license information 7 | // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 | 9 | import XCTest 10 | 11 | import TSCUtility 12 | 13 | class HexTests: XCTestCase { 14 | func testHexDecode() { 15 | XCTAssert("0".tsc_hexDecode() == nil) 16 | XCTAssert("0x".tsc_hexDecode() == nil) 17 | XCTAssertEqual("00".tsc_hexDecode()!, [0]) 18 | XCTAssertEqual("01".tsc_hexDecode()!, [1]) 19 | XCTAssertEqual("0a".tsc_hexDecode()!, [10]) 20 | XCTAssertEqual("10".tsc_hexDecode()!, [16]) 21 | XCTAssertEqual("a0".tsc_hexDecode()!, [160]) 22 | } 23 | 24 | func testHexEncode() { 25 | XCTAssertEqual(hexEncode([0]), "00") 26 | XCTAssertEqual(hexEncode([1]), "01") 27 | XCTAssertEqual(hexEncode([10]), "0a") 28 | XCTAssertEqual(hexEncode([16]), "10") 29 | XCTAssertEqual(hexEncode([160]), "a0") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/TSCBasicPerformanceTests/ByteStringPerfTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | import TSCTestSupport 15 | 16 | class ByteStringPerfTests: XCTestCasePerf { 17 | func testInitialization() { 18 | #if canImport(Darwin) 19 | let listOfStrings: [String] = (0..<10).map { "This is the number: \($0)!\n" } 20 | let expectedTotalCount = listOfStrings.map({ $0.utf8.count }).reduce(0, +) 21 | measure { 22 | var count = 0 23 | let N = 10000 24 | for _ in 0..?] 20 | 21 | /// Creates an instance from an array of strings. 22 | public init(_ array: [String]) { 23 | #if os(Windows) 24 | cArray = array.map({ $0.withCString({ _strdup($0) }) }) + [nil] 25 | #else 26 | cArray = array.map({ $0.withCString({ strdup($0) }) }) + [nil] 27 | #endif 28 | } 29 | 30 | deinit { 31 | for case let element? in cArray { 32 | free(element) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/TSCBasic/DictionaryExtensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | extension Dictionary { 12 | /// Returns a new dictionary containing the keys of this dictionary with the 13 | /// values transformed by the given closure, if transformed is not nil. 14 | public func spm_flatMapValues(_ transform: (Value) throws -> T?) rethrows -> [Key: T] { 15 | var transformed: [Key: T] = [:] 16 | for (key, value) in self { 17 | if let value = try transform(value) { 18 | transformed[key] = value 19 | } 20 | } 21 | return transformed 22 | } 23 | } 24 | 25 | extension Array { 26 | /// Create a dictionary with given sequence of elements. 27 | public func spm_createDictionary( 28 | _ uniqueKeysWithValues: (Element) -> (Key, Value) 29 | ) -> [Key: Value] { 30 | return Dictionary(uniqueKeysWithValues: self.map(uniqueKeysWithValues)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/InterruptHandlerTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | import TSCLibc 14 | import TSCUtility 15 | import TSCTestSupport 16 | 17 | class InterruptHandlerTests: XCTestCase { 18 | func testBasics() throws { 19 | // Disabled because it sometimes hangs the CI, possibly due to https://bugs.swift.org/browse/SR-5042 20 | #if false 21 | mktmpdir { path in 22 | let exec = TestSupportExecutable.path.description 23 | let waitFile = path.appending(component: "waitFile") 24 | let process = Process(args: exec, "interruptHandlerTest", waitFile.description) 25 | try process.launch() 26 | guard waitForFile(waitFile) else { 27 | return XCTFail("Couldn't launch the process") 28 | } 29 | process.signal(SIGINT) 30 | let result = try process.waitUntilExit() 31 | XCTAssertEqual(try result.utf8Output(), "Hello from handler!\n") 32 | } 33 | #endif 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/TSCUtility/CollectionExtensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | extension Collection where Iterator.Element : Equatable { 12 | /// Split around a delimiting subsequence with maximum number of splits == 2 13 | public func spm_split(around delimiter: [Iterator.Element]) -> ([Iterator.Element], [Iterator.Element]?) { 14 | 15 | let orig = Array(self) 16 | let end = orig.endIndex 17 | let delimCount = delimiter.count 18 | 19 | var index = orig.startIndex 20 | while index+delimCount <= end { 21 | let cur = Array(orig[index.. [Element] { 33 | var set = Set() 34 | var result = [Element]() 35 | for element in self { 36 | if set.insert(element).inserted { 37 | result.append(element) 38 | } 39 | } 40 | return result 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/AwaitTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import Dispatch 13 | 14 | import TSCBasic 15 | 16 | class AwaitTests: XCTestCase { 17 | 18 | enum DummyError: Error { 19 | case error 20 | } 21 | 22 | func async(_ param: String, _ completion: @escaping (Result) -> Void) { 23 | DispatchQueue.global().async { 24 | completion(.success(param)) 25 | } 26 | } 27 | 28 | func throwingAsync(_ param: String, _ completion: @escaping (Result) -> Void) { 29 | DispatchQueue.global().async { 30 | completion(.failure(DummyError.error)) 31 | } 32 | } 33 | 34 | func testBasics() throws { 35 | let value = try tsc_await { async("Hi", $0) } 36 | XCTAssertEqual("Hi", value) 37 | 38 | do { 39 | let value = try tsc_await { throwingAsync("Hi", $0) } 40 | XCTFail("Unexpected success \(value)") 41 | } catch { 42 | XCTAssertEqual(error as? DummyError, DummyError.error) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/LazyCacheTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class LazyCacheTests: XCTestCase { 16 | @available(*, deprecated, message: "LazyCache's implementation is broken -- https://github.com/apple/swift-tools-support-core/issues/385") 17 | func testBasics() { 18 | class Foo { 19 | var numCalls = 0 20 | 21 | var bar: Int { return barCache.getValue(self) } 22 | var barCache = LazyCache(someExpensiveMethod) 23 | func someExpensiveMethod() -> Int { 24 | numCalls += 1 25 | return 42 26 | } 27 | 28 | } 29 | 30 | // FIXME: Make this a more interesting test once we have concurrency primitives. 31 | for _ in 0..<10 { 32 | let foo = Foo() 33 | XCTAssertEqual(foo.numCalls, 0) 34 | for _ in 0..<10 { 35 | XCTAssertEqual(foo.bar, 42) 36 | XCTAssertEqual(foo.numCalls, 1) 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/TSCUtility/BuildFlags.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | // FIXME: This belongs somewhere else, but we don't have a layer specifically 12 | // for BuildSupport style logic yet. 13 | // 14 | /// Build-tool independent flags. 15 | @available(*, deprecated, message: "replace with SwiftPM `PackageModel.BuildFlags`") 16 | public struct BuildFlags: Encodable { 17 | 18 | /// Flags to pass to the C compiler. 19 | public var cCompilerFlags: [String] 20 | 21 | /// Flags to pass to the C++ compiler. 22 | public var cxxCompilerFlags: [String] 23 | 24 | /// Flags to pass to the linker. 25 | public var linkerFlags: [String] 26 | 27 | /// Flags to pass to the Swift compiler. 28 | public var swiftCompilerFlags: [String] 29 | 30 | public init( 31 | xcc: [String]? = nil, 32 | xcxx: [String]? = nil, 33 | xswiftc: [String]? = nil, 34 | xlinker: [String]? = nil 35 | ) { 36 | cCompilerFlags = xcc ?? [] 37 | cxxCompilerFlags = xcxx ?? [] 38 | linkerFlags = xlinker ?? [] 39 | swiftCompilerFlags = xswiftc ?? [] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/TSCUtility/URL.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | // deprecated 9/2021 12 | @available(*, deprecated, message: "use Foundation APIs instead") 13 | public struct URL { 14 | 15 | /// Parses the URL type of a git repository 16 | /// e.g. https://github.com/apple/swift returns "https" 17 | /// e.g. git@github.com:apple/swift returns "git" 18 | /// 19 | /// This is *not* a generic URI scheme parser! 20 | public static func scheme(_ url: String) -> String? { 21 | 22 | func prefixOfSplitBy(_ delimiter: String) -> String? { 23 | let (head, tail) = url.spm_split(around: delimiter) 24 | if tail == nil { 25 | //not found 26 | return nil 27 | } else { 28 | //found, return head 29 | //lowercase the "scheme", as specified by the URI RFC (just in case) 30 | return head.lowercased() 31 | } 32 | } 33 | 34 | for delim in ["://", "@"] { 35 | if let found = prefixOfSplitBy(delim), !found.contains("/") { 36 | return found 37 | } 38 | } 39 | 40 | return nil 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/TSCTestSupport/JSONExtensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCBasic 12 | 13 | /// Useful extensions to JSON to use in assert methods where 14 | /// the type diagnostics is not that important. 15 | public extension JSON { 16 | var dictionary: [String: JSON]? { 17 | if case let .dictionary(contents) = self { 18 | return contents 19 | } 20 | return nil 21 | } 22 | 23 | var array: [JSON]? { 24 | if case let .array(contents) = self { 25 | return contents 26 | } 27 | return nil 28 | } 29 | 30 | var string: String? { 31 | if case let .string(contents) = self { 32 | return contents 33 | } 34 | return nil 35 | } 36 | 37 | var stringValue: String { 38 | return string ?? "" 39 | } 40 | 41 | subscript(_ string: String) -> JSON? { 42 | return dictionary?[string] 43 | } 44 | 45 | subscript(_ idx: Int) -> JSON? { 46 | if let array = array { 47 | guard idx >= 0 && array.count > idx else { 48 | return nil 49 | } 50 | return array[idx] 51 | } 52 | return nil 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/EditDistanceTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | @testable import TSCBasic 12 | import XCTest 13 | 14 | import TSCBasic 15 | 16 | class EditDistanceTests: XCTestCase { 17 | 18 | @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) 19 | func testEditDistanceWithCollectionDiff() { 20 | XCTAssertEqual(collectionDiffEditDistance("Foo", "Fo"), 1) 21 | XCTAssertEqual(collectionDiffEditDistance("Foo", "Foo"), 0) 22 | XCTAssertEqual(collectionDiffEditDistance("Bar", "Foo"), 3) 23 | XCTAssertEqual(collectionDiffEditDistance("ABCDE", "ABDE"), 1) 24 | XCTAssertEqual(collectionDiffEditDistance("sunday", "saturday"), 3) 25 | XCTAssertEqual(collectionDiffEditDistance("FOO", "foo"), 3) 26 | } 27 | 28 | func testInternalEditDistance() { 29 | XCTAssertEqual(internalEditDistance("Foo", "Fo"), 1) 30 | XCTAssertEqual(internalEditDistance("Foo", "Foo"), 0) 31 | XCTAssertEqual(internalEditDistance("Bar", "Foo"), 3) 32 | XCTAssertEqual(internalEditDistance("ABCDE", "ABDE"), 1) 33 | XCTAssertEqual(internalEditDistance("sunday", "saturday"), 3) 34 | XCTAssertEqual(internalEditDistance("FOO", "foo"), 3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull request 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | types: [opened, reopened, synchronize] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | tests: 16 | name: Test 17 | uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main 18 | with: 19 | linux_os_versions: '["amazonlinux2", "bookworm", "noble", "jammy", "rhel-ubi9"]' 20 | linux_swift_versions: '["nightly-main", "nightly-6.2"]' 21 | linux_build_command: 'swift build' 22 | enable_linux_static_sdk_build: true 23 | linux_static_sdk_build_command: SWIFTTOOLSSUPPORTCORE_STATIC_LINK=1 swift build 24 | windows_swift_versions: '["nightly-main"]' 25 | windows_build_command: 'Invoke-Program swift build' 26 | enable_android_sdk_build: true 27 | android_sdk_build_command: "swift build --build-tests" 28 | android_ndk_versions: '["r27d", "r29"]' 29 | enable_ios_checks: true 30 | enable_macos_checks: true 31 | macos_exclude_xcode_versions: "[{\"xcode_version\": \"16.3\"}, {\"xcode_version\": \"16.4\"}]" 32 | 33 | soundness: 34 | name: Soundness 35 | uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main 36 | with: 37 | license_header_check_project_name: "Swift.org" 38 | license_header_check_enabled: false 39 | unacceptable_language_check_enabled: false 40 | api_breakage_check_enabled: false 41 | format_check_enabled: false 42 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/KeyedPairTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class KeyedPairTests: XCTestCase { 16 | func testBasics() { 17 | class Airport { 18 | // The name of the airport. 19 | let name: String 20 | // The destination airports for outgoing flights. 21 | var destinations: [Airport] = [] 22 | 23 | init(name: String) { 24 | self.name = name 25 | } 26 | } 27 | 28 | func whereCanIGo(from airport: Airport) -> [Airport] { 29 | let closure = transitiveClosure([KeyedPair(airport, key: airport.name)]) { 30 | return $0.item.destinations.map{ KeyedPair($0, key: $0.name) } 31 | } 32 | return closure.map{ $0.item } 33 | } 34 | 35 | let sf = Airport(name: "San Francisco") 36 | let beijing = Airport(name: "北京市") 37 | let newDelhi = Airport(name: "नई दिल्ली") 38 | let moscow = Airport(name: "Москва") 39 | sf.destinations = [newDelhi] 40 | newDelhi.destinations = [beijing, moscow] 41 | 42 | XCTAssertEqual(whereCanIGo(from: sf).map{ $0.name }.sorted(), ["Москва", "नई दिल्ली", "北京市"]) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/DeltaAlgorithmTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class DeltaAlgorithmTests: XCTestCase { 16 | 17 | func testBasics() { 18 | let da = DeltaAlgorithm() 19 | 20 | do { 21 | // [0, 20) should minimize to {3,5,7} 22 | let failureSet: Set = [3, 5, 7] 23 | let result = da.run(changes: Set(0..<20)) { 24 | // If changes includes failure set. 25 | $0.union(failureSet) == $0 26 | } 27 | XCTAssertEqual(result, failureSet) 28 | } 29 | 30 | do { 31 | let failureSet: Set = [3, 5, 7] 32 | // [10, 20) should minimize to [10,20) 33 | let result = da.run(changes: Set(10..<20)) { 34 | $0.union(failureSet) == $0 35 | } 36 | XCTAssertEqual(result, Set(10..<20)) 37 | } 38 | 39 | do { 40 | let failureSet = Set(0..<10) 41 | // [0, 4) should minimize to [0,4) in 11 tests. 42 | let result = da.run(changes: Set(0..<4)) { 43 | $0.union(failureSet) == $0 44 | } 45 | XCTAssertEqual(result, Set(0..<4)) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/TSCBasic/CollectionAlgorithms.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | extension Sequence where Element: Hashable { 12 | 13 | /// Finds duplicates in given sequence of Hashables. 14 | /// - Returns: duplicated elements in the invoking sequence. 15 | public func spm_findDuplicates() -> [Element] { 16 | var counts: [Element: Int] = [:] 17 | for element in self { 18 | counts[element, default: 0] += 1 19 | } 20 | return Array(counts.lazy.filter({ $0.value > 1 }).map({ $0.key })) 21 | } 22 | } 23 | 24 | extension Collection where Element: Hashable { 25 | 26 | /// Finds duplicates in given collection of Hashables. 27 | public func spm_findDuplicateElements() -> [[Element]] { 28 | var table: [Element: [Element]] = [:] 29 | for element in self { 30 | table[element, default: []].append(element) 31 | } 32 | return table.values.filter({ $0.count > 1 }) 33 | } 34 | } 35 | 36 | extension Sequence { 37 | public func spm_findDuplicateElements( 38 | by keyPath: KeyPath 39 | ) -> [[Element]] { 40 | return Dictionary(grouping: self, by: { $0[keyPath: keyPath] }) 41 | .values 42 | .filter({ $0.count > 1 }) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ObjectIdentifierProtocolTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | @available(*, deprecated) 16 | final class Person { 17 | let name: String 18 | init(_ name: String) { 19 | self.name = name 20 | } 21 | } 22 | 23 | @available(*, deprecated) 24 | extension Person: ObjectIdentifierProtocol {} 25 | 26 | @available(*, deprecated) 27 | class ObjectIdentifierProtocolTests: XCTestCase { 28 | 29 | func testBasics() { 30 | let foo = Person("Foo") 31 | let foo2 = Person("Foo2") 32 | let foo3 = foo 33 | let bar = Person("Bar") 34 | let bar2 = bar 35 | 36 | XCTAssertNotEqual(foo, foo2) 37 | XCTAssertNotEqual(foo2, foo3) 38 | XCTAssertEqual(foo, foo3) 39 | 40 | XCTAssertNotEqual(foo, bar) 41 | XCTAssertNotEqual(foo, bar2) 42 | XCTAssertEqual(bar, bar2) 43 | 44 | var dict = [Person: String]() 45 | dict[foo] = foo.name 46 | dict[bar] = bar.name 47 | 48 | XCTAssertEqual(dict[foo], "Foo") 49 | XCTAssertEqual(dict[foo2], nil) 50 | XCTAssertEqual(dict[foo3], "Foo") 51 | 52 | XCTAssertEqual(dict[bar], "Bar") 53 | XCTAssertEqual(dict[bar2], "Bar") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/DictionaryLiteralExtensionsTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | 14 | class DictionaryLiteralExtensionsTests: XCTestCase { 15 | 16 | func testDescription() { 17 | XCTAssertEqual(KeyValuePairs(dictionaryLiteral: ("foo", 1)).description, "[foo: 1]") 18 | XCTAssertEqual(KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2)).description, "[foo: 1, bar: 2]") 19 | } 20 | 21 | func testEquality() { 22 | XCTAssertTrue(KeyValuePairs(dictionaryLiteral: ("foo", 1)) == KeyValuePairs(dictionaryLiteral: ("foo", 1))) 23 | XCTAssertTrue(KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2)) == KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2))) 24 | 25 | XCTAssertFalse(KeyValuePairs(dictionaryLiteral: ("no-foo", 1), ("bar", 2)) == KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2))) 26 | XCTAssertFalse(KeyValuePairs(dictionaryLiteral: ("foo", 0), ("bar", 2)) == KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2))) 27 | XCTAssertFalse(KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2), ("hoge", 3)) == KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2))) 28 | XCTAssertFalse(KeyValuePairs(dictionaryLiteral: ("foo", 1), ("bar", 2)) == KeyValuePairs(dictionaryLiteral: ("bar", 2), ("foo", 1))) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/TSCBasic/DictionaryLiteralExtensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | // Can't conform a protocol explicitly with certain where clause for now but it'd be resolved by SE-0143. 12 | // ref: https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md 13 | // MARK: CustomStringConvertible 14 | extension KeyValuePairs where Key: CustomStringConvertible, Value: CustomStringConvertible { 15 | /// A string that represents the contents of the dictionary literal. 16 | public var description: String { 17 | let lastCount = self.count - 1 18 | var desc = "[" 19 | for (i, item) in self.enumerated() { 20 | desc.append("\(item.key.description): \(item.value.description)") 21 | desc.append(i == lastCount ? "]" : ", ") 22 | } 23 | return desc 24 | } 25 | } 26 | 27 | // MARK: Equatable 28 | extension KeyValuePairs where Key: Equatable, Value: Equatable { 29 | public static func ==(lhs: KeyValuePairs, rhs: KeyValuePairs) -> Bool { 30 | if lhs.count != rhs.count { 31 | return false 32 | } 33 | for i in 0.. String? { 36 | var buf: [CChar] = [CChar](repeating: 0, count: n) 37 | if read(primary, &buf, n) <= 0 { 38 | return nil 39 | } 40 | return String(cString: buf) 41 | } 42 | 43 | public func closeSecondary() { 44 | _ = TSCLibc.close(secondary) 45 | } 46 | 47 | public func closePrimary() { 48 | _ = TSCLibc.close(primary) 49 | } 50 | 51 | public func close() { 52 | closeSecondary() 53 | closePrimary() 54 | } 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/Base64URLTests.swift: -------------------------------------------------------------------------------- 1 | // This source file is part of the Swift.org open source project 2 | // 3 | // Copyright (c) 2020 Apple Inc. and the Swift project authors 4 | // Licensed under Apache License v2.0 with Runtime Library Exception 5 | // 6 | // See http://swift.org/LICENSE.txt for license information 7 | // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 | 9 | import XCTest 10 | 11 | import TSCBasic 12 | 13 | 14 | class Base64URLTests: XCTestCase { 15 | 16 | func testEncode() { 17 | XCTAssertEqual([UInt8]([]).base64URL(), "") 18 | XCTAssertEqual([UInt8]([65]).base64URL(), "QQ==") 19 | XCTAssertEqual([UInt8]([65, 65]).base64URL(), "QUE=") 20 | XCTAssertEqual([UInt8]([65, 65, 65]).base64URL(), "QUFB") 21 | } 22 | 23 | func testDecode() { 24 | XCTAssertEqual([UInt8](base64URL: ""), []) 25 | XCTAssertEqual([UInt8](base64URL: "QQ=="), [65]) 26 | XCTAssertEqual([UInt8](base64URL: "QUE="), [65, 65]) 27 | XCTAssertEqual([UInt8](base64URL: "QUFB"), [65, 65, 65]) 28 | XCTAssertEqual([UInt8](base64URL: "dGVzdGluZwo="), 29 | [0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x0a]) 30 | } 31 | 32 | func testRoundTrip() { 33 | for count in 1...10 { 34 | for _ in 0...100 { 35 | var data = [UInt8](repeating: 0, count: count) 36 | for n in 0..D*[$;()^><" 38 | XCTAssertEqual("'hello\nA\"B C>D*[$;()^><'", str.spm_shellEscaped()) 39 | } 40 | 41 | func testLocalizedJoin() { 42 | XCTAssertEqual("foo", ["foo"].spm_localizedJoin(type: .conjunction)) 43 | XCTAssertEqual("foo", ["foo"].spm_localizedJoin(type: .disjunction)) 44 | 45 | XCTAssertEqual("foo or bar", ["foo", "bar"].spm_localizedJoin(type: .disjunction)) 46 | XCTAssertEqual("foo, bar, and baz", ["foo", "bar", "baz"].spm_localizedJoin(type: .conjunction)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/TSCBasic/Await.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Converts an asynchronous method having callback using Result enum to synchronous. 12 | /// 13 | /// - Parameter body: The async method must be called inside this body and closure provided in the parameter 14 | /// should be passed to the async method's completion handler. 15 | /// - Returns: The value wrapped by the async method's result. 16 | /// - Throws: The error wrapped by the async method's result 17 | @available(*, noasync) 18 | public func tsc_await(_ body: (@escaping (Result) -> Void) -> Void) throws -> T { 19 | return try tsc_await(body).get() 20 | } 21 | 22 | @available(*, noasync) 23 | public func tsc_await(_ body: (@escaping (T) -> Void) -> Void) -> T { 24 | let condition = Condition() 25 | var result: T? = nil 26 | body { theResult in 27 | condition.whileLocked { 28 | result = theResult 29 | condition.signal() 30 | } 31 | } 32 | condition.whileLocked { 33 | while result == nil { 34 | condition.wait() 35 | } 36 | } 37 | return result! 38 | } 39 | 40 | @available(*, deprecated, renamed: "tsc_await") 41 | public func await(_ body: (@escaping (Result) -> Void) -> Void) throws -> T { 42 | return try tsc_await(body).get() 43 | } 44 | 45 | @available(*, deprecated, renamed: "tsc_await") 46 | public func await(_ body: (@escaping (T) -> Void) -> Void) -> T { 47 | return tsc_await(body) 48 | } 49 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ThreadTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | typealias Thread = TSCBasic.Thread 16 | 17 | class ThreadTests: XCTestCase { 18 | 19 | func testSingleThread() { 20 | var finished = false 21 | 22 | let thread = Thread { 23 | finished = true 24 | } 25 | 26 | thread.start() 27 | thread.join() 28 | 29 | XCTAssertTrue(finished) 30 | } 31 | 32 | func testMultipleThread() { 33 | var finishedOne = false 34 | var finishedTwo = false 35 | 36 | let threadOne = Thread { 37 | finishedOne = true 38 | } 39 | 40 | let threadTwo = Thread { 41 | finishedTwo = true 42 | } 43 | 44 | threadOne.start() 45 | threadTwo.start() 46 | threadOne.join() 47 | threadTwo.join() 48 | 49 | XCTAssertTrue(finishedOne) 50 | XCTAssertTrue(finishedTwo) 51 | } 52 | 53 | func testNotDeinitBeforeExecutingTask() { 54 | let finishedCondition = Condition() 55 | var finished = false 56 | 57 | Thread { 58 | finishedCondition.whileLocked{ 59 | finished = true 60 | finishedCondition.signal() 61 | } 62 | }.start() 63 | 64 | finishedCondition.whileLocked{ 65 | while !finished { 66 | finishedCondition.wait() 67 | } 68 | } 69 | 70 | XCTAssertTrue(finished) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/TSCBasic/Result.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import protocol Foundation.CustomNSError 12 | import var Foundation.NSLocalizedDescriptionKey 13 | 14 | extension Result where Failure == Error { 15 | public func tryMap(_ closure: (Success) throws -> NewSuccess) -> Result { 16 | flatMap({ value in 17 | Result(catching: { 18 | try closure(value) 19 | }) 20 | }) 21 | } 22 | } 23 | 24 | /// Represents a string error. 25 | public struct StringError: Equatable, Codable, CustomStringConvertible, Error { 26 | 27 | /// The description of the error. 28 | public let description: String 29 | 30 | /// Create an instance of StringError. 31 | public init(_ description: String) { 32 | self.description = description 33 | } 34 | } 35 | 36 | extension StringError: CustomNSError { 37 | public var errorUserInfo: [String : Any] { 38 | return [NSLocalizedDescriptionKey: self.description] 39 | } 40 | } 41 | 42 | extension Result where Failure == StringError { 43 | /// Create an instance of Result. 44 | /// 45 | /// Errors will be encoded as StringError using their description. 46 | public init(string body: () throws -> Success) { 47 | do { 48 | self = .success(try body()) 49 | } catch let error as StringError { 50 | self = .failure(error) 51 | } catch { 52 | self = .failure(StringError(String(describing: error))) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/TSCBasic/Condition.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | 13 | /// Simple wrapper around NSCondition. 14 | /// - SeeAlso: NSCondition 15 | public struct Condition { 16 | private let _condition = NSCondition() 17 | 18 | /// Create a new condition. 19 | public init() {} 20 | 21 | /// Wait for the condition to become available. 22 | public func wait() { 23 | _condition.wait() 24 | } 25 | 26 | /// Blocks the current thread until the condition is signaled or the specified time limit is reached. 27 | /// 28 | /// - Returns: true if the condition was signaled; otherwise, false if the time limit was reached. 29 | public func wait(until limit: Date) -> Bool { 30 | return _condition.wait(until: limit) 31 | } 32 | 33 | /// Signal the availability of the condition (awake one thread waiting on 34 | /// the condition). 35 | public func signal() { 36 | _condition.signal() 37 | } 38 | 39 | /// Broadcast the availability of the condition (awake all threads waiting 40 | /// on the condition). 41 | public func broadcast() { 42 | _condition.broadcast() 43 | } 44 | 45 | /// A helper method to execute the given body while condition is locked. 46 | /// - Note: Will ensure condition unlocks even if `body` throws. 47 | public func whileLocked(_ body: () throws -> T) rethrows -> T { 48 | _condition.lock() 49 | defer { _condition.unlock() } 50 | return try body() 51 | } 52 | } 53 | 54 | #if compiler(>=5.7) 55 | extension Condition: Sendable {} 56 | #endif 57 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/PlatformTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | import TSCTestSupport 14 | 15 | @testable import TSCUtility 16 | 17 | @available(*, deprecated) 18 | final class PlatformTests: XCTestCase { 19 | func testFindCurrentPlatformDebian() { 20 | let fs = InMemoryFileSystem(files: ["/etc/debian_version": "xxx"]) 21 | XCTAssertEqual(Platform.linux(.debian), Platform.findCurrentPlatformLinux(fs)) 22 | } 23 | 24 | func testFindCurrentPlatformAndroid() { 25 | var fs = InMemoryFileSystem(files: ["/system/bin/toolbox": "xxx"]) 26 | XCTAssertEqual(Platform.android, Platform.findCurrentPlatformLinux(fs)) 27 | 28 | fs = InMemoryFileSystem(files: ["/system/bin/toybox": "xxx"]) 29 | XCTAssertEqual(Platform.android, Platform.findCurrentPlatformLinux(fs)) 30 | } 31 | 32 | func testFindCurrentPlatformFedora() { 33 | var fs = InMemoryFileSystem(files: ["/etc/fedora-release": "xxx"]) 34 | XCTAssertEqual(Platform.linux(.fedora), Platform.findCurrentPlatformLinux(fs)) 35 | 36 | fs = InMemoryFileSystem(files: ["/etc/redhat-release": "xxx"]) 37 | XCTAssertEqual(Platform.linux(.fedora), Platform.findCurrentPlatformLinux(fs)) 38 | 39 | fs = InMemoryFileSystem(files: ["/etc/centos-release": "xxx"]) 40 | XCTAssertEqual(Platform.linux(.fedora), Platform.findCurrentPlatformLinux(fs)) 41 | 42 | fs = InMemoryFileSystem(files: ["/etc/system-release": "Amazon Linux release 2 (Karoo)"]) 43 | XCTAssertEqual(Platform.linux(.fedora), Platform.findCurrentPlatformLinux(fs)) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/TSCBasic/KeyedPair.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Wrapper for exposing an item indexed by some other key type. 12 | /// 13 | /// This is intended to be used when an algorithm wants to temporarily expose 14 | /// some object as hashable based on a derived property (most commonly some 15 | /// member of the object itself), without erasing the underlying object. 16 | /// 17 | /// Example: 18 | /// 19 | /// struct Airport { 20 | /// // The name of the airport. 21 | /// let name: String 22 | /// // The names of destination airports for outgoing flights. 23 | /// let destinations: [String] 24 | /// } 25 | /// 26 | /// func whereCanIGo(from here: Airport) -> [Airport] { 27 | /// let closure = transitiveClosure([KeyedPair(airport, key: airport.name]) { 28 | /// return $0.destinations.map({ KeyedPair($0, key: $0.name) }) 29 | /// } 30 | /// return closure.map({ $0.item }) 31 | /// } 32 | public struct KeyedPair: Hashable { 33 | /// The wrapped item. 34 | public let item: T 35 | 36 | /// The exposed key. 37 | public let key: K 38 | 39 | /// Create a new hashable pair for `item` indexed by `key`. 40 | public init(_ item: T, key: K) { 41 | self.item = item 42 | self.key = key 43 | } 44 | 45 | public func hash(into hasher: inout Hasher) { 46 | hasher.combine(key) 47 | } 48 | 49 | public static func ==(lhs: KeyedPair, rhs: KeyedPair) -> Bool { 50 | return lhs.key == rhs.key 51 | } 52 | } 53 | 54 | extension KeyedPair: Sendable where T: Sendable, K: Sendable {} 55 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/POSIXTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class POSIXTests : XCTestCase { 16 | 17 | func testFileStatus() throws { 18 | try withTemporaryFile { file in 19 | XCTAssertTrue(localFileSystem.exists(file.path)) 20 | XCTAssertTrue(localFileSystem.isFile(file.path)) 21 | XCTAssertFalse(localFileSystem.isDirectory(file.path)) 22 | 23 | try withTemporaryDirectory(removeTreeOnDeinit: true) { dirPath in 24 | XCTAssertTrue(localFileSystem.exists(dirPath)) 25 | XCTAssertFalse(localFileSystem.isFile(dirPath)) 26 | XCTAssertTrue(localFileSystem.isDirectory(dirPath)) 27 | 28 | let sym = dirPath.appending(component: "hello") 29 | try localFileSystem.createSymbolicLink(sym, pointingAt: file.path, relative: false) 30 | XCTAssertTrue(localFileSystem.exists(sym)) 31 | XCTAssertTrue(localFileSystem.isFile(sym)) 32 | XCTAssertFalse(localFileSystem.isDirectory(sym)) 33 | 34 | try withTemporaryDirectory(removeTreeOnDeinit: true) { dir2Path in 35 | let dirSym = dirPath.appending(component: "dir2") 36 | try localFileSystem.createSymbolicLink(dirSym, pointingAt: dir2Path, relative: false) 37 | XCTAssertTrue(localFileSystem.exists(dirSym)) 38 | XCTAssertFalse(localFileSystem.isFile(dirSym)) 39 | XCTAssertTrue(localFileSystem.isDirectory(dirSym)) 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/miscTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | import TSCTestSupport 14 | import TSCUtility 15 | 16 | class miscTests: XCTestCase { 17 | func testClangVersionOutput() { 18 | var versionOutput = "" 19 | XCTAssert(getClangVersion(versionOutput: versionOutput) == nil) 20 | 21 | versionOutput = "some - random - string" 22 | XCTAssert(getClangVersion(versionOutput: versionOutput) == nil) 23 | 24 | versionOutput = "Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)" 25 | XCTAssert(getClangVersion(versionOutput: versionOutput) ?? Version(0, 0, 0) == Version(3, 6, 0)) 26 | 27 | versionOutput = "Ubuntu clang version 2.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)" 28 | XCTAssert(getClangVersion(versionOutput: versionOutput) ?? Version(0, 0, 0) == Version(2, 4, 0)) 29 | } 30 | 31 | func testVersion() throws { 32 | // Valid. 33 | XCTAssertEqual(Version("0.9.21-alpha.beta+1011"), 34 | Version(0,9,21, prereleaseIdentifiers: ["alpha", "beta"], buildMetadataIdentifiers: ["1011"])) 35 | XCTAssertEqual(Version("0.9.21+1011"), 36 | Version(0,9,21, prereleaseIdentifiers: [], buildMetadataIdentifiers: ["1011"])) 37 | XCTAssertEqual(Version("01.002.0003"), Version(1,2,3)) 38 | XCTAssertEqual(Version("0.9.21"), Version(0,9,21)) 39 | 40 | // Invalid. 41 | let invalidVersions = ["foo", "1", "1.0", "1.0.", "1.0.0."] 42 | for v in invalidVersions { 43 | XCTAssertTrue(Version(v) == nil) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/TSCBasicPerformanceTests/SortedArrayPerfTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCBasic 12 | import TSCTestSupport 13 | 14 | class SortedArrayPerfTests: XCTestCasePerf { 15 | func testPerformanceOfSortedArrayInAscendingOrder() { 16 | #if canImport(Darwin) 17 | measure() { 18 | var arr = SortedArray(areInIncreasingOrder: <) 19 | for i in 1...200_000 { 20 | arr.insert(i) 21 | } 22 | } 23 | #endif 24 | } 25 | 26 | func testPerformanceOfSortedArrayInsertWithDuplicates() { 27 | #if canImport(Darwin) 28 | let initial = SortedArray(0..<80_000, areInIncreasingOrder: <) 29 | 30 | measure() { 31 | var arr = initial 32 | for element in 40_000..<120_000 { 33 | arr.insert(element) 34 | } 35 | } 36 | #endif 37 | } 38 | 39 | func testPerformanceOfSortedArrayInsertContentsOfWithDuplicates() { 40 | #if canImport(Darwin) 41 | let initial = SortedArray(0..<120_000, areInIncreasingOrder: <) 42 | 43 | measure() { 44 | var arr = initial 45 | arr.insert(contentsOf: 60_000..<180_000) 46 | } 47 | #endif 48 | } 49 | 50 | func testPerformanceOfSmallSortedArrayInsertContentsOfWithDuplicates() { 51 | #if canImport(Darwin) 52 | let initial = SortedArray(0..<100, areInIncreasingOrder: <) 53 | 54 | measure() { 55 | for _ in 1...2000 { 56 | var arr = initial 57 | arr.insert(contentsOf: 50..<150) 58 | } 59 | } 60 | #endif 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/OrderedSetTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | typealias OrderedSet = TSCBasic.OrderedSet 16 | 17 | class OrderedSetTests: XCTestCase { 18 | func testBasics() { 19 | // Create an empty set. 20 | var set = OrderedSet() 21 | XCTAssertTrue(set.isEmpty) 22 | XCTAssertEqual(set.contents, []) 23 | 24 | // Create a new set with some strings. 25 | set = OrderedSet(["one", "two", "three"]) 26 | XCTAssertFalse(set.isEmpty) 27 | XCTAssertEqual(set.count, 3) 28 | XCTAssertEqual(set[0], "one") 29 | XCTAssertEqual(set[1], "two") 30 | XCTAssertEqual(set[2], "three") 31 | XCTAssertEqual(set.contents, ["one", "two", "three"]) 32 | 33 | // Try adding the same item again - the set should be unchanged. 34 | XCTAssertEqual(set.append("two"), false) 35 | XCTAssertEqual(set.count, 3) 36 | XCTAssertEqual(set[0], "one") 37 | XCTAssertEqual(set[1], "two") 38 | XCTAssertEqual(set[2], "three") 39 | 40 | // Remove the last element. 41 | let three = set.removeLast() 42 | XCTAssertEqual(set.count, 2) 43 | XCTAssertEqual(set[0], "one") 44 | XCTAssertEqual(set[1], "two") 45 | XCTAssertEqual(three, "three") 46 | 47 | // Remove all the objects. 48 | set.removeAll(keepingCapacity: true) 49 | XCTAssertEqual(set.count, 0) 50 | XCTAssertTrue(set.isEmpty) 51 | XCTAssertEqual(set.contents, []) 52 | 53 | set.append("Hello") 54 | XCTAssertEqual(set.remove("Hello"), "Hello") 55 | XCTAssertEqual(set.remove("Hello"), nil) 56 | XCTAssertEqual(set.remove("cool"), nil) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/TSCBasic/FileInfo.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | 13 | #if swift(<5.6) 14 | extension FileAttributeType: UnsafeSendable {} 15 | extension Date: UnsafeSendable {} 16 | #endif 17 | 18 | /// File system information for a particular file. 19 | public struct FileInfo: Equatable, Codable, Sendable { 20 | 21 | /// The device number. 22 | public let device: UInt64 23 | 24 | /// The inode number. 25 | public let inode: UInt64 26 | 27 | /// The size of the file. 28 | public let size: UInt64 29 | 30 | /// The modification time of the file. 31 | public let modTime: Date 32 | 33 | /// Kind of file system entity. 34 | public let posixPermissions: Int16 35 | 36 | /// Kind of file system entity. 37 | public let fileType: FileAttributeType 38 | 39 | public init(_ attrs: [FileAttributeKey : Any]) { 40 | let device = (attrs[.systemNumber] as? NSNumber)?.uint64Value 41 | assert(device != nil) 42 | self.device = device! 43 | 44 | let inode = attrs[.systemFileNumber] as? UInt64 45 | assert(inode != nil) 46 | self.inode = inode! 47 | 48 | let posixPermissions = (attrs[.posixPermissions] as? NSNumber)?.int16Value 49 | assert(posixPermissions != nil) 50 | self.posixPermissions = posixPermissions! 51 | 52 | let fileType = attrs[.type] as? FileAttributeType 53 | assert(fileType != nil) 54 | self.fileType = fileType! 55 | 56 | let size = attrs[.size] as? UInt64 57 | assert(size != nil) 58 | self.size = size! 59 | 60 | let modTime = attrs[.modificationDate] as? Date 61 | assert(modTime != nil) 62 | self.modTime = modTime! 63 | } 64 | } 65 | 66 | extension FileAttributeType: Codable {} 67 | -------------------------------------------------------------------------------- /Sources/TSCUtility/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This source file is part of the Swift.org open source project 2 | # 3 | # Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 4 | # Licensed under Apache License v2.0 with Runtime Library Exception 5 | # 6 | # See http://swift.org/LICENSE.txt for license information 7 | # See http://swift.org/CONTRIBUTORS.txt for Swift project authors 8 | 9 | add_library(TSCUtility 10 | Archiver.swift 11 | ArgumentParser.swift 12 | ArgumentParserShellCompletion.swift 13 | Bits.swift 14 | Bitstream.swift 15 | BitstreamReader.swift 16 | BitstreamWriter.swift 17 | BuildFlags.swift 18 | CollectionExtensions.swift 19 | Diagnostics.swift 20 | FSWatch.swift 21 | FloatingPointExtensions.swift 22 | Git.swift 23 | IndexStore.swift 24 | InterruptHandler.swift 25 | JSONMessageStreamingParser.swift 26 | misc.swift 27 | Netrc.swift 28 | OSLog.swift 29 | PkgConfig.swift 30 | Platform.swift 31 | PolymorphicCodable.swift 32 | ProgressAnimation.swift 33 | SerializedDiagnostics.swift 34 | SimplePersistence.swift 35 | StringExtensions.swift 36 | StringMangling.swift 37 | Triple.swift 38 | URL.swift 39 | Verbosity.swift 40 | Version.swift 41 | Versioning.swift 42 | dlopen.swift 43 | misc.swift 44 | ) 45 | target_link_libraries(TSCUtility PUBLIC 46 | TSCBasic) 47 | target_link_libraries(TSCUtility PRIVATE 48 | TSCclibc 49 | ${CMAKE_DL_LIBS} 50 | Threads::Threads) 51 | 52 | if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) 53 | if(CMAKE_SYSTEM_NAME STREQUAL OpenBSD OR CMAKE_SYSTEM_NAME STREQUAL FreeBSD) 54 | target_link_directories(TSCUtility PRIVATE /usr/local/lib) 55 | endif() 56 | if(Foundation_FOUND) 57 | target_link_libraries(TSCUtility PUBLIC 58 | FoundationNetworking) 59 | endif() 60 | endif() 61 | # NOTE(compnerd) workaround for CMake not setting up include flags yet 62 | set_target_properties(TSCUtility PROPERTIES 63 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 64 | 65 | install(TARGETS TSCUtility 66 | ARCHIVE DESTINATION lib 67 | LIBRARY DESTINATION lib 68 | RUNTIME DESTINATION bin) 69 | 70 | set_property(GLOBAL APPEND PROPERTY TSC_EXPORTS TSCUtility) 71 | -------------------------------------------------------------------------------- /Sources/TSCUtility/PolymorphicCodable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Allows encoding and decoding known polymorphic types. 12 | public protocol PolymorphicCodableProtocol: Codable { 13 | static var implementations: [PolymorphicCodableProtocol.Type] { get } 14 | } 15 | 16 | @propertyWrapper 17 | public struct PolymorphicCodable: Codable { 18 | public let value: T 19 | 20 | public init(wrappedValue value: T) { 21 | self.value = value 22 | } 23 | 24 | public var wrappedValue: T { 25 | return value 26 | } 27 | 28 | public func encode(to encoder: Encoder) throws { 29 | var container = encoder.unkeyedContainer() 30 | try container.encode(String(reflecting: type(of: value))) 31 | try container.encode(value) 32 | } 33 | 34 | public init(from decoder: Decoder) throws { 35 | var container = try decoder.unkeyedContainer() 36 | let typeCode = try container.decode(String.self) 37 | guard let klass = T.implementations.first(where: { String(reflecting: $0) == typeCode }) else { 38 | throw DecodingError.dataCorruptedError(in: container, debugDescription: "Unexpected Codable type code for concrete '\(type(of: T.self))': \(typeCode)") 39 | } 40 | 41 | self.value = try klass.init(from: container.superDecoder()) as! T 42 | } 43 | } 44 | 45 | extension PolymorphicCodable: Sendable where T: Sendable {} 46 | 47 | @propertyWrapper 48 | public struct PolymorphicCodableArray: Codable { 49 | public let value: [PolymorphicCodable] 50 | 51 | public init(wrappedValue value: [T]) { 52 | self.value = value.map{ PolymorphicCodable(wrappedValue: $0) } 53 | } 54 | 55 | public var wrappedValue: [T] { 56 | return value.map{ $0.value } 57 | } 58 | } 59 | 60 | extension PolymorphicCodableArray: Sendable where T: Sendable {} 61 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ResultTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | import TSCTestSupport 15 | 16 | private enum DummyError: Error, Equatable { 17 | case somethingWentWrong 18 | } 19 | 20 | private enum OtherDummyError: Error, Equatable { 21 | case somethingElseWentWrong 22 | case andYetAnotherThingToGoWrong 23 | } 24 | 25 | class ResultTests: XCTestCase { 26 | func testTryMap() { 27 | let result1 = Result.success(1).tryMap({ $0 + 1 }) 28 | XCTAssertEqual(result1.success, 2) 29 | 30 | let result2 = Result.failure(DummyError.somethingWentWrong).tryMap({ (value: Int) -> Int in 31 | XCTFail("should not reach here") 32 | return value 33 | }) 34 | XCTAssertEqual(result2.failure as? DummyError, DummyError.somethingWentWrong) 35 | 36 | let result3 = Result.success(1).tryMap({ (value: Int) -> Int in 37 | throw OtherDummyError.somethingElseWentWrong 38 | }) 39 | XCTAssertEqual(result3.failure as? OtherDummyError, OtherDummyError.somethingElseWentWrong) 40 | 41 | let result4 = Result.failure(DummyError.somethingWentWrong).tryMap({ (value: Int) -> Int in 42 | XCTFail("should not reach here") 43 | throw OtherDummyError.somethingElseWentWrong 44 | }) 45 | XCTAssertEqual(result4.failure as? DummyError, DummyError.somethingWentWrong) 46 | } 47 | } 48 | 49 | extension Result { 50 | var success: Success? { 51 | switch self { 52 | case .success(let success): 53 | return success 54 | case .failure: 55 | return nil 56 | } 57 | } 58 | 59 | var failure: Failure? { 60 | switch self { 61 | case .success: 62 | return nil 63 | case .failure(let error): 64 | return error 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/TSCUtility/misc.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCBasic 12 | import Foundation 13 | 14 | /// Get clang's version from the given version output string on Ubuntu. 15 | public func getClangVersion(versionOutput: String) -> Version? { 16 | // Clang outputs version in this format on Ubuntu: 17 | // Ubuntu clang version 3.6.0-2ubuntu1~trusty1 (tags/RELEASE_360/final) (based on LLVM 3.6.0) 18 | let versionStringPrefix = "Ubuntu clang version " 19 | let versionStrings = versionOutput.utf8.split(separator: UInt8(ascii: "-")).compactMap(String.init) 20 | guard let clangVersionString = versionStrings.first, 21 | clangVersionString.hasPrefix(versionStringPrefix) else { 22 | return nil 23 | } 24 | let versionStartIndex = clangVersionString.index(clangVersionString.startIndex, 25 | offsetBy: versionStringPrefix.utf8.count) 26 | let versionString = clangVersionString[versionStartIndex...] 27 | // Split major minor patch etc. 28 | let versions = versionString.utf8.split(separator: UInt8(ascii: ".")).compactMap(String.init) 29 | guard versions.count > 1, let major = Int(versions[0]), let minor = Int(versions[1]) else { 30 | return nil 31 | } 32 | return Version(major, minor, versions.count > 2 ? Int(versions[2]) ?? 0 : 0) 33 | } 34 | 35 | /// Prints the time taken to execute a closure. 36 | /// 37 | /// Note: Only for debugging purposes. 38 | public func measure(_ label: String = "", _ f: () throws -> (T)) rethrows -> T { 39 | let startTime = Date() 40 | let result = try f() 41 | let endTime = Date().timeIntervalSince(startTime) 42 | print("\(label): Time taken", endTime) 43 | return result 44 | } 45 | 46 | // for internal usage 47 | extension NSLock { 48 | internal func withLock (_ body: () throws -> T) rethrows -> T { 49 | self.lock() 50 | defer { self.unlock() } 51 | return try body() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/StringConversionTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCUtility 13 | 14 | class StringConversionTests: XCTestCase { 15 | 16 | func testManglingToBundleIdentifier() { 17 | XCTAssertEqual("foo".spm_mangledToBundleIdentifier(), "foo") 18 | XCTAssertEqual("1foo__ò".spm_mangledToBundleIdentifier(), "1foo---") 19 | XCTAssertEqual("com.example.🐴🔄".spm_mangledToBundleIdentifier(), "com.example.----") 20 | XCTAssertEqual("٠٠٠".spm_mangledToBundleIdentifier(), "---") 21 | } 22 | 23 | func testManglingToC99ExtendedIdentifier() { 24 | 25 | // Simple cases. 26 | XCTAssertEqual("foo".spm_mangledToC99ExtendedIdentifier(), "foo") 27 | 28 | // Edge cases. 29 | XCTAssertEqual("".spm_mangledToC99ExtendedIdentifier(), "") 30 | XCTAssertEqual("_".spm_mangledToC99ExtendedIdentifier(), "_") 31 | XCTAssertEqual("\n".spm_mangledToC99ExtendedIdentifier(), "_") 32 | 33 | // Invalid non-leading characters. 34 | XCTAssertEqual("_-".spm_mangledToC99ExtendedIdentifier(), "__") 35 | XCTAssertEqual("foo-bar".spm_mangledToC99ExtendedIdentifier(), "foo_bar") 36 | 37 | // Invalid leading characters. 38 | XCTAssertEqual("1".spm_mangledToC99ExtendedIdentifier(), "_") 39 | XCTAssertEqual("1foo".spm_mangledToC99ExtendedIdentifier(), "_foo") 40 | XCTAssertEqual("٠٠٠".spm_mangledToC99ExtendedIdentifier(), "_٠٠") 41 | XCTAssertEqual("12 3".spm_mangledToC99ExtendedIdentifier(), "_2_3") 42 | 43 | // FIXME: There are lots more interesting test cases to add here. 44 | var str1 = "" 45 | str1.spm_mangleToC99ExtendedIdentifier() 46 | XCTAssertEqual(str1, "") 47 | 48 | var str2 = "_" 49 | str2.spm_mangleToC99ExtendedIdentifier() 50 | XCTAssertEqual(str2, "_") 51 | 52 | var str3 = "-" 53 | str3.spm_mangleToC99ExtendedIdentifier() 54 | XCTAssertEqual(str3, "_") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/TSCBasicPerformanceTests/SynchronizedQueuePerfTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | import TSCTestSupport 15 | 16 | class SynchronizedQueuePerfTests: XCTestCasePerf { 17 | 18 | // Mock the UnitTest struct in SwiftPM/SwiftTestTool.swift 19 | struct Item { 20 | let productPath: AbsolutePath 21 | 22 | let name: String 23 | 24 | let testCase: String 25 | 26 | var specifier: String { 27 | return testCase + "/" + name 28 | } 29 | } 30 | 31 | 32 | func testEnqueueDequeue_10000() { 33 | let queue = SynchronizedQueue() 34 | let test = Item(productPath: AbsolutePath.root, name: "TestName", testCase: "TestCaseName") 35 | measure { 36 | let N = 10000 37 | for _ in 0..() 48 | let test = Item(productPath: AbsolutePath.root, name: "TestName", testCase: "TestCaseName") 49 | measure { 50 | let N = 1000 51 | for _ in 0..() 62 | let test = Item(productPath: AbsolutePath.root, name: "TestName", testCase: "TestCaseName") 63 | measure { 64 | let N = 100 65 | for _ in 0..:SHELL:-Xcc -D_CRT_SECURE_NO_WARNINGS>") 57 | target_link_libraries(TSCBasic PRIVATE 58 | TSCclibc 59 | TSCLibc) 60 | if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) 61 | if(Foundation_FOUND) 62 | target_link_libraries(TSCBasic PUBLIC 63 | Foundation) 64 | endif() 65 | endif() 66 | target_link_libraries(TSCBasic PRIVATE 67 | $<$:Pathcch>) 68 | # NOTE(compnerd) workaround for CMake not setting up include flags yet 69 | set_target_properties(TSCBasic PROPERTIES 70 | INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) 71 | 72 | install(TARGETS TSCBasic 73 | ARCHIVE DESTINATION lib 74 | LIBRARY DESTINATION lib 75 | RUNTIME DESTINATION bin) 76 | 77 | set_property(GLOBAL APPEND PROPERTY TSC_EXPORTS TSCBasic) 78 | -------------------------------------------------------------------------------- /Sources/TSCclibc/process.c: -------------------------------------------------------------------------------- 1 | #if !defined(_WIN32) 2 | 3 | #if defined(__linux__) 4 | #ifndef _GNU_SOURCE 5 | #define _GNU_SOURCE /* for posix_spawn_file_actions_addchdir_np */ 6 | #endif 7 | #endif 8 | 9 | #ifndef __GLIBC_PREREQ 10 | #define __GLIBC_PREREQ(maj, min) 0 11 | #endif 12 | 13 | #include 14 | 15 | #include "process.h" 16 | 17 | int SPM_posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t *restrict file_actions, const char *restrict path) { 18 | #if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 29) 19 | // Glibc versions prior to 2.29 don't support posix_spawn_file_actions_addchdir_np, impacting: 20 | // - Amazon Linux 2 (EoL mid-2025) 21 | return ENOSYS; 22 | #elif defined(__ANDROID__) && __ANDROID_API__ < 34 23 | // Android versions prior to 14 (API level 34) don't support posix_spawn_file_actions_addchdir_np 24 | return ENOSYS; 25 | #elif defined(__OpenBSD__) || defined(__QNX__) 26 | // Currently missing as of: 27 | // - OpenBSD 7.5 (April 2024) 28 | // - QNX 8 (December 2023) 29 | return ENOSYS; 30 | #elif defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__ANDROID__) || defined(__musl__) 31 | // Pre-standard posix_spawn_file_actions_addchdir_np version available in: 32 | // - Solaris 11.3 (October 2015) 33 | // - Glibc 2.29 (February 2019) 34 | // - macOS 10.15 (October 2019) 35 | // - musl 1.1.24 (October 2019) 36 | // - FreeBSD 13.1 (May 2022) 37 | // - Android 14 (October 2023) 38 | return posix_spawn_file_actions_addchdir_np((posix_spawn_file_actions_t *)file_actions, path); 39 | #else 40 | // Standardized posix_spawn_file_actions_addchdir version (POSIX.1-2024, June 2024) available in: 41 | // - Solaris 11.4 (August 2018) 42 | // - NetBSD 10.0 (March 2024) 43 | return posix_spawn_file_actions_addchdir((posix_spawn_file_actions_t *)file_actions, path); 44 | #endif 45 | } 46 | 47 | bool SPM_posix_spawn_file_actions_addchdir_np_supported() { 48 | #if (defined(__GLIBC__) && !__GLIBC_PREREQ(2, 29)) || (defined(__OpenBSD__)) || (defined(__ANDROID__) && __ANDROID_API__ < 34) || (defined(__QNX__)) 49 | return false; 50 | #else 51 | return true; 52 | #endif 53 | } 54 | 55 | int SPM_posix_spawnp(pid_t *pid, const char *file, const posix_spawn_file_actions_t *actions, const posix_spawnattr_t *attr, char *const argv[], char *const env[]) { 56 | return posix_spawnp(pid, file, actions, attr, argv, env); 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /Sources/TSCBasic/RegEx.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | 13 | /// A helpful wrapper around NSRegularExpression. 14 | /// - SeeAlso: NSRegularExpression 15 | @available(*, deprecated, message: "Use Swift `Regex` type instead") 16 | public struct RegEx { 17 | private let regex: NSRegularExpression 18 | public typealias Options = NSRegularExpression.Options 19 | 20 | /// Creates a new Regex using `pattern`. 21 | /// 22 | /// - Parameters: 23 | /// - pattern: A valid Regular Expression pattern 24 | /// - options: NSRegularExpression.Options on how the RegEx should be processed. 25 | /// - Note: Deliminators must be double escaped. Once for Swift and once for RegEx. 26 | /// Example, to math a negative integer: `RegEx(pattern: "-\d")` -> `RegEx(pattern: "-\\d")` 27 | /// - SeeAlso: NSRegularExpression 28 | /// - Throws: An Error if `pattern` is an invalid Regular Expression. 29 | public init(pattern: String, options: Options = []) throws { 30 | self.regex = try NSRegularExpression(pattern: pattern, options: options) 31 | } 32 | 33 | /// Returns match groups for every match of the Regex. 34 | /// 35 | /// For every match in the string, it constructs the collection 36 | /// of groups matched. 37 | /// 38 | /// RegEx(pattern: "([a-z]+)([0-9]+)").matchGroups(in: "foo1 bar2 baz3") 39 | /// 40 | /// Returns `[["foo", "1"], ["bar", "2"], ["baz", "3"]]`. 41 | /// 42 | /// - Parameters: 43 | /// - in: A string to check for matches to the whole Regex. 44 | /// - Returns: A collection where each elements is the collection of matched groups. 45 | public func matchGroups(in string: String) -> [[String]] { 46 | let nsString = NSString(string: string) 47 | 48 | return regex.matches(in: string, options: [], range: NSMakeRange(0, nsString.length)).map{ match -> [String] in 49 | return (1 ..< match.numberOfRanges).map { idx -> String in 50 | let range = match.range(at: idx) 51 | return range.location == NSNotFound ? "" : nsString.substring(with: range) 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/TSCBasic/CacheableSequence.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2018 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Wrapper for caching an arbitrary sequence. 12 | public final class CacheableSequence: Sequence { 13 | public typealias Element = T.Element 14 | public typealias Iterator = CacheableSequenceIterator 15 | 16 | /// The list of consumed items. 17 | fileprivate var items: [Element] = [] 18 | 19 | /// An iterator on the underlying sequence, until complete. 20 | fileprivate var it: T.Iterator? 21 | 22 | public init(_ sequence: T) { 23 | self.it = sequence.makeIterator() 24 | } 25 | 26 | public func makeIterator() -> Iterator { 27 | return CacheableSequenceIterator(self) 28 | } 29 | 30 | /// Get the item at the given index. 31 | /// 32 | /// The index must either be at most one past the number of already captured 33 | /// items. 34 | fileprivate subscript(_ index: Int) -> Element? { 35 | assert(index >= 0 && index <= items.count) 36 | if index < items.count { 37 | return items[index] 38 | } else if self.it != nil { 39 | // If we still have an iterator, attempt to consume a new item. 40 | guard let item = it!.next() else { 41 | // We reached the end of the sequence, we can discard the iterator. 42 | self.it = nil 43 | return nil 44 | } 45 | items.append(item) 46 | return items[index] 47 | } else { 48 | return nil 49 | } 50 | } 51 | } 52 | 53 | /// An iterator for a CacheableSequence. 54 | public final class CacheableSequenceIterator: IteratorProtocol { 55 | public typealias Element = T.Element 56 | 57 | /// The index of the iterator. 58 | var index = 0 59 | 60 | /// The sequence being iterated. 61 | let sequence: CacheableSequence 62 | 63 | init(_ sequence: CacheableSequence) { 64 | self.sequence = sequence 65 | } 66 | 67 | public func next() -> Element? { 68 | if let item = self.sequence[index] { 69 | index += 1 70 | return item 71 | } 72 | return nil 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Sources/TSCBasic/LazyCache.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import class Foundation.NSLock 12 | 13 | // FIXME: This wrapper could benefit from local static variables, in which case 14 | // we could embed the cache object inside the accessor. 15 | // 16 | /// Thread-safe lazily cached methods. 17 | /// 18 | /// The `lazy` annotation in Swift does not result in a thread-safe accessor, 19 | /// which can make it an easy source of hard-to-find concurrency races. This 20 | /// class defines a wrapper designed to be used as an alternative for 21 | /// `lazy`. Example usage: 22 | /// 23 | /// ``` 24 | /// class Foo { 25 | /// var bar: Int { return barCache.getValue(self) } 26 | /// var barCache = LazyCache(someExpensiveMethod) 27 | /// 28 | /// func someExpensiveMethod() -> Int { ... } 29 | /// } 30 | /// ``` 31 | /// 32 | /// See: https://bugs.swift.org/browse/SR-1042 33 | @available(*, deprecated, message: "This implementation does not work -- https://github.com/apple/swift-tools-support-core/issues/385") 34 | public struct LazyCache { 35 | // FIXME: It would be nice to avoid a per-instance lock, but this type isn't 36 | // intended for creating large numbers of instances of. We also really want 37 | // a reader-writer lock or something similar here. 38 | private var lock = NSLock() 39 | let body: (Class) -> () -> T 40 | var cachedValue: T? 41 | 42 | /// Create a lazy cache from a method value. 43 | public init(_ body: @escaping (Class) -> () -> T) { 44 | self.body = body 45 | } 46 | 47 | /// Get the cached value, computing it if necessary. 48 | public mutating func getValue(_ instance: Class) -> T { 49 | // FIXME: This is unfortunate, see note w.r.t. the lock. 50 | return lock.withLock { 51 | if let value = cachedValue { 52 | return value 53 | } else { 54 | let result = body(instance)() 55 | cachedValue = result 56 | return result 57 | } 58 | } 59 | } 60 | } 61 | 62 | #if swift(>=5.6) 63 | @available(*, unavailable) // until https://github.com/apple/swift-tools-support-core/issues/385 is fixed 64 | extension LazyCache: Sendable {} 65 | #endif 66 | -------------------------------------------------------------------------------- /Sources/TSCUtility/Hex.swift: -------------------------------------------------------------------------------- 1 | // This source file is part of the Swift.org open source project 2 | // 3 | // Copyright (c) 2020 Apple Inc. and the Swift project authors 4 | // Licensed under Apache License v2.0 with Runtime Library Exception 5 | // 6 | // See http://swift.org/LICENSE.txt for license information 7 | // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 8 | 9 | import Foundation 10 | 11 | 12 | @usableFromInline 13 | internal func char(forNibble value: UInt8) -> CChar { 14 | switch value { 15 | case 0 ..< 10: 16 | return CChar(UInt8(ascii: "0") + value) 17 | default: 18 | precondition(value < 16) 19 | return CChar(UInt8(ascii: "a") + value - 10) 20 | } 21 | } 22 | 23 | @usableFromInline 24 | internal func nibble(forHexChar char: UInt8) -> UInt8? { 25 | switch char { 26 | case UInt8(ascii: "0")...UInt8(ascii: "9"): 27 | return char - UInt8(ascii: "0") 28 | case UInt8(ascii: "a")...UInt8(ascii: "f"): 29 | return 10 + char - UInt8(ascii: "a") 30 | case UInt8(ascii: "A")...UInt8(ascii: "F"): 31 | return 10 + char - UInt8(ascii: "a") 32 | default: 33 | return nil 34 | } 35 | } 36 | 37 | @inlinable 38 | public func hexEncode(_ bytes: T) -> [CChar] where T.Element == UInt8, T.Index == Int { 39 | var output = [CChar](repeating: 0, count: Int(bytes.count) * 2) 40 | for (i, byte) in bytes.enumerated() { 41 | output[i*2 + 0] = char(forNibble: (byte >> 4) & 0xF) 42 | output[i*2 + 1] = char(forNibble: (byte >> 0) & 0xF) 43 | } 44 | return output 45 | } 46 | 47 | @inlinable 48 | public func hexEncode(_ bytes: T) -> String where T.Element == UInt8, T.Index == Int { 49 | let chars = hexEncode(bytes) as [CChar] 50 | return String(tsc_fromUTF8: chars.map{ UInt8($0) }) 51 | } 52 | 53 | extension String { 54 | /// Decode the string as a sequence of hex bytes (with no leading 0x prefix). 55 | @inlinable 56 | public func tsc_hexDecode() -> [UInt8]? { 57 | let utf8 = self.utf8 58 | let count = utf8.count 59 | let byteCount = count / 2 60 | if count != byteCount * 2 { return nil } 61 | 62 | var result = [UInt8](repeating: 0, count: byteCount) 63 | var seq = utf8.makeIterator() 64 | for i in 0 ..< byteCount { 65 | guard let hi = nibble(forHexChar: seq.next()!) else { return nil } 66 | guard let lo = nibble(forHexChar: seq.next()!) else { return nil } 67 | result[i] = (hi << 4) | lo 68 | } 69 | return result 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/TSCBasic/SynchronizedQueue.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// This class can be used as a shared queue between multiple threads providing 12 | /// thread safe APIs. 13 | public final class SynchronizedQueue { 14 | 15 | /// Linked list node. 16 | private final class Node { 17 | var value: Element 18 | var next: Node? 19 | init(_ value: Element) { 20 | self.value = value 21 | } 22 | } 23 | 24 | /// Head node of the queue. 25 | private var head: Node? = nil 26 | 27 | /// Tail node of the queue. 28 | private weak var tail: Node? = nil 29 | 30 | /// Condition variable to block the thread trying dequeue and queue is empty. 31 | private var notEmptyCondition: Condition 32 | 33 | /// Create a default instance of queue. 34 | public init() { 35 | notEmptyCondition = Condition() 36 | } 37 | 38 | /// Safely enqueue an element to end of the queue and signals a thread blocked on dequeue. 39 | /// 40 | /// - Parameters: 41 | /// - element: The element to be enqueued. 42 | public func enqueue(_ element: Element) { 43 | notEmptyCondition.whileLocked { 44 | let node = Node(element) 45 | if head == nil { 46 | head = node 47 | } else { 48 | tail?.next = node 49 | } 50 | // Update the tail node. 51 | tail = node 52 | 53 | // Signal a thread blocked on dequeue. 54 | notEmptyCondition.signal() 55 | } 56 | } 57 | 58 | /// Dequeue an element from front of the queue. Blocks the calling thread until an element is available. 59 | /// 60 | /// - Returns: First element in the queue. 61 | public func dequeue() -> Element { 62 | return notEmptyCondition.whileLocked { 63 | // Wait until we have an element available in the queue. 64 | while head == nil { 65 | notEmptyCondition.wait() 66 | } 67 | 68 | // There are elements in the queue, `head` is not nil. 69 | let element = head!.value 70 | 71 | // Remove the first node. 72 | head = head!.next 73 | 74 | return element 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/TSCBasic/CodableResult.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2019 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Codable wrapper for Result 12 | public struct CodableResult: Codable where Success: Codable, Failure: Codable & Error { 13 | private enum CodingKeys: String, CodingKey { 14 | case success, failure 15 | } 16 | 17 | public let result: Result 18 | public init(result: Result) { 19 | self.result = result 20 | } 21 | 22 | public func encode(to encoder: Encoder) throws { 23 | var container = encoder.container(keyedBy: CodingKeys.self) 24 | switch self.result { 25 | case .success(let value): 26 | var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .success) 27 | try unkeyedContainer.encode(value) 28 | case .failure(let error): 29 | var unkeyedContainer = container.nestedUnkeyedContainer(forKey: .failure) 30 | try unkeyedContainer.encode(error) 31 | } 32 | } 33 | 34 | public init(from decoder: Decoder) throws { 35 | let values = try decoder.container(keyedBy: CodingKeys.self) 36 | guard let key = values.allKeys.first(where: values.contains) else { 37 | throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Did not find a matching key")) 38 | } 39 | switch key { 40 | case .success: 41 | var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key) 42 | let value = try unkeyedValues.decode(Success.self) 43 | self.init(result: .success(value)) 44 | case .failure: 45 | var unkeyedValues = try values.nestedUnkeyedContainer(forKey: key) 46 | let error = try unkeyedValues.decode(Failure.self) 47 | self.init(result: .failure(error)) 48 | } 49 | } 50 | } 51 | 52 | extension CodableResult: Sendable where Success: Sendable, Failure: Sendable {} 53 | 54 | extension CodableResult where Failure == StringError { 55 | public init(body: () throws -> Success) { 56 | do { 57 | self.init(result: .success(try body())) 58 | } catch let error as StringError { 59 | self.init(result: .failure(error)) 60 | } catch { 61 | self.init(result: .failure(StringError(String(describing: error)))) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/PathShimTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | import XCTest 13 | 14 | import TSCBasic 15 | 16 | class PathShimTests : XCTestCase { 17 | 18 | func testRescursiveDirectoryCreation() { 19 | // For the tests we'll need a temporary directory. 20 | try! withTemporaryDirectory(removeTreeOnDeinit: true) { path in 21 | // Create a directory under several ancestor directories. 22 | let dirPath = path.appending(components: "abc", "def", "ghi", "mno", "pqr") 23 | try! makeDirectories(dirPath) 24 | 25 | // Check that we were able to actually create the directory. 26 | XCTAssertTrue(localFileSystem.isDirectory(dirPath)) 27 | 28 | // Check that there's no error if we try to create the directory again. 29 | try! makeDirectories(dirPath) 30 | } 31 | } 32 | } 33 | 34 | class WalkTests : XCTestCase { 35 | 36 | func testNonRecursive() throws { 37 | #if os(Android) 38 | let root = "/system" 39 | var expected: [AbsolutePath] = [ 40 | "\(root)/usr", 41 | "\(root)/bin", 42 | "\(root)/etc" 43 | ] 44 | #else 45 | let root = "" 46 | var expected: [AbsolutePath] = [ 47 | "/usr", 48 | "/bin", 49 | "/sbin" 50 | ] 51 | #endif 52 | for x in try walk(AbsolutePath(validating: "\(root)/"), recursively: false) { 53 | if let i = expected.firstIndex(of: x) { 54 | expected.remove(at: i) 55 | } 56 | #if os(Android) 57 | XCTAssertEqual(3, x.components.count) 58 | #else 59 | XCTAssertEqual(2, x.components.count) 60 | #endif 61 | } 62 | XCTAssertEqual(expected.count, 0) 63 | } 64 | 65 | func testRecursive() { 66 | let root = AbsolutePath(#file).parentDirectory.parentDirectory.parentDirectory.appending(component: "Sources") 67 | var expected = [ 68 | root.appending(component: "TSCBasic"), 69 | root.appending(component: "TSCUtility") 70 | ] 71 | for x in try! walk(root) { 72 | if let i = expected.firstIndex(of: x) { 73 | expected.remove(at: i) 74 | } 75 | } 76 | XCTAssertEqual(expected, []) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ProcessEnvTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | import XCTest 13 | 14 | import TSCBasic 15 | import TSCTestSupport 16 | 17 | class ProcessEnvTests: XCTestCase { 18 | func testEnvVars() throws { 19 | let key = "SWIFTPM_TEST_FOO" 20 | XCTAssertEqual(ProcessEnv.vars[key], nil) 21 | try ProcessEnv.setVar(key, value: "BAR") 22 | XCTAssertEqual(ProcessEnv.vars[key], "BAR") 23 | try ProcessEnv.unsetVar(key) 24 | XCTAssertEqual(ProcessEnv.vars[key], nil) 25 | } 26 | 27 | func testChdir() throws { 28 | try testWithTemporaryDirectory { tmpdir in 29 | let path = try resolveSymlinks(tmpdir) 30 | try ProcessEnv.chdir(path) 31 | XCTAssertEqual(ProcessEnv.cwd, path) 32 | } 33 | } 34 | 35 | func testWithCustomEnv() throws { 36 | enum CustomEnvError: Swift.Error { 37 | case someError 38 | } 39 | 40 | let key = "XCTEST_TEST" 41 | let value = "TEST" 42 | XCTAssertNil(ProcessEnv.vars[key]) 43 | try withCustomEnv([key: value]) { 44 | XCTAssertEqual(value, ProcessEnv.vars[key]) 45 | } 46 | XCTAssertNil(ProcessEnv.vars[key]) 47 | do { 48 | try withCustomEnv([key: value]) { 49 | XCTAssertEqual(value, ProcessEnv.vars[key]) 50 | throw CustomEnvError.someError 51 | } 52 | } catch CustomEnvError.someError { 53 | } catch { 54 | XCTFail("Incorrect error thrown") 55 | } 56 | XCTAssertNil(ProcessEnv.vars[key]) 57 | } 58 | 59 | func testEnvironmentKeys() throws { 60 | XCTAssertEqual(ProcessEnvironmentKey("Key"), "Key") 61 | #if os(Windows) 62 | XCTAssertEqual(ProcessEnvironmentKey("Key"), "KEY") 63 | #else 64 | XCTAssertNotEqual(ProcessEnvironmentKey("Key"), "KEY") 65 | #endif 66 | } 67 | 68 | func testEnvironmentKeysCodable() throws { 69 | let encoder = JSONEncoder() 70 | let json = try encoder.encode(ProcessEnvironmentKey("foo")) 71 | XCTAssertEqual(String(decoding: json, as: UTF8.self), #""foo""#) 72 | 73 | let decoder = JSONDecoder() 74 | let result = try decoder.decode(ProcessEnvironmentKey.self, from: json) 75 | XCTAssertEqual(result, ProcessEnvironmentKey("foo")) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/TripleTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCUtility 12 | import XCTest 13 | 14 | @available(*, deprecated) 15 | class TripleTests : XCTestCase { 16 | func testTriple() { 17 | let linux = try? Triple("x86_64-unknown-linux-gnu") 18 | XCTAssertNotNil(linux) 19 | XCTAssertEqual(linux!.os, .linux) 20 | XCTAssertNil(linux!.osVersion) 21 | XCTAssertEqual(linux!.abi, .other(name: "gnu")) 22 | XCTAssertNil(linux!.abiVersion) 23 | 24 | let macos = try? Triple("x86_64-apple-macosx10.15") 25 | XCTAssertNotNil(macos!) 26 | XCTAssertEqual(macos!.osVersion, "10.15") 27 | let newVersion = "10.12" 28 | let tripleString = macos!.tripleString(forPlatformVersion: newVersion) 29 | XCTAssertEqual(tripleString, "x86_64-apple-macosx10.12") 30 | let macosNoX = try? Triple("x86_64-apple-macos12.2") 31 | XCTAssertNotNil(macosNoX!) 32 | XCTAssertEqual(macosNoX!.os, .macOS) 33 | XCTAssertEqual(macosNoX!.osVersion, "12.2") 34 | 35 | let android = try? Triple("aarch64-unknown-linux-android24") 36 | XCTAssertNotNil(android) 37 | XCTAssertEqual(android!.os, .linux) 38 | XCTAssertEqual(android!.abi, .android) 39 | XCTAssertEqual(android!.abiVersion, "24") 40 | 41 | let linuxWithABIVersion = try? Triple("x86_64-unknown-linux-gnu42") 42 | XCTAssertEqual(linuxWithABIVersion!.abi, .other(name: "gnu")) 43 | XCTAssertEqual(linuxWithABIVersion!.abiVersion, "42") 44 | } 45 | 46 | func testEquality() throws { 47 | let macOSTriple = try Triple("arm64-apple-macos") 48 | let macOSXTriple = try Triple("arm64-apple-macosx") 49 | XCTAssertEqual(macOSTriple, macOSXTriple) 50 | 51 | let intelMacOSTriple = try Triple("x86_64-apple-macos") 52 | XCTAssertNotEqual(macOSTriple, intelMacOSTriple) 53 | 54 | let linuxWithoutGNUABI = try Triple("x86_64-unknown-linux") 55 | let linuxWithGNUABI = try Triple("x86_64-unknown-linux-gnu") 56 | XCTAssertNotEqual(linuxWithoutGNUABI, linuxWithGNUABI) 57 | } 58 | 59 | func testWASI() throws { 60 | let wasi = try Triple("wasm32-unknown-wasi") 61 | 62 | // WASI dynamic libraries are only experimental, 63 | // but SwiftPM requires this property not to crash. 64 | _ = wasi.dynamicLibraryExtension 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/SHA256Tests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright 2016 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | @testable import TSCBasic 12 | import XCTest 13 | 14 | class SHA256Tests: XCTestCase { 15 | 16 | func testBasics() throws { 17 | let sha256 = SHA256() 18 | let knownHashes = [ 19 | "": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 20 | "The quick brown fox jumps over the lazy dog": "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", 21 | "@#$%^&*&^%$#$%^&*()&^%$#$%^&*()@#$%^&*()": "8abeb32e2aed588be8fc73e995c79ee535651bc9642faf03fb6f111d270e9e2e", 22 | "Hello!": "334d016f755cd6dc58c53a86e183882f8ec14f52fb05345887c8a5edd42c87b7", 23 | "तुमसे न हो पाएगा": "bcd3be1284e4d5ef65de89a6203f174fbc4f6bafb376a5e62d36afd3cf93427f", 24 | ] 25 | 26 | // Test known hashes. 27 | for (input, hash) in knownHashes { 28 | XCTAssertEqual(sha256.hash(ByteString(input)).hexadecimalRepresentation, hash, "Incorrect value for \(input)") 29 | } 30 | 31 | // Test a big input. 32 | let byte = "f" 33 | let stream = BufferedOutputByteStream() 34 | for _ in 0..<20000 { 35 | stream.send(byte) 36 | } 37 | XCTAssertEqual(sha256.hash(stream.bytes).hexadecimalRepresentation, "23d00697ba26b4140869bab958431251e7e41982794d41b605b6a1d5dee56abf") 38 | } 39 | 40 | func testLargeData() { 41 | let data: [UInt8] = (0..<1788).map { _ in 0x03 } 42 | let digest = SHA256().hash(ByteString(data)).hexadecimalRepresentation 43 | XCTAssertEqual(digest, "907422e2f24d749d0add2b504ccae8ad1aa392477591905880fb2dc494e33d63") 44 | } 45 | 46 | #if canImport(Darwin) 47 | @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) 48 | func testCryptoKitSHA256() { 49 | let sha = _CryptoKitSHA256() 50 | XCTAssertEqual( 51 | sha.hash(ByteString("The quick brown fox jumps over the lazy dog")).hexadecimalRepresentation, 52 | "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" 53 | ) 54 | } 55 | #endif 56 | 57 | @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) 58 | func testInternalSHA256() { 59 | let sha = InternalSHA256() 60 | XCTAssertEqual( 61 | sha.hash(ByteString("The quick brown fox jumps over the lazy dog")).hexadecimalRepresentation, 62 | "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ByteStringTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | // Allow simple conversion from String, in the tests target. 16 | extension ByteString { 17 | init(_ string: String) { 18 | self.init(encodingAsUTF8: string) 19 | } 20 | } 21 | 22 | class ByteStringTests: XCTestCase { 23 | func testInitializers() { 24 | do { 25 | let data: ByteString = [1] 26 | XCTAssertEqual(data.contents, [1]) 27 | } 28 | 29 | XCTAssertEqual(ByteString([1]).contents, [1]) 30 | 31 | XCTAssertEqual(ByteString("A").contents, [65]) 32 | 33 | // Test ExpressibleByStringLiteral initialization. 34 | XCTAssertEqual(ByteString([65]), "A") 35 | } 36 | 37 | func testAccessors() { 38 | // Test basic accessors. 39 | XCTAssertEqual(ByteString([]).count, 0) 40 | XCTAssertEqual(ByteString([1, 2]).count, 2) 41 | } 42 | 43 | func testAsString() { 44 | XCTAssertEqual(ByteString("hello").validDescription, "hello") 45 | XCTAssertEqual(ByteString([0xFF,0xFF]).validDescription, nil) 46 | } 47 | 48 | func testDescription() { 49 | XCTAssertEqual(ByteString("Hello, world!").description, "Hello, world!") 50 | } 51 | 52 | func testHashable() { 53 | var s = Set([ByteString([1]), ByteString([2])]) 54 | XCTAssert(s.contains(ByteString([1]))) 55 | XCTAssert(s.contains(ByteString([2]))) 56 | XCTAssert(!s.contains(ByteString([3]))) 57 | 58 | // Insert a long string which tests overflow in the hash function. 59 | let long = ByteString([UInt8](0 ..< 100)) 60 | XCTAssert(!s.contains(long)) 61 | s.insert(long) 62 | XCTAssert(s.contains(long)) 63 | } 64 | 65 | func testByteStreamable() { 66 | let s = BufferedOutputByteStream() 67 | s.send(ByteString([1, 2, 3])) 68 | XCTAssertEqual(s.bytes, [1, 2, 3]) 69 | } 70 | 71 | func testWithData() { 72 | let byteString = ByteString([0xde, 0xad, 0xbe, 0xef]) 73 | byteString.withData { data in 74 | XCTAssertEqual(data.count, 4) 75 | XCTAssertEqual(data[0], 0xde) 76 | XCTAssertEqual(data[1], 0xad) 77 | XCTAssertEqual(data[2], 0xbe) 78 | XCTAssertEqual(data[3], 0xef) 79 | } 80 | } 81 | 82 | func testHexadecimalRepresentation() { 83 | let byteString = ByteString([0xde, 0xad, 0xbe, 0xef]) 84 | XCTAssertEqual(byteString.hexadecimalRepresentation, "deadbeef") 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ConditionTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import Foundation 13 | 14 | import TSCBasic 15 | 16 | class ConditionTests: XCTestCase { 17 | func testSignal() { 18 | let condition = Condition() 19 | var waiting = false 20 | var doneWaiting = false 21 | let thread = Thread { 22 | condition.whileLocked{ 23 | waiting = true 24 | condition.wait() 25 | doneWaiting = true 26 | } 27 | } 28 | thread.start() 29 | 30 | // Wait for the thread to start waiting 31 | while condition.whileLocked({ !waiting }) {} 32 | 33 | // Signal and wait for the thread to complete. 34 | condition.whileLocked{ 35 | condition.signal() 36 | } 37 | thread.join() 38 | 39 | // Wait for the thread to complete. 40 | XCTAssert(doneWaiting) 41 | } 42 | 43 | func testBroadcast() { 44 | let condition = Condition() 45 | var waiting = [false, false] 46 | var doneWaiting = [false, false] 47 | let threads = [0, 1].map { i -> Thread in 48 | let thread = Thread { 49 | condition.whileLocked{ 50 | waiting[i] = true 51 | condition.wait() 52 | doneWaiting[i] = true 53 | } 54 | } 55 | thread.start() 56 | return thread 57 | } 58 | 59 | // Wait for each thread to start waiting. 60 | while condition.whileLocked({ !waiting[0] || !waiting[1] }) {} 61 | 62 | // Signal and wait for the thread to complete. 63 | condition.whileLocked{ 64 | condition.broadcast() 65 | } 66 | threads.forEach{ $0.join() } 67 | 68 | // Wait for each thread to complete. 69 | XCTAssert(doneWaiting[0]) 70 | XCTAssert(doneWaiting[1]) 71 | } 72 | 73 | func testWaitUntil() { 74 | let condition = Condition() 75 | var waiting = false 76 | var doneWaiting = false 77 | 78 | let thread = Thread { 79 | condition.whileLocked{ 80 | waiting = true 81 | let timeLimitReached = !condition.wait(until: Date() + 0.1) 82 | if timeLimitReached { 83 | doneWaiting = true 84 | } 85 | } 86 | } 87 | thread.start() 88 | 89 | // Wait for the thread to start waiting 90 | while condition.whileLocked({ !waiting }) {} 91 | // We expect condition wait to timeout. 92 | thread.join() 93 | 94 | XCTAssert(doneWaiting) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/StringTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCUtility 12 | import XCTest 13 | 14 | class StringTests: XCTestCase { 15 | func testTrailingChomp() { 16 | XCTAssertEqual("abc\n".spm_chomp(), "abc") 17 | XCTAssertEqual("abc\r\n".spm_chomp(), "abc") 18 | XCTAssertEqual("abc\r\n\r\n".spm_chomp(), "abc") 19 | XCTAssertEqual("abc\r\n\r\r\n".spm_chomp(), "abc\r\n\r") 20 | XCTAssertEqual("abc\n \n".spm_chomp(), "abc\n ") 21 | } 22 | 23 | func testSeparatorChomp() { 24 | XCTAssertEqual("abc".spm_chomp(separator: "c"), "ab") 25 | XCTAssertEqual("abc\n".spm_chomp(separator: "c"), "abc\n") 26 | XCTAssertEqual("abc\n c".spm_chomp(separator: "c"), "abc\n ") 27 | } 28 | 29 | func testEmptyChomp() { 30 | XCTAssertEqual("".spm_chomp(), "") 31 | XCTAssertEqual(" ".spm_chomp(), " ") 32 | XCTAssertEqual("\n\n".spm_chomp(), "") 33 | } 34 | 35 | func testChuzzle() { 36 | XCTAssertNil("".spm_chuzzle()) 37 | XCTAssertNil(" ".spm_chuzzle()) 38 | XCTAssertNil(" \t ".spm_chuzzle()) 39 | XCTAssertNil(" \t\n".spm_chuzzle()) 40 | XCTAssertNil(" \t\r\n".spm_chuzzle()) 41 | XCTAssertEqual(" a\t\r\n".spm_chuzzle(), "a") 42 | XCTAssertEqual("b".spm_chuzzle(), "b") 43 | } 44 | 45 | func testSplitAround() { 46 | func eq(_ lhs: (String, String?), _ rhs: (String, String?), file: StaticString = #file, line: UInt = #line) { 47 | XCTAssertEqual(lhs.0, rhs.0, file: file, line: line) 48 | XCTAssertEqual(lhs.1, rhs.1, file: file, line: line) 49 | } 50 | 51 | eq("".spm_split(around: "::"), ("", nil)) 52 | eq("foo".spm_split(around: "::"), ("foo", nil)) 53 | eq("foo::".spm_split(around: "::"), ("foo", "")) 54 | eq("::bar".spm_split(around: "::"), ("", "bar")) 55 | eq("foo::bar".spm_split(around: "::"), ("foo", "bar")) 56 | } 57 | } 58 | 59 | class URLTests: XCTestCase { 60 | @available(*, deprecated) 61 | func testSchema() { 62 | XCTAssertEqual(TSCUtility.URL.scheme("http://github.com/foo/bar"), "http") 63 | XCTAssertEqual(TSCUtility.URL.scheme("https://github.com/foo/bar"), "https") 64 | XCTAssertEqual(TSCUtility.URL.scheme("HTTPS://github.com/foo/bar"), "https") 65 | XCTAssertEqual(TSCUtility.URL.scheme("git@github.com/foo/bar"), "git") 66 | XCTAssertEqual(TSCUtility.URL.scheme("ssh@github.com/foo/bar"), "ssh") 67 | XCTAssertNil(TSCUtility.URL.scheme("github.com/foo/bar")) 68 | XCTAssertNil(TSCUtility.URL.scheme("user:/github.com/foo/bar")) 69 | XCTAssertNil(TSCUtility.URL.scheme("/path/to/something@2/hello")) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sources/TSCBasic/EditDistance.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Computes the number of edits needed to transform source string to target. 12 | /// 13 | /// - Complexity: O(_n*m_), where *n* is the length of the source String and 14 | /// *m* is the length of the target one. 15 | public func editDistance(_ source: String, _ target: String) -> Int { 16 | // FIXME: We should use the new `CollectionDifference` API once the 17 | // deployment target is bumped. 18 | if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { 19 | return collectionDiffEditDistance(source, target) 20 | } 21 | else { 22 | return internalEditDistance(source, target) 23 | } 24 | } 25 | 26 | @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) 27 | func collectionDiffEditDistance(_ source: String, _ target: String) -> Int { 28 | let difference = target.difference(from: source) 29 | return max(difference.insertions.count, difference.removals.count) 30 | } 31 | 32 | func internalEditDistance(_ first: String, _ second: String) -> Int { 33 | let a = Array(first.utf16) 34 | let b = Array(second.utf16) 35 | var distance = [[Int]](repeating: [Int](repeating: 0, count: b.count + 1), count: a.count + 1) 36 | for i in 0...a.count { 37 | for j in 0...b.count { 38 | if i == 0 { 39 | distance[i][j] = j 40 | } else if j == 0 { 41 | distance[i][j] = i 42 | } else if a[i - 1] == b[j - 1] { 43 | distance[i][j] = distance[i - 1][j - 1] 44 | } else { 45 | let insertion = distance[i][ j - 1] 46 | let deletion = distance[i - 1][j] 47 | let replacement = distance[i - 1][j - 1] 48 | distance[i][j] = 1 + min(insertion, deletion, replacement) 49 | } 50 | } 51 | } 52 | return distance[a.count][b.count] 53 | } 54 | 55 | /// Finds the "best" match for a `String` from an array of possible options. 56 | /// 57 | /// - Parameters: 58 | /// - input: The input `String` to match. 59 | /// - options: The available options for `input`. 60 | /// 61 | /// - Returns: The best match from the given `options`, or `nil` if none were sufficiently close. 62 | public func bestMatch(for input: String, from options: [String]) -> String? { 63 | return options 64 | .map { ($0, editDistance(input, $0)) } 65 | // Filter out unreasonable edit distances. Based on: 66 | // https://github.com/apple/swift/blob/37daa03b7dc8fb3c4d91dc560a9e0e631c980326/lib/Sema/TypeCheckNameLookup.cpp#L606 67 | .filter { $0.1 <= ($0.0.count + 2) / 3 } 68 | // Sort by edit distance 69 | .sorted { $0.1 < $1.1 } 70 | .first?.0 71 | } 72 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/ProcessSetTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCBasic 13 | import TSCLibc 14 | import TSCUtility 15 | import TSCTestSupport 16 | 17 | @available(*, deprecated) 18 | class ProcessSetTests: XCTestCase { 19 | #if !os(Windows) // Signals are not supported in Windows 20 | func script(_ name: String) -> String { 21 | return AbsolutePath(#file).parentDirectory.appending(components: "processInputs", name).pathString 22 | } 23 | 24 | func testSigInt() throws { 25 | try runProcessSetTest("blocking") 26 | } 27 | 28 | func testSigKillEscalation() throws { 29 | try runProcessSetTest("blocking-ignore-sigint", killTimeout: 0.1) 30 | } 31 | 32 | /// Helper method to run process set test. 33 | func runProcessSetTest(_ scriptName: String, killTimeout: Double = 2, file: StaticString = #file, line: UInt = #line) throws { 34 | 35 | // We launch the script in a separate thread and then call terminate method on the process set. 36 | // We expect that the process will be terminated via some signal (sigint or sigkill). 37 | 38 | let processSet = ProcessSet(killTimeout: killTimeout) 39 | let threadStartCondition = Condition() 40 | var processLaunched = false 41 | 42 | let t = Thread { 43 | do { 44 | // Launch the script and notify main thread. 45 | try withTemporaryFile { tempFile in 46 | let waitFile = tempFile.path 47 | let process = Process(args: self.script(scriptName), waitFile.pathString) 48 | try processSet.add(process) 49 | try process.launch() 50 | guard waitForFile(waitFile) else { 51 | XCTFail("Couldn't launch the process") 52 | return 53 | } 54 | threadStartCondition.whileLocked { 55 | processLaunched = true 56 | threadStartCondition.signal() 57 | } 58 | let result = try process.waitUntilExit() 59 | // Ensure we did terminated due to signal. 60 | switch result.exitStatus { 61 | case .signalled: break 62 | default: XCTFail("Expected to exit via signal") 63 | } 64 | } 65 | } catch { 66 | XCTFail("Error \(String(describing: error))") 67 | } 68 | } 69 | t.start() 70 | 71 | threadStartCondition.whileLocked { 72 | while !processLaunched { 73 | threadStartCondition.wait() 74 | } 75 | } 76 | processSet.terminate() 77 | 78 | t.join() 79 | } 80 | #endif 81 | } 82 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/miscTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | import TSCTestSupport 13 | import TSCBasic 14 | 15 | class miscTests: XCTestCase { 16 | 17 | func testExecutableLookup() throws { 18 | try testWithTemporaryDirectory { path in 19 | 20 | let pathEnv1 = path.appending(component: "pathEnv1") 21 | try localFileSystem.createDirectory(pathEnv1) 22 | let pathEnvClang = pathEnv1.appending(component: "clang") 23 | try localFileSystem.writeFileContents(pathEnvClang, bytes: "") 24 | let pathEnv = [path.appending(component: "pathEnv2"), pathEnv1] 25 | 26 | try! Process.checkNonZeroExit(args: "chmod", "+x", pathEnvClang.pathString) 27 | 28 | // nil and empty string should fail. 29 | XCTAssertNil(lookupExecutablePath(filename: nil, currentWorkingDirectory: path, searchPaths: pathEnv)) 30 | XCTAssertNil(lookupExecutablePath(filename: "", currentWorkingDirectory: path, searchPaths: pathEnv)) 31 | 32 | // Absolute path to a binary should return it. 33 | var exec = lookupExecutablePath(filename: pathEnvClang.pathString, currentWorkingDirectory: path, searchPaths: pathEnv) 34 | XCTAssertEqual(exec, pathEnvClang) 35 | 36 | // This should lookup from PATH variable since executable is not present in cwd. 37 | exec = lookupExecutablePath(filename: "clang", currentWorkingDirectory: path, searchPaths: pathEnv) 38 | XCTAssertEqual(exec, pathEnvClang) 39 | 40 | // Create the binary relative to cwd and make it executable. 41 | let clang = path.appending(component: "clang") 42 | try localFileSystem.writeFileContents(clang, bytes: "") 43 | try! Process.checkNonZeroExit(args: "chmod", "+x", clang.pathString) 44 | // We should now find clang which is in cwd. 45 | exec = lookupExecutablePath(filename: "clang", currentWorkingDirectory: path, searchPaths: pathEnv) 46 | XCTAssertEqual(exec, clang) 47 | } 48 | } 49 | 50 | func testEnvSearchPaths() throws { 51 | let cwd = AbsolutePath("/dummy") 52 | let paths = getEnvSearchPaths(pathString: "something:.:abc/../.build/debug:/usr/bin:/bin/", currentWorkingDirectory: cwd) 53 | XCTAssertEqual(paths, try ["/dummy/something", "/dummy", "/dummy/.build/debug", "/usr/bin", "/bin"].map({ try AbsolutePath(validating: $0)})) 54 | } 55 | 56 | func testEmptyEnvSearchPaths() throws { 57 | let cwd = AbsolutePath("/dummy") 58 | let paths = getEnvSearchPaths(pathString: "", currentWorkingDirectory: cwd) 59 | XCTAssertEqual(paths, []) 60 | 61 | let nilPaths = getEnvSearchPaths(pathString: nil, currentWorkingDirectory: cwd) 62 | XCTAssertEqual(nilPaths, []) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/TSCUtility/Archiver.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCBasic 12 | import Dispatch 13 | 14 | /// The `Archiver` protocol abstracts away the different operations surrounding archives. 15 | // FIXME: deprecate 2/2022, remove once clients transitioned 16 | @available(*, deprecated, message: "moved to SwiftPM") 17 | public protocol Archiver { 18 | 19 | /// A set of extensions the current archiver supports. 20 | var supportedExtensions: Set { get } 21 | 22 | /// Asynchronously extracts the contents of an archive to a destination folder. 23 | /// 24 | /// - Parameters: 25 | /// - archivePath: The `AbsolutePath` to the archive to extract. 26 | /// - destinationPath: The `AbsolutePath` to the directory to extract to. 27 | /// - completion: The completion handler that will be called when the operation finishes to notify of its success. 28 | func extract( 29 | from archivePath: AbsolutePath, 30 | to destinationPath: AbsolutePath, 31 | completion: @escaping (Result) -> Void 32 | ) 33 | } 34 | 35 | /// An `Archiver` that handles ZIP archives using the command-line `zip` and `unzip` tools. 36 | // FIXME: deprecate 2/2022, remove once clients transitioned 37 | @available(*, deprecated, message: "moved to SwiftPM") 38 | public struct ZipArchiver: Archiver { 39 | public var supportedExtensions: Set { ["zip"] } 40 | 41 | /// The file-system implementation used for various file-system operations and checks. 42 | private let fileSystem: FileSystem 43 | 44 | /// Creates a `ZipArchiver`. 45 | /// 46 | /// - Parameters: 47 | /// - fileSystem: The file-system to used by the `ZipArchiver`. 48 | public init(fileSystem: FileSystem = localFileSystem) { 49 | self.fileSystem = fileSystem 50 | } 51 | 52 | public func extract( 53 | from archivePath: AbsolutePath, 54 | to destinationPath: AbsolutePath, 55 | completion: @escaping (Result) -> Void 56 | ) { 57 | guard fileSystem.exists(archivePath) else { 58 | completion(.failure(FileSystemError(.noEntry, archivePath))) 59 | return 60 | } 61 | 62 | guard fileSystem.isDirectory(destinationPath) else { 63 | completion(.failure(FileSystemError(.notDirectory, destinationPath))) 64 | return 65 | } 66 | 67 | DispatchQueue.global(qos: .userInitiated).async { 68 | do { 69 | let result = try Process.popen(args: "unzip", archivePath.pathString, "-d", destinationPath.pathString) 70 | guard result.exitStatus == .terminated(code: 0) else { 71 | throw try StringError(result.utf8stderrOutput()) 72 | } 73 | 74 | completion(.success(())) 75 | } catch { 76 | completion(.failure(error)) 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/TSCUtility/Versioning.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | @_implementationOnly import TSCclibc 12 | 13 | // FIXME: deprecate 2/2021, remove once clients transitioned 14 | @available(*, deprecated, message: "moved to SwiftPM") 15 | public struct SwiftVersion { 16 | /// The version number. 17 | public var version: (major: Int, minor: Int, patch: Int) 18 | 19 | /// Whether or not this is a development version. 20 | public var isDevelopment: Bool 21 | 22 | /// Build information, as an unstructured string. 23 | public var buildIdentifier: String? 24 | 25 | /// The major component of the version number. 26 | public var major: Int { return version.major } 27 | /// The minor component of the version number. 28 | public var minor: Int { return version.minor } 29 | /// The patch component of the version number. 30 | public var patch: Int { return version.patch } 31 | 32 | /// The version as a readable string. 33 | public var displayString: String { 34 | var result = "\(major).\(minor).\(patch)" 35 | if isDevelopment { 36 | result += "-dev" 37 | } 38 | if let buildIdentifier = self.buildIdentifier { 39 | result += " (" + buildIdentifier + ")" 40 | } 41 | return result 42 | } 43 | 44 | /// The complete product version display string (including the name). 45 | public var completeDisplayString: String { 46 | var vendorPrefix = String(cString: SPM_VendorNameString()) 47 | if !vendorPrefix.isEmpty { 48 | vendorPrefix += " " 49 | } 50 | return vendorPrefix + "Swift Package Manager - Swift " + displayString 51 | } 52 | 53 | /// The list of version specific identifiers to search when attempting to 54 | /// load version specific package or version information, in order of 55 | /// preference. 56 | public var versionSpecificKeys: [String] { 57 | return [ 58 | "@swift-\(major).\(minor).\(patch)", 59 | "@swift-\(major).\(minor)", 60 | "@swift-\(major)", 61 | ] 62 | } 63 | 64 | } 65 | 66 | private func getBuildIdentifier() -> String? { 67 | let buildIdentifier = String(cString: SPM_BuildIdentifierString()) 68 | return buildIdentifier.isEmpty ? nil : buildIdentifier 69 | } 70 | 71 | // FIXME: deprecate 2/2021, remove once clients transitioned 72 | @available(*, deprecated, message: "moved to SwiftPM") 73 | public struct Versioning { 74 | 75 | /// The current version of the package manager. 76 | public static let currentVersion = SwiftVersion( 77 | version: (5, 4, 0), 78 | isDevelopment: true, 79 | buildIdentifier: getBuildIdentifier()) 80 | 81 | /// The list of version specific "keys" to search when attempting to load 82 | /// version specific package or version information, in order of preference. 83 | public static let currentVersionSpecificKeys = currentVersion.versionSpecificKeys 84 | } 85 | -------------------------------------------------------------------------------- /Sources/TSCTestSupport/DiagnosticsEngine.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import TSCBasic 12 | import TSCUtility 13 | 14 | import XCTest 15 | 16 | public func DiagnosticsEngineTester( 17 | _ engine: DiagnosticsEngine, 18 | ignoreNotes: Bool = false, 19 | file: StaticString = #file, 20 | line: UInt = #line, 21 | result: (DiagnosticsEngineResult) throws -> Void 22 | ) { 23 | let engineResult = DiagnosticsEngineResult(engine, ignoreNotes: ignoreNotes) 24 | 25 | do { 26 | try result(engineResult) 27 | } catch { 28 | XCTFail("error \(String(describing: error))", file: file, line: line) 29 | } 30 | 31 | if !engineResult.uncheckedDiagnostics.isEmpty { 32 | XCTFail("unchecked diagnostics \(engineResult.uncheckedDiagnostics)", file: file, line: line) 33 | } 34 | } 35 | 36 | /// Helper to check diagnostics in the engine. 37 | final public class DiagnosticsEngineResult { 38 | 39 | fileprivate var uncheckedDiagnostics: [Diagnostic] 40 | 41 | init(_ engine: DiagnosticsEngine, ignoreNotes: Bool = false) { 42 | self.uncheckedDiagnostics = engine.diagnostics 43 | } 44 | 45 | public func check( 46 | diagnostic: StringPattern, 47 | checkContains: Bool = false, 48 | behavior: Diagnostic.Behavior, 49 | location: String? = nil, 50 | file: StaticString = #file, 51 | line: UInt = #line 52 | ) { 53 | guard !uncheckedDiagnostics.isEmpty else { 54 | return XCTFail("No diagnostics left to check", file: file, line: line) 55 | } 56 | 57 | let location = location ?? UnknownLocation.location.description 58 | let theDiagnostic = uncheckedDiagnostics.removeFirst() 59 | 60 | XCTAssertMatch(theDiagnostic.description, diagnostic, file: file, line: line) 61 | XCTAssertEqual(theDiagnostic.message.behavior, behavior, file: file, line: line) 62 | XCTAssertEqual(theDiagnostic.location.description, location, file: file, line: line) 63 | } 64 | 65 | public func checkUnordered( 66 | diagnostic diagnosticPattern: StringPattern, 67 | checkContains: Bool = false, 68 | behavior: Diagnostic.Behavior, 69 | location: String? = nil, 70 | file: StaticString = #file, 71 | line: UInt = #line 72 | ) { 73 | guard !uncheckedDiagnostics.isEmpty else { 74 | return XCTFail("No diagnostics left to check", file: file, line: line) 75 | } 76 | 77 | let locationDescription = location ?? UnknownLocation.location.description 78 | let matchIndex = uncheckedDiagnostics.firstIndex(where: { diagnostic in 79 | diagnosticPattern ~= diagnostic.description && 80 | diagnostic.message.behavior == behavior && 81 | diagnostic.location.description == locationDescription 82 | }) 83 | 84 | if let index = matchIndex { 85 | uncheckedDiagnostics.remove(at: index) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/SortedArrayTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class SortedArrayTests: XCTestCase { 16 | 17 | func testSortedArrayInAscendingOrder() throws { 18 | var arr = SortedArray(areInIncreasingOrder: <) 19 | arr.insert(15) 20 | arr.insert(14) 21 | arr.insert(100) 22 | arr.insert(-13) 23 | arr.insert(0) 24 | arr.insert(198) 25 | arr.insert(13) 26 | XCTAssertEqual(arr.values, [-13, 0, 13, 14, 15, 100, 198]) 27 | 28 | arr.insert(contentsOf: [2, 1, 3, 0, 9]) 29 | XCTAssertEqual(arr.values, [-13, 0, 0, 1, 2, 3, 9, 13, 14, 15, 100, 198]) 30 | 31 | arr += [2, 3] 32 | XCTAssertEqual(arr.values, [-13, 0, 0, 1, 2, 2, 3, 3, 9, 13, 14, 15, 100, 198]) 33 | 34 | arr.insert(-13) 35 | XCTAssertEqual(arr.values, [-13, -13, 0, 0, 1, 2, 2, 3, 3, 9, 13, 14, 15, 100, 198]) 36 | 37 | arr.insert(198) 38 | XCTAssertEqual(arr.values, [-13, -13, 0, 0, 1, 2, 2, 3, 3, 9, 13, 14, 15, 100, 198, 198]) 39 | 40 | arr.insert(contentsOf: [-15, -14]) 41 | XCTAssertEqual(arr.values, [-15, -14, -13, -13, 0, 0, 1, 2, 2, 3, 3, 9, 13, 14, 15, 100, 198, 198]) 42 | } 43 | 44 | func testSortedArrayInDescendingOrder() throws { 45 | var arr = SortedArray(areInIncreasingOrder: >) 46 | arr.insert(15) 47 | arr.insert(14) 48 | arr.insert(100) 49 | arr.insert(-13) 50 | arr.insert(0) 51 | arr.insert(198) 52 | arr.insert(13) 53 | XCTAssertEqual(arr.values, [198, 100, 15, 14, 13, 0, -13]) 54 | 55 | arr.insert(contentsOf: [2, 1, 3, 0, 9]) 56 | XCTAssertEqual(arr.values, [198, 100, 15, 14, 13, 9, 3, 2, 1, 0, 0, -13]) 57 | 58 | arr += [2, 3] 59 | XCTAssertEqual(arr.values, [198, 100, 15, 14, 13, 9, 3, 3, 2, 2, 1, 0, 0, -13]) 60 | 61 | arr.insert(-13) 62 | XCTAssertEqual(arr.values, [198, 100, 15, 14, 13, 9, 3, 3, 2, 2, 1, 0, 0, -13, -13]) 63 | 64 | arr.insert(198) 65 | XCTAssertEqual(arr.values, [198, 198, 100, 15, 14, 13, 9, 3, 3, 2, 2, 1, 0, 0, -13, -13]) 66 | } 67 | 68 | func testSortedArrayInsertIntoSmallerArray() throws { 69 | var arr = SortedArray(areInIncreasingOrder: <) 70 | arr.insert(contentsOf: [3]) 71 | 72 | arr.insert(contentsOf: [1, 2]) 73 | XCTAssertEqual(arr.values, [1, 2, 3]) 74 | 75 | arr.insert(contentsOf: [4, 5, 6, 7]) 76 | XCTAssertEqual(arr.values, [1, 2, 3, 4, 5, 6, 7]) 77 | } 78 | 79 | func testSortedArrayInsertSlice() throws { 80 | let target = [0, 1, 2, 3, 4, 5, 6] 81 | var arr = SortedArray(areInIncreasingOrder: <) 82 | arr.insert(contentsOf: target[2...2]) 83 | 84 | arr.insert(contentsOf: target[0...1]) 85 | XCTAssertEqual(arr.values, [0, 1, 2]) 86 | 87 | arr.insert(contentsOf: target[3...6]) 88 | XCTAssertEqual(arr.values, target) 89 | } 90 | 91 | func testSortedArrayWithValues() throws { 92 | let arr = SortedArray([5, 4, 3, 2, 1], areInIncreasingOrder: <) 93 | XCTAssertEqual(arr.values, [1, 2, 3, 4, 5]) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Sources/TSCBasic/Thread.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | #if os(Windows) 13 | import WinSDK 14 | #endif 15 | 16 | /// This class bridges the gap between Darwin and Linux Foundation Threading API. 17 | /// It provides closure based execution and a join method to block the calling thread 18 | /// until the thread is finished executing. 19 | final public class Thread { 20 | 21 | /// The thread implementation which is Foundation.Thread on Linux and 22 | /// a Thread subclass which provides closure support on Darwin. 23 | private var thread: ThreadImpl! 24 | 25 | /// Condition variable to support blocking other threads using join when this thread has not finished executing. 26 | private var finishedCondition: Condition 27 | 28 | /// A boolean variable to track if this thread has finished executing its task. 29 | private var isFinished: Bool 30 | 31 | /// Creates an instance of thread class with closure to be executed when start() is called. 32 | public init(task: @escaping () -> Void) { 33 | isFinished = false 34 | finishedCondition = Condition() 35 | 36 | // Wrap the task with condition notifying any other threads blocked due to this thread. 37 | // Capture self weakly to avoid reference cycle. In case Thread is deinited before the task 38 | // runs, skip the use of finishedCondition. 39 | let theTask = { [weak self] in 40 | if let strongSelf = self { 41 | precondition(!strongSelf.isFinished) 42 | strongSelf.finishedCondition.whileLocked { 43 | task() 44 | strongSelf.isFinished = true 45 | strongSelf.finishedCondition.broadcast() 46 | } 47 | } else { 48 | // If the containing thread has been destroyed, we can ignore the finished condition and just run the 49 | // task. 50 | task() 51 | } 52 | } 53 | 54 | self.thread = ThreadImpl(block: theTask) 55 | } 56 | 57 | /// Starts the thread execution. 58 | public func start() { 59 | thread.start() 60 | } 61 | 62 | /// Blocks the calling thread until this thread is finished execution. 63 | public func join() { 64 | finishedCondition.whileLocked { 65 | while !isFinished { 66 | finishedCondition.wait() 67 | } 68 | } 69 | } 70 | 71 | /// Causes the calling thread to yield execution to another thread. 72 | public static func yield() { 73 | #if os(Windows) 74 | SwitchToThread() 75 | #else 76 | sched_yield() 77 | #endif 78 | } 79 | } 80 | 81 | #if canImport(Darwin) 82 | /// A helper subclass of Foundation's Thread with closure support. 83 | final private class ThreadImpl: Foundation.Thread { 84 | 85 | /// The task to be executed. 86 | private let task: () -> Void 87 | 88 | override func main() { 89 | task() 90 | } 91 | 92 | init(block task: @escaping () -> Void) { 93 | self.task = task 94 | } 95 | } 96 | #else 97 | // Thread on Linux supports closure so just use it directly. 98 | typealias ThreadImpl = Foundation.Thread 99 | #endif 100 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/JSONTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | class JSONTests: XCTestCase { 16 | func testEncoding() { 17 | // Test the basics of encoding each object type. 18 | func encode(_ item: JSON) -> String { 19 | return item.toBytes().validDescription ?? "" 20 | } 21 | 22 | XCTAssertEqual(encode(.null), "null") 23 | XCTAssertEqual(encode(.bool(false)), "false") 24 | XCTAssertEqual(encode(.int(1)), "1") 25 | XCTAssertEqual(encode(.string("hi")), "\"hi\"") 26 | XCTAssertEqual(encode(.array([.int(1), .string("hi")])), "[1, \"hi\"]") 27 | XCTAssertEqual(encode(.dictionary(["a": .int(1), "b": .string("hi")])), "{\"a\": 1, \"b\": \"hi\"}") 28 | XCTAssertEqual(encode(.orderedDictionary(["b": .string("hi"), "a": .int(1)])), "{\"b\": \"hi\", \"a\": 1}") 29 | } 30 | 31 | func testDecoding() { 32 | // Test the basics of encoding each object type. 33 | func decode(_ string: String) -> JSON? { 34 | return try? JSON(bytes: ByteString(string)) 35 | } 36 | 37 | XCTAssertEqual(decode(""), nil) 38 | XCTAssertEqual(decode("this is not json"), nil) 39 | XCTAssertEqual(decode("null"), .null) 40 | XCTAssertEqual(decode("false"), .bool(false)) 41 | XCTAssertEqual(decode("true"), .bool(true)) 42 | XCTAssertEqual(decode("1"), .int(1)) 43 | XCTAssertEqual(decode("1.2"), .double(1.2)) 44 | XCTAssertEqual(decode("\"hi\""), .string("hi")) 45 | XCTAssertEqual(decode("[null, \"hi\"]"), .array([.null, .string("hi")])) 46 | XCTAssertEqual(decode("[[null], [null]]"), .array([.array([.null]), .array([.null])])) 47 | XCTAssertEqual(decode("{\"a\": null, \"b\": \"hi\"}"), .dictionary(["a": .null, "b": .string("hi")])) 48 | } 49 | 50 | func testStringInitalizer() { 51 | let jsonString = "{\"name\" : \"jon doe\"}" 52 | let json = try? JSON(string: jsonString) 53 | XCTAssertEqual(json, .dictionary(["name": .string("jon doe")])) 54 | } 55 | 56 | func testPrettyPrinting() { 57 | let c1 = JSON.dictionary([ 58 | "name": .string("child1"), 59 | "age": .int(2), 60 | ]) 61 | 62 | let c2 = JSON.dictionary([ 63 | "name": .string("child2"), 64 | "age": .int(3), 65 | ]) 66 | 67 | let person = JSON.dictionary([ 68 | "first": .string("john"), 69 | "last": .string("doe"), 70 | "age": .int(22), 71 | "children": .array([c1, c2]), 72 | "houses": .array([1, 2].map(JSON.int)), 73 | ]) 74 | 75 | XCTAssertEqual(person.toString(prettyPrint: true), """ 76 | { 77 | "age": 22, 78 | "children": [ 79 | { 80 | "age": 2, 81 | "name": "child1" 82 | }, 83 | { 84 | "age": 3, 85 | "name": "child2" 86 | } 87 | ], 88 | "first": "john", 89 | "houses": [ 90 | 1, 91 | 2 92 | ], 93 | "last": "doe" 94 | } 95 | 96 | """) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Tests/TSCUtilityTests/PolymorphicCodableTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | import XCTest 13 | 14 | import TSCBasic 15 | import TSCUtility 16 | 17 | class Animal: PolymorphicCodableProtocol { 18 | static var implementations: [PolymorphicCodableProtocol.Type] = [ 19 | Dog.self, 20 | Cat.self, 21 | ] 22 | 23 | let age: Int 24 | 25 | init(age: Int) { 26 | self.age = age 27 | } 28 | } 29 | 30 | struct Animals: Codable { 31 | @PolymorphicCodable 32 | var animal1: Animal 33 | 34 | @PolymorphicCodable 35 | var animal2: Animal 36 | 37 | @PolymorphicCodableArray 38 | var animals: [Animal] 39 | } 40 | 41 | final class PolymorphicCodableTests: XCTestCase { 42 | 43 | func testBasic() throws { 44 | let dog = Dog(age: 5, dogCandy: "bone") 45 | let cat = Cat(age: 3, catToy: "wool") 46 | 47 | let animals = Animals(animal1: dog, animal2: cat, animals: [dog, cat]) 48 | let encoded = try JSONEncoder().encode(animals) 49 | let decoded = try JSONDecoder().decode(Animals.self, from: encoded) 50 | 51 | let animal1 = try XCTUnwrap(decoded.animal1 as? Dog) 52 | XCTAssertEqual(animal1.age, 5) 53 | XCTAssertEqual(animal1.dogCandy, "bone") 54 | 55 | let animal2 = try XCTUnwrap(decoded.animal2 as? Cat) 56 | XCTAssertEqual(animal2.age, 3) 57 | XCTAssertEqual(animal2.catToy, "wool") 58 | 59 | XCTAssertEqual(decoded.animals.count, 2) 60 | XCTAssertEqual(decoded.animals.map{ $0.age }, [5, 3]) 61 | XCTAssertEqual(decoded.animals.map{ String(reflecting: $0) }, ["TSCUtilityTests.Dog", "TSCUtilityTests.Cat"]) 62 | } 63 | } 64 | 65 | // MARK:- Subclasses 66 | 67 | class Dog: Animal { 68 | let dogCandy: String 69 | 70 | init(age: Int, dogCandy: String) { 71 | self.dogCandy = dogCandy 72 | super.init(age: age) 73 | } 74 | 75 | enum CodingKeys: CodingKey { 76 | case dogCandy 77 | } 78 | 79 | public override func encode(to encoder: Encoder) throws { 80 | try super.encode(to: encoder) 81 | var container = encoder.container(keyedBy: CodingKeys.self) 82 | try container.encode(dogCandy, forKey: .dogCandy) 83 | } 84 | 85 | required init(from decoder: Decoder) throws { 86 | let container = try decoder.container(keyedBy: CodingKeys.self) 87 | self.dogCandy = try container.decode(String.self, forKey: .dogCandy) 88 | try super.init(from: decoder) 89 | } 90 | } 91 | 92 | class Cat: Animal { 93 | let catToy: String 94 | 95 | init(age: Int, catToy: String) { 96 | self.catToy = catToy 97 | super.init(age: age) 98 | } 99 | 100 | enum CodingKeys: CodingKey { 101 | case catToy 102 | } 103 | 104 | public override func encode(to encoder: Encoder) throws { 105 | try super.encode(to: encoder) 106 | var container = encoder.container(keyedBy: CodingKeys.self) 107 | try container.encode(catToy, forKey: .catToy) 108 | } 109 | 110 | required init(from decoder: Decoder) throws { 111 | let container = try decoder.container(keyedBy: CodingKeys.self) 112 | self.catToy = try container.decode(String.self, forKey: .catToy) 113 | try super.init(from: decoder) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sources/TSCUtility/Bits.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import Foundation 12 | import TSCBasic 13 | 14 | struct Bits: RandomAccessCollection { 15 | var buffer: ByteString 16 | 17 | var startIndex: Int { return 0 } 18 | var endIndex: Int { return buffer.count * 8 } 19 | 20 | subscript(index: Int) -> UInt8 { 21 | let byte = buffer.contents[index / 8] 22 | return (byte >> UInt8(index % 8)) & 1 23 | } 24 | 25 | func readBits(atOffset offset: Int, count: Int) -> UInt64 { 26 | precondition(count >= 0 && count <= 64) 27 | precondition(offset >= 0) 28 | precondition(offset &+ count >= offset) 29 | precondition(offset &+ count <= self.endIndex) 30 | 31 | return buffer.contents.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in 32 | let upperBound = offset &+ count 33 | let topByteIndex = upperBound >> 3 34 | var result: UInt64 = 0 35 | if upperBound & 7 != 0 { 36 | let mask: UInt8 = (1 << UInt8(upperBound & 7)) &- 1 37 | result = UInt64(bytes[topByteIndex] & mask) 38 | } 39 | for i in ((offset >> 3)..<(upperBound >> 3)).reversed() { 40 | result <<= 8 41 | result |= UInt64(bytes[i]) 42 | } 43 | if offset & 7 != 0 { 44 | result >>= UInt64(offset & 7) 45 | } 46 | return result 47 | } 48 | } 49 | 50 | struct Cursor { 51 | enum Error: Swift.Error { case bufferOverflow } 52 | 53 | let buffer: Bits 54 | private var offset: Int = 0 55 | 56 | init(buffer: Bits) { 57 | self.buffer = buffer 58 | } 59 | 60 | init(buffer: ByteString) { 61 | self.init(buffer: Bits(buffer: buffer)) 62 | } 63 | 64 | var isAtStart: Bool { 65 | return offset == buffer.startIndex 66 | } 67 | 68 | var isAtEnd: Bool { 69 | return offset == buffer.count 70 | } 71 | 72 | func peek(_ count: Int) throws -> UInt64 { 73 | if buffer.count - offset < count { throw Error.bufferOverflow } 74 | return buffer.readBits(atOffset: offset, count: count) 75 | } 76 | 77 | mutating func read(_ count: Int) throws -> UInt64 { 78 | defer { offset += count } 79 | return try peek(count) 80 | } 81 | 82 | mutating func read(bytes count: Int) throws -> ArraySlice { 83 | precondition(count >= 0) 84 | precondition(offset & 0b111 == 0) 85 | let newOffset = offset &+ (count << 3) 86 | precondition(newOffset >= offset) 87 | if newOffset > buffer.count { throw Error.bufferOverflow } 88 | defer { offset = newOffset } 89 | return buffer.buffer.contents.dropFirst(offset >> 3).prefix((newOffset - offset) >> 3) 90 | } 91 | 92 | mutating func skip(bytes count: Int) throws { 93 | precondition(count >= 0) 94 | precondition(offset & 0b111 == 0) 95 | let newOffset = offset &+ (count << 3) 96 | precondition(newOffset >= offset) 97 | if newOffset > buffer.count { throw Error.bufferOverflow } 98 | offset = newOffset 99 | } 100 | 101 | mutating func advance(toBitAlignment align: Int) throws { 102 | precondition(align > 0) 103 | precondition(offset &+ (align&-1) >= offset) 104 | precondition(align & (align &- 1) == 0) 105 | if offset % align == 0 { return } 106 | offset = (offset &+ align) & ~(align &- 1) 107 | if offset > buffer.count { throw Error.bufferOverflow } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Sources/TSCLibc/libc.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | #if canImport(Glibc) 12 | @_exported import Glibc 13 | #elseif canImport(Musl) 14 | @_exported import Musl 15 | #elseif os(Windows) 16 | @_exported import CRT 17 | @_exported import WinSDK 18 | #elseif canImport(Android) 19 | @_exported import Android 20 | #else 21 | @_exported import Darwin.C 22 | #endif 23 | 24 | #if os(Windows) 25 | private func __randname(_ buffer: UnsafeMutablePointer) { 26 | let alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 27 | _ = (0 ..< 6).map { index in 28 | buffer[index] = CChar(alpha.shuffled().randomElement()!.utf8.first!) 29 | } 30 | } 31 | 32 | // char *mkdtemp(char *template); 33 | // NOTE(compnerd) this is unsafe! This assumes that the template is *ASCII*. 34 | public func mkdtemp( 35 | _ template: UnsafeMutablePointer? 36 | ) -> UnsafeMutablePointer? { 37 | // Although the signature of the function is `char *(*)(char *)`, the C 38 | // library treats it as `char *(*)(char * _Nonull)`. Most implementations 39 | // will simply use and trigger a segmentation fault on x86 (and similar faults 40 | // on other architectures) when the memory is accessed. This roughly emulates 41 | // that by terminating in the case even though it is possible for us to return 42 | // an error. 43 | guard let template = template else { fatalError() } 44 | 45 | let length: Int = strlen(template) 46 | 47 | // Validate the precondition: the template must terminate with 6 `X` which 48 | // will be filled in to generate a unique directory. 49 | guard length >= 6, memcmp(template + length - 6, "XXXXXX", 6) == 0 else { 50 | _set_errno(EINVAL) 51 | return nil 52 | } 53 | 54 | // Attempt to create the directory 55 | var retries: Int = 100 56 | repeat { 57 | __randname(template + length - 6) 58 | if _mkdir(template) == 0 { 59 | return template 60 | } 61 | retries = retries - 1 62 | } while retries > 0 63 | 64 | return nil 65 | } 66 | 67 | // int mkstemps(char *template, int suffixlen); 68 | public func mkstemps( 69 | _ template: UnsafeMutablePointer?, 70 | _ suffixlen: Int32 71 | ) -> Int32 { 72 | // Although the signature of the function is `char *(*)(char *)`, the C 73 | // library treats it as `char *(*)(char * _Nonull)`. Most implementations 74 | // will simply use and trigger a segmentation fault on x86 (and similar faults 75 | // on other architectures) when the memory is accessed. This roughly emulates 76 | // that by terminating in the case even though it is possible for us to return 77 | // an error. 78 | guard let template = template else { fatalError() } 79 | 80 | let length: Int = strlen(template) 81 | 82 | // Validate the precondition: the template must terminate with 6 `X` which 83 | // will be filled in to generate a unique directory. 84 | guard length >= 6, memcmp(template + length - Int(suffixlen) - 6, "XXXXXX", 6) == 0 else { 85 | _set_errno(EINVAL) 86 | return -1 87 | } 88 | 89 | // Attempt to create file 90 | var retries: Int = 100 91 | repeat { 92 | __randname(template + length - Int(suffixlen) - 6) 93 | var fd: CInt = -1 94 | if _sopen_s(&fd, template, _O_RDWR | _O_CREAT | _O_BINARY | _O_NOINHERIT, 95 | _SH_DENYNO, _S_IREAD | _S_IWRITE) == 0 { 96 | return fd 97 | } 98 | retries = retries - 1 99 | } while retries > 0 100 | 101 | return -1 102 | } 103 | #endif 104 | -------------------------------------------------------------------------------- /Tests/TSCBasicTests/DiagnosticsEngineTests.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import XCTest 12 | 13 | import TSCBasic 14 | 15 | private struct FooDiag: DiagnosticData { 16 | let arr: [String] 17 | let str: String 18 | let int: Int 19 | 20 | var description: String { 21 | return "literal \(arr.joined(separator: ", ")) \(int) \(str) bar \(str)" 22 | } 23 | } 24 | 25 | private struct FooLocation: DiagnosticLocation { 26 | let name: String 27 | 28 | var description: String { 29 | return name 30 | } 31 | } 32 | 33 | class DiagnosticsEngineTests: XCTestCase { 34 | func testBasics() { 35 | let diagnostics = DiagnosticsEngine() 36 | diagnostics.emit(.error( 37 | FooDiag(arr: ["foo", "bar"], str: "str", int: 2)), 38 | location: FooLocation(name: "foo loc") 39 | ) 40 | let diag = diagnostics.diagnostics[0] 41 | 42 | XCTAssertEqual(diagnostics.diagnostics.count, 1) 43 | XCTAssertEqual(diag.location.description, "foo loc") 44 | XCTAssertEqual(diag.description, "literal foo, bar 2 str bar str") 45 | XCTAssertEqual(diag.message.behavior, .error) 46 | } 47 | 48 | func testMerging() { 49 | let engine1 = DiagnosticsEngine() 50 | engine1.emit( 51 | .error(FooDiag(arr: ["foo", "bar"], str: "str", int: 2)), 52 | location: FooLocation(name: "foo loc") 53 | ) 54 | XCTAssertEqual(engine1.diagnostics.count, 1) 55 | 56 | let engine2 = DiagnosticsEngine() 57 | engine2.emit( 58 | .error(FooDiag(arr: ["foo", "bar"], str: "str", int: 2)), 59 | location: FooLocation(name: "foo loc") 60 | ) 61 | engine2.emit( 62 | .error(FooDiag(arr: ["foo", "bar"], str: "str", int: 2)), 63 | location: FooLocation(name: "foo loc") 64 | ) 65 | XCTAssertEqual(engine2.diagnostics.count, 2) 66 | 67 | engine1.merge(engine2) 68 | XCTAssertEqual(engine1.diagnostics.count, 3) 69 | XCTAssertEqual(engine2.diagnostics.count, 2) 70 | } 71 | 72 | func testHandlers() { 73 | var handledDiagnostics: [Diagnostic] = [] 74 | let handler: DiagnosticsEngine.DiagnosticsHandler = { diagnostic in 75 | handledDiagnostics.append(diagnostic) 76 | } 77 | 78 | let diagnostics = DiagnosticsEngine(handlers: [handler]) 79 | let location = FooLocation(name: "location") 80 | diagnostics.emit( 81 | .error(FooDiag(arr: ["foo", "bar"], str: "str", int: 2)), 82 | location: location 83 | ) 84 | diagnostics.emit(.error(StringDiagnostic("diag 2")), location: location) 85 | diagnostics.emit(.note(StringDiagnostic("diag 3")), location: location) 86 | diagnostics.emit(.remark(StringDiagnostic("diag 4")), location: location) 87 | diagnostics.emit(.error(StringDiagnostic("end")), location: location) 88 | 89 | XCTAssertEqual(handledDiagnostics.count, 5) 90 | for diagnostic in handledDiagnostics { 91 | XCTAssertEqual(diagnostic.location.description, location.description) 92 | } 93 | XCTAssertEqual(handledDiagnostics[0].description, "literal foo, bar 2 str bar str") 94 | XCTAssertEqual(handledDiagnostics[1].description, "diag 2") 95 | XCTAssertEqual(handledDiagnostics[2].description, "diag 3") 96 | XCTAssertEqual(handledDiagnostics[3].description, "diag 4") 97 | XCTAssertEqual(handledDiagnostics[4].description, "end") 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Sources/TSCUtility/Git.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | import class Foundation.ProcessInfo 12 | import TSCBasic 13 | 14 | public enum Git { 15 | /// A shell command to run for Git. Can be either a name or a path. 16 | /// - Note: modification is not thread safe, designed for testing only 17 | public static var tool: String = "git\(executableFileSuffix)" 18 | 19 | /// Returns true if the git reference name is well formed. 20 | public static func checkRefFormat(ref: String) -> Bool { 21 | do { 22 | let result = try Process.popen(args: tool, "check-ref-format", "--allow-onelevel", ref) 23 | return result.exitStatus == .terminated(code: 0) 24 | } catch { 25 | return false 26 | } 27 | } 28 | 29 | private static var _gitEnvironment = ProcessEnv.block 30 | 31 | @available(*, 32 | deprecated, 33 | renamed: "environmentBlock", 34 | message: "Previous `[String: String]` representation did not account for case insensitivity on Windows." 35 | ) 36 | public static var environment: [String: String] { 37 | get { 38 | var env = Self._gitEnvironment 39 | 40 | // These variables are inserted into the environment when shelling out 41 | // to git if not already present. 42 | let underrideVariables: ProcessEnvironmentBlock = [ 43 | // Disable terminal prompts in git. This will make git error out and return 44 | // when it needs a user/pass etc instead of hanging the terminal (SR-3981). 45 | "GIT_TERMINAL_PROMPT": "0", 46 | 47 | // The above is env variable is not enough. However, ssh_config's batch 48 | // mode is made for this purpose. see: https://linux.die.net/man/5/ssh_config 49 | "GIT_SSH_COMMAND": "ssh -oBatchMode=yes", 50 | ] 51 | 52 | for (key, value) in underrideVariables { 53 | // Skip this key is already present in the env. 54 | if env.keys.contains(key) { continue } 55 | 56 | env[key] = value 57 | } 58 | 59 | return Dictionary(env.map { ($0.value, $1) }, uniquingKeysWith: { $1 }) 60 | } 61 | set { 62 | Self._gitEnvironment = .init(newValue) 63 | } 64 | } 65 | 66 | /// Returns the environment variables for launching the git subprocess. 67 | /// 68 | /// This contains the current environment with custom overrides for using 69 | /// git from swift build. 70 | /// - Note: modification is not thread safe, designed for testing only 71 | public static var environmentBlock: ProcessEnvironmentBlock { 72 | get { 73 | var env = Self._gitEnvironment 74 | 75 | // These variables are inserted into the environment when shelling out 76 | // to git if not already present. 77 | let underrideVariables: ProcessEnvironmentBlock = [ 78 | // Disable terminal prompts in git. This will make git error out and return 79 | // when it needs a user/pass etc instead of hanging the terminal (SR-3981). 80 | "GIT_TERMINAL_PROMPT": "0", 81 | 82 | // The above is env variable is not enough. However, ssh_config's batch 83 | // mode is made for this purpose. see: https://linux.die.net/man/5/ssh_config 84 | "GIT_SSH_COMMAND": "ssh -oBatchMode=yes", 85 | ] 86 | 87 | for (key, value) in underrideVariables { 88 | // Skip this key is already present in the env. 89 | if env.keys.contains(key) { continue } 90 | 91 | env[key] = value 92 | } 93 | 94 | return env 95 | } 96 | set { 97 | Self._gitEnvironment = newValue 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.7 2 | 3 | /* 4 | This source file is part of the Swift.org open source project 5 | 6 | Copyright (c) 2019 - 2021 Apple Inc. and the Swift project authors 7 | Licensed under Apache License v2.0 with Runtime Library Exception 8 | 9 | See http://swift.org/LICENSE.txt for license information 10 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 11 | */ 12 | 13 | 14 | import PackageDescription 15 | import class Foundation.ProcessInfo 16 | 17 | let macOSPlatform: SupportedPlatform 18 | let iOSPlatform: SupportedPlatform 19 | if let deploymentTarget = ProcessInfo.processInfo.environment["SWIFTTSC_MACOS_DEPLOYMENT_TARGET"] { 20 | macOSPlatform = .macOS(deploymentTarget) 21 | } else { 22 | macOSPlatform = .macOS(.v10_15) 23 | } 24 | if let deploymentTarget = ProcessInfo.processInfo.environment["SWIFTTSC_IOS_DEPLOYMENT_TARGET"] { 25 | iOSPlatform = .iOS(deploymentTarget) 26 | } else { 27 | iOSPlatform = .iOS(.v13) 28 | } 29 | 30 | let isStaticBuild = ProcessInfo.processInfo.environment["SWIFTTOOLSSUPPORTCORE_STATIC_LINK"] != nil 31 | 32 | let CMakeFiles = ["CMakeLists.txt"] 33 | 34 | let package = Package( 35 | name: "swift-tools-support-core", 36 | platforms: [ 37 | macOSPlatform, 38 | iOSPlatform, 39 | ], 40 | products: [ 41 | .library( 42 | name: "TSCBasic", 43 | targets: ["TSCBasic"]), 44 | .library( 45 | name: "SwiftToolsSupport", 46 | type: .dynamic, 47 | targets: ["TSCBasic", "TSCUtility"]), 48 | .library( 49 | name: "SwiftToolsSupport-auto", 50 | targets: ["TSCBasic", "TSCUtility"]), 51 | 52 | .library( 53 | name: "TSCTestSupport", 54 | targets: ["TSCTestSupport"]), 55 | ], 56 | dependencies: [], 57 | targets: [ 58 | 59 | // MARK: Tools support core targets 60 | 61 | .target( 62 | /** Shim target to import missing C headers in Darwin and Glibc modulemap. */ 63 | name: "TSCclibc", 64 | dependencies: [], 65 | exclude: CMakeFiles, 66 | cSettings: [ 67 | .define("_GNU_SOURCE", .when(platforms: [.linux])), 68 | ]), 69 | .target( 70 | /** Cross-platform access to bare `libc` functionality. */ 71 | name: "TSCLibc", 72 | dependencies: [], 73 | exclude: CMakeFiles), 74 | .target( 75 | /** TSCBasic support library */ 76 | name: "TSCBasic", 77 | dependencies: [ 78 | "TSCLibc", 79 | "TSCclibc", 80 | ], 81 | exclude: CMakeFiles + ["README.md"], 82 | cxxSettings: [ 83 | .define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows])), 84 | ], 85 | linkerSettings: [ 86 | .linkedLibrary("Pathcch", .when(platforms: [.windows])), 87 | ]), 88 | .target( 89 | /** Abstractions for common operations, should migrate to TSCBasic */ 90 | name: "TSCUtility", 91 | dependencies: ["TSCBasic", "TSCclibc"], 92 | exclude: CMakeFiles), 93 | 94 | // MARK: Additional Test Dependencies 95 | 96 | .target( 97 | /** Generic test support library */ 98 | name: "TSCTestSupport", 99 | dependencies: ["TSCBasic", "TSCUtility"]), 100 | 101 | 102 | // MARK: Tools support core tests 103 | 104 | .testTarget( 105 | name: "TSCBasicTests", 106 | dependencies: ["TSCTestSupport", "TSCclibc"], 107 | exclude: ["processInputs", "Inputs"]), 108 | .testTarget( 109 | name: "TSCBasicPerformanceTests", 110 | dependencies: ["TSCBasic", "TSCTestSupport"]), 111 | .testTarget( 112 | name: "TSCTestSupportTests", 113 | dependencies: ["TSCTestSupport"]), 114 | .testTarget( 115 | name: "TSCUtilityTests", 116 | dependencies: ["TSCUtility", "TSCTestSupport"], 117 | exclude: ["pkgconfigInputs", "Inputs"]), 118 | ] 119 | ) 120 | 121 | if isStaticBuild { 122 | package.targets = package.targets.filter { target in 123 | target.type != .test && !target.name.hasSuffix("TestSupport") 124 | } 125 | package.products = package.products.filter { product in 126 | !product.name.hasSuffix("TestSupport") && product.name != "SwiftToolsSupport" 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Sources/TSCBasic/StringConversions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | This source file is part of the Swift.org open source project 3 | 4 | Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See http://swift.org/LICENSE.txt for license information 8 | See http://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | */ 10 | 11 | /// Check if the given code unit needs shell escaping. 12 | // 13 | /// - Parameters: 14 | /// - codeUnit: The code unit to be checked. 15 | /// 16 | /// - Returns: True if shell escaping is not needed. 17 | private func inShellAllowlist(_ codeUnit: UInt8) -> Bool { 18 | #if os(Windows) 19 | if codeUnit == UInt8(ascii: "\\") { 20 | return true 21 | } 22 | #endif 23 | switch codeUnit { 24 | case UInt8(ascii: "a")...UInt8(ascii: "z"), 25 | UInt8(ascii: "A")...UInt8(ascii: "Z"), 26 | UInt8(ascii: "0")...UInt8(ascii: "9"), 27 | UInt8(ascii: "-"), 28 | UInt8(ascii: "_"), 29 | UInt8(ascii: "/"), 30 | UInt8(ascii: ":"), 31 | UInt8(ascii: "@"), 32 | UInt8(ascii: "%"), 33 | UInt8(ascii: "+"), 34 | UInt8(ascii: "="), 35 | UInt8(ascii: "."), 36 | UInt8(ascii: ","): 37 | return true 38 | default: 39 | return false 40 | } 41 | } 42 | 43 | extension String { 44 | 45 | /// Creates a shell escaped string. If the string does not need escaping, returns the original string. 46 | /// Otherwise escapes using single quotes on Unix and double quotes on Windows. For example: 47 | /// hello -> hello, hello$world -> 'hello$world', input A -> 'input A' 48 | /// 49 | /// - Returns: Shell escaped string. 50 | public func spm_shellEscaped() -> String { 51 | 52 | // If all the characters in the string are in the allow list then no need to escape. 53 | guard let pos = utf8.firstIndex(where: { !inShellAllowlist($0) }) else { 54 | return self 55 | } 56 | 57 | #if os(Windows) 58 | let quoteCharacter: Character = "\"" 59 | let escapedQuoteCharacter = "\"\"" 60 | #else 61 | let quoteCharacter: Character = "'" 62 | let escapedQuoteCharacter = "'\\''" 63 | #endif 64 | // If there are no quote characters then we can just wrap the string within the quotes. 65 | guard let quotePos = utf8[pos...].firstIndex(of: quoteCharacter.asciiValue!) else { 66 | return String(quoteCharacter) + self + String(quoteCharacter) 67 | } 68 | 69 | // Otherwise iterate and escape all the single quotes. 70 | var newString = String(quoteCharacter) + String(self[.. String { 104 | var result = "" 105 | 106 | for (i, item) in enumerated() { 107 | // Add the separator, if necessary. 108 | if i == count - 1 { 109 | switch count { 110 | case 1: 111 | break 112 | case 2: 113 | result += " \(type.rawValue) " 114 | default: 115 | result += ", \(type.rawValue) " 116 | } 117 | } else if i != 0 { 118 | result += ", " 119 | } 120 | 121 | result += item 122 | } 123 | 124 | return result 125 | } 126 | } 127 | --------------------------------------------------------------------------------