diff --git a/Fixtures/MixedTargets/DummyTargets/Package.swift b/Fixtures/MixedTargets/DummyTargets/Package.swift new file mode 100644 index 00000000000..844b1b6a835 --- /dev/null +++ b/Fixtures/MixedTargets/DummyTargets/Package.swift @@ -0,0 +1,51 @@ +// swift-tools-version: 999.0 +// FIXME(ncooke3): Update above version with the next version of SwiftPM. + +import PackageDescription + +// This package vends targets to aid in testing the MixedTargetsWithObjC package. +let package = Package( + name: "DummyTargets", + dependencies: [ + .package(path: "../MixedTargetsWithObjC") + ], + targets: [ + .executableTarget( + name: "ClangExecutableDependsOnDynamicallyLinkedMixedTarget", + dependencies: [ + .product( + name: "DynamicallyLinkedBasicMixedTarget", + package: "MixedTargetsWithObjC" + ) + ] + ), + .executableTarget( + name: "ClangExecutableDependsOnStaticallyLinkedMixedTarget", + dependencies: [ + .product( + name: "StaticallyLinkedBasicMixedTarget", + package: "MixedTargetsWithObjC" + ) + ] + ), + .executableTarget( + name: "SwiftExecutableDependsOnDynamicallyLinkedMixedTarget", + dependencies: [ + .product( + name: "DynamicallyLinkedBasicMixedTarget", + package: "MixedTargetsWithObjC" + ) + ] + ), + .executableTarget( + name: "SwiftExecutableDependsOnStaticallyLinkedMixedTarget", + dependencies: [ + .product( + name: "StaticallyLinkedBasicMixedTarget", + package: "MixedTargetsWithObjC" + ) + ] + ) + + ] +) diff --git a/Fixtures/MixedTargets/DummyTargets/Sources/ClangExecutableDependsOnDynamicallyLinkedMixedTarget/main.m b/Fixtures/MixedTargets/DummyTargets/Sources/ClangExecutableDependsOnDynamicallyLinkedMixedTarget/main.m new file mode 100644 index 00000000000..2d33cd9ff80 --- /dev/null +++ b/Fixtures/MixedTargets/DummyTargets/Sources/ClangExecutableDependsOnDynamicallyLinkedMixedTarget/main.m @@ -0,0 +1,14 @@ +#import + +@import BasicMixedTarget; + +int main(int argc, const char *argv[]) { + @autoreleasepool { + // Ensure that the module is actually loaded. + Engine *engine = [[Engine alloc] init]; + OldCar *oldCar = [[OldCar alloc] init]; + + printf("Hello, world!\n"); + } + return 0; +} diff --git a/Fixtures/MixedTargets/DummyTargets/Sources/ClangExecutableDependsOnStaticallyLinkedMixedTarget/main.m b/Fixtures/MixedTargets/DummyTargets/Sources/ClangExecutableDependsOnStaticallyLinkedMixedTarget/main.m new file mode 100644 index 00000000000..15c303c6d97 --- /dev/null +++ b/Fixtures/MixedTargets/DummyTargets/Sources/ClangExecutableDependsOnStaticallyLinkedMixedTarget/main.m @@ -0,0 +1,14 @@ +#import + +@import BasicMixedTarget; + +int main(int argc, const char * argv[]) { + @autoreleasepool { + // Ensure that the module is actually loaded. + Engine *engine = [[Engine alloc] init]; + OldCar *oldCar = [[OldCar alloc] init]; + + printf("Hello, world!\n"); + } + return 0; +} diff --git a/Fixtures/MixedTargets/DummyTargets/Sources/SwiftExecutableDependsOnDynamicallyLinkedMixedTarget/main.swift b/Fixtures/MixedTargets/DummyTargets/Sources/SwiftExecutableDependsOnDynamicallyLinkedMixedTarget/main.swift new file mode 100644 index 00000000000..39b867d7aa4 --- /dev/null +++ b/Fixtures/MixedTargets/DummyTargets/Sources/SwiftExecutableDependsOnDynamicallyLinkedMixedTarget/main.swift @@ -0,0 +1,7 @@ +import BasicMixedTarget + +// Ensure that the module is actually loaded. +let _ = NewCar() +let _ = OldCar() + +print("Hello, world!") diff --git a/Fixtures/MixedTargets/DummyTargets/Sources/SwiftExecutableDependsOnStaticallyLinkedMixedTarget/main.swift b/Fixtures/MixedTargets/DummyTargets/Sources/SwiftExecutableDependsOnStaticallyLinkedMixedTarget/main.swift new file mode 100644 index 00000000000..39b867d7aa4 --- /dev/null +++ b/Fixtures/MixedTargets/DummyTargets/Sources/SwiftExecutableDependsOnStaticallyLinkedMixedTarget/main.swift @@ -0,0 +1,7 @@ +import BasicMixedTarget + +// Ensure that the module is actually loaded. +let _ = NewCar() +let _ = OldCar() + +print("Hello, world!") diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Package.swift b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Package.swift new file mode 100644 index 00000000000..8fd2edd1556 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Package.swift @@ -0,0 +1,32 @@ +// swift-tools-version: 999.0 +// FIXME(ncooke3): Update above version with the next version of SwiftPM. + +// NOTE: This is package is intended to build on all platforms (macOS, Linux, and Windows). + +import PackageDescription + +let package = Package( + name: "MixedTargetsWithCXX_InteropDisabled", + products: [ + .library( + name: "MixedTarget", + targets: ["MixedTarget"] + ), + .library( + name: "StaticallyLinkedMixedTarget", + type: .static, + targets: ["MixedTarget"] + ), + .library( + name: "DynamicallyLinkedMixedTarget", + type: .dynamic, + targets: ["MixedTarget"] + ) + ], + dependencies: [], + targets: [ + .target( + name: "MixedTarget" + ) + ] +) diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/CxxCountdown.cpp b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/CxxCountdown.cpp new file mode 100644 index 00000000000..1458da19d01 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/CxxCountdown.cpp @@ -0,0 +1,23 @@ +#include "CxxCountdown.hpp" + +#include + +#include "MixedTarget-Swift.h" +#include + +CxxCountdown::CxxCountdown(bool printCount) : printCount(printCount) {} + +void CxxCountdown::countdown(int x )const { + if (x < 0) + std::cout << "[c++] Cannot count down from a negative number.\n"; + return; + + if (printCount) + std::cout << "[c++] T-minus " << x << "... \n"; + + if (x == 0) + std::cout << "[c++] We have liftoff!"; + return; + + CxxCountdown::countdown(x - 1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/SwiftCountdown.swift b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/SwiftCountdown.swift new file mode 100644 index 00000000000..8920e89a14c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/SwiftCountdown.swift @@ -0,0 +1,27 @@ +import Foundation + +public struct SwiftCountdown { + private let printCount: Bool + + public init(printCount: Bool) { + self.printCount = printCount + } + + public func countdown(x: Int) { + if x < 0 { + print("[swift] Cannot count down from a negative number.") + return + } + + if printCount { + print("[swift] T-minus \(x)...") + } + + if x == 0 { + print("[swift] We have liftoff!") + return + } + + countdown(x: x - 1) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/include/CxxCountdown.hpp b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/include/CxxCountdown.hpp new file mode 100644 index 00000000000..9e0b988b010 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropDisabled/Sources/MixedTarget/include/CxxCountdown.hpp @@ -0,0 +1,10 @@ +#ifdef __cplusplus +class CxxCountdown +{ +public: + CxxCountdown(bool printCount); + void countdown(int x) const; +private: + bool printCount; +}; +#endif // __cplusplus diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Package.swift b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Package.swift new file mode 100644 index 00000000000..57aa762e33c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Package.swift @@ -0,0 +1,37 @@ +// swift-tools-version: 999.0 +// FIXME(ncooke3): Update above version with the next version of SwiftPM. + +// NOTE: This is package is intended to build on all platforms (macOS, Linux, and Windows). + +import PackageDescription + +let package = Package( + name: "MixedTargetsWithCXX_InteropEnabled", + products: [ + .library( + name: "MixedTarget", + targets: ["MixedTarget"] + ), + .library( + name: "StaticallyLinkedMixedTarget", + type: .static, + targets: ["MixedTarget"] + ), + .library( + name: "DynamicallyLinkedMixedTarget", + type: .dynamic, + targets: ["MixedTarget"] + ) + ], + dependencies: [], + targets: [ + .target( + name: "MixedTarget", + swiftSettings: [.interoperabilityMode(.Cxx)] + ) + ], + // TODO(ncooke3): Is the below note behavior that we want to be intended? + // This is needed for targets with that have + // `swiftSettings: [.interoperabilityMode(.Cxx)]` set. + cxxLanguageStandard: .cxx11 +) diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/CxxCountdown.cpp b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/CxxCountdown.cpp new file mode 100644 index 00000000000..bca733ded47 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/CxxCountdown.cpp @@ -0,0 +1,24 @@ +#include "CxxCountdown.hpp" + +#include + +#include "MixedTarget-Swift.h" +#include + +CxxCountdown::CxxCountdown(bool printCount) : printCount(printCount) {} + +void CxxCountdown::countdown(int x )const { + if (x < 0) + std::cout << "[c++] Cannot count down from a negative number.\n"; + return; + + if (printCount) + std::cout << "[c++] T-minus " << x << "... \n"; + + if (x == 0) + std::cout << "[c++] We have liftoff!"; + return; + + auto swiftCountdown = MixedTarget::SwiftCountdown::init(printCount); + swiftCountdown.countdown(x - 1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/SwiftCountdown.swift b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/SwiftCountdown.swift new file mode 100644 index 00000000000..1439c793d1e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/SwiftCountdown.swift @@ -0,0 +1,28 @@ +import Foundation + +public struct SwiftCountdown { + private let printCount: Bool + + public init(printCount: Bool) { + self.printCount = printCount + } + + public func countdown(x: Int) { + if x < 0 { + print("[swift] Cannot count down from a negative number.") + return + } + + if printCount { + print("[swift] T-minus \(x)...") + } + + if x == 0 { + print("[swift] We have liftoff!") + return + } + + let cxxCountdown = CxxCountdown(printCount) + cxxCountdown.countdown(Int32(x) - 1) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/include/CxxCountdown.hpp b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/include/CxxCountdown.hpp new file mode 100644 index 00000000000..d42c728ad5c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithCXX_InteropEnabled/Sources/MixedTarget/include/CxxCountdown.hpp @@ -0,0 +1,8 @@ +class CxxCountdown +{ +public: + CxxCountdown(bool printCount); + void countdown(int x) const; +private: + bool printCount; +}; diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Driver.m new file mode 100644 index 00000000000..776934b7d1e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Driver.m @@ -0,0 +1,9 @@ +#import + +// All three import statements should be supported. +#import "Driver.h" +#import "Public/Driver.h" +#import "MixedTargetWithCustomPaths/Sources/Public/Driver.h" + +@implementation Driver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Engine.swift new file mode 100644 index 00000000000..da1ee757331 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Engine.swift @@ -0,0 +1,4 @@ +import Foundation + +// This type is Objective-C compatible and used in `OldCar`. +@objc public class Engine: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/NewCar.swift new file mode 100644 index 00000000000..d50c1a21185 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/NewCar.swift @@ -0,0 +1,10 @@ +import Foundation + +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // The following types are defined in Objective-C. + var driver: Driver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/OldCar.m new file mode 100644 index 00000000000..e5be86f7ffa --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/OldCar.m @@ -0,0 +1,22 @@ +#import + +// The below import statements should be supported. +#import "MixedTargetWithCustomPaths/Sources/Public/OldCar.h" +#import "OldCar.h" +#import "Public/OldCar.h" +#import +#import + +// Import the Swift part of the module. +#import "MixedTargetWithCustomPaths-Swift.h" + +// Both import statements should be supported. +#import "MixedTargetWithCustomPaths/Sources/Transmission.h" +#import "Transmission.h" + +@interface OldCar () +@property(nonatomic) Transmission *transmission; +@end + +@implementation OldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Public/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Public/Driver.h new file mode 100644 index 00000000000..8326bf2f179 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Public/Driver.h @@ -0,0 +1,6 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +@interface Driver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Public/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Public/OldCar.h new file mode 100644 index 00000000000..995e09f0ff9 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Public/OldCar.h @@ -0,0 +1,14 @@ +#import + +#import "Driver.h" + +// The `Engine` type is declared in the Swift part of the module. Such types +// must be forward declared in headers. +@class Engine; + +@interface OldCar : NSObject +// `Engine` is defined in Swift. +@property(nullable) Engine *engine; +// `Driver` is defined in Objective-C. +@property(nullable) Driver *driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Transmission.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Transmission.h new file mode 100644 index 00000000000..57d0f1524b8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Transmission.h @@ -0,0 +1,10 @@ +#import + +typedef NS_ENUM(NSInteger, TransmissionKind) { + TransmissionKindManual, + TransmissionKindAutomatic +}; + +@interface Transmission : NSObject +@property (nonatomic, readonly, assign) TransmissionKind transmissionKind; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Transmission.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Transmission.m new file mode 100644 index 00000000000..f456e749b76 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/MixedTargetWithCustomPaths/Sources/Transmission.m @@ -0,0 +1,8 @@ +#import + +// Both import statements should be supported. +#import "Transmission.h" +#import "MixedTargetWithCustomPaths/Sources/Transmission.h" + +@implementation Transmission +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Package.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Package.swift new file mode 100644 index 00000000000..304972434e4 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Package.swift @@ -0,0 +1,251 @@ +// swift-tools-version: 999.0 +// FIXME(ncooke3): Update above version with the next version of SwiftPM. + +import PackageDescription + +let package = Package( + name: "MixedTargets", + products: [ + .library( + name: "BasicMixedTarget", + targets: ["BasicMixedTarget"] + ), + .library( + name: "StaticallyLinkedBasicMixedTarget", + type: .static, + targets: ["BasicMixedTarget"] + ), + .library( + name: "DynamicallyLinkedBasicMixedTarget", + type: .dynamic, + targets: ["BasicMixedTarget"] + ) + ], + dependencies: [], + targets: [ + // MARK: - BasicMixedTarget + .target( + name: "BasicMixedTarget" + ), + .testTarget( + name: "BasicMixedTargetTests", + dependencies: ["BasicMixedTarget"] + ), + + // FIXME(ncooke3): Re-enable with Swift compiler change (see proposal). + // // MARK: - BasicMixedTargetWithUmbrellaHeader + // .target( + // name: "BasicMixedTargetWithUmbrellaHeader" + // ), + // .testTarget( + // name: "BasicMixedTargetWithUmbrellaHeaderTests", + // dependencies: ["BasicMixedTargetWithUmbrellaHeader"] + // ), + + // MARK: - MixedTargetWithResources + .target( + name: "MixedTargetWithResources", + resources: [ + .process("foo.txt") + ] + ), + .testTarget( + name: "MixedTargetWithResourcesTests", + dependencies: ["MixedTargetWithResources"] + ), + + // MARK: - MixedTargetWithCustomModuleMap + .target( + name: "MixedTargetWithCustomModuleMap" + ), + .testTarget( + name: "MixedTargetWithCustomModuleMapTests", + dependencies: ["MixedTargetWithCustomModuleMap"] + ), + + // MARK: - MixedTargetWithInvalidCustomModuleMap + .target( + name: "MixedTargetWithInvalidCustomModuleMap" + ), + + // MARK: - MixedTargetWithCustomModuleMapAndResources + .target( + name: "MixedTargetWithCustomModuleMapAndResources", + resources: [ + .process("foo.txt") + ] + ), + .testTarget( + name: "MixedTargetWithCustomModuleMapAndResourcesTests", + dependencies: ["MixedTargetWithCustomModuleMapAndResources"] + ), + + // MARK: - MixedTargetWithCXX + .target( + name: "MixedTargetWithCXX" + ), + .testTarget( + name: "MixedTargetWithCXXTests", + dependencies: ["MixedTargetWithCXX"] + ), + + // MARK: - MixedTargetWithCXXAndCustomModuleMap + .target( + name: "MixedTargetWithCXXAndCustomModuleMap" + ), + + // MARK: - MixedTargetWithCXXPublicAPI + .target( + name: "MixedTargetWithCXXPublicAPI" + ), + .testTarget( + name: "MixedTargetWithCXXPublicAPITests", + dependencies: ["MixedTargetWithCXXPublicAPI"], + cSettings: [ + // To import `MixedTargetWithCXXPublicAPI` via a module style + // import, the following unsafe flags must be passed. See + // the Objective-C++ file in the test target. + .unsafeFlags(["-fcxx-modules", "-fmodules"]) + ] + ), + + // MARK: - MixedTargetWithCXXPublicAPIAndCustomModuleMap + // In order to import this target into downstream targets, two + // additional things must be done (depending on whether the target is + // being imported into a Clang vs. Swift context): + // - Clang context: If the client wants to import the module, client + // must pass `-fcxx-modules` and `-fmodules` as unsafe flags in + // the target's `cSettings`. Else, the client can just import + // individual public headers without further configuring the target. + // - Swift context: The mixed target needs to make a custom module + // map that only exposes public CXX headers in a non-Swift context. + // + // // module.modulemap + // module MixedTargetWithCXXPublicAPIAndCustomModuleMap { + // umbrella header "PublicNonCXXHeaders.h" + // + // module CXX { + // header "PublicCXXHeaders.h" + // export * + // requires !swift + // } + // + // export * + // } + // + .target( + name: "MixedTargetWithCXXPublicAPIAndCustomModuleMap" + ), + .testTarget( + name: "MixedTargetWithCXXPublicAPIAndCustomModuleMapTests", + dependencies: ["MixedTargetWithCXXPublicAPIAndCustomModuleMap"], + cSettings: [ + // To import `MixedTargetWithCXXPublicAPIAndCustomModuleMap` + // via a module style import, the following unsafe flags must + // be passed. See the Objective-C++ file in the test target. + .unsafeFlags(["-fcxx-modules", "-fmodules"]) + ], + linkerSettings: [ + .linkedLibrary("c++"), + ] + ), + + // MARK: - MixedTargetWithC + .target( + name: "MixedTargetWithC" + ), + .testTarget( + name: "MixedTargetWithCTests", + dependencies: ["MixedTargetWithC"] + ), + + // MARK: - MixedTargetWithNonPublicHeaders + .target( + name: "MixedTargetWithNonPublicHeaders" + ), + + // MARK: - MixedTargetWithCustomPaths + .target( + name: "MixedTargetWithCustomPaths", + path: "MixedTargetWithCustomPaths/Sources", + publicHeadersPath: "Public", + cSettings: [ + .headerSearchPath("../../") + ] + ), + + // MARK: - MixedTargetWithNestedPublicHeaders + .target( + name: "MixedTargetWithNestedPublicHeaders", + publicHeadersPath: "Blah/Public" + ), + + // MARK: - MixedTargetWithNestedPublicHeadersAndCustomModuleMap + .target( + name: "MixedTargetWithNestedPublicHeadersAndCustomModuleMap", + publicHeadersPath: "Blah/Public" + ), + + // MARK: - MixedTargetWithNoPublicObjectiveCHeaders + // This target is an edge case. It's purpose may or may not be + // useful, but it shouldn't fail to build. + .target( + name: "MixedTargetWithNoPublicObjectiveCHeaders" + ), + .testTarget( + name: "MixedTargetWithNoPublicObjectiveCHeadersTests", + dependencies: ["MixedTargetWithNoPublicObjectiveCHeaders"] + ), + + // MARK: - MixedTargetWithNoObjectiveCCompatibleSwiftAPI + .target( + name: "MixedTargetWithNoObjectiveCCompatibleSwiftAPI" + ), + + // MARK: - MixedTestTargetWithSharedTestUtilities + .testTarget( + name: "MixedTestTargetWithSharedUtilitiesTests" + ), + + // MARK: - PrivateHeadersCanBeTestedViaHeaderSearchPathsTests + .testTarget( + name: "PrivateHeadersCanBeTestedViaHeaderSearchPathsTests", + dependencies: ["MixedTargetWithNonPublicHeaders"], + cSettings: [ + // Adding a header search path at the root of the package will + // enable the Objective-C tests to import private headers. + .headerSearchPath("../../") + ] + ), + + // MARK: - MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource + .target( + name: "MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource" + ), + + // MARK: - Targets for testing the integration of a mixed target + .target( + name: "ClangTargetDependsOnMixedTarget", + dependencies: ["BasicMixedTarget"] + ), + .target( + name: "SwiftTargetDependsOnMixedTarget", + dependencies: ["BasicMixedTarget"] + ), + .target( + name: "MixedTargetDependsOnMixedTarget", + dependencies: ["BasicMixedTarget"] + ), + .target( + name: "MixedTargetDependsOnClangTarget", + dependencies: ["ClangTarget"] + ), + .target( + name: "MixedTargetDependsOnSwiftTarget", + dependencies: ["SwiftTarget"] + ), + // The below two targets are used for testing the above targets. + .target(name: "SwiftTarget"), + .target(name: "ClangTarget") + ] +) diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/CarPart.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/CarPart.m new file mode 100644 index 00000000000..ecd82eb5ea7 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/CarPart.m @@ -0,0 +1,10 @@ +#import + +// The below import statements should be supported. +#import "CarPart.h" +#import "include/CarPart.h" +#import +#import + +@implementation CarPart +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Driver.m new file mode 100644 index 00000000000..2fc73e43de1 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Driver.m @@ -0,0 +1,6 @@ +#import + +#import "include/Driver.h" + +@implementation Driver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Engine.swift new file mode 100644 index 00000000000..ef5bfd0f69d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Engine.swift @@ -0,0 +1,7 @@ +import Foundation + +// This type is Objective-C compatible and used in `OldCar`. +// FIXME(ncooke3): When Swift compiler's header generation logic is updated, +// subclass type from Objective-C. +// @objc public class Engine: CarPart {} +@objc public class Engine: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/FluxCapacitor.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/FluxCapacitor.h new file mode 100644 index 00000000000..ed4b7708434 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/FluxCapacitor.h @@ -0,0 +1,9 @@ +@import Foundation; + +#import +#import "CarPart.h" + +@interface FluxCapacitor : CarPart +@property (nonatomic, readonly) NSString *serialNumber; +- (instancetype)initWithSerialNumber:(NSString *)serialNumber; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/FluxCapacitor.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/FluxCapacitor.m new file mode 100644 index 00000000000..1cc37a5960e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/FluxCapacitor.m @@ -0,0 +1,13 @@ +#import "FluxCapacitor.h" + +@implementation FluxCapacitor + +- (instancetype)initWithSerialNumber:(NSString *)serialNumber { + self = [super init]; + if (self) { + _serialNumber = serialNumber; + } + return self; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/NewCar.swift new file mode 100644 index 00000000000..26195a50c56 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/NewCar.swift @@ -0,0 +1,15 @@ +import Foundation + +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // The following types are defined in Objective-C. + var driver: Driver? = nil + // TODO(ncooke3): Update when non-public headers are exposed to Swift. + // var transmission: Transmission? = nil + // var hasStickShift: Bool { + // return transmission != nil && transmission!.transmissionKind == .manual + // } + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/OldCar.m new file mode 100644 index 00000000000..0e519c718fe --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/OldCar.m @@ -0,0 +1,16 @@ +#import + +#import "OldCar.h" +#import "include/OldCar.h" + +// Import the Swift part of the module. +#import "BasicMixedTarget-Swift.h" + +#import "Transmission.h" + +@interface OldCar () +@property(nonatomic) Transmission *transmission; +@end + +@implementation OldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Transmission.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Transmission.h new file mode 100644 index 00000000000..57d0f1524b8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Transmission.h @@ -0,0 +1,10 @@ +#import + +typedef NS_ENUM(NSInteger, TransmissionKind) { + TransmissionKindManual, + TransmissionKindAutomatic +}; + +@interface Transmission : NSObject +@property (nonatomic, readonly, assign) TransmissionKind transmissionKind; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Transmission.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Transmission.m new file mode 100644 index 00000000000..ced7e283e83 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/Transmission.m @@ -0,0 +1,6 @@ +#import + +#import "Transmission.h" + +@implementation Transmission +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/CarPart.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/CarPart.h new file mode 100644 index 00000000000..68c343e6201 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/CarPart.h @@ -0,0 +1,5 @@ +#import + +// This type is subclassed by the Objective-C compatible Swift `Engine` type. +@interface CarPart : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/Driver.h new file mode 100644 index 00000000000..8326bf2f179 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/Driver.h @@ -0,0 +1,6 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +@interface Driver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/OldCar.h new file mode 100644 index 00000000000..995e09f0ff9 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTarget/include/OldCar.h @@ -0,0 +1,14 @@ +#import + +#import "Driver.h" + +// The `Engine` type is declared in the Swift part of the module. Such types +// must be forward declared in headers. +@class Engine; + +@interface OldCar : NSObject +// `Engine` is defined in Swift. +@property(nullable) Engine *engine; +// `Driver` is defined in Objective-C. +@property(nullable) Driver *driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Bakery.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Bakery.m new file mode 100644 index 00000000000..9bc34a981d2 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Bakery.m @@ -0,0 +1,14 @@ +#import + +#import "include/Bakery.h" +#import "Bakery.h" + +#import "BasicMixedTargetWithUmbrellaHeader-Swift.h" + +@implementation Bakery + +- (Cookie*)cookieOfTheDay { + return [[Cookie alloc] init]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Coffee.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Coffee.swift new file mode 100644 index 00000000000..3f763e40c63 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Coffee.swift @@ -0,0 +1,5 @@ +import Foundation + +public struct Coffee { + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Cookie.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Cookie.swift new file mode 100644 index 00000000000..ac522961d17 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Cookie.swift @@ -0,0 +1,3 @@ +import Foundation + +@objc open class Cookie: Dessert {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Dessert.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Dessert.m new file mode 100644 index 00000000000..334f70338ef --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/Dessert.m @@ -0,0 +1,7 @@ +#import + +#import "include/Dessert.h" +#import "Dessert.h" + +@implementation Dessert +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/Bakery.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/Bakery.h new file mode 100644 index 00000000000..90ebc14227e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/Bakery.h @@ -0,0 +1,7 @@ +#import + +@class Cookie; + +@interface Bakery : NSObject +- (Cookie*)cookieOfTheDay; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/BasicMixedTargetWithUmbrellaHeader.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/BasicMixedTargetWithUmbrellaHeader.h new file mode 100644 index 00000000000..1e831674929 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/BasicMixedTargetWithUmbrellaHeader.h @@ -0,0 +1,2 @@ +#import "Bakery.h" +#import "Dessert.h" diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/Dessert.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/Dessert.h new file mode 100644 index 00000000000..85bdc173623 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/BasicMixedTargetWithUmbrellaHeader/include/Dessert.h @@ -0,0 +1,4 @@ +#import + +@interface Dessert : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTarget/Vessel.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTarget/Vessel.m new file mode 100644 index 00000000000..d5a92f7251b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTarget/Vessel.m @@ -0,0 +1,6 @@ +#import + +#import "include/Vessel.h" + +@implementation Vessel +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTarget/include/Vessel.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTarget/include/Vessel.h new file mode 100644 index 00000000000..97af0487b92 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTarget/include/Vessel.h @@ -0,0 +1,5 @@ +#import + +@interface Vessel : NSObject +@property (assign, getter=hasLifeJackets) BOOL lifeJackets; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTargetDependsOnMixedTarget/JunkYard.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTargetDependsOnMixedTarget/JunkYard.m new file mode 100644 index 00000000000..476fd61352b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTargetDependsOnMixedTarget/JunkYard.m @@ -0,0 +1,30 @@ +#import + +#import "include/JunkYard.h" + +#if TEST_MODULE_IMPORTS +// Import the mixed target's module. +@import BasicMixedTarget; +#else +// Import the mixed target's public headers. Both "..." and <...> style imports +// should resolve. +#import "CarPart.h" +#import +#import "Driver.h" +#import +#import "OldCar.h" +#import +#import "BasicMixedTarget-Swift.h" +#import +#endif // TEST_MODULE_IMPORTS + +@interface JunkYard () +// The below types come from the `BasicMixedTarget` module. +@property(nullable) Engine *engine; +@property(nullable) Driver *driver; +@property(nullable) OldCar *oldCar; +@property(nullable) CarPart *carPart; +@end + +@implementation JunkYard +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTargetDependsOnMixedTarget/include/JunkYard.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTargetDependsOnMixedTarget/include/JunkYard.h new file mode 100644 index 00000000000..eb0ff084158 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/ClangTargetDependsOnMixedTarget/include/JunkYard.h @@ -0,0 +1,4 @@ +#import + +@interface JunkYard : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/NewBoat.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/NewBoat.swift new file mode 100644 index 00000000000..de0f955a5fe --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/NewBoat.swift @@ -0,0 +1,14 @@ +import Foundation +import ClangTarget + +@objc public class NewBoat: Vessel { + + func checkForLifeJackets() { + if hasLifeJackets { + print("Life jackets on board!") + } else { + print("Life jackets missing!") + } + } + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/OldBoat.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/OldBoat.m new file mode 100644 index 00000000000..5fdf97b6acb --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/OldBoat.m @@ -0,0 +1,16 @@ +#import + +#import "include/OldBoat.h" + +@implementation OldBoat + +- (void)checkForLifeJackets { + // Check that property from superclass is visible. + if (self.hasLifeJackets) { + NSLog(@"Life jackets on board!"); + } else { + NSLog(@"Life jackets missing!"); + } +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/include/OldBoat.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/include/OldBoat.h new file mode 100644 index 00000000000..7b1124d827d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnClangTarget/include/OldBoat.h @@ -0,0 +1,8 @@ +#import + +@import ClangTarget; +#import "Vessel.h" +#import + +@interface OldBoat : Vessel +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/NewBoat.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/NewBoat.swift new file mode 100644 index 00000000000..f32b60da772 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/NewBoat.swift @@ -0,0 +1,10 @@ +import Foundation +import BasicMixedTarget + +public class NewBoat { + // The below types comes from the `BasicMixedTarget` module`. + var engine: Engine? = nil + var driver: Driver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/OldBoat.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/OldBoat.m new file mode 100644 index 00000000000..990e705ba6a --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/OldBoat.m @@ -0,0 +1,28 @@ +#import + +#import "include/OldBoat.h" + +#if TEST_MODULE_IMPORTS +// Import the mixed target's module. +@import BasicMixedTarget; +#else +// Import the mixed target's public headers. Both "..." and <...> style imports +// should resolve. +#import "CarPart.h" +#import +#import "Driver.h" +#import +#import "OldCar.h" +#import +#import "BasicMixedTarget-Swift.h" +#import +#endif // TEST_MODULE_IMPORTS + +@interface OldBoat () +// The below types comes from the `BasicMixedTarget` module`. +@property(nonatomic, strong) Engine *engine; +@property(nonatomic, strong) Driver *driver; +@end + +@implementation OldBoat +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/include/OldBoat.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/include/OldBoat.h new file mode 100644 index 00000000000..984adffd288 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnMixedTarget/include/OldBoat.h @@ -0,0 +1,4 @@ +#import + +@interface OldBoat : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/NewBoat.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/NewBoat.swift new file mode 100644 index 00000000000..8cb5992ed01 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/NewBoat.swift @@ -0,0 +1,7 @@ +import Foundation +import SwiftTarget + +@objc public class NewBoat: NSObject { + // The below types comes from the `SwiftTarget` module`. + var lifeJacket: [LifeJacket] = [] +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/OldBoat.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/OldBoat.m new file mode 100644 index 00000000000..665f617f3a7 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/OldBoat.m @@ -0,0 +1,16 @@ +#import + +#import "include/OldBoat.h" + +@implementation OldBoat + +- (void)checkForLifeJackets { + // Check that `LifeJacket` type from `SwiftTarget` is visible. + if (self.lifeJackets.count > 0) { + NSLog(@"Life jackets on board!"); + } else { + NSLog(@"Life jackets missing!"); + } +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/include/OldBoat.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/include/OldBoat.h new file mode 100644 index 00000000000..b3fe04a7e44 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetDependsOnSwiftTarget/include/OldBoat.h @@ -0,0 +1,7 @@ +#import + +@import SwiftTarget; + +@interface OldBoat : NSObject +@property(nonatomic, readonly) NSArray *lifeJackets; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/factorial.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/factorial.swift new file mode 100644 index 00000000000..844a0f7a585 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/factorial.swift @@ -0,0 +1,5 @@ +import Foundation + +public func factorial(_ x: Int32) -> CLong { + return find_factorial(x) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/find_factorial.c b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/find_factorial.c new file mode 100644 index 00000000000..7c7895a0c3c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/find_factorial.c @@ -0,0 +1,6 @@ +#include "include/find_factorial.h" + +long find_factorial(int x) { + if (x == 0 || x == 1) return 1; + return x * find_factorial(x-1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/include/find_factorial.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/include/find_factorial.h new file mode 100644 index 00000000000..4f15e5d8adc --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithC/include/find_factorial.h @@ -0,0 +1 @@ +long find_factorial(int x); diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/Calculator.mm b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/Calculator.mm new file mode 100644 index 00000000000..c06770ecd5e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/Calculator.mm @@ -0,0 +1,15 @@ +#import + +#import "include/Calculator.h" + +// Import C++ header. +#import "FactorialFinder.hpp" + +@implementation Calculator + ++ (long)factorialForInt:(int)integer { + FactorialFinder ff; + return ff.factorial(integer); +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/Factorial.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/Factorial.swift new file mode 100644 index 00000000000..51ba08e4d75 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/Factorial.swift @@ -0,0 +1,5 @@ +import Foundation + +public func factorial(_ x: Int32) -> Int { + return Calculator.factorial(for: x) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/FactorialFinder.cpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/FactorialFinder.cpp new file mode 100644 index 00000000000..3e5e6bfe031 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/FactorialFinder.cpp @@ -0,0 +1,6 @@ +#include "FactorialFinder.hpp" + +long FactorialFinder::factorial(int n) { + if (n == 0 || n == 1) return 1; + return n * factorial(n-1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/FactorialFinder.hpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/FactorialFinder.hpp new file mode 100644 index 00000000000..dfb6e36793b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/FactorialFinder.hpp @@ -0,0 +1,9 @@ +#ifdef __cplusplus + +class FactorialFinder +{ +public: + long factorial(int n); +}; + +#endif // __cplusplus diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/include/Calculator.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/include/Calculator.h new file mode 100644 index 00000000000..7de7e3bbe1d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXX/include/Calculator.h @@ -0,0 +1,5 @@ +#import + +@interface Calculator : NSObject ++ (long)factorialForInt:(int)integer; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/Calculator.mm b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/Calculator.mm new file mode 100644 index 00000000000..c06770ecd5e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/Calculator.mm @@ -0,0 +1,15 @@ +#import + +#import "include/Calculator.h" + +// Import C++ header. +#import "FactorialFinder.hpp" + +@implementation Calculator + ++ (long)factorialForInt:(int)integer { + FactorialFinder ff; + return ff.factorial(integer); +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/Factorial.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/Factorial.swift new file mode 100644 index 00000000000..51ba08e4d75 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/Factorial.swift @@ -0,0 +1,5 @@ +import Foundation + +public func factorial(_ x: Int32) -> Int { + return Calculator.factorial(for: x) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/FactorialFinder.cpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/FactorialFinder.cpp new file mode 100644 index 00000000000..3e5e6bfe031 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/FactorialFinder.cpp @@ -0,0 +1,6 @@ +#include "FactorialFinder.hpp" + +long FactorialFinder::factorial(int n) { + if (n == 0 || n == 1) return 1; + return n * factorial(n-1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/FactorialFinder.hpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/FactorialFinder.hpp new file mode 100644 index 00000000000..dfb6e36793b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/FactorialFinder.hpp @@ -0,0 +1,9 @@ +#ifdef __cplusplus + +class FactorialFinder +{ +public: + long factorial(int n); +}; + +#endif // __cplusplus diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/include/Calculator.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/include/Calculator.h new file mode 100644 index 00000000000..7de7e3bbe1d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/include/Calculator.h @@ -0,0 +1,5 @@ +#import + +@interface Calculator : NSObject ++ (long)factorialForInt:(int)integer; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/include/module.modulemap b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/include/module.modulemap new file mode 100644 index 00000000000..ee6a48eb15c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXAndCustomModuleMap/include/module.modulemap @@ -0,0 +1,4 @@ +module MixedTargetWithCXXAndCustomModuleMap { + header "Calculator.h" + export * +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXFactorialFinder.cpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXFactorialFinder.cpp new file mode 100644 index 00000000000..a9a78c1feb9 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXFactorialFinder.cpp @@ -0,0 +1,6 @@ +#include "CXXFactorialFinder.hpp" + +long CXXFactorialFinder::factorial(int n) { + if (n == 0 || n == 1) return 1; + return n * factorial(n-1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXFactorialFinder.hpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXFactorialFinder.hpp new file mode 100644 index 00000000000..9e5ab1803ca --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXFactorialFinder.hpp @@ -0,0 +1,10 @@ +#ifdef __cplusplus + +class CXXFactorialFinder +{ +public: + long factorial(int n); +}; + +#endif // __cplusplus + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXSumFinder.cpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXSumFinder.cpp new file mode 100644 index 00000000000..24f3061cc6b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/CXXSumFinder.cpp @@ -0,0 +1,6 @@ +#include "CXXSumFinder.hpp" + +long CXXSumFinder::sum(int x, int y) { + return x + y; +} + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/Factorial.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/Factorial.swift new file mode 100644 index 00000000000..e23f0a7f93b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/Factorial.swift @@ -0,0 +1,11 @@ +import Foundation + +@objc public class Factorial: NSObject { + @objc public static func text() -> String { + return "Hello, World!" + } +} + +public func factorial(_ x: Int32) -> Int { + return ObjcCalculator.factorial(for: x) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/ObjcCalculator.mm b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/ObjcCalculator.mm new file mode 100644 index 00000000000..51abb927965 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/ObjcCalculator.mm @@ -0,0 +1,22 @@ +#import + +#import "include/ObjcCalculator.h" + +// Import C++ headers. +#import "CXXFactorialFinder.hpp" +#import "CXXSumFinder.hpp" + +@implementation ObjcCalculator + ++ (long)factorialForInt:(int)integer { + CXXFactorialFinder ff; + return ff.factorial(integer); +} + ++ (long)sumX:(int)x andY:(int)y { + CXXSumFinder sf; + return sf.sum(x, y); +} + +@end + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/Sum.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/Sum.swift new file mode 100644 index 00000000000..ba47080c761 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/Sum.swift @@ -0,0 +1,5 @@ +import Foundation + +public func sum(x: Int32, y: Int32) -> Int { + return ObjcCalculator.sum(x:x, y:y) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/include/CXXSumFinder.hpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/include/CXXSumFinder.hpp new file mode 100644 index 00000000000..39bd407e74e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/include/CXXSumFinder.hpp @@ -0,0 +1,9 @@ +#ifdef __cplusplus + +class CXXSumFinder +{ +public: + long sum(int x, int y); +}; + +#endif // __cplusplus diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/include/ObjcCalculator.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/include/ObjcCalculator.h new file mode 100644 index 00000000000..14378035ebe --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPI/include/ObjcCalculator.h @@ -0,0 +1,6 @@ +#import + +@interface ObjcCalculator : NSObject ++ (long)factorialForInt:(int)integer; ++ (long)sumX:(int)x andY:(int)y NS_SWIFT_NAME(sum(x:y:)); +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/Factorial.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/Factorial.swift new file mode 100644 index 00000000000..08e13752ccf --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/Factorial.swift @@ -0,0 +1,11 @@ +import Foundation + +@objc public class Factorial: NSObject { + @objc public static func text() -> String { + return "Hello, World!" + } +} + +public func factorial(_ x: Int32) -> Int { + return XYZObjcCalculator.factorial(for: x) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/Sum.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/Sum.swift new file mode 100644 index 00000000000..837bf1eb77e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/Sum.swift @@ -0,0 +1,5 @@ +import Foundation + +public func sum(x: Int32, y: Int32) -> Int { + return XYZObjcCalculator.sum(x:x, y:y) +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxFactorialFinder.cpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxFactorialFinder.cpp new file mode 100644 index 00000000000..41af6ed34ab --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxFactorialFinder.cpp @@ -0,0 +1,6 @@ +#include "XYZCxxFactorialFinder.hpp" + +long XYZCxxFactorialFinder::factorial(int n) { + if (n == 0 || n == 1) return 1; + return n * factorial(n-1); +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxFactorialFinder.hpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxFactorialFinder.hpp new file mode 100644 index 00000000000..4e615bfcab3 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxFactorialFinder.hpp @@ -0,0 +1,10 @@ +#ifdef __cplusplus + +class XYZCxxFactorialFinder +{ +public: + long factorial(int n); +}; + +#endif // __cplusplus + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxSumFinder.cpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxSumFinder.cpp new file mode 100644 index 00000000000..858cb8950a8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZCxxSumFinder.cpp @@ -0,0 +1,5 @@ +#include "XYZCxxSumFinder.hpp" + +long XYZCxxSumFinder::sum(int x, int y) { + return x + y; +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZObjcCalculator.mm b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZObjcCalculator.mm new file mode 100644 index 00000000000..ae3c8abb78d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/XYZObjcCalculator.mm @@ -0,0 +1,21 @@ +#import + +#import "include/XYZObjcCalculator.h" + +// Import C++ headers. +#import "XYZCxxFactorialFinder.hpp" +#import "XYZCxxSumFinder.hpp" + +@implementation XYZObjcCalculator + ++ (long)factorialForInt:(int)integer { + XYZCxxFactorialFinder ff; + return ff.factorial(integer); +} + ++ (long)sumX:(int)x andY:(int)y { + XYZCxxSumFinder sf; + return sf.sum(x, y); +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/XYZCxxSumFinder.hpp b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/XYZCxxSumFinder.hpp new file mode 100644 index 00000000000..87b3dbe24a7 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/XYZCxxSumFinder.hpp @@ -0,0 +1,9 @@ +#ifdef __cplusplus + +class XYZCxxSumFinder +{ +public: + long sum(int x, int y); +}; + +#endif // __cplusplus diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/XYZObjcCalculator.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/XYZObjcCalculator.h new file mode 100644 index 00000000000..7e12ee2e958 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/XYZObjcCalculator.h @@ -0,0 +1,6 @@ +#import + +@interface XYZObjcCalculator : NSObject ++ (long)factorialForInt:(int)integer; ++ (long)sumX:(int)x andY:(int)y NS_SWIFT_NAME(sum(x:y:)); +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/module.modulemap b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/module.modulemap new file mode 100644 index 00000000000..e9aaea85bad --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCXXPublicAPIAndCustomModuleMap/include/module.modulemap @@ -0,0 +1,11 @@ +module MixedTargetWithCXXPublicAPIAndCustomModuleMap { + header "XYZObjcCalculator.h" + + module PublicCXXAPI { + header "XYZCxxSumFinder.hpp" + requires !swift + export * + } + + export * +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Driver.m new file mode 100644 index 00000000000..3de8905ffa2 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Driver.m @@ -0,0 +1,8 @@ +#import + +// Both import statements should be supported. +#import "Driver.h" +#import "include/Driver.h" + +@implementation MyDriver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Engine.swift new file mode 100644 index 00000000000..93bc3a224da --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Engine.swift @@ -0,0 +1,4 @@ +import Foundation + +// This type is Objective-C compatible and used in `OldCar`. +@objc public class Engine: MyMachine {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Machine.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Machine.m new file mode 100644 index 00000000000..1bbd6928ffd --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/Machine.m @@ -0,0 +1,8 @@ +#import + +// Both import statements should be supported. +#import "Machine.h" +#import "include/Machine.h" + +@implementation MyMachine +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/NewCar.swift new file mode 100644 index 00000000000..2bccb0068fc --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/NewCar.swift @@ -0,0 +1,10 @@ +import Foundation + +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // `MyDriver` is defined in Objective-C. + var driver: MyDriver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/OldCar.m new file mode 100644 index 00000000000..9eb7a363945 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/OldCar.m @@ -0,0 +1,11 @@ +#import + +// Both import statements should be supported. +#import "OldCar.h" +#import "include/OldCar.h" + +// Import the Swift part of the module. +#import "MixedTargetWithCustomModuleMap-Swift.h" + +@implementation MyOldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/Driver.h new file mode 100644 index 00000000000..2fbaa124098 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/Driver.h @@ -0,0 +1,7 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +// `My` prefix is to avoid naming collision in test bundle. +@interface MyDriver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/Machine.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/Machine.h new file mode 100644 index 00000000000..48a724bff9e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/Machine.h @@ -0,0 +1,6 @@ +#import + +// This type is subclassed by the Swift type `Engine`. +// `My` prefix is to avoid naming collision in test bundle. +@interface MyMachine : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/MixedTarget.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/MixedTarget.h new file mode 100644 index 00000000000..7909c0ae8e3 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/MixedTarget.h @@ -0,0 +1,3 @@ +#import "Machine.h" +#import "OldCar.h" +#import "Driver.h" diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/MixedTargetWithCustomModuleMap/MixedTargetWithCustomModuleMap.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/MixedTargetWithCustomModuleMap/MixedTargetWithCustomModuleMap.h new file mode 100644 index 00000000000..d2bd3472ff2 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/MixedTargetWithCustomModuleMap/MixedTargetWithCustomModuleMap.h @@ -0,0 +1,2 @@ +#import "../MixedTarget.h" + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/OldCar.h new file mode 100644 index 00000000000..4ccd96616fc --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/OldCar.h @@ -0,0 +1,15 @@ +#import + +#import "Driver.h" + +// The `Engine` type is declared in the Swift part of the module. Such types +// must be forward declared in headers. +@class Engine; + +// `My` prefix is to avoid naming collision in test bundle. +@interface MyOldCar : NSObject +// `Engine` is defined in Swift. +@property(nullable) Engine *engine; +// `Driver` is defined in Objective-C. +@property(nullable) MyDriver *driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/module.modulemap b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/module.modulemap new file mode 100644 index 00000000000..0c7823261fc --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMap/include/module.modulemap @@ -0,0 +1,4 @@ +module MixedTargetWithCustomModuleMap { + umbrella header "MixedTarget.h" + export * +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Driver.m new file mode 100644 index 00000000000..fc76ea21a8e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Driver.m @@ -0,0 +1,8 @@ +#import + +// Both import statements should be supported. +#import "Driver.h" +#import "include/Driver.h" + +@implementation ABCDriver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Engine.swift new file mode 100644 index 00000000000..da1ee757331 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Engine.swift @@ -0,0 +1,4 @@ +import Foundation + +// This type is Objective-C compatible and used in `OldCar`. +@objc public class Engine: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/NewCar.swift new file mode 100644 index 00000000000..07ea404b85c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/NewCar.swift @@ -0,0 +1,10 @@ +import Foundation + +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // `Driver` is defined in Objective-C. + var driver: Driver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/OldCar.m new file mode 100644 index 00000000000..741d1bc3d46 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/OldCar.m @@ -0,0 +1,11 @@ +#import + +// Both import statements should be supported. +#import "OldCar.h" +#import "include/OldCar.h" + +// Import the Swift part of the module. +#import "MixedTargetWithCustomModuleMapAndResources-Swift.h" + +@implementation ABCOldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Transmission.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Transmission.h new file mode 100644 index 00000000000..9dae75e314e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Transmission.h @@ -0,0 +1,11 @@ +#import + +typedef NS_ENUM(NSInteger, TransmissionKind) { + TransmissionKindManual, + TransmissionKindAutomatic +}; + +NS_SWIFT_NAME(Transmission) +@interface ABCTransmission : NSObject +@property (nonatomic, readonly, assign) TransmissionKind transmissionKind; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Transmission.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Transmission.m new file mode 100644 index 00000000000..36f9d4edad3 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/Transmission.m @@ -0,0 +1,6 @@ +#import + +#import "Transmission.h" + +@implementation ABCTransmission +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/foo.txt b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/foo.txt new file mode 100644 index 00000000000..cd0875583aa --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/foo.txt @@ -0,0 +1 @@ +Hello world! diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/Driver.h new file mode 100644 index 00000000000..8a70e1f9915 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/Driver.h @@ -0,0 +1,7 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +NS_SWIFT_NAME(Driver) +@interface ABCDriver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/MixedTargetWithCustomModuleMapAndResources.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/MixedTargetWithCustomModuleMapAndResources.h new file mode 100644 index 00000000000..cbe8b7ebef6 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/MixedTargetWithCustomModuleMapAndResources.h @@ -0,0 +1,2 @@ +#import "OldCar.h" +#import "Driver.h" diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/OldCar.h new file mode 100644 index 00000000000..122c9846647 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/OldCar.h @@ -0,0 +1,15 @@ +#import + +#import "Driver.h" + +// The `Engine` type is declared in the Swift part of the module. Such types +// must be forward declared in headers. +@class Engine; + +NS_SWIFT_NAME(OldCar) +@interface ABCOldCar : NSObject +// `Engine` is defined in Swift. +@property(nullable) Engine *engine; +// `Driver` is defined in Objective-C. +@property(nullable) ABCDriver *driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/module.modulemap b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/module.modulemap new file mode 100644 index 00000000000..3b68fe493c8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithCustomModuleMapAndResources/include/module.modulemap @@ -0,0 +1,4 @@ +module MixedTargetWithCustomModuleMapAndResources { + umbrella header "MixedTargetWithCustomModuleMapAndResources.h" + export * +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/Foo.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/Foo.m new file mode 100644 index 00000000000..5c41623fa3d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/Foo.m @@ -0,0 +1 @@ +// File is intentionally left blank. diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/Foo.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/Foo.swift new file mode 100644 index 00000000000..5c41623fa3d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/Foo.swift @@ -0,0 +1 @@ +// File is intentionally left blank. diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/include/Foo.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/include/Foo.h new file mode 100644 index 00000000000..5c41623fa3d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/include/Foo.h @@ -0,0 +1 @@ +// File is intentionally left blank. diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/include/module.modulemap b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/include/module.modulemap new file mode 100644 index 00000000000..747426e4769 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithInvalidCustomModuleMap/include/module.modulemap @@ -0,0 +1,3 @@ +module MixedTargetWithInvalidCustomModuleMap { + header "Foo.h" +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Blah/Public/Driver/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Blah/Public/Driver/Driver.h new file mode 100644 index 00000000000..8326bf2f179 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Blah/Public/Driver/Driver.h @@ -0,0 +1,6 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +@interface Driver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Blah/Public/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Blah/Public/OldCar.h new file mode 100644 index 00000000000..60a945def50 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Blah/Public/OldCar.h @@ -0,0 +1,18 @@ +#import + +// Both import statements should be supported. +// - This one is from the root of the `publicHeadersPath`. +#import "Driver/Driver.h" +// - This one is from the root of the target's sources directory. +#import "Blah/Public/Driver/Driver.h" + +// The `Engine` type is declared in the Swift part of the module. Such types +// must be forward declared in headers. +@class Engine; + +@interface OldCar : NSObject +// `Engine` is defined in Swift. +@property(nullable) Engine *engine; +// `Driver` is defined in Objective-C. +@property(nullable) Driver *driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Driver.m new file mode 100644 index 00000000000..6fa02dafaf7 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Driver.m @@ -0,0 +1,10 @@ +#import + +// Both import statements should be supported. +// - This one is from the root of the `publicHeadersPath`. +#import "Driver/Driver.h" +// - This one is from the root of the target's sources directory. +#import "Blah/Public/Driver/Driver.h" + +@implementation Driver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Engine.swift new file mode 100644 index 00000000000..da1ee757331 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Engine.swift @@ -0,0 +1,4 @@ +import Foundation + +// This type is Objective-C compatible and used in `OldCar`. +@objc public class Engine: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/NewCar.swift new file mode 100644 index 00000000000..d50c1a21185 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/NewCar.swift @@ -0,0 +1,10 @@ +import Foundation + +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // The following types are defined in Objective-C. + var driver: Driver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/OldCar.m new file mode 100644 index 00000000000..10522bd7820 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/OldCar.m @@ -0,0 +1,19 @@ +#import + +// Both import statements should be supported. +// - This one is from the root of the `publicHeadersPath`. +#import "OldCar.h" +// - This one is from the root of the target's sources directory. +#import "Blah/Public/OldCar.h" + +// Import the Swift part of the module. +#import "MixedTargetWithNestedPublicHeaders-Swift.h" + +#import "Transmission.h" + +@interface OldCar () +@property(nonatomic) Transmission *transmission; +@end + +@implementation OldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Transmission.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Transmission.h new file mode 100644 index 00000000000..57d0f1524b8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Transmission.h @@ -0,0 +1,10 @@ +#import + +typedef NS_ENUM(NSInteger, TransmissionKind) { + TransmissionKindManual, + TransmissionKindAutomatic +}; + +@interface Transmission : NSObject +@property (nonatomic, readonly, assign) TransmissionKind transmissionKind; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Transmission.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Transmission.m new file mode 100644 index 00000000000..ced7e283e83 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeaders/Transmission.m @@ -0,0 +1,6 @@ +#import + +#import "Transmission.h" + +@implementation Transmission +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/Driver/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/Driver/Driver.h new file mode 100644 index 00000000000..8326bf2f179 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/Driver/Driver.h @@ -0,0 +1,6 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +@interface Driver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/OldCar.h new file mode 100644 index 00000000000..60a945def50 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/OldCar.h @@ -0,0 +1,18 @@ +#import + +// Both import statements should be supported. +// - This one is from the root of the `publicHeadersPath`. +#import "Driver/Driver.h" +// - This one is from the root of the target's sources directory. +#import "Blah/Public/Driver/Driver.h" + +// The `Engine` type is declared in the Swift part of the module. Such types +// must be forward declared in headers. +@class Engine; + +@interface OldCar : NSObject +// `Engine` is defined in Swift. +@property(nullable) Engine *engine; +// `Driver` is defined in Objective-C. +@property(nullable) Driver *driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/module.modulemap b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/module.modulemap new file mode 100644 index 00000000000..6dde727eaea --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Blah/Public/module.modulemap @@ -0,0 +1,5 @@ +module MixedTargetWithNestedPublicHeadersAndCustomModuleMap { + header "Driver/Driver.h" + header "OldCar.h" + export * +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Driver.m new file mode 100644 index 00000000000..6fa02dafaf7 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Driver.m @@ -0,0 +1,10 @@ +#import + +// Both import statements should be supported. +// - This one is from the root of the `publicHeadersPath`. +#import "Driver/Driver.h" +// - This one is from the root of the target's sources directory. +#import "Blah/Public/Driver/Driver.h" + +@implementation Driver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Engine.swift new file mode 100644 index 00000000000..da1ee757331 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Engine.swift @@ -0,0 +1,4 @@ +import Foundation + +// This type is Objective-C compatible and used in `OldCar`. +@objc public class Engine: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/NewCar.swift new file mode 100644 index 00000000000..d50c1a21185 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/NewCar.swift @@ -0,0 +1,10 @@ +import Foundation + +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // The following types are defined in Objective-C. + var driver: Driver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/OldCar.m new file mode 100644 index 00000000000..6fe0be3ad91 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/OldCar.m @@ -0,0 +1,19 @@ +#import + +// Both import statements should be supported. +// - This one is from the root of the `publicHeadersPath`. +#import "OldCar.h" +// - This one is from the root of the target's sources directory. +#import "Blah/Public/OldCar.h" + +// Import the Swift part of the module. +#import "MixedTargetWithNestedPublicHeadersAndCustomModuleMap-Swift.h" + +#import "Transmission.h" + +@interface OldCar () +@property(nonatomic) Transmission *transmission; +@end + +@implementation OldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Transmission.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Transmission.h new file mode 100644 index 00000000000..57d0f1524b8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Transmission.h @@ -0,0 +1,10 @@ +#import + +typedef NS_ENUM(NSInteger, TransmissionKind) { + TransmissionKindManual, + TransmissionKindAutomatic +}; + +@interface Transmission : NSObject +@property (nonatomic, readonly, assign) TransmissionKind transmissionKind; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Transmission.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Transmission.m new file mode 100644 index 00000000000..ced7e283e83 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNestedPublicHeadersAndCustomModuleMap/Transmission.m @@ -0,0 +1,6 @@ +#import + +#import "Transmission.h" + +@implementation Transmission +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Driver.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Driver.m new file mode 100644 index 00000000000..2fc73e43de1 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Driver.m @@ -0,0 +1,6 @@ +#import + +#import "include/Driver.h" + +@implementation Driver +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Engine.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Engine.swift new file mode 100644 index 00000000000..d036e1fa3be --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Engine.swift @@ -0,0 +1,6 @@ +import Foundation + +// This type is intentionally *NOT* Objective-C compatible. +public class Engine { + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/NewCar.swift new file mode 100644 index 00000000000..80b725b488e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/NewCar.swift @@ -0,0 +1,11 @@ +import Foundation + +// This type is intentionally *NOT* Objective-C compatible. +public class NewCar { + // `Engine` is defined in Swift. + var engine: Engine? = nil + // The following types are defined in Objective-C. + var driver: Driver? = nil + + public init() {} +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/OldCar.m new file mode 100644 index 00000000000..6c1ad2b75eb --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/OldCar.m @@ -0,0 +1,20 @@ +#import + +#import "OldCar.h" +#import "include/OldCar.h" + +// Import the Swift part of the module. Note that this header wouldn't vend any +// API as this target intentionally has no Objective-C compatible Swift API. +#import "MixedTargetWithNoObjectiveCCompatibleSwiftAPI-Swift.h" + +#import "Transmission.h" + +@interface OldCar () +@property(nonatomic) Transmission *transmission; +#if EXPECT_FAILURE +@property(nonatomic) Driver *driver; +#endif +@end + +@implementation OldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Transmission.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Transmission.h new file mode 100644 index 00000000000..57d0f1524b8 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Transmission.h @@ -0,0 +1,10 @@ +#import + +typedef NS_ENUM(NSInteger, TransmissionKind) { + TransmissionKindManual, + TransmissionKindAutomatic +}; + +@interface Transmission : NSObject +@property (nonatomic, readonly, assign) TransmissionKind transmissionKind; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Transmission.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Transmission.m new file mode 100644 index 00000000000..ced7e283e83 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/Transmission.m @@ -0,0 +1,6 @@ +#import + +#import "Transmission.h" + +@implementation Transmission +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/include/Driver.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/include/Driver.h new file mode 100644 index 00000000000..8326bf2f179 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/include/Driver.h @@ -0,0 +1,6 @@ +#import + +// This type is Swift compatible and used in `NewCar`. +@interface Driver : NSObject +@property(nonnull) NSString* name; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/include/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/include/OldCar.h new file mode 100644 index 00000000000..01290cc43e6 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoObjectiveCCompatibleSwiftAPI/include/OldCar.h @@ -0,0 +1,8 @@ +#import + +#import "Driver.h" + +@interface OldCar : NSObject +// `Driver` is defined in Objective-C. +@property(nullable) Driver* driver; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/BarBaz.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/BarBaz.swift new file mode 100644 index 00000000000..4288a78c2e6 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/BarBaz.swift @@ -0,0 +1,17 @@ +import Foundation + +// NOTE: This target is an edge case. It's purpose may or may not be useful, +// but it shouldn't fail to build. + +// This type is Objective-C compatible and used in `OldCar`. +@objc public class Bar: NSObject { + @objc public func doStuff() {} +} + +public struct Baz { + public let bar: Bar? + public init(bar: Bar? = nil) { + self.bar = bar + } +} + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/OnLoadHook.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/OnLoadHook.h new file mode 100644 index 00000000000..67544ebf664 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/OnLoadHook.h @@ -0,0 +1,4 @@ +#import + +@interface OnLoadHook : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/OnLoadHook.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/OnLoadHook.m new file mode 100644 index 00000000000..2cc681ba38d --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNoPublicObjectiveCHeaders/OnLoadHook.m @@ -0,0 +1,14 @@ +#import + +#import "OnLoadHook.h" + +// Import the Swift part of the module. +#import "MixedTargetWithNoPublicObjectiveCHeaders-Swift.h" + +@implementation OnLoadHook + ++ (void)load { + [[[Bar alloc] init] doStuff]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bar.h new file mode 100644 index 00000000000..fcafb67fdf6 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bar.h @@ -0,0 +1,5 @@ +#import + +// This is a non-public header. +@interface Bar : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bar.m new file mode 100644 index 00000000000..b9947d8eed4 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bar.m @@ -0,0 +1,6 @@ +#import + +#import "Bar.h" + +@implementation Bar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bat.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bat.swift new file mode 100644 index 00000000000..d44d379724e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Bat.swift @@ -0,0 +1,9 @@ +import Foundation + +public class Bat { + #if EXPECT_FAILURE + // The following Objective-C types are defined in non-public headers. + let foo: Foo? = nil + let bar: Bar? = nil + #endif // EXPECT_FAILURE +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Baz.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Baz.m new file mode 100644 index 00000000000..6bdd81d4941 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Baz.m @@ -0,0 +1,6 @@ +#import + +#import "include/Baz.h" + +@implementation Baz +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.h new file mode 100644 index 00000000000..4b35d352e75 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.h @@ -0,0 +1,5 @@ +#import + +// This is a non-public header. +@interface Foo : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.m new file mode 100644 index 00000000000..cf98126670e --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.m @@ -0,0 +1,6 @@ +#import + +#import "Foo/Foo/Foo.h" + +@implementation Foo +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/include/Baz.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/include/Baz.h new file mode 100644 index 00000000000..efc750d007f --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithNonPublicHeaders/include/Baz.h @@ -0,0 +1,4 @@ +#import + +@interface Baz : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/ObjcResourceReader.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/ObjcResourceReader.m new file mode 100644 index 00000000000..92983302e7a --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/ObjcResourceReader.m @@ -0,0 +1,18 @@ +#import + +#import "include/ObjcResourceReader.h" + +@implementation ObjcResourceReader + ++ (NSString *)readResource:(NSString *)resource + ofType:(NSString *)type { + // The `SWIFTPM_MODULE_BUNDLE` macro is generated by SwiftPM to provide + // access to the target's bundled resources. + NSBundle *moduleBundle = SWIFTPM_MODULE_BUNDLE; + NSString *path = [moduleBundle pathForResource:resource ofType:type]; + return [[NSString alloc] initWithContentsOfFile:path + encoding:NSUTF8StringEncoding + error:nil]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/SwiftResourceReader.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/SwiftResourceReader.swift new file mode 100644 index 00000000000..054c5996a3c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/SwiftResourceReader.swift @@ -0,0 +1,10 @@ +import Foundation + +public class SwiftResourceReader { + public static func read(_ resource: String, type: String?) throws -> String { + // The `Bundle.module` is generated by SwiftPM to provide access to the + // target's bundled resources. + let path = Bundle.module.path(forResource: resource, ofType: type) + return try String(contentsOfFile: path!, encoding: .utf8) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/foo.txt b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/foo.txt new file mode 100644 index 00000000000..cd0875583aa --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/foo.txt @@ -0,0 +1 @@ +Hello world! diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/include/ObjcResourceReader.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/include/ObjcResourceReader.h new file mode 100644 index 00000000000..b0db1bb491b --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetWithResources/include/ObjcResourceReader.h @@ -0,0 +1,6 @@ +#import + +@interface ObjcResourceReader : NSObject ++ (NSString *)readResource:(NSString*)resource + ofType:(NSString*)type; +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/NewCar.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/NewCar.swift new file mode 100644 index 00000000000..e8c724a2bce --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/NewCar.swift @@ -0,0 +1,4 @@ +import Foundation + +@objc public class NewCar: NSObject { +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/OldCar.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/OldCar.m new file mode 100644 index 00000000000..b0e6d0739c4 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/OldCar.m @@ -0,0 +1,8 @@ +#import + +// The following import statements should be supported. +#import "OldCar.h" +#import "include/OldCar.h" + +@implementation OldCar +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/include/OldCar.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/include/OldCar.h new file mode 100644 index 00000000000..4f158fe5141 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource/include/OldCar.h @@ -0,0 +1,4 @@ +#import + +@interface OldCar : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/SwiftTarget/Vessel.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/SwiftTarget/Vessel.swift new file mode 100644 index 00000000000..137f890fd55 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/SwiftTarget/Vessel.swift @@ -0,0 +1,3 @@ +import Foundation + +@objc public class LifeJacket: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/SwiftTargetDependsOnMixedTarget/JunkYard.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/SwiftTargetDependsOnMixedTarget/JunkYard.swift new file mode 100644 index 00000000000..ae680cf2c06 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Sources/SwiftTargetDependsOnMixedTarget/JunkYard.swift @@ -0,0 +1,10 @@ +import Foundation +import BasicMixedTarget + +public struct JunkYard { + // The below types come from the `BasicMixedTarget` module. + var newCar: NewCar? + var engine: Engine? + var oldCar: OldCar? + var driver: Driver? +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetTests/BasicMixedTargetTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetTests/BasicMixedTargetTests.swift new file mode 100644 index 00000000000..d4626948315 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetTests/BasicMixedTargetTests.swift @@ -0,0 +1,27 @@ +import XCTest +import BasicMixedTarget + +final class BasicMixedTargetTests: XCTestCase { + + func testPublicSwiftAPI() throws { + // Check that Swift API surface is exposed... + let _ = NewCar() + let _ = Engine() + } + + func testPublicObjcAPI() throws { + // Check that Objective-C API surface is exposed... + let _ = OldCar() + let _ = Driver() + let _ = CarPart() + } + + func testModulePrefixingWorks() throws { + let _ = BasicMixedTarget.NewCar() + let _ = BasicMixedTarget.Engine() + let _ = BasicMixedTarget.OldCar() + let _ = BasicMixedTarget.Driver() + let _ = BasicMixedTarget.CarPart() + } + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetTests/ObjcBasicMixedTargetTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetTests/ObjcBasicMixedTargetTests.m new file mode 100644 index 00000000000..2f48e888865 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetTests/ObjcBasicMixedTargetTests.m @@ -0,0 +1,29 @@ +#import + +#if TEST_MODULE_IMPORTS +@import BasicMixedTarget; +#else +#import "CarPart.h" +#import "Driver.h" +#import "OldCar.h" +#import "BasicMixedTarget-Swift.h" +#endif // TEST_MODULE_IMPORTS + +@interface ObjcBasicMixedTargetTests : XCTestCase +@end + +@implementation ObjcBasicMixedTargetTests + +- (void)testPublicSwiftAPI { + // Check that Objective-C compatible Swift API surface is exposed... + Engine *engine = [[Engine alloc] init]; +} + +- (void)testPublicObjcAPI { + // Check that Objective-C API surface is exposed... + OldCar *oldCar = [[OldCar alloc] init]; + Driver *driver = [[Driver alloc] init]; + CarPart *carPart = [[CarPart alloc] init]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetWithUmbrellaHeaderTests/BasicMixedTargetWithUmbrellaHeaderTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetWithUmbrellaHeaderTests/BasicMixedTargetWithUmbrellaHeaderTests.swift new file mode 100644 index 00000000000..315766a4beb --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetWithUmbrellaHeaderTests/BasicMixedTargetWithUmbrellaHeaderTests.swift @@ -0,0 +1,25 @@ +import XCTest +import BasicMixedTargetWithUmbrellaHeader + +final class BasicMixedTargetWithNestellaHeaderTests: XCTestCase { + + func testPublicSwiftAPI() throws { + // Check that Swift API surface is exposed... + let _ = Cookie() + let _ = Coffee() + } + + func testPublicObjcAPI() throws { + // Check that Objective-C API surface is exposed... + let _ = Bakery() + let _ = Dessert() + } + + func testModulePrefixingWorks() throws { + let _ = BasicMixedTargetWithUmbrellaHeader.Cookie() + let _ = BasicMixedTargetWithUmbrellaHeader.Coffee() + let _ = BasicMixedTargetWithUmbrellaHeader.Bakery() + let _ = BasicMixedTargetWithUmbrellaHeader.Dessert() + } + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetWithUmbrellaHeaderTests/ObjcBasicMixedTargetWithUmbrellaHeaderTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetWithUmbrellaHeaderTests/ObjcBasicMixedTargetWithUmbrellaHeaderTests.m new file mode 100644 index 00000000000..a7de6369b5f --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/BasicMixedTargetWithUmbrellaHeaderTests/ObjcBasicMixedTargetWithUmbrellaHeaderTests.m @@ -0,0 +1,22 @@ +#import + +@import BasicMixedTargetWithUmbrellaHeader; + +@interface ObjcBasicMixedTargetWithUmbrellaHeaderTests : XCTestCase + +@end + +@implementation ObjcBasicMixedTargetWithUmbrellaHeaderTests + +- (void)testPublicSwiftAPI { + // Check that Objective-C compatible Swift API surface is exposed... + Cookie *cookie = [[Cookie alloc] init]; +} + +- (void)testPublicObjcAPI { + // Check that Objective-C API surface is exposed... + Bakery *bakery = [[Bakery alloc] init]; + Dessert *dessert = [[Dessert alloc] init]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetDoesNotSupportModuleAliasingTests/MixedTargetDoesNotSupportModuleAliasingTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetDoesNotSupportModuleAliasingTests/MixedTargetDoesNotSupportModuleAliasingTests.swift new file mode 100644 index 00000000000..38484f23622 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetDoesNotSupportModuleAliasingTests/MixedTargetDoesNotSupportModuleAliasingTests.swift @@ -0,0 +1 @@ +// Intentially left blank. diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCTests/MixedTargetWithCTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCTests/MixedTargetWithCTests.swift new file mode 100644 index 00000000000..f8b9e16f7ed --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCTests/MixedTargetWithCTests.swift @@ -0,0 +1,8 @@ +import XCTest +import MixedTargetWithC + +final class MixedTargetWithCTests: XCTestCase { + func testFactorial() throws { + XCTAssertEqual(factorial(5), 120) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests.swift new file mode 100644 index 00000000000..e4801f27ea6 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests.swift @@ -0,0 +1,12 @@ +import XCTest +import MixedTargetWithCXXPublicAPIAndCustomModuleMap + +final class MixedTargetWithCXXPublicAPIAndCustomModuleMapTests: XCTestCase { + func testFactorial() throws { + XCTAssertEqual(factorial(5), 120) + } + + func testSum() throws { + XCTAssertEqual(sum(x: 60, y: 40), 100) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests/ObjcMixedTargetWithCXXPublicAPIAndCustomModuleMapTests.mm b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests/ObjcMixedTargetWithCXXPublicAPIAndCustomModuleMapTests.mm new file mode 100644 index 00000000000..446094e4314 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPIAndCustomModuleMapTests/ObjcMixedTargetWithCXXPublicAPIAndCustomModuleMapTests.mm @@ -0,0 +1,30 @@ +#import + +#if TEST_MODULE_IMPORTS +@import MixedTargetWithCXXPublicAPIAndCustomModuleMap; +#else +#import "XYZCxxSumFinder.hpp" +#import "XYZObjcCalculator.h" +#import "MixedTargetWithCXXPublicAPIAndCustomModuleMap-Swift.h" +#endif // TEST_MODULE_IMPORTS + +@interface ObjcMixedTargetWithCXXPublicAPIAndCustomModuleMapTests : XCTestCase +@end + +@implementation ObjcMixedTargetWithCXXPublicAPIAndCustomModuleMapTests + +- (void)testPublicObjcAPI { + XCTAssertEqual([XYZObjcCalculator factorialForInt:5], 120); + XCTAssertEqual([XYZObjcCalculator sumX:1 andY:2], 3); +} + +- (void)testPublicSwiftAPI { + XCTAssertEqualObjects([Factorial text], @"Hello, World!"); +} + +- (void)testPublicCXXAPI { + XYZCxxSumFinder sf; + XCTAssertEqual(sf.sum(1,2), 3); +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPITests/MixedTargetWithCXXPublicAPITests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPITests/MixedTargetWithCXXPublicAPITests.swift new file mode 100644 index 00000000000..79b6100d78f --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPITests/MixedTargetWithCXXPublicAPITests.swift @@ -0,0 +1,12 @@ +import XCTest +import MixedTargetWithCXXPublicAPI + +final class MixedTargetWithCXXPublicAPITests: XCTestCase { + func testFactorial() throws { + XCTAssertEqual(factorial(5), 120) + } + + func testSum() throws { + XCTAssertEqual(sum(x: 60, y: 40), 100) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPITests/ObjcMixedTargetWithCXXPublicAPITests.mm b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPITests/ObjcMixedTargetWithCXXPublicAPITests.mm new file mode 100644 index 00000000000..13d02e36817 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXPublicAPITests/ObjcMixedTargetWithCXXPublicAPITests.mm @@ -0,0 +1,31 @@ +#import + +#if TEST_MODULE_IMPORTS +@import MixedTargetWithCXXPublicAPI; +#else +#import "CXXSumFinder.hpp" +#import "ObjcCalculator.h" +#import "MixedTargetWithCXXPublicAPI-Swift.h" +#endif // TEST_MODULE_IMPORTS + +@interface ObjcMixedTargetWithCXXPublicAPITests : XCTestCase +@end + +@implementation ObjcMixedTargetWithCXXPublicAPITests + +- (void)testPublicObjcAPI { + XCTAssertEqual([ObjcCalculator factorialForInt:5], 120); + XCTAssertEqual([ObjcCalculator sumX:1 andY:2], 3); +} + +- (void)testPublicSwiftAPI { + XCTAssertEqualObjects([Factorial text], @"Hello, World!"); +} + +- (void)testPublicCXXAPI { + CXXSumFinder sf; + XCTAssertEqual(sf.sum(1,2), 3); +} + +@end + diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXTests/MixedTargetWithCXXTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXTests/MixedTargetWithCXXTests.swift new file mode 100644 index 00000000000..eb541659e10 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCXXTests/MixedTargetWithCXXTests.swift @@ -0,0 +1,8 @@ +import XCTest +import MixedTargetWithCXX + +final class MixedTargetWithCXXTests: XCTestCase { + func testFactorial() throws { + XCTAssertEqual(factorial(5), 120) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapAndResourcesTests/MixedTargetWithCustomModuleMapAndResourcesTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapAndResourcesTests/MixedTargetWithCustomModuleMapAndResourcesTests.swift new file mode 100644 index 00000000000..70f1e106667 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapAndResourcesTests/MixedTargetWithCustomModuleMapAndResourcesTests.swift @@ -0,0 +1,25 @@ +import XCTest +import MixedTargetWithCustomModuleMapAndResources + +final class MixedTargetWithCustomModuleMapAndResourcesTests: XCTestCase { + + func testPublicSwiftAPI() throws { + // Check that Swift API surface is exposed... + let _ = NewCar() + let _ = Engine() + } + + func testPublicObjcAPI() throws { + // Check that Objective-C API surface is exposed... + let _ = OldCar() + let _ = Driver() + } + + func testModulePrefixingWorks() throws { + let _ = MixedTargetWithCustomModuleMapAndResources.NewCar() + let _ = MixedTargetWithCustomModuleMapAndResources.Engine() + let _ = MixedTargetWithCustomModuleMapAndResources.OldCar() + let _ = MixedTargetWithCustomModuleMapAndResources.Driver() + } + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapAndResourcesTests/ObjcMixedTargetWithCustomModuleMapAndResourcesTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapAndResourcesTests/ObjcMixedTargetWithCustomModuleMapAndResourcesTests.m new file mode 100644 index 00000000000..00e11053c74 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapAndResourcesTests/ObjcMixedTargetWithCustomModuleMapAndResourcesTests.m @@ -0,0 +1,21 @@ +#import + +@import MixedTargetWithCustomModuleMapAndResources; + +@interface ObjcMixedTargetWithCustomModuleMapAndResourcesTests : XCTestCase +@end + +@implementation ObjcMixedTargetWithCustomModuleMapAndResourcesTests + +- (void)testPublicSwiftAPI { + // Check that Objective-C compatible Swift API surface is exposed... + Engine *engine = [[Engine alloc] init]; +} + +- (void)testPublicObjcAPI { + // Check that Objective-C API surface is exposed... + ABCOldCar *oldCar = [[ABCOldCar alloc] init]; + ABCDriver *driver = [[ABCDriver alloc] init]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapTests/MixedTargetWithCustomModuleMapTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapTests/MixedTargetWithCustomModuleMapTests.swift new file mode 100644 index 00000000000..e9dcb12be3c --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapTests/MixedTargetWithCustomModuleMapTests.swift @@ -0,0 +1,27 @@ +import XCTest +import MixedTargetWithCustomModuleMap + +final class MixedTargetWithCustomModuleMapTests: XCTestCase { + + func testPublicSwiftAPI() throws { + // Check that Swift API surface is exposed... + let _ = NewCar() + let _ = Engine() + } + + func testPublicObjcAPI() throws { + // Check that Objective-C API surface is exposed... + let _ = MyOldCar() + let _ = MyDriver() + let _ = MyMachine() + } + + func testModulePrefixingWorks() throws { + let _ = MixedTargetWithCustomModuleMap.MyMachine() + let _ = MixedTargetWithCustomModuleMap.NewCar() + let _ = MixedTargetWithCustomModuleMap.Engine() + let _ = MixedTargetWithCustomModuleMap.MyOldCar() + let _ = MixedTargetWithCustomModuleMap.MyDriver() + } + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapTests/ObjcMixedTargetWithCustomModuleMapTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapTests/ObjcMixedTargetWithCustomModuleMapTests.m new file mode 100644 index 00000000000..82c4a15f0e7 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithCustomModuleMapTests/ObjcMixedTargetWithCustomModuleMapTests.m @@ -0,0 +1,30 @@ +#import + +#if TEST_MODULE_IMPORTS +@import MixedTargetWithCustomModuleMap; +#else +#import "Machine.h" +#import "MixedTarget.h" +#import "Driver.h" +#import "OldCar.h" +#import "MixedTargetWithCustomModuleMap-Swift.h" +#endif // TEST_MODULE_IMPORTS + +@interface ObjcMixedTargetWithCustomModuleMapTests : XCTestCase +@end + +@implementation ObjcMixedTargetWithCustomModuleMapTests + +- (void)testPublicSwiftAPI { + // Check that Objective-C compatible Swift API surface is exposed... + Engine *engine = [[Engine alloc] init]; +} + +- (void)testPublicObjcAPI { + // Check that Objective-C API surface is exposed... + MyMachine *machine = [[MyMachine alloc] init]; + MyOldCar *oldCar = [[MyOldCar alloc] init]; + MyDriver *driver = [[MyDriver alloc] init]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithNoPublicObjectiveCHeadersTests/MixedTargetWithNoPublicObjectiveCHeadersTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithNoPublicObjectiveCHeadersTests/MixedTargetWithNoPublicObjectiveCHeadersTests.swift new file mode 100644 index 00000000000..7077f77f1fb --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithNoPublicObjectiveCHeadersTests/MixedTargetWithNoPublicObjectiveCHeadersTests.swift @@ -0,0 +1,21 @@ +import XCTest +import MixedTargetWithNoPublicObjectiveCHeaders + +final class MixedTargetWithNoPublicObjectiveCHeadersTests: XCTestCase { + + func testPublicSwiftAPI() throws { + // Check that Swift API surface is exposed... + let _ = Bar() + let _ = Baz() + let _ = MixedTargetWithNoPublicObjectiveCHeaders.Bar() + let _ = MixedTargetWithNoPublicObjectiveCHeaders.Baz() + } + + #if EXPECT_FAILURE + func testObjcAPI() throws { + // No Objective-C API should be exposed... + let _ = OnLoadHook() + } + #endif + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithNoPublicObjectiveCHeadersTests/ObjcMixedTargetWithNoPublicObjectiveCHeadersTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithNoPublicObjectiveCHeadersTests/ObjcMixedTargetWithNoPublicObjectiveCHeadersTests.m new file mode 100644 index 00000000000..ed4e7b317b5 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithNoPublicObjectiveCHeadersTests/ObjcMixedTargetWithNoPublicObjectiveCHeadersTests.m @@ -0,0 +1,23 @@ +#import + +@import MixedTargetWithNoPublicObjectiveCHeaders; + +@interface ObjcMixedTargetWithNoPublicObjectiveCHeadersTests : XCTestCase + +@end + +@implementation ObjcMixedTargetWithNoPublicObjectiveCHeadersTests + +- (void)testPublicSwiftAPI { + // Check that Objective-C compatible Swift API surface is exposed... + Bar *bar = [[Bar alloc] init]; +} + +#if EXPECT_FAILURE +- (void)testObjcAPI { + // No Objective-C API surface should be exposed... + OnLoadHook *onLoadHook = [[OnLoadHook alloc] init]; +} +#endif + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithResourcesTests/MixedTargetWithResourcesTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithResourcesTests/MixedTargetWithResourcesTests.swift new file mode 100644 index 00000000000..2582cc2e335 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTargetWithResourcesTests/MixedTargetWithResourcesTests.swift @@ -0,0 +1,18 @@ +import XCTest +import MixedTargetWithResources + +final class MixedTargetWithResourcesTests: XCTestCase { + func testResourceCanBeAccessed() throws { + // From Swift context... + XCTAssertEqual( + try SwiftResourceReader.read("foo", type: "txt"), + "Hello world!\n" + ) + + // From Objective-C context... + XCTAssertEqual( + ObjcResourceReader.readResource("foo", ofType: "txt")!, + "Hello world!\n" + ) + } +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/MixedTargetTests.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/MixedTargetTests.swift new file mode 100644 index 00000000000..83955c290ea --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/MixedTargetTests.swift @@ -0,0 +1,17 @@ +import XCTest + +final class MixedTargetTests: XCTestCase { + + func testSwiftUtilityIsVisible() throws { + let _ = SwiftTestHelper() + } + + func testObjcUtilityIsVisibile() throws { + let _ = ObjcTestHelper() + } + + func testOtherObjcUtilityIsVisibile() throws { + let _ = OtherObjcTestHelper() + } + +} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcMixedTargetTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcMixedTargetTests.m new file mode 100644 index 00000000000..a814976b9c5 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcMixedTargetTests.m @@ -0,0 +1,23 @@ +#import + +// Import test helper defined in Swift. +#import "MixedTestTargetWithSharedUtilitiesTests-Swift.h" +// Import test helpers defined in Objective-C. +#import "ObjcTestHelper.h" +#import "Subdirectory1/Subdirectory2/OtherObjcTestHelper.h" + +@interface ObjcMixedTargetTests : XCTestCase + +@end + +@implementation ObjcMixedTargetTests + +- (void)testSwiftUtilityIsVisible { + SwiftTestHelper *helper = [[SwiftTestHelper alloc] init]; +} + +- (void)testObjcUtilityIsVisibile { + ObjcTestHelper *helper = [[ObjcTestHelper alloc] init]; +} + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcTestHelper.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcTestHelper.h new file mode 100644 index 00000000000..7b98b400a14 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcTestHelper.h @@ -0,0 +1,4 @@ +#import + +@interface ObjcTestHelper : NSObject +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcTestHelper.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcTestHelper.m new file mode 100644 index 00000000000..69c903b2097 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/ObjcTestHelper.m @@ -0,0 +1,7 @@ +#import + +#import "ObjcTestHelper.h" + +@implementation ObjcTestHelper + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/OtherObjcTestHelper.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/OtherObjcTestHelper.m new file mode 100644 index 00000000000..ef42693c32f --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/OtherObjcTestHelper.m @@ -0,0 +1,7 @@ +#import + +#import "Subdirectory1/Subdirectory2/OtherObjcTestHelper.h" + +@implementation OtherObjcTestHelper + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/Subdirectory1/Subdirectory2/OtherObjcTestHelper.h b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/Subdirectory1/Subdirectory2/OtherObjcTestHelper.h new file mode 100644 index 00000000000..5dc87c0c787 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/Subdirectory1/Subdirectory2/OtherObjcTestHelper.h @@ -0,0 +1,7 @@ +#import + +// The point of this header is that it is in a nested subdirectory of the root +// directory. It should be included in the test target's module. +@interface OtherObjcTestHelper : NSObject + +@end diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/SwiftTestHelper.swift b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/SwiftTestHelper.swift new file mode 100644 index 00000000000..1a9eb90c9e2 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/MixedTestTargetWithSharedUtilitiesTests/SwiftTestHelper.swift @@ -0,0 +1,3 @@ +import Foundation + +@objc public class SwiftTestHelper: NSObject {} diff --git a/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/PrivateHeadersCanBeTestedViaHeaderSearchPathsTests/PrivateHeadersCanBeTestedViaHeaderSearchPathsTests.m b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/PrivateHeadersCanBeTestedViaHeaderSearchPathsTests/PrivateHeadersCanBeTestedViaHeaderSearchPathsTests.m new file mode 100644 index 00000000000..c81fea234c5 --- /dev/null +++ b/Fixtures/MixedTargets/MixedTargetsWithObjC/Tests/PrivateHeadersCanBeTestedViaHeaderSearchPathsTests/PrivateHeadersCanBeTestedViaHeaderSearchPathsTests.m @@ -0,0 +1,18 @@ +#import + +@import MixedTargetWithNonPublicHeaders; + +#import "Sources/MixedTargetWithNonPublicHeaders/Foo/Foo/Foo.h" +#import "Sources/MixedTargetWithNonPublicHeaders/Bar.h" + +@interface PrivateHeadersCanBeTestedViaHeaderSearchPathsTests : XCTestCase +@end + +@implementation PrivateHeadersCanBeTestedViaHeaderSearchPathsTests + +- (void)testPrivateHeaders { + Foo *foo = [[Foo alloc] init]; + Bar *bar = [[Bar alloc] init]; +} + +@end diff --git a/Sources/Basics/FileSystem/FileSystem+Extensions.swift b/Sources/Basics/FileSystem/FileSystem+Extensions.swift index 4a61f56f006..2a1a8f35651 100644 --- a/Sources/Basics/FileSystem/FileSystem+Extensions.swift +++ b/Sources/Basics/FileSystem/FileSystem+Extensions.swift @@ -622,6 +622,26 @@ extension FileSystem { } } +extension FileSystem { + /// Writes the given string to the given path. If the file at the given path already exists with the given + /// string, nothing is done. + /// - Parameters: + /// - path: The given path to write to. + /// - string: The given string to write. + /// - Note: If the given path's parent directory does not exist, the file system will recursively create + /// intermediate directories so the given path can be resolved. + public func writeFileContentsIfNeeded(_ path: AbsolutePath, string: String) throws { + try createDirectory(path.parentDirectory, recursive: true) + + // If the file exists with the identical contents, there is no need to + // rewrite it. + if let contents: String = try? readFileContents(path), contents == string { + return + } + try writeFileContents(path, string: string) + } +} + // MARK: - Locking extension FileLock { diff --git a/Sources/Basics/FileSystem/VFSOverlay.swift b/Sources/Basics/FileSystem/VFSOverlay.swift index bbe902ec631..83541da0715 100644 --- a/Sources/Basics/FileSystem/VFSOverlay.swift +++ b/Sources/Basics/FileSystem/VFSOverlay.swift @@ -13,37 +13,82 @@ import class Foundation.JSONEncoder public struct VFSOverlay: Encodable { - public struct File: Encodable { - enum CodingKeys: String, CodingKey { + + public class Resource: Encodable { + private let name: String + private let type: String + + fileprivate init(name: String, type: String) { + self.name = name + self.type = type + } + } + + public class File: Resource { + private enum CodingKeys: String, CodingKey { case externalContents = "external-contents" - case name - case type } private let externalContents: String - private let name: String - private let type = "file" public init(name: String, externalContents: String) { - self.name = name self.externalContents = externalContents + super.init(name: name, type: "file") + } + + public override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(externalContents, forKey: .externalContents) + try super.encode(to: encoder) + } + } + + public class Directory: Resource { + private enum CodingKeys: CodingKey { + case contents + } + + private let contents: [Resource] + + public init(name: String, contents: [Resource]) { + self.contents = contents + super.init(name: name, type: "directory") + } + + public convenience init( + name: String, + contents: () -> [VFSOverlay.Resource] + ) { + self.init(name: name, contents: contents()) + } + + public override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(contents, forKey: .contents) + try super.encode(to: encoder) } } enum CodingKeys: String, CodingKey { case roots case useExternalNames = "use-external-names" + case caseSensitive = "case-sensitive" case version } - private let roots: [File] + private let roots: [Resource] private let useExternalNames = false + private let caseSensitive = false private let version = 0 public init(roots: [File]) { self.roots = roots } + public init(roots: [Directory]) { + self.roots = roots + } + public func write(to path: AbsolutePath, fileSystem: FileSystem) throws { // VFS overlay files are YAML, but ours is simple enough that it works when being written using `JSONEncoder`. try JSONEncoder.makeWithDefaults(prettified: false).encode(path: path, fileSystem: fileSystem, self) diff --git a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift index 0b3b0dc39d0..dd8d321bd5e 100644 --- a/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/ClangTargetBuildDescription.swift @@ -121,6 +121,7 @@ public final class ClangTargetBuildDescription { buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [], prebuildCommandResults: [PrebuildCommandResult] = [], fileSystem: FileSystem, + isWithinMixedTarget: Bool = false, observabilityScope: ObservabilityScope ) throws { guard let clangTarget = target.underlying as? ClangTarget else { @@ -155,8 +156,10 @@ public final class ClangTargetBuildDescription { self.pluginDerivedResources = [] } - // Try computing modulemap path for a C library. This also creates the file in the file system, if needed. - if target.type == .library { + // Try computing the modulemap path, creating a module map in the + // file system if necessary. If building for a mixed target, the mixed + // target build description handle the module map. + if target.type == .library, !isWithinMixedTarget { // If there's a custom module map, use it as given. if case .custom(let path) = clangTarget.moduleMapType { self.moduleMap = path diff --git a/Sources/Build/BuildDescription/MixedTargetBuildDescription.swift b/Sources/Build/BuildDescription/MixedTargetBuildDescription.swift new file mode 100644 index 00000000000..eab1cf60241 --- /dev/null +++ b/Sources/Build/BuildDescription/MixedTargetBuildDescription.swift @@ -0,0 +1,342 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2015-2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Basics +import Foundation +import PackageGraph +import PackageLoading +import PackageModel +import SPMBuildCore + +public final class MixedTargetBuildDescription { + /// The target described by this target. + let target: ResolvedTarget + + /// The list of all resource files in the target. + var resources: [Resource] { self.target.underlying.resources } + + /// If this target is a test target. + var isTestTarget: Bool { self.target.underlying.type == .test } + + /// The objects in this target. This includes both the Swift and Clang object files. + var objects: [AbsolutePath] { + get throws { + try self.swiftTargetBuildDescription.objects + + self.clangTargetBuildDescription.objects + } + } + + /// Path to the bundle generated for this module (if any). + var bundlePath: AbsolutePath? { self.swiftTargetBuildDescription.bundlePath } + + /// Path to the resource Info.plist file, if generated. + var resourceBundleInfoPlistPath: AbsolutePath? { + self.swiftTargetBuildDescription.resourceBundleInfoPlistPath + } + + /// The path to the VFS overlay file that overlays the public headers of + /// the Clang part of the target over the target's build directory. + let allProductHeadersOverlay: AbsolutePath + + /// The paths to the targets's public headers. + var publicHeadersDir: AbsolutePath { + clangTargetBuildDescription.clangTarget.includeDir + } + + /// The modulemap file for this target. + let moduleMap: AbsolutePath + + /// Paths to the binary libraries the target depends on. + var libraryBinaryPaths: Set { + self.swiftTargetBuildDescription.libraryBinaryPaths + .union(self.clangTargetBuildDescription.libraryBinaryPaths) + } + + /// The results of applying any build tool plugins to this target. + let buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] + + /// The build description for the Clang sources. + let clangTargetBuildDescription: ClangTargetBuildDescription + + /// The build description for the Swift sources. + let swiftTargetBuildDescription: SwiftTargetBuildDescription + + init( + package: ResolvedPackage, + target: ResolvedTarget, + toolsVersion: ToolsVersion, + additionalFileRules: [FileRuleDescription] = [], + buildParameters: BuildParameters, + toolsBuildParameters: BuildParameters, + buildToolPluginInvocationResults: [BuildToolPluginInvocationResult] = [], + prebuildCommandResults: [PrebuildCommandResult] = [], + fileSystem: FileSystem, + shouldDisableSandbox: Bool, + observabilityScope: ObservabilityScope + ) throws { + guard let mixedTarget = target.underlying as? MixedTarget else { + throw InternalError("underlying target type mismatch \(target)") + } + + self.target = target + self.buildToolPluginInvocationResults = buildToolPluginInvocationResults + + let clangResolvedTarget = ResolvedTarget( + packageIdentity: package.identity, + underlying: mixedTarget.clangTarget, + dependencies: target.dependencies, + defaultLocalization: target.defaultLocalization, + supportedPlatforms: target.supportedPlatforms, + platformVersionProvider: target.platformVersionProvider + ) + self.clangTargetBuildDescription = try ClangTargetBuildDescription( + package: package, + target: clangResolvedTarget, + toolsVersion: toolsVersion, + buildParameters: buildParameters, + buildToolPluginInvocationResults: buildToolPluginInvocationResults, + prebuildCommandResults: prebuildCommandResults, + fileSystem: fileSystem, + isWithinMixedTarget: true, + observabilityScope: observabilityScope + ) + + let swiftResolvedTarget = ResolvedTarget( + packageIdentity: package.identity, + underlying: mixedTarget.swiftTarget, + dependencies: target.dependencies, + defaultLocalization: target.defaultLocalization, + supportedPlatforms: target.supportedPlatforms, + platformVersionProvider: target.platformVersionProvider + ) + self.swiftTargetBuildDescription = try SwiftTargetBuildDescription( + package: package, + target: swiftResolvedTarget, + toolsVersion: toolsVersion, + additionalFileRules: additionalFileRules, + buildParameters: buildParameters, + buildToolPluginInvocationResults: buildToolPluginInvocationResults, + prebuildCommandResults: prebuildCommandResults, + shouldDisableSandbox: shouldDisableSandbox, + fileSystem: fileSystem, + observabilityScope: observabilityScope, + isWithinMixedTarget: true + ) + + let interopHeaderPath = self.swiftTargetBuildDescription.objCompatibilityHeaderPath + + // A mixed target's build directory uses three subdirectories to + // distinguish between build artifacts: + // - Product: Stores artifacts used by clients of the target. + let tempsPath = buildParameters.buildPath.appending(component: target.c99name + ".build") + + // Filenames for VFS overlay files. + let allProductHeadersFilename = "all-product-headers.yaml" + let unextendedModuleOverlayFilename = "unextended-module-overlay.yaml" + + // Used to generate both product and intermediate artifacts for the + // target. + let moduleMapGenerator = ModuleMapGenerator( + targetName: mixedTarget.clangTarget.name, + moduleName: mixedTarget.clangTarget.c99name, + publicHeadersDir: mixedTarget.clangTarget.includeDir, + fileSystem: fileSystem + ) + + // MARK: Generate products to be used by client of the target. + + switch mixedTarget.clangTarget.moduleMapType { + // When the mixed target has a custom module map, clients of the target + // will be passed a module map *and* VFS overlay at buildtime to access + // the mixed target's public API. The following is therefore needed: + // - Create a copy of the custom module map, and extend its contents by + // adding a submodule that modularizes the target's generated interop + // header. This allows clients of the target to consume the mixed + // target's public Obj-C compatible Swift API in a Clang context. + // - Create a VFS overlay to swap in the extended module map for the + // original custom module map. This is done so relative paths in the + // modified module map can be resolved as they would have been in the + // original module map. + case .custom(let customModuleMapPath): + // Set the original custom module map path as the module map path + // for the target. With the below VFS overlay, evaluating the + // custom module map path will redirect to the extended module map + // path. + self.moduleMap = customModuleMapPath + self.allProductHeadersOverlay = tempsPath.appending(component: allProductHeadersFilename) + + // If it's named 'module.modulemap', there will be a module + // redeclaration error as both the public headers dir. and the + // build dir. are passed as import paths and there will be a + // `module.modulemap` in each directory. + let extendedCustomModuleMapPath = tempsPath.appending(component: "extended-custom-module.modulemap") + try generateExtendedModuleMap( + from: customModuleMapPath, + at: extendedCustomModuleMapPath, + fileSystem: fileSystem + ) + + try VFSOverlay(roots: [ + VFSOverlay.Directory( + name: customModuleMapPath.parentDirectory.pathString, + contents: [ + // Redirect the custom `module.modulemap` to the + // modified module map in the product directory. + VFSOverlay.File( + name: moduleMapFilename, + externalContents: extendedCustomModuleMapPath.pathString + ), + // Add a generated Swift header that redirects to the + // generated header in the build directory's root. + VFSOverlay.File( + name: interopHeaderPath.basename, + externalContents: interopHeaderPath.pathString + ), + ] + ), + ]).write(to: self.allProductHeadersOverlay, fileSystem: fileSystem) + + // Importing the underlying module will build the Objective-C + // part of the module. In order to find the underlying module, + // a `module.modulemap` needs to be discoverable via directory passed + // as a header search path. + self.swiftTargetBuildDescription.additionalFlags += [ + "-import-underlying-module", + "-I", mixedTarget.clangTarget.includeDir.pathString + ] + + // When the mixed target does not have a custom module map, one will be + // generated as a product for use by clients. + // - Note: When `.none`, the mixed target has no public headers. Even + // then, a module map is created to expose the generated interop + // header so clients can access the public Objective-C compatible + // Swift API in a Clang context. + case .umbrellaHeader, .umbrellaDirectory, .none: + let generatedModuleMapType = mixedTarget.clangTarget.moduleMapType.generatedModuleMapType + let unextendedModuleMapPath = tempsPath.appending(component: unextendedModuleMapFilename) + + try moduleMapGenerator.generateModuleMap( + type: generatedModuleMapType, + at: unextendedModuleMapPath + ) + + let unextendedModuleMapOverlayPath = tempsPath.appending(component: unextendedModuleOverlayFilename) + try VFSOverlay(roots: [ + VFSOverlay.Directory( + name: tempsPath.pathString, + contents: [ + // Redirect the `module.modulemap` to the *unextended* + // module map in the intermediates directory. + VFSOverlay.File( + name: moduleMapFilename, + externalContents: unextendedModuleMapPath.pathString + ), + ] + ), + ]).write(to: unextendedModuleMapOverlayPath, fileSystem: fileSystem) + + let extendedModuleMapPath = tempsPath.appending(component: moduleMapFilename) + + // Set the generated module map as the module map for the target. + self.moduleMap = extendedModuleMapPath + self.allProductHeadersOverlay = tempsPath.appending(component: allProductHeadersFilename) + + try generateExtendedModuleMap( + from: unextendedModuleMapPath, + at: extendedModuleMapPath, + fileSystem: fileSystem + ) + + try VFSOverlay(roots: [ + VFSOverlay.Directory( + name: mixedTarget.clangTarget.includeDir.pathString, + contents: [ + // Add a generated Swift header that redirects to the + // generated header in the build directory's root. + VFSOverlay.File( + name: interopHeaderPath.basename, + externalContents: interopHeaderPath.pathString + ), + ] + ), + ]).write(to: self.allProductHeadersOverlay, fileSystem: fileSystem) + + // Importing the underlying module will build the Objective-C + // part of the module. In order to find the underlying module, + // a `module.modulemap` needs to be discoverable via directory passed + // as a header search path. + self.swiftTargetBuildDescription.additionalFlags += [ + "-import-underlying-module", + "-I", tempsPath.pathString, + "-Xcc", "-ivfsoverlay", "-Xcc", unextendedModuleMapOverlayPath.pathString + ] + } + + self.swiftTargetBuildDescription.appendClangFlags( + // Adding the root of the target's source as a header search + // path allows for importing headers (within the mixed target's + // headers) using paths relative to the root. + "-I", mixedTarget.path.pathString, + // Adding the public headers directory as a header search + // path allows for importing public headers within the mixed + // target's headers. Note that this directory may not exist in the + // case that there are no public headers. In this case, adding this + // header search path is a no-op. + "-I", mixedTarget.clangTarget.includeDir.pathString + ) + + self.clangTargetBuildDescription.additionalFlags += [ + // Adding the root of the target's source as a header search + // path allows for importing headers using paths relative to + // the root. + "-I", mixedTarget.path.pathString, + // Include overlay file to add interop header to overlay directory. + "-I", interopHeaderPath.parentDirectory.pathString + ] + } + + /// Extends the contents of the given module map to contain the target's generated Swift header + /// modularized in a submodule. This "extended" module map is written at the given `path`. + private func generateExtendedModuleMap( + from unextendedModuleMapPath: AbsolutePath, + at path: AbsolutePath, + fileSystem: FileSystem + ) throws { + let unextendedModuleMapContents: String = + try fileSystem.readFileContents(unextendedModuleMapPath) + + // Check that custom module map does not contain a Swift submodule. + if unextendedModuleMapContents.contains("\(target.c99name).Swift") { + throw StringError( + "The target's module map may not contain a Swift " + + "submodule for the module \(target.c99name)." + ) + } + + // Extend the contents and write it to disk, if needed. + let productModuleMap = """ + \(unextendedModuleMapContents) + module \(target.c99name).Swift { + header "\(target.c99name)-Swift.h" + } + """ + try fileSystem.writeFileContentsIfNeeded( + path, + string: productModuleMap + ) + } + + func symbolGraphExtractArguments() throws -> [String] { + try clangTargetBuildDescription.symbolGraphExtractArguments() + + swiftTargetBuildDescription.symbolGraphExtractArguments() + } +} diff --git a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift index 514e05f9957..90afbb72784 100644 --- a/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift @@ -147,6 +147,11 @@ public final class SwiftTargetBuildDescription { /// Any addition flags to be added. These flags are expected to be computed during build planning. var additionalFlags: [String] = [] + /// Whether or not the target belongs to a mixed language target. + /// + /// Mixed language targets consist of an underlying Swift and Clang target. + let isWithinMixedTarget: Bool + /// Describes the purpose of a test target, including any special roles such as containing a list of discovered /// tests or serving as the manifest target which contains the main entry point. public enum TestTargetRole { @@ -260,7 +265,8 @@ public final class SwiftTargetBuildDescription { shouldGenerateTestObservation: Bool = false, shouldDisableSandbox: Bool, fileSystem: FileSystem, - observabilityScope: ObservabilityScope + observabilityScope: ObservabilityScope, + isWithinMixedTarget: Bool = false ) throws { guard let swiftTarget = target.underlying as? SwiftTarget else { throw InternalError("underlying target type mismatch \(target)") @@ -290,6 +296,7 @@ public final class SwiftTargetBuildDescription { self.shouldDisableSandbox = shouldDisableSandbox self.fileSystem = fileSystem self.observabilityScope = observabilityScope + self.isWithinMixedTarget = isWithinMixedTarget (self.pluginDerivedSources, self.pluginDerivedResources) = ModulesGraph.computePluginGeneratedFiles( target: target, @@ -301,7 +308,10 @@ public final class SwiftTargetBuildDescription { observabilityScope: observabilityScope ) - if self.shouldEmitObjCCompatibilityHeader { + // If building for a mixed target, the mixed target build + // description will create the module map and include the Swift + // interoptability header. + if self.shouldEmitObjCCompatibilityHeader, !isWithinMixedTarget { self.moduleMap = try self.generateModuleMap() } @@ -601,7 +611,7 @@ public final class SwiftTargetBuildDescription { isPackageNameSupported: self.buildParameters.driverParameters.isPackageAccessModifierSupported ) args += try self.macroArguments() - + // rdar://117578677 // Pass -fno-omit-frame-pointer to support backtraces // this can be removed once the backtracer uses DWARF instead of frame pointers @@ -738,9 +748,19 @@ public final class SwiftTargetBuildDescription { return result } + func appendClangFlags(_ flags: String...) { + flags.forEach { flag in + additionalFlags.append("-Xcc") + additionalFlags.append(flag) + } + } + /// Returns true if ObjC compatibility header should be emitted. private var shouldEmitObjCCompatibilityHeader: Bool { - self.buildParameters.triple.isDarwin() && self.target.type == .library + // Emitting the interop header for mixed test targets enables the + // sharing of Objective-C compatible Swift test helpers between + // Swift and Objective-C test files. + self.buildParameters.triple.isDarwin() && (self.target.type == .library || self.target.type == .test && self.isWithinMixedTarget) } func writeOutputFileMap() throws -> AbsolutePath { @@ -833,6 +853,7 @@ public final class SwiftTargetBuildDescription { private func generateModuleMap() throws -> AbsolutePath { let path = self.tempsPath.appending(component: moduleMapFilename) + // TODO(ncooke3): Is the `requires objc` an issue for C++ interop? let bytes = ByteString( #""" module \#(self.target.c99name) { diff --git a/Sources/Build/BuildDescription/TargetBuildDescription.swift b/Sources/Build/BuildDescription/TargetBuildDescription.swift index 3e97fede5b7..c5d6fa70001 100644 --- a/Sources/Build/BuildDescription/TargetBuildDescription.swift +++ b/Sources/Build/BuildDescription/TargetBuildDescription.swift @@ -29,6 +29,9 @@ public enum TargetBuildDescription { /// Clang target description. case clang(ClangTargetBuildDescription) + /// Mixed (Swift + Clang) target description. + case mixed(MixedTargetBuildDescription) + /// The objects in this target. var objects: [AbsolutePath] { get throws { @@ -37,6 +40,8 @@ public enum TargetBuildDescription { return try target.objects case .clang(let target): return try target.objects + case .mixed(let target): + return try target.objects } } } @@ -48,6 +53,8 @@ public enum TargetBuildDescription { return target.resources case .clang(let target): return target.resources + case .mixed(let target): + return target.resources } } @@ -58,6 +65,8 @@ public enum TargetBuildDescription { return target.bundlePath case .clang(let target): return target.bundlePath + case .mixed(let target): + return target.bundlePath } } @@ -67,6 +76,8 @@ public enum TargetBuildDescription { return target.target case .clang(let target): return target.target + case .mixed(let target): + return target.target } } @@ -77,6 +88,8 @@ public enum TargetBuildDescription { return target.libraryBinaryPaths case .clang(let target): return target.libraryBinaryPaths + case .mixed(let target): + return target.libraryBinaryPaths } } @@ -86,6 +99,8 @@ public enum TargetBuildDescription { return target.resourceBundleInfoPlistPath case .clang(let target): return target.resourceBundleInfoPlistPath + case .mixed(let target): + return target.resourceBundleInfoPlistPath } } @@ -95,6 +110,8 @@ public enum TargetBuildDescription { return target.buildToolPluginInvocationResults case .clang(let target): return target.buildToolPluginInvocationResults + case .mixed(let target): + return target.buildToolPluginInvocationResults } } @@ -104,6 +121,9 @@ public enum TargetBuildDescription { return swiftTargetBuildDescription.buildParameters case .clang(let clangTargetBuildDescription): return clangTargetBuildDescription.buildParameters + // TODO(ncooke3): How to handle the mixed case? + case .mixed(let mixedTargetBuildDescription): + return mixedTargetBuildDescription.clangTargetBuildDescription.buildParameters } } @@ -113,6 +133,9 @@ public enum TargetBuildDescription { return swiftTargetBuildDescription.toolsVersion case .clang(let clangTargetBuildDescription): return clangTargetBuildDescription.toolsVersion + // TODO(ncooke3): How to handle the mixed case? + case .mixed(let mixedTargetBuildDescription): + return mixedTargetBuildDescription.clangTargetBuildDescription.toolsVersion } } @@ -122,6 +145,7 @@ public enum TargetBuildDescription { switch self { case .swift(let target): try target.symbolGraphExtractArguments() case .clang(let target): try target.symbolGraphExtractArguments() + case .mixed(let target): try target.symbolGraphExtractArguments() } } } diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift index 2f053b90153..33aad6c7558 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Clang.swift @@ -18,23 +18,31 @@ import struct PackageGraph.ResolvedModule import PackageModel extension LLBuildManifestBuilder { - /// Create a llbuild target for a Clang target description. + /// Create a llbuild target for a Clang target description and returns the Clang target's outputs. + @discardableResult func createClangCompileCommand( - _ target: ClangTargetBuildDescription - ) throws { - var inputs: [Node] = [] - - // Add resources node as the input to the target. This isn't great because we - // don't need to block building of a module until its resources are assembled but - // we don't currently have a good way to express that resources should be built - // whenever a module is being built. - if let resourcesNode = try self.createResourcesBundle(for: .clang(target)) { - inputs.append(resourcesNode) + _ target: ClangTargetBuildDescription, + addTargetCmd: Bool = true, + inputs: [Node] = [], + createResourceBundle: Bool = true + ) throws -> [Node] { + var inputs: [Node] = inputs + + if createResourceBundle { + // Add resources node as the input to the target. This isn't great because we + // don't need to block building of a module until its resources are assembled but + // we don't currently have a good way to express that resources should be built + // whenever a module is being built. + if let resourcesNode = try self.createResourcesBundle(for: .clang(target)) { + inputs.append(resourcesNode) + } } func addStaticTargetInputs(_ target: ResolvedModule) { if case .swift(let desc)? = self.plan.targetMap[target.id], target.type == .library { inputs.append(file: desc.moduleOutputPath) + } else if case .mixed(let desc)? = plan.targetMap[target.id], target.type == .library { + inputs.append(file: desc.swiftTargetBuildDescription.moduleOutputPath) } } @@ -92,23 +100,15 @@ extension LLBuildManifestBuilder { let additionalInputs = try addBuildToolPlugins(.clang(target)) - // Create a phony node to represent the entire target. - let targetName = target.llbuildTargetName - let output: Node = .virtual(targetName) - - self.manifest.addNode(output, toTarget: targetName) - self.manifest.addPhonyCmd( - name: output.name, - inputs: objectFileNodes + additionalInputs, - outputs: [output] - ) - - if self.plan.graph.isInRootPackages(target.target, satisfying: target.buildParameters.buildEnvironment) { - if !target.isTestTarget { - self.addNode(output, toTarget: .main) - } - self.addNode(output, toTarget: .test) + if addTargetCmd { + self.addTargetCmd( + target: .clang(target), + isTestTarget: target.isTestTarget, + inputs: objectFileNodes + additionalInputs + ) } + + return objectFileNodes } /// Create a llbuild target for a Clang target preparation diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift index d66233d0bd5..0bf7a66ee90 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder+Swift.swift @@ -33,10 +33,12 @@ import SwiftDriver import PackageModel extension LLBuildManifestBuilder { - /// Create a llbuild target for a Swift target description. + /// Create a llbuild target for a Swift target description and returns the Swift target's outputs. + @discardableResult func createSwiftCompileCommand( - _ target: SwiftTargetBuildDescription - ) throws { + _ target: SwiftTargetBuildDescription, + addTargetCmd: Bool = true + ) throws -> [Node] { // Inputs. let inputs = try self.computeSwiftCompileCmdInputs(target) @@ -55,8 +57,16 @@ extension LLBuildManifestBuilder { try self.addCmdWithBuiltinSwiftTool(target, inputs: inputs, cmdOutputs: cmdOutputs) } - self.addTargetCmd(target, cmdOutputs: cmdOutputs) + if addTargetCmd { + self.addTargetCmd( + target: .swift(target), + isTestTarget: target.isTestTarget, + inputs: cmdOutputs + ) + } try self.addModuleWrapCmd(target) + + return cmdOutputs } private func addSwiftCmdsViaIntegratedDriver( @@ -237,6 +247,8 @@ extension LLBuildManifestBuilder { ) case .clang(let desc): try self.createClangCompileCommand(desc) + case .mixed(let desc): + try self.createMixedCompileCommand(desc) } } } @@ -262,7 +274,11 @@ extension LLBuildManifestBuilder { explicitDependencyJobTracker: explicitDependencyJobTracker ) - self.addTargetCmd(description, cmdOutputs: cmdOutputs) + self.addTargetCmd( + target: .swift(description), + isTestTarget: description.isTestTarget, + inputs: cmdOutputs + ) try self.addModuleWrapCmd(description) } @@ -458,6 +474,12 @@ extension LLBuildManifestBuilder { inputs.append(file: object) } } + case .mixed(let target)?: + inputs.append(file: target.swiftTargetBuildDescription.moduleOutputPath) + + for object in try target.clangTargetBuildDescription.objects { + inputs.append(file: object) + } case nil: throw InternalError("unexpected: target \(target) not in target map \(self.plan.targetMap)") } @@ -509,7 +531,11 @@ extension LLBuildManifestBuilder { } /// Adds a top-level phony command that builds the entire target. - private func addTargetCmd(_ target: SwiftTargetBuildDescription, cmdOutputs: [Node]) { + func addTargetCmd( + target: TargetBuildDescription, // ResolvedTarget, + isTestTarget: Bool, + inputs: [Node] + ) { // Create a phony node to represent the entire target. let targetName = target.getLLBuildTargetName() let targetOutput: Node = .virtual(targetName) @@ -517,11 +543,11 @@ extension LLBuildManifestBuilder { self.manifest.addNode(targetOutput, toTarget: targetName) self.manifest.addPhonyCmd( name: targetOutput.name, - inputs: cmdOutputs, + inputs: inputs, outputs: [targetOutput] ) if self.plan.graph.isInRootPackages(target.target, satisfying: target.buildParameters.buildEnvironment) { - if !target.isTestTarget { + if !isTestTarget { self.addNode(targetOutput, toTarget: .main) } self.addNode(targetOutput, toTarget: .test) @@ -589,6 +615,33 @@ private class UniqueExplicitDependencyJobTracker { } } +// MARK: - Compile Mixed Languages + +// TODO(ncooke3): Post-merge move to other file? +extension LLBuildManifestBuilder { + /// Create a llbuild target for a mixed target description. + func createMixedCompileCommand( + _ target: MixedTargetBuildDescription + ) throws { + let swiftOutputs = try createSwiftCompileCommand(target.swiftTargetBuildDescription, addTargetCmd: false) + let clangOutputs = try createClangCompileCommand( + target.clangTargetBuildDescription, + addTargetCmd: false, + // This forces the Swift sub-target to build first. This is needed + // since the Clang sub-target depends on build artifacts from the + // Swift sub-target (e.g. generated Swift header). + inputs: swiftOutputs, + // The Swift compile command already created the resource bundle. + createResourceBundle: false + ) + self.addTargetCmd( + target: .mixed(target), + isTestTarget: target.isTestTarget, + inputs: swiftOutputs + clangOutputs + ) + } +} + extension TypedVirtualPath { /// Resolve a typed virtual path provided by the Swift driver to /// a node in the build graph. diff --git a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift index f83ec18193d..c7bbc6c82fe 100644 --- a/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift +++ b/Sources/Build/BuildManifest/LLBuildManifestBuilder.swift @@ -109,6 +109,8 @@ public class LLBuildManifestBuilder { try self.createSwiftCompileCommand(desc) case .clang(let desc): try self.createClangCompileCommand(desc) + case .mixed(let desc): + try self.createMixedCompileCommand(desc) } } } @@ -148,6 +150,10 @@ public class LLBuildManifestBuilder { // Hook up the clang module target try self.createClangPrepareCommand(desc) } + case .mixed(let desc): + // TODO(ncooke3): Should the below implementation handle the + // underlying Clang build description like the above case does? + try self.createMixedCompileCommand(desc) } } @@ -360,6 +366,15 @@ extension TargetBuildDescription { } } +// TODO(ncooke3): Prioritize getting this in separately. Result of May merge, +// specifically 5749d8619d78d03c5c5bb1cd242a9d9e3ce8c2f8. Same with helper +// function that uses it. +extension TargetBuildDescription { + public func getLLBuildTargetName() -> String { + self.target.getLLBuildTargetName(buildParameters: self.buildParameters) + } +} + extension ClangTargetBuildDescription { package var llbuildTargetName: String { self.target.getLLBuildTargetName(buildParameters: self.buildParameters) diff --git a/Sources/Build/BuildPlan/BuildPlan+Clang.swift b/Sources/Build/BuildPlan/BuildPlan+Clang.swift index e56a5e16585..0f00e78f39a 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Clang.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Clang.swift @@ -12,6 +12,7 @@ import class PackageModel.BinaryTarget import class PackageModel.ClangTarget +import class PackageModel.MixedTarget import class PackageModel.SwiftTarget import class PackageModel.SystemLibraryTarget @@ -39,6 +40,22 @@ extension BuildPlan { clangTarget.additionalFlags += ["-fmodule-map-file=\(moduleMap.pathString)"] } } + case let target as MixedTarget where target.type == .library: + // Add the modulemap of the dependency. + if case let .mixed(dependencyTargetDescription)? = targetMap[dependency.id] { + // Add the dependency's modulemap. + clangTarget.additionalFlags.append( + "-fmodule-map-file=\(dependencyTargetDescription.moduleMap.pathString)" + ) + + // Add the dependency's public headers. + clangTarget.additionalFlags += [ "-I", dependencyTargetDescription.publicHeadersDir.pathString ] + + // Add the dependency's public VFS overlay. + clangTarget.additionalFlags += [ + "-ivfsoverlay", dependencyTargetDescription.allProductHeadersOverlay.pathString + ] + } case let target as SystemLibraryTarget: clangTarget.additionalFlags += ["-fmodule-map-file=\(target.moduleMapPath.pathString)"] clangTarget.additionalFlags += try pkgConfig(for: target).cFlags @@ -56,5 +73,4 @@ extension BuildPlan { } } } - } diff --git a/Sources/Build/BuildPlan/BuildPlan+Swift.swift b/Sources/Build/BuildPlan/BuildPlan+Swift.swift index 079fce95e72..6d318222cab 100644 --- a/Sources/Build/BuildPlan/BuildPlan+Swift.swift +++ b/Sources/Build/BuildPlan/BuildPlan+Swift.swift @@ -13,6 +13,8 @@ import struct Basics.InternalError import class PackageModel.BinaryTarget import class PackageModel.ClangTarget +import class PackageModel.MixedTarget +import class PackageModel.SwiftTarget import class PackageModel.SystemLibraryTarget import class PackageModel.ProvidedLibraryTarget @@ -32,10 +34,10 @@ extension BuildPlan { // one. However, in that case it will not be importable in Swift targets. We may want to emit a warning // in that case from here. guard let moduleMap = target.moduleMap else { break } - swiftTarget.additionalFlags += [ - "-Xcc", "-fmodule-map-file=\(moduleMap.pathString)", - "-Xcc", "-I", "-Xcc", target.clangTarget.includeDir.pathString, - ] + swiftTarget.appendClangFlags( + "-fmodule-map-file=\(moduleMap.pathString)", + "-I", target.clangTarget.includeDir.pathString + ) case let target as SystemLibraryTarget: swiftTarget.additionalFlags += ["-Xcc", "-fmodule-map-file=\(target.moduleMapPath.pathString)"] swiftTarget.additionalFlags += try pkgConfig(for: target).cFlags @@ -49,6 +51,36 @@ extension BuildPlan { swiftTarget.libraryBinaryPaths.insert(library.libraryPath) } } + case let underlyingTarget as MixedTarget where underlyingTarget.type == .library: + guard case let .mixed(target)? = targetMap[dependency.id] else { + throw InternalError("unexpected mixed target \(underlyingTarget)") + } + + // Add the dependency's modulemap. + swiftTarget.appendClangFlags( + "-fmodule-map-file=\(target.moduleMap.pathString)" + ) + + // Add the dependency's public headers. + swiftTarget.appendClangFlags("-I", target.publicHeadersDir.pathString) + case let underlyingTarget as SwiftTarget where underlyingTarget.type == .library && swiftTarget.isWithinMixedTarget: + // The mixed target build description exposes it's public ObjC + // headers to it's Swift module in case the Swift source in the + // mixed module references types from the ObjC headers. + // + // In doing so, there is the possibility that one of the + // exposedpublic ObjC headers (directly or transitively) + // exports a Swift module import. Such a Swift module import + // (handled by this case) needs to be added to Swift import + // paths of the given mixed target's swift module. + + guard case let .swift(target)? = targetMap[dependency.id] else { + throw InternalError("unexpected swift target \(underlyingTarget)") + } + + // NOTE(ncooke3): Could also be `target.moduleMap!.parentDirectory`. + swiftTarget.appendClangFlags("-I", target.buildParameters.buildPath.pathString) + break case let target as ProvidedLibraryTarget: swiftTarget.additionalFlags += [ "-I", target.path.pathString @@ -58,5 +90,4 @@ extension BuildPlan { } } } - } diff --git a/Sources/Build/BuildPlan/BuildPlan.swift b/Sources/Build/BuildPlan/BuildPlan.swift index bbbe94b243c..d56ac845025 100644 --- a/Sources/Build/BuildPlan/BuildPlan.swift +++ b/Sources/Build/BuildPlan/BuildPlan.swift @@ -173,7 +173,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan { switch self { case .noBuildableTarget: return """ - The package does not contain a buildable target. + The package does not contain a buildable target. Add at least one `.target` or `.executableTarget` to your `Package.swift`. """ } @@ -408,6 +408,21 @@ public class BuildPlan: SPMBuildCore.BuildPlan { observabilityScope: observabilityScope ) ) + // TODO(ncooke3): I guess pass in `shouldGenerateTestObservation: generateTestObservation`? + case is MixedTarget: + guard let package = graph.package(for: target) else { + throw InternalError("package not found for \(target)") + } + + targetMap[target.id] = try .mixed(MixedTargetBuildDescription( + package: package, + target: target, + toolsVersion: toolsVersion, + buildParameters: buildParameters, + toolsBuildParameters: toolsBuildParameters, + fileSystem: fileSystem, + shouldDisableSandbox: shouldDisableSandbox, + observabilityScope: observabilityScope)) case is PluginTarget: guard let package = graph.package(for: target) else { throw InternalError("package not found for \(target)") @@ -511,6 +526,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan { try self.plan(swiftTarget: target) case .clang(let target): try self.plan(clangTarget: target) + case .mixed(let target): + try self.plan(mixedTarget: target) } } @@ -523,6 +540,13 @@ public class BuildPlan: SPMBuildCore.BuildPlan { // handle that situation. } + // TODO(ncooke3): Post-merge–– move to own file. + /// Plan a Mixed target. + private func plan(mixedTarget: MixedTargetBuildDescription) throws { + try plan(swiftTarget: mixedTarget.swiftTargetBuildDescription) + try plan(clangTarget: mixedTarget.clangTargetBuildDescription) + } + public func createAPIToolCommonArgs(includeLibrarySearchPaths: Bool) throws -> [String] { // API tool runs on products, hence using `self.productsBuildParameters`, not `self.toolsBuildParameters` let buildPath = self.destinationBuildParameters.buildPath.pathString @@ -555,6 +579,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { if let includeDir = targetDescription.moduleMap?.parentDirectory { arguments += ["-I", includeDir.pathString] } + case .mixed(let targetDescription): + let includeDir = targetDescription.moduleMap.parentDirectory + arguments += ["-I", includeDir.pathString] } } @@ -592,6 +619,9 @@ public class BuildPlan: SPMBuildCore.BuildPlan { if let includeDir = targetDescription.moduleMap?.parentDirectory { arguments += ["-I\(includeDir.pathString)"] } + case .mixed(let targetDescription): + let includeDir = targetDescription.moduleMap.parentDirectory + arguments += ["-I\(includeDir.pathString)"] } } diff --git a/Sources/Build/CMakeLists.txt b/Sources/Build/CMakeLists.txt index d5e960f3c15..9566ee5add4 100644 --- a/Sources/Build/CMakeLists.txt +++ b/Sources/Build/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(Build BuildDescription/ProductBuildDescription.swift BuildDescription/ResolvedModule+BuildDescription.swift BuildDescription/SwiftTargetBuildDescription.swift + BuildDescription/MixedTargetBuildDescription.swift BuildDescription/TargetBuildDescription.swift BuildManifest/LLBuildManifestBuilder.swift BuildManifest/LLBuildManifestBuilder+Clang.swift diff --git a/Sources/PackageGraph/Resolution/ResolvedModule.swift b/Sources/PackageGraph/Resolution/ResolvedModule.swift index f218a35886d..cb0cfd218a4 100644 --- a/Sources/PackageGraph/Resolution/ResolvedModule.swift +++ b/Sources/PackageGraph/Resolution/ResolvedModule.swift @@ -144,7 +144,7 @@ public struct ResolvedModule { /// The list of platforms that are supported by this target. public let supportedPlatforms: [SupportedPlatform] - private let platformVersionProvider: PlatformVersionProvider + public let platformVersionProvider: PlatformVersionProvider /// Triple for which this resolved target should be compiled for. public package(set) var buildTriple: BuildTriple { diff --git a/Sources/PackageLoading/ModuleMapGenerator.swift b/Sources/PackageLoading/ModuleMapGenerator.swift index 141930fa892..d9619c6ae10 100644 --- a/Sources/PackageLoading/ModuleMapGenerator.swift +++ b/Sources/PackageLoading/ModuleMapGenerator.swift @@ -17,6 +17,9 @@ import PackageModel /// Name of the module map file recognized by the Clang and Swift compilers. public let moduleMapFilename = "module.modulemap" +/// Name of the auxilliary module map file used in the Clang VFS overlay sytem. +public let unextendedModuleMapFilename = "unextended-module.modulemap" + extension AbsolutePath { fileprivate var moduleEscapedPathString: String { return self.pathString.replacingOccurrences(of: "\\", with: "\\\\") @@ -50,7 +53,7 @@ extension ClangTarget: ModuleMapProtocol { } } -/// A module map generator for Clang targets. Module map generation consists of two steps: +/// A module map generator for Clang and Mixed language targets. Module map generation consists of two steps: /// 1. Examining a target's public-headers directory to determine the appropriate module map type /// 2. Generating a module map for any target that doesn't have a custom module map file /// @@ -122,6 +125,8 @@ public struct ModuleMapGenerator { // Filter out headers and directories at the top level of the public-headers directory. // FIXME: What about .hh files, or .hpp, etc? We should centralize the detection of file types based on names (and ideally share with SwiftDriver). + // TODO(ncooke3): Per above FIXME and last line in function, public header + // directories with only C++ headers will default to umbrella directory. let headers = entries.filter({ fileSystem.isFile($0) && $0.suffix == ".h" }) let directories = entries.filter({ fileSystem.isDirectory($0) }) @@ -173,14 +178,19 @@ public struct ModuleMapGenerator { return .umbrellaDirectory(publicHeadersDir) } - /// Generates a module map based of the specified type, throwing an error if anything goes wrong. Any diagnostics are added to the receiver's diagnostics engine. - public func generateModuleMap(type: GeneratedModuleMapType, at path: AbsolutePath) throws { + /// Generates a module map based of the specified type, throwing an error if anything goes wrong. Any diagnostics are added to the receiver's diagnostics engine. + public func generateModuleMap( + type: GeneratedModuleMapType?, + at path: AbsolutePath + ) throws { var moduleMap = "module \(moduleName) {\n" - switch type { - case .umbrellaHeader(let hdr): - moduleMap.append(" umbrella header \"\(hdr.moduleEscapedPathString)\"\n") - case .umbrellaDirectory(let dir): - moduleMap.append(" umbrella \"\(dir.moduleEscapedPathString)\"\n") + if let type = type { + switch type { + case .umbrellaHeader(let hdr): + moduleMap.append(" umbrella header \"\(hdr.moduleEscapedPathString)\"\n") + case .umbrellaDirectory(let dir): + moduleMap.append(" umbrella \"\(dir.moduleEscapedPathString)\"\n") + } } moduleMap.append( """ @@ -190,15 +200,9 @@ public struct ModuleMapGenerator { """ ) - // FIXME: This doesn't belong here. - try fileSystem.createDirectory(path.parentDirectory, recursive: true) - // If the file exists with the identical contents, we don't need to rewrite it. // Otherwise, compiler will recompile even if nothing else has changed. - if let contents = try? fileSystem.readFileContents(path).validDescription, contents == moduleMap { - return - } - try fileSystem.writeFileContents(path, string: moduleMap) + try fileSystem.writeFileContentsIfNeeded(path, string: moduleMap) } } diff --git a/Sources/PackageLoading/PackageBuilder.swift b/Sources/PackageLoading/PackageBuilder.swift index 6f5f17f9563..459f2f62112 100644 --- a/Sources/PackageLoading/PackageBuilder.swift +++ b/Sources/PackageLoading/PackageBuilder.swift @@ -205,9 +205,11 @@ extension Target.Error: CustomStringConvertible { var description: String { switch self { case .invalidName(let path, let problem): - "invalid target name at '\(path)'; \(problem)" + return "invalid target name at '\(path)'; \(problem)" case .mixedSources(let path): - "target at '\(path)' contains mixed language source files; feature not supported" + // FIXME(ncooke3): Update error message with support version. + return "target at '\(path)' contains mixed language source " + + "files; feature not supported until tools version XX" } } } @@ -986,7 +988,49 @@ public final class PackageBuilder { } // Create and return the right kind of target depending on what kind of sources we found. - if sources.hasSwiftSources { + if sources.hasSwiftSources && sources.hasClangSources { + + let mixedTargetPublicHeadersPath: AbsolutePath + let moduleMapType: ModuleMapType + // Mixed test targets use the target's root as an umbrella + // directory to expose all headers to the Swift portion of the test + // target. This enables the sharing of test utility files. + if targetType == .test { + mixedTargetPublicHeadersPath = potentialModule.path + moduleMapType = .umbrellaDirectory(potentialModule.path) + } else { + mixedTargetPublicHeadersPath = publicHeadersPath + moduleMapType = findModuleMapType( + for: potentialModule, + targetType: targetType, + publicHeadersPath: publicHeadersPath + ) + } + + return try MixedTarget( + name: potentialModule.name, + potentialBundleName: potentialBundleName, + cLanguageStandard: manifest.cLanguageStandard, + cxxLanguageStandard: manifest.cxxLanguageStandard, + includeDir: mixedTargetPublicHeadersPath, + moduleMapType: moduleMapType, + headers: headers, + type: targetType, + path: potentialModule.path, + sources: sources, + resources: resources, + ignored: ignored, + others: others, + dependencies: dependencies, + packageAccess: potentialModule.packageAccess, + toolsSwiftVersion: self.toolsSwiftVersion(), + buildSettings: buildSettings, + buildSettingsDescription: manifestTarget.settings, + usesUnsafeFlags: manifestTarget.usesUnsafeFlags + + ) + + } else if sources.hasSwiftSources { return try SwiftTarget( name: potentialModule.name, potentialBundleName: potentialBundleName, @@ -1004,32 +1048,23 @@ public final class PackageBuilder { usesUnsafeFlags: manifestTarget.usesUnsafeFlags ) } else { - // It's not a Swift target, so it's a Clang target (those are the only two types of source target currently - // supported). - - // First determine the type of module map that will be appropriate for the target based on its header - // layout. - let moduleMapType: ModuleMapType + // It's not a Mixed or Swift target, so it's a Clang target. - if self.fileSystem.exists(publicHeadersPath) { - let moduleMapGenerator = ModuleMapGenerator( - targetName: potentialModule.name, - moduleName: potentialModule.name.spm_mangledToC99ExtendedIdentifier(), - publicHeadersDir: publicHeadersPath, - fileSystem: self.fileSystem - ) - moduleMapType = moduleMapGenerator.determineModuleMapType(observabilityScope: self.observabilityScope) - } else if targetType == .library, self.manifest.toolsVersion >= .v5_5 { - // If this clang target is a library, it must contain "include" directory. - throw ModuleError.invalidPublicHeadersDirectory(potentialModule.name) - } else { - moduleMapType = .none - } + let moduleMapType = findModuleMapType( + for: potentialModule, + targetType: targetType, + publicHeadersPath: publicHeadersPath + ) if resources.contains(where: { $0.rule == .embedInCode }) { throw ModuleError.embedInCodeNotSupported(target: potentialModule.name) } + if moduleMapType == .none, targetType == .library, manifest.toolsVersion >= .v5_5 { + // If this clang target is a library, it must contain "include" directory. + throw ModuleError.invalidPublicHeadersDirectory(potentialModule.name) + } + return try ClangTarget( name: potentialModule.name, potentialBundleName: potentialBundleName, @@ -1277,6 +1312,26 @@ public final class PackageBuilder { } } + /// Determines the type of module map that will be appropriate for a potential target based on its header layout. + // TODO(ncooke3): Send separate PR for this. + private func findModuleMapType( + for potentialModule: PotentialModule, + targetType: Target.Kind, + publicHeadersPath: AbsolutePath + ) -> ModuleMapType { + guard self.fileSystem.exists(publicHeadersPath) else { + return .none + } + + let moduleMapGenerator = ModuleMapGenerator( + targetName: potentialModule.name, + moduleName: potentialModule.name.spm_mangledToC99ExtendedIdentifier(), + publicHeadersDir: publicHeadersPath, + fileSystem: self.fileSystem + ) + return moduleMapGenerator.determineModuleMapType(observabilityScope: self.observabilityScope) + } + /// Find the test entry point file for the package. private func findTestEntryPoint(in testTargets: [Target]) throws -> AbsolutePath? { if let testEntryPointPath { diff --git a/Sources/PackageLoading/TargetSourcesBuilder.swift b/Sources/PackageLoading/TargetSourcesBuilder.swift index d6506555077..c061613e510 100644 --- a/Sources/PackageLoading/TargetSourcesBuilder.swift +++ b/Sources/PackageLoading/TargetSourcesBuilder.swift @@ -214,7 +214,8 @@ public struct TargetSourcesBuilder { diagnoseInvalidResource(in: target.resources) // It's an error to contain mixed language source files. - if sources.containsMixedLanguage { + // FIXME(ncooke3): Update with next version of SPM. + if sources.containsMixedLanguage, toolsVersion < .vNext { throw Target.Error.mixedSources(targetPath) } diff --git a/Sources/PackageModel/CMakeLists.txt b/Sources/PackageModel/CMakeLists.txt index 4d795c6d873..cb4ef379075 100644 --- a/Sources/PackageModel/CMakeLists.txt +++ b/Sources/PackageModel/CMakeLists.txt @@ -51,6 +51,7 @@ add_library(PackageModel SwiftSDKs/SwiftSDKBundleStore.swift Target/BinaryTarget.swift Target/ClangTarget.swift + Target/MixedTarget.swift Target/PluginTarget.swift Target/ProvidedLibraryTarget.swift Target/SwiftTarget.swift diff --git a/Sources/PackageModel/Sources.swift b/Sources/PackageModel/Sources.swift index efe55605920..d984e3becc3 100644 --- a/Sources/PackageModel/Sources.swift +++ b/Sources/PackageModel/Sources.swift @@ -41,7 +41,7 @@ public struct Sources: Codable { }) } - /// Returns true if the sources contain C++ files. + /// Returns true if the sources contain Objective-C files. public var containsObjcFiles: Bool { return paths.contains(where: { guard let ext = $0.extension else { diff --git a/Sources/PackageModel/Target/MixedTarget.swift b/Sources/PackageModel/Target/MixedTarget.swift new file mode 100644 index 00000000000..966d02f7798 --- /dev/null +++ b/Sources/PackageModel/Target/MixedTarget.swift @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import struct Basics.AbsolutePath +import struct Basics.StringError + +public final class MixedTarget: Target { + + // The Clang target for the mixed target's Clang sources. + public let clangTarget: ClangTarget + + // The Swift target for the mixed target's Swift sources. + public let swiftTarget: SwiftTarget + + public init( + name: String, + potentialBundleName: String? = nil, + cLanguageStandard: String?, + cxxLanguageStandard: String?, + includeDir: AbsolutePath, + moduleMapType: ModuleMapType, + headers: [AbsolutePath] = [], + type: Kind, + path: AbsolutePath, + sources: Sources, + resources: [Resource] = [], + ignored: [AbsolutePath] = [], + others: [AbsolutePath] = [], + dependencies: [Target.Dependency] = [], + packageAccess: Bool, + toolsSwiftVersion: SwiftLanguageVersion, + buildSettings: BuildSettings.AssignmentTable = .init(), + buildSettingsDescription: [TargetBuildSettingDescription.Setting], + pluginUsages: [PluginUsage] = [], + usesUnsafeFlags: Bool + ) throws { + guard type == .library || type == .test else { + throw StringError( + "Target with mixed sources at '\(path)' is a \(type) " + + "target; targets with mixed language sources are only " + + "supported for library and test targets." + ) + } + + let swiftSources = Sources( + paths: sources.paths.filter { path in + guard let ext = path.extension else { return false } + return SupportedLanguageExtension.swiftExtensions.contains(ext) + }, + root: sources.root + ) + + self.swiftTarget = SwiftTarget( + name: name, + potentialBundleName: potentialBundleName, + type: type, + path: path, + sources: swiftSources, + resources: resources, + ignored: ignored, + others: others, + dependencies: dependencies, + packageAccess: packageAccess, + buildSettings: buildSettings, + pluginUsages: pluginUsages, + usesUnsafeFlags: usesUnsafeFlags + ) + + let clangSources = Sources( + paths: sources.paths.filter { path in + guard let ext = path.extension else { return false } + return SupportedLanguageExtension.clangTargetExtensions(toolsVersion: .current).contains(ext) + }, + root: sources.root + ) + + self.clangTarget = try ClangTarget( + name: name, + potentialBundleName: potentialBundleName, + cLanguageStandard: cLanguageStandard, + cxxLanguageStandard: cxxLanguageStandard, + includeDir: includeDir, + moduleMapType: moduleMapType, + headers: headers, + type: type, + path: path, + sources: clangSources, + resources: resources, + ignored: ignored, + others: others, + buildSettings: buildSettings, + usesUnsafeFlags: usesUnsafeFlags + ) + + super.init( + name: name, + potentialBundleName: potentialBundleName, + type: type, + path: path, + sources: sources, + resources: resources, + ignored: ignored, + others: others, + dependencies: dependencies, + packageAccess: packageAccess, + buildSettings: buildSettings, + buildSettingsDescription: buildSettingsDescription, + pluginUsages: pluginUsages, + usesUnsafeFlags: usesUnsafeFlags + ) + } + + private enum CodingKeys: String, CodingKey { + case clangTarget, swiftTarget + } + + public override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(clangTarget, forKey: .clangTarget) + try container.encode(swiftTarget, forKey: .swiftTarget) + try super.encode(to: encoder) + } + + required public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.clangTarget = try container.decode(ClangTarget.self, forKey: .clangTarget) + self.swiftTarget = try container.decode(SwiftTarget.self, forKey: .swiftTarget) + try super.init(from: decoder) + } +} diff --git a/Sources/PackageModel/Target/Target.swift b/Sources/PackageModel/Target/Target.swift index a91176091bc..a0671f48eb9 100644 --- a/Sources/PackageModel/Target/Target.swift +++ b/Sources/PackageModel/Target/Target.swift @@ -18,6 +18,7 @@ public class Target: PolymorphicCodableProtocol { public static var implementations: [PolymorphicCodableProtocol.Type] = [ SwiftTarget.self, ClangTarget.self, + MixedTarget.self, SystemLibraryTarget.self, BinaryTarget.self, PluginTarget.self, diff --git a/Sources/PackagePlugin/PackageModel.swift b/Sources/PackagePlugin/PackageModel.swift index fd61fe2b272..d3aeaa81352 100644 --- a/Sources/PackagePlugin/PackageModel.swift +++ b/Sources/PackagePlugin/PackageModel.swift @@ -369,6 +369,84 @@ public struct ClangSourceModuleTarget: SourceModuleTarget { public let pluginGeneratedResources: [URL] } +/// Represents a target consisting of a source code module containing both Swift and C-family language sources. +public struct MixedSourceModuleTarget: SourceModuleTarget { + + /// Describes attributes specific to the target's Swift sources. + public struct SwiftSourceAttributes { + /// Any custom compilation conditions specified for the target's Swift sources. + public let compilationConditions: [String] + } + + /// Describes settings specific to the target's C-family language sources. + public struct ClangSourceAttributes { + /// Any preprocessor definitions specified for the Clang target. + public let preprocessorDefinitions: [String] + + /// Any custom header search paths specified for the Clang target. + public let headerSearchPaths: [String] + + /// The directory containing public C headers, if applicable. This will + /// only be set for targets that have a directory of a public headers. + public let publicHeadersDirectory: URL? + } + + /// Unique identifier for the target. + public let id: ID + + /// The name of the target, as defined in the package manifest. This name + /// is unique among the targets of the package in which it is defined. + public let name: String + + /// The kind of module, describing whether it contains unit tests, contains + /// the main entry point of an executable, or neither. + public let kind: ModuleKind + + /// The absolute path of the target directory in the local file system. + @available(_PackageDescription, deprecated: 5.11) + public let directory: Path + + /// The absolute path of the target directory in the local file system. + @available(_PackageDescription, introduced: 5.11) + public let directoryURL: URL + + + /// Any other targets on which this target depends, in the same order as + /// they are specified in the package manifest. Conditional dependencies + /// that do not apply have already been filtered out. + public let dependencies: [TargetDependency] + + /// The name of the module produced by the target (derived from the target + /// name, though future SwiftPM versions may allow this to be customized). + public let moduleName: String + + /// The source files that are associated with this target (any files that + /// have been excluded in the manifest have already been filtered out). + public let sourceFiles: FileList + + /// Attributes specific to the target's Swift sources. + public let swift: SwiftSourceAttributes + + /// Attributes specific to the target's Clang sources. + public let clang: ClangSourceAttributes + + /// Any custom linked libraries required by the module, as specified in the + /// package manifest. + public let linkedLibraries: [String] + + /// Any custom linked frameworks required by the module, as specified in the + /// package manifest. + public let linkedFrameworks: [String] + + /// Paths of any sources generated by other plugins that have been applied to the given target before the plugin currently being executed. + @available(_PackageDescription, introduced: 5.11) + public let pluginGeneratedSources: [URL] + + /// Paths of any resources generated by other plugins that have been applied to the given target before the plugin currently being executed. + @available(_PackageDescription, introduced: 5.11) + public let pluginGeneratedResources: [URL] +} + /// Represents a target describing an artifact (e.g. a library or executable) /// that is distributed as a binary. public struct BinaryArtifactTarget: Target { diff --git a/Sources/PackagePlugin/PluginContextDeserializer.swift b/Sources/PackagePlugin/PluginContextDeserializer.swift index 716d8f56d5b..6ec3a55b9e5 100644 --- a/Sources/PackagePlugin/PluginContextDeserializer.swift +++ b/Sources/PackagePlugin/PluginContextDeserializer.swift @@ -146,6 +146,44 @@ internal struct PluginContextDeserializer { pluginGeneratedResources: pluginGeneratedResources ) + + case let .mixedSourceModuleInfo(moduleName, kind, sourceFiles, compilationConditions, preprocessorDefinitions, headerSearchPaths, publicHeadersDirId, linkedLibraries, linkedFrameworks): + let publicHeadersDir = try publicHeadersDirId.map { try self.url(for: $0) } + let sourceFiles = FileList(try sourceFiles.map { + let path = try self.url(for: $0.basePathId).appendingPathComponent($0.name) + let type: FileType + switch $0.type { + case .source: + type = .source + case .header: + type = .header + case .resource: + type = .resource + case .unknown: + type = .unknown + } + return File(path: Path(url: path), url: path, type: type) + }) + target = MixedSourceModuleTarget( + id: String(id), + name: wireTarget.name, + kind: .init(kind), + directory: Path(url: directory), + directoryURL: directory, + dependencies: dependencies, + moduleName: moduleName, + sourceFiles: sourceFiles, + swift: .init(compilationConditions: compilationConditions), + clang: .init( + preprocessorDefinitions: preprocessorDefinitions, + headerSearchPaths: headerSearchPaths, + publicHeadersDirectory: publicHeadersDir), + linkedLibraries: linkedLibraries, + linkedFrameworks: linkedFrameworks, + pluginGeneratedSources: pluginGeneratedSources, + pluginGeneratedResources: pluginGeneratedResources + ) + case let .binaryArtifactInfo(kind, origin, artifactId): let artifact = try self.url(for: artifactId) let artifactKind: BinaryArtifactTarget.Kind diff --git a/Sources/PackagePlugin/PluginMessages.swift b/Sources/PackagePlugin/PluginMessages.swift index 75b03fdab06..b0f1835eceb 100644 --- a/Sources/PackagePlugin/PluginMessages.swift +++ b/Sources/PackagePlugin/PluginMessages.swift @@ -153,6 +153,17 @@ enum HostToPluginMessage: Codable { linkedLibraries: [String], linkedFrameworks: [String]) + case mixedSourceModuleInfo( + moduleName: String, + kind: SourceModuleKind, + sourceFiles: [File], + compilationConditions: [String], + preprocessorDefinitions: [String], + headerSearchPaths: [String], + publicHeadersDirId: URL.Id?, + linkedLibraries: [String], + linkedFrameworks: [String]) + case binaryArtifactInfo( kind: BinaryArtifactKind, origin: BinaryArtifactOrigin, diff --git a/Sources/SPMBuildCore/BuiltTestProduct.swift b/Sources/SPMBuildCore/BuiltTestProduct.swift index 881ade7175f..ca0dd354e25 100644 --- a/Sources/SPMBuildCore/BuiltTestProduct.swift +++ b/Sources/SPMBuildCore/BuiltTestProduct.swift @@ -41,7 +41,7 @@ public struct BuiltTestProduct: Codable { guard let bundlePath = hierarchySequence.first(where: { $0.basename.hasSuffix(pathExtension) }) else { fatalError("could not find test bundle path from '\(binaryPath)'") } - + return bundlePath } diff --git a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift index 340f02d1cf8..82f1f1c9c5a 100644 --- a/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift +++ b/Sources/SPMBuildCore/Plugins/PluginContextSerializer.swift @@ -101,6 +101,18 @@ internal struct PluginContextSerializer { linkedLibraries: scope.evaluate(.LINK_LIBRARIES), linkedFrameworks: scope.evaluate(.LINK_FRAMEWORKS)) + case let target as MixedTarget: + targetInfo = .mixedSourceModuleInfo( + moduleName: target.c99name, + kind: try .init(target.type), + sourceFiles: targetFiles, + compilationConditions: scope.evaluate(.SWIFT_ACTIVE_COMPILATION_CONDITIONS), + preprocessorDefinitions: scope.evaluate(.GCC_PREPROCESSOR_DEFINITIONS), + headerSearchPaths: scope.evaluate(.HEADER_SEARCH_PATHS), + publicHeadersDirId: try serialize(path: target.clangTarget.includeDir), + linkedLibraries: scope.evaluate(.LINK_LIBRARIES), + linkedFrameworks: scope.evaluate(.LINK_FRAMEWORKS)) + case let target as SystemLibraryTarget: var cFlags: [String] = [] var ldFlags: [String] = [] diff --git a/Sources/SPMTestSupport/MockBuildTestHelper.swift b/Sources/SPMTestSupport/MockBuildTestHelper.swift index 8e2a0327f85..a8a57de3cfa 100644 --- a/Sources/SPMTestSupport/MockBuildTestHelper.swift +++ b/Sources/SPMTestSupport/MockBuildTestHelper.swift @@ -335,4 +335,13 @@ extension TargetBuildDescription { throw BuildError.error("Unexpected \(self) type") } } + + public func mixedTarget() throws -> MixedTargetBuildDescription { + switch self { + case .mixed(let target): + return target + default: + throw BuildError.error("Unexpected \(self) type") + } + } } diff --git a/Sources/SPMTestSupport/SwiftPMProduct.swift b/Sources/SPMTestSupport/SwiftPMProduct.swift index 38ff2e2a415..539c883c507 100644 --- a/Sources/SPMTestSupport/SwiftPMProduct.swift +++ b/Sources/SPMTestSupport/SwiftPMProduct.swift @@ -87,10 +87,10 @@ extension SwiftPM { packagePath: packagePath, env: env ) - + let stdout = try result.utf8Output() let stderr = try result.utf8stderrOutput() - + if result.exitStatus == .terminated(code: 0) { return (stdout: stdout, stderr: stderr) } @@ -100,7 +100,7 @@ extension SwiftPM { stderr: stderr ) } - + private func executeProcess( _ args: [String], packagePath: AbsolutePath? = nil, diff --git a/Sources/SPMTestSupport/XCTAssertHelpers.swift b/Sources/SPMTestSupport/XCTAssertHelpers.swift index 5e401a38fa7..06914bf1142 100644 --- a/Sources/SPMTestSupport/XCTAssertHelpers.swift +++ b/Sources/SPMTestSupport/XCTAssertHelpers.swift @@ -123,6 +123,7 @@ public func XCTAssertSwiftTest( @discardableResult public func XCTAssertBuildFails( _ path: AbsolutePath, + extraArgs: [String] = [], Xcc: [String] = [], Xld: [String] = [], Xswiftc: [String] = [], @@ -131,7 +132,7 @@ public func XCTAssertBuildFails( line: UInt = #line ) -> CommandExecutionError? { var failure: CommandExecutionError? = nil - XCTAssertThrowsCommandExecutionError(try executeSwiftBuild(path, Xcc: Xcc, Xld: Xld, Xswiftc: Xswiftc), file: file, line: line) { error in + XCTAssertThrowsCommandExecutionError(try executeSwiftBuild(path, extraArgs: extraArgs, Xcc: Xcc, Xld: Xld, Xswiftc: Xswiftc), file: file, line: line) { error in failure = error } return failure diff --git a/Sources/SourceKitLSPAPI/BuildDescription.swift b/Sources/SourceKitLSPAPI/BuildDescription.swift index 74614635454..2f4506656e3 100644 --- a/Sources/SourceKitLSPAPI/BuildDescription.swift +++ b/Sources/SourceKitLSPAPI/BuildDescription.swift @@ -122,6 +122,9 @@ public struct BuildDescription { description: description, isPartOfRootPackage: modulesGraph.rootPackages.map(\.id).contains(description.package.id) ) + default: + // TODO(ncooke3): What is supposed to happen here? + fatalError("`MixedTargetDescription` does not conform to `BuildTarget`.") } } else { if target.type == .plugin, let package = self.buildPlan.graph.package(for: target) { diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 0a811c59dce..665f474ef34 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -1747,6 +1747,306 @@ final class BuildPlanTests: XCTestCase { } } +// func testBasicMixedLanguages() throws { +// let fs = InMemoryFileSystem(emptyFiles: +// "/Pkg/Sources/exe/main.swift", +// "/Pkg/Sources/lib/Foo.swift", +// "/Pkg/Sources/lib/include/Bar.h", +// "/Pkg/Sources/lib/Bar.m" +// ) +// +// let observability = ObservabilitySystem.makeForTesting() +// let graph = try loadModulesGraph( +// fileSystem: fs, +// manifests: [ +// Manifest.createRootManifest( +// displayName: "Pkg", +// path: "/Pkg", +// // FIXME(ncooke3): Update error message with support version. +// toolsVersion: .vNext, +// targets: [ +// TargetDescription(name: "exe", dependencies: ["lib"], type: .executable), +// TargetDescription( +// name: "lib", +// // Configure some settings to verify they are +// // passed to mixed target's subtargets. +// settings: [ +// .init(tool: .swift, kind: .unsafeFlags(["-Xfrontend", "-super-cool-swift-only-flag"]), condition: .init(config: "debug")), +// .init(tool: .linker, kind: .linkedFramework("OtherFramework")), +// .init(tool: .c, kind: .define("HELLO_CLANG=1")), +// .init(tool: .swift, kind: .define("HELLO_SWIFT=1")) +// ] +// ) +// ] +// ) +// ], +// observabilityScope: observability.topScope +// ) +// XCTAssertNoDiagnostics(observability.diagnostics) +// +// let result = try BuildPlanResult(plan: BuildPlan( +// buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), +// graph: graph, +// fileSystem: fs, +// observabilityScope: observability.topScope +// )) +// +// result.checkProductsCount(1) +// result.checkTargetsCount(2) +// +// let buildPath: AbsolutePath = result.plan.productsBuildPath +// +// let exe = try result.target(for: "exe").swiftTarget().compileArguments() +// #if os(macOS) +// XCTAssertMatch(exe, [ +// "-target", "\(defaultTargetTriple)", +// "-enable-batch-mode", "-Onone", "-enable-testing", .equal(j), +// "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", +// "-fmodule-map-file=\(buildPath.appending(components: "lib.build", "module.modulemap"))", +// "-Xcc", "-I", "-Xcc", "/Pkg/Sources/lib/include", +// "-module-cache-path", +// "\(buildPath.appending(components: "ModuleCache"))", +// "-parseable-output", "-swift-version", "6", "-g", +// "-Xcc", "-g", "-package-name", "pkg", +// ]) +// #elseif os(Linux) +// XCTAssertMatch(exe, [ +// "-target", "\(defaultTargetTriple)", "-swift-version", "6", +// "-enable-batch-mode", "-Onone", "-enable-testing", .equal(j), +// "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", +// "-fmodule-map-file=\(buildPath.appending(components: "lib.build", "module.modulemap"))", +// "-Xcc", "-I", "-Xcc", "/Pkg/Sources/lib/include", +// "-module-cache-path", +// "\(buildPath.appending(components: "ModuleCache"))", +// .anySequence, "-static-stdlib", "-parseable-output", "-g", "-Xcc", "-g", +// ]) +// #endif +// +// let swiftPartOfLib = try result.target(for: "lib").mixedTarget().swiftTargetBuildDescription.compileArguments() +// XCTAssertMatch(swiftPartOfLib, [ +// "-target", "\(defaultTargetTriple)", +// "-enable-batch-mode", "-Onone", "-enable-testing", .equal(j), +// "-DSWIFT_PACKAGE", "-DDEBUG", "-import-underlying-module", "-I", +// "\(buildPath.appending(components: "lib.build"))", "-Xcc", "-ivfsoverlay", +// "-Xcc", "\(buildPath.appending(components: "lib.build", "unextended-module-overlay.yaml"))", +// "-Xcc", "-I", "-Xcc", "/Pkg/Sources/lib", "-Xcc", "-I", "-Xcc", +// "/Pkg/Sources/lib/include", "-module-cache-path", +// "\(buildPath.appending(components: "ModuleCache"))", .anySequence, +// "-parseable-output", "-parse-as-library", "-emit-objc-header", +// "-emit-objc-header-path", "\(buildPath.appending(components: "lib.build", "lib-Swift.h"))", +// "-DHELLO_SWIFT=1", "-Xfrontend", "-super-cool-swift-only-flag", +// "-Xcc", "-DHELLO_CLANG=1", "-swift-version", "6", "-g", "-Xcc", +// "-g", "-package-name", "pkg", +// ]) +// +// let clangPartOfLib = try result.target(for: "lib").mixedTarget().clangTargetBuildDescription.basicArguments(isCXX: false) +// #if os(macOS) +// XCTAssertMatch(clangPartOfLib, [ +// "-fobjc-arc", "-target", "\(defaultTargetTriple)", "-O0", +// "-DSWIFT_PACKAGE=1", "-DDEBUG=1", "-fblocks", "-fmodules", +// "-fmodule-name=lib", "-I", "/Pkg/Sources/lib/include", "-I", +// "/Pkg/Sources/lib", "-I", "\(buildPath.appending(components: "lib.build"))", +// "-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))", "-DHELLO_CLANG=1", +// "-g" +// ]) +// #elseif os(Linux) +// XCTAssertMatch(clangPartOfLib, [ +// "-target", "\(defaultTargetTriple)", "-O0", +// "-DSWIFT_PACKAGE=1", "-DDEBUG=1", "-fblocks", "-fmodules", +// "-fmodule-name=lib", "-I", "/Pkg/Sources/lib/include", "-I", +// "/Pkg/Sources/lib", "-I", "/path/to/build/debug/lib.build", +// "-fmodules-cache-path=/path/to/build/debug/ModuleCache", "-DHELLO_CLANG=1", +// "-g" +// ]) +// #endif +// +// XCTAssertEqual( +// try result.target(for: "lib").mixedTarget().objects, +// [ +// buildPath.appending(components: "lib.build", "Foo.swift.o"), +// buildPath.appending(components: "lib.build", "Bar.m.o"), +// ] +// ) +// +// XCTAssertEqual( +// try result.target(for: "lib").mixedTarget().moduleMap.pathString, +// buildPath.appending(components: "lib.build", "module.modulemap").pathString +// ) +// +// #if os(macOS) +// XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [ +// result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, +// "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, +// "-module-name", "exe", "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", +// "-Xlinker", "@loader_path", +// "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", +// "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", +// "-target", defaultTargetTriple, "-framework", "OtherFramework", "-Xlinker", "-add_ast_path", +// "-Xlinker", buildPath.appending(components: "Modules", "exe.swiftmodule").pathString, +// "-g" +// ]) +// #elseif os(Linux) +// XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [ +// result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, +// "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, +// "-module-name", "exe", "-static-stdlib", "-emit-executable", "-Xlinker", "-rpath=$ORIGIN", +// "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", +// "-target", defaultTargetTriple, "-g" +// ]) +// #endif +// +// #if os(macOS) +// testDiagnostics(observability.diagnostics) { result in +// result.check(diagnostic: .contains("can be downloaded"), severity: .warning) +// } +// #else +// XCTAssertNoDiagnostics(observability.diagnostics) +// #endif +// } + +// func testBasicMixedLanguagesWithCustomModuleMap() throws { +// let fs = InMemoryFileSystem(emptyFiles: +// "/Pkg/Sources/exe/main.swift", +// "/Pkg/Sources/lib/Foo.swift", +// "/Pkg/Sources/lib/include/Bar.h", +// "/Pkg/Sources/lib/include/module.modulemap", +// "/Pkg/Sources/lib/Bar.m" +// ) +// +// let observability = ObservabilitySystem.makeForTesting() +// let graph = try loadModulesGraph( +// fileSystem: fs, +// manifests: [ +// Manifest.createRootManifest( +// displayName: "Pkg", +// path: "/Pkg", +// // FIXME(ncooke3): Update error message with support version. +// toolsVersion: .vNext, +// targets: [ +// TargetDescription(name: "exe", dependencies: ["lib"], type: .executable), +// TargetDescription(name: "lib") +// ] +// ) +// ], +// observabilityScope: observability.topScope +// ) +// XCTAssertNoDiagnostics(observability.diagnostics) +// +// let result = try BuildPlanResult(plan: BuildPlan( +// buildParameters: mockBuildParameters(shouldLinkStaticSwiftStdlib: true), +// graph: graph, +// fileSystem: fs, +// observabilityScope: observability.topScope +// )) +// +// result.checkProductsCount(1) +// result.checkTargetsCount(2) +// +// let buildPath: AbsolutePath = result.plan.productsBuildPath +// let exe = try result.target(for: "exe").swiftTarget().compileArguments() +// #if os(macOS) +// XCTAssertMatch(exe, [ +// "-target", "\(defaultTargetTriple)", +// "-enable-batch-mode", "-Onone", "-enable-testing", .equal(j), +// "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", +// "-fmodule-map-file=/Pkg/Sources/lib/include/module.modulemap", +// "-Xcc", "-I", "-Xcc", "/Pkg/Sources/lib/include", +// "-module-cache-path", +// "\(buildPath.appending(components: "ModuleCache"))", .anySequence, +// "-parseable-output", "-swift-version", "6", "-g", "-Xcc", "-g", +// "-package-name", "pkg" +// ]) +// #elseif os(Linux) +// XCTAssertMatch(exe, [ +// "-target", "\(defaultTargetTriple)", "-swift-version", "6", +// "-enable-batch-mode", "-Onone", "-enable-testing", .equal(j), +// "-DSWIFT_PACKAGE", "-DDEBUG", "-Xcc", +// "-fmodule-map-file=/Pkg/Sources/lib/include/module.modulemap", +// "-Xcc", "-I", "-Xcc", "/Pkg/Sources/lib/include", +// "-module-cache-path", +// "\(buildPath.appending(components: "ModuleCache"))", .anySequence, +// "-static-stdlib", "-parseable-output", "-g", "-Xcc", "-g" +// ]) +// #endif +// +// let swiftPartOfLib = try result.target(for: "lib").mixedTarget().swiftTargetBuildDescription.compileArguments() +// XCTAssertMatch(swiftPartOfLib, [ +// "-target", "\(defaultTargetTriple)", +// "-enable-batch-mode", "-Onone", "-enable-testing", .equal(j), +// "-DSWIFT_PACKAGE", "-DDEBUG", "-import-underlying-module", "-I", +// "/Pkg/Sources/lib/include", "-Xcc", "-I", "-Xcc", +// "/Pkg/Sources/lib", "-Xcc", "-I", "-Xcc", +// "/Pkg/Sources/lib/include", "-module-cache-path", +// "\(buildPath.appending(components: "ModuleCache"))", .anySequence, +// "-parseable-output", "-parse-as-library", "-emit-objc-header", +// "-emit-objc-header-path", +// "\(buildPath.appending(components: "lib.build", "lib-Swift.h"))", +// "-swift-version", "6", "-g", "-Xcc", "-g", "-package-name", "pkg" +// ]) +// +// let clangPartOfLib = try result.target(for: "lib").mixedTarget().clangTargetBuildDescription.basicArguments(isCXX: false) +// #if os(macOS) +// XCTAssertMatch(clangPartOfLib, [ +// "-fobjc-arc", "-target", "\(defaultTargetTriple)", "-O0", +// "-DSWIFT_PACKAGE=1", "-DDEBUG=1", "-fblocks", "-fmodules", +// "-fmodule-name=lib", "-I", "/Pkg/Sources/lib/include", "-I", +// "/Pkg/Sources/lib", "-I", "\(buildPath.appending(components: "lib.build"))", +// "-fmodules-cache-path=\(buildPath.appending(components: "ModuleCache"))", "-g" +// ]) +// #elseif os(Linux) +// XCTAssertMatch(clangPartOfLib, [ +// "-target", "\(defaultTargetTriple)", "-O0", +// "-DSWIFT_PACKAGE=1", "-DDEBUG=1", "-fblocks", "-fmodules", +// "-fmodule-name=lib", "-I", "/Pkg/Sources/lib/include", "-I", +// "/Pkg/Sources/lib", "-I", "/path/to/build/debug/lib.build", +// "-fmodules-cache-path=/path/to/build/debug/ModuleCache", "-g" +// ]) +// #endif +// +// XCTAssertEqual( +// try result.target(for: "lib").mixedTarget().objects, +// [ +// buildPath.appending(components: "lib.build", "Foo.swift.o"), +// buildPath.appending(components: "lib.build", "Bar.m.o"), +// ] +// ) +// +// XCTAssertEqual( +// try result.target(for: "lib").mixedTarget().moduleMap.pathString, +// "/Pkg/Sources/lib/include/module.modulemap" +// ) +// +// #if os(macOS) +// XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [ +// result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, +// "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, +// "-module-name", "exe", "-Xlinker", "-no_warn_duplicate_libraries", "-emit-executable", "-Xlinker", "-rpath", +// "-Xlinker", "@loader_path", +// "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", +// "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", +// "-target", defaultTargetTriple, "-Xlinker", "-add_ast_path", +// "-Xlinker", buildPath.appending(components: "Modules", "exe.swiftmodule").pathString, +// "-g" +// ]) +// #elseif os(Linux) +// XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [ +// result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, +// "-L", buildPath.pathString, "-o", buildPath.appending(components: "exe").pathString, +// "-module-name", "exe", "-static-stdlib", "-emit-executable", "-Xlinker", "-rpath=$ORIGIN", +// "@\(buildPath.appending(components: "exe.product", "Objects.LinkFileList"))", +// "-target", defaultTargetTriple, "-g" +// ]) +// #endif +// +// #if os(macOS) +// testDiagnostics(observability.diagnostics) { result in +// result.check(diagnostic: .contains("can be downloaded"), severity: .warning) +// } +// #else +// XCTAssertNoDiagnostics(observability.diagnostics) +// #endif +// } + func testSwiftCMixed() throws { let Pkg: AbsolutePath = "/Pkg" @@ -1834,6 +2134,12 @@ final class BuildPlanTests: XCTestCase { .anySequence, ] ) + + let isFlagSupportedInDriver = try DriverSupport.checkToolchainDriverFlags( + flags: ["package-name"], + toolchain: UserToolchain.default, + fileSystem: localFileSystem + ) #if os(macOS) XCTAssertEqual(try result.buildProduct(for: "exe").linkArguments(), [ @@ -2977,6 +3283,219 @@ final class BuildPlanTests: XCTestCase { #endif } + func testDynamicProductsForMixedTarget() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Foo/Sources/Foo/main.swift", + "/Bar/Source/Bar/source.swift", + "/Bar/Source/Bar/include/source.h", + "/Bar/Source/Bar/source.c" + ) + + let observability = ObservabilitySystem.makeForTesting() + let g = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createFileSystemManifest( + displayName: "Bar", + path: "/Bar", + // FIXME(ncooke3): Update with next version of SPM. + toolsVersion: .vNext, + products: [ + ProductDescription( + name: "Bar-Baz", + type: .library(.dynamic), + targets: ["Bar"] + ), + ], + targets: [ + TargetDescription(name: "Bar"), + ]), + Manifest.createRootManifest( + displayName: "Foo", + path: "/Foo", + dependencies: [ + .localSourceControl( + path: "/Bar", + requirement: .upToNextMajor(from: "1.0.0") + ), + ], + targets: [ + TargetDescription( + name: "Foo", + dependencies: ["Bar-Baz"], + type: .executable + ) + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + let result = try BuildPlanResult(plan: mockBuildPlan( + graph: g, + fileSystem: fs, + observabilityScope: observability.topScope + )) + result.checkProductsCount(2) + result.checkTargetsCount(2) + + let buildPath: AbsolutePath = result.plan.productsBuildPath + + let fooLinkArgs = try result.buildProduct(for: "Foo").linkArguments() + let barLinkArgs = try result.buildProduct(for: "Bar-Baz").linkArguments() + + #if os(macOS) + XCTAssertEqual(fooLinkArgs, [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "Foo").pathString, + "-module-name", "Foo", + "-lBar-Baz", + "-Xlinker", "-no_warn_duplicate_libraries", + "-emit-executable", + "-Xlinker", "-rpath", "-Xlinker", "@loader_path", + "@\(buildPath.appending(components: "Foo.product", "Objects.LinkFileList"))", + "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", + "-target", defaultTargetTriple, + "-Xlinker", "-add_ast_path", "-Xlinker", buildPath.appending(components: "Foo.build", "Foo.swiftmodule").pathString, + "-g" + ]) + + XCTAssertEqual(barLinkArgs, [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "libBar-Baz.dylib").pathString, + "-module-name", "Bar_Baz", + "-Xlinker", "-no_warn_duplicate_libraries", + "-emit-library", + "-Xlinker", "-install_name", "-Xlinker", "@rpath/libBar-Baz.dylib", + "-Xlinker", "-rpath", "-Xlinker", "@loader_path", + "@\(buildPath.appending(components: "Bar-Baz.product", "Objects.LinkFileList"))", + "-runtime-compatibility-version", "none", "-target", defaultTargetTriple, + "-g" + ]) + #elseif os(Linux) + XCTAssertEqual(fooLinkArgs, [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "Foo").pathString, + "-module-name", "Foo", + "-lBar-Baz", + "-emit-executable", + "-Xlinker", "-rpath=$ORIGIN", + "@\(buildPath.appending(components: "Foo.product", "Objects.LinkFileList"))", + "-target", defaultTargetTriple, "-g" + ]) + + XCTAssertEqual(barLinkArgs, [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "libBar-Baz.so").pathString, + "-module-name", "Bar_Baz", + "-emit-library", + "-Xlinker", "-rpath=$ORIGIN", + "@\(buildPath.appending(components: "Bar-Baz.product", "Objects.LinkFileList"))", + "-runtime-compatibility-version", "none", "-target", defaultTargetTriple, + "-g" + ]) + #endif + } + + func testStaticProductsForMixedTarget() throws { + let fs = InMemoryFileSystem(emptyFiles: + "/Foo/Sources/Foo/main.swift", + "/Bar/Source/Bar/source.swift", + "/Bar/Source/Bar/include/source.h", + "/Bar/Source/Bar/source.c" + ) + + let observability = ObservabilitySystem.makeForTesting() + let g = try loadModulesGraph( + fileSystem: fs, + manifests: [ + Manifest.createFileSystemManifest( + displayName: "Bar", + path: "/Bar", + // FIXME(ncooke3): Update with next version of SPM. + toolsVersion: .vNext, + products: [ + ProductDescription( + name: "Bar-Baz", + type: .library(.static), + targets: ["Bar"] + ), + ], + targets: [ + TargetDescription(name: "Bar"), + ]), + Manifest.createRootManifest( + displayName: "Foo", + path: "/Foo", + dependencies: [ + .localSourceControl( + path: "/Bar", + requirement: .upToNextMajor(from: "1.0.0") + ), + ], + targets: [ + TargetDescription( + name: "Foo", + dependencies: ["Bar-Baz"], + type: .executable + ) + ] + ), + ], + observabilityScope: observability.topScope + ) + XCTAssertNoDiagnostics(observability.diagnostics) + + let result = try BuildPlanResult(plan: mockBuildPlan( + graph: g, + fileSystem: fs, + observabilityScope: observability.topScope + )) + result.checkProductsCount(2) + result.checkTargetsCount(2) + + let buildPath: AbsolutePath = result.plan.productsBuildPath + + let fooLinkArgs = try result.buildProduct(for: "Foo").linkArguments() + let barLinkArgs = try result.buildProduct(for: "Bar-Baz").linkArguments() + + #if os(macOS) + XCTAssertEqual(fooLinkArgs, [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "Foo").pathString, + "-module-name", "Foo", + "-Xlinker", "-no_warn_duplicate_libraries", + "-emit-executable", + "-Xlinker", "-rpath", "-Xlinker", "@loader_path", + "@\(buildPath.appending(components: "Foo.product", "Objects.LinkFileList"))", + "-Xlinker", "-rpath", "-Xlinker", "/fake/path/lib/swift-5.5/macosx", + "-target", defaultTargetTriple, + "-Xlinker", "-add_ast_path", "-Xlinker", buildPath.appending(components: "Foo.build", "Foo.swiftmodule").pathString, + "-g" + ]) + #elseif os(Linux) + XCTAssertEqual(fooLinkArgs, [ + result.plan.destinationBuildParameters.toolchain.swiftCompilerPath.pathString, + "-L", buildPath.pathString, + "-o", buildPath.appending(components: "Foo").pathString, + "-module-name", "Foo", + "-emit-executable", + "-Xlinker", "-rpath=$ORIGIN", + "@\(buildPath.appending(components: "Foo.product", "Objects.LinkFileList"))", + "-target", defaultTargetTriple, "-g" + ]) + #endif + + // No arguments for linking static libraries. + XCTAssertEqual(barLinkArgs, []) + } + func testExecAsDependency() throws { let fs = InMemoryFileSystem( emptyFiles: @@ -3014,6 +3533,8 @@ final class BuildPlanTests: XCTestCase { result.checkTargetsCount(2) let buildPath = result.plan.productsBuildPath + + let exe = try result.target(for: "exe").swiftTarget().compileArguments() XCTAssertMatch( @@ -6270,7 +6791,7 @@ final class BuildPlanTests: XCTestCase { if #available(macOS 13, *) { // `.contains` is only available in macOS 13 or newer XCTAssertTrue(try swiftTarget.compileArguments().contains(["-user-module-version", "1.0.0"])) } - case .clang: + case .clang, .mixed: XCTFail("expected a Swift target") } } diff --git a/Tests/BuildTests/ModuleAliasingBuildTests.swift b/Tests/BuildTests/ModuleAliasingBuildTests.swift index 3167775f2de..87d509b963b 100644 --- a/Tests/BuildTests/ModuleAliasingBuildTests.swift +++ b/Tests/BuildTests/ModuleAliasingBuildTests.swift @@ -852,7 +852,6 @@ final class ModuleAliasingBuildTests: XCTestCase { let otherLoggingArgs = try result.target(for: "OtherLogging").swiftTarget().compileArguments() let loggingArgs = try result.target(for: "Logging").swiftTarget().compileArguments() - #if os(macOS) XCTAssertMatch( otherLoggingArgs, @@ -1031,7 +1030,8 @@ final class ModuleAliasingBuildTests: XCTestCase { "/thisPkg/Sources/Logging/file.swift", "/fooPkg/Sources/Utils/fileUtils.swift", "/fooPkg/Sources/Logging/fileLogging.m", - "/fooPkg/Sources/Logging/include/fileLogging.h" + "/fooPkg/Sources/Logging/include/fileLogging.h", + "/fooPkg/Sources/Logging/FileLogging.swift" ) let observability = ObservabilitySystem.makeForTesting() let _ = try loadModulesGraph( @@ -1040,6 +1040,8 @@ final class ModuleAliasingBuildTests: XCTestCase { Manifest.createRootManifest( displayName: "fooPkg", path: "/fooPkg", + // FIXME(ncooke3): Update with next version of SPM. + toolsVersion: .vNext, products: [ ProductDescription(name: "Utils", type: .library(.automatic), targets: ["Utils"]), ], diff --git a/Tests/CommandsTests/PackageCommandTests.swift b/Tests/CommandsTests/PackageCommandTests.swift index 0535c88fccc..2402d33a6c5 100644 --- a/Tests/CommandsTests/PackageCommandTests.swift +++ b/Tests/CommandsTests/PackageCommandTests.swift @@ -3381,4 +3381,149 @@ final class PackageCommandTests: CommandsTestCase { XCTAssertNoDiagnostics(observability.diagnostics) } } + + func testPluginsOnMixedLanguageTargets() throws { + // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). + try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") + + try testWithTemporaryDirectory { tmpPath in + // Create a sample package with a library target and a plugin. + let packageDir = tmpPath.appending(components: "MyPackage") + // FIXME(ncooke3): Update with next version of SPM. + try localFileSystem.writeFileContents(packageDir.appending("Package.swift"), string: + """ + // swift-tools-version: 999.0 + import PackageDescription + let package = Package( + name: "MyPackage", + targets: [ + .target( + name: "MyLibrary", + plugins: [ + "MyPlugin", + ] + ), + .plugin( + name: "MyPlugin", + capability: .buildTool() + ), + .plugin( + name: "CmdPlugin", + capability: .command(intent: .custom(verb: "mycmd", description: "Determine if a target has mixed language sources.")) + ) + ] + ) + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "Foo.swift"), string: + """ + // a file with a filename suffix handled by the plugin + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "include", "Bar.h"), string: + """ + // a file with a filename suffix handled by the plugin + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "Bar.m"), string: + """ + // a file with a filename suffix handled by the plugin + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "library.foo"), string: + """ + a file with a filename suffix handled by the plugin + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Sources", "MyLibrary", "library.bar"), string: + """ + a file with a filename suffix not handled by the plugin + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "MyPlugin", "plugin.swift"), string: + """ + import PackagePlugin + import Foundation + @main + struct MyBuildToolPlugin: BuildToolPlugin { + func createBuildCommands( + context: PackagePlugin.PluginContext, + target: PackagePlugin.Target + ) async throws -> [PackagePlugin.Command] { + // Expect the initial working directory for build tool plugins is the package directory. + guard FileManager.default.currentDirectoryPath == context.package.directory.string else { + throw "expected initial working directory ‘\\(FileManager.default.currentDirectoryPath)’" + } + + // Check that the package display name is what we expect. + guard context.package.displayName == "MyPackage" else { + throw "expected display name to be ‘MyPackage’ but found ‘\\(context.package.displayName)’" + } + + guard let mixedTarget = target as? MixedSourceModuleTarget else { + throw "target \\(target.name) is not a Mixed Language target" + } + + // Create and return a build command that uses all the `.baz` files in the target as inputs, so they get counted as having been handled. + let fooFiles = mixedTarget.sourceFiles.compactMap { $0.path.extension == "foo" ? $0.path : nil } + return [ .buildCommand(displayName: "A command", executable: Path("/bin/echo"), arguments: fooFiles, inputFiles: fooFiles) ] + } + } + + extension String : Error {} + """ + ) + try localFileSystem.writeFileContents(packageDir.appending(components: "Plugins", "CmdPlugin", "CmdPlugin.swift"), string: + """ + import PackagePlugin + import Foundation + + @main + struct CmdPlugin: CommandPlugin { + func performCommand(context: PackagePlugin.PluginContext, arguments: [String]) async throws { + var argExtractor = ArgumentExtractor(arguments) + let targetNames = argExtractor.extractOption(named: "target") + guard targetNames.count == 1 else { + throw "Only one target may be specified." + } + + let targets = try context.package.targets(named: targetNames) + guard targets.count == 1 else { + throw "Multiple targets named \\(targetNames[0]) exist." + } + + let target = targets[0] + + guard let rootURL = URL(string: target.directory.string) else { + throw "Could not create URL from \\(target.directory.string)" + } + let subpaths = try FileManager.default.subpathsOfDirectory(atPath: rootURL.absoluteString) + let extensionsSet = Set(subpaths.compactMap { Path($0).extension }) + + if extensionsSet.contains("swift") && extensionsSet.contains("m") { + print("This target contains mixed language sources.") + } + } + } + + extension String : Error {} + """ + ) + + // Test build plugin... + // Invoke it, and check the results. + let (stdout_build, stderr) = try SwiftPM.Build.execute(packagePath: packageDir) + XCTAssert(stdout_build.contains("Build complete!")) + + // We expect a warning about `library.bar` but not about `library.foo`. + XCTAssertMatch(stderr, .contains("found 1 file(s) which are unhandled")) + XCTAssertNoMatch(stderr, .contains("Sources/MyLibrary/library.foo")) + XCTAssertMatch(stderr, .contains("Sources/MyLibrary/library.bar")) + + // Test command plugin... + // Invoke it, and check the results. + let (stdout_cmd, _) = try SwiftPM.Package.execute(["mycmd", "--target", "MyLibrary"], packagePath: packageDir) + XCTAssertMatch(stdout_cmd, .contains("This target contains mixed language sources.")) + } + } } diff --git a/Tests/FunctionalTests/MixedTargetTests.swift b/Tests/FunctionalTests/MixedTargetTests.swift new file mode 100644 index 00000000000..99581d50a8c --- /dev/null +++ b/Tests/FunctionalTests/MixedTargetTests.swift @@ -0,0 +1,473 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import XCTest +import SPMTestSupport + +final class MixedTargetTests: XCTestCase { + // MARK: - All Platforms Tests + + // The below tests build targets with C++ interoperability mode enabled, a + // feature that requires Swift 5.9 or greater. + // FIXME(ncooke3): Update with next version of SPM. + #if swift(>=5.9) + func testMixedTargetWithCXX_InteropEnabled() throws { + try fixture(name: "MixedTargets/MixedTargetsWithCXX_InteropEnabled") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTarget"] + ) + } + } + #endif // swift(>=5.9) + + func testMixedTargetWithCXX_InteropDisabled() throws { + try fixture(name: "MixedTargets/MixedTargetsWithCXX_InteropDisabled") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTarget"] + ) + } + } + + #if os(macOS) + // MARK: - macOS Tests + // The targets tested contain Objective-C, and thus require macOS to be tested. + + func testMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "BasicMixedTarget"] + ) + } + } + +// FIXME(ncooke3): Re-enable with Swift compiler change (see proposal). +// func testMixedTargetWithUmbrellaHeader() throws { +// try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in +// XCTAssertBuilds( +// fixturePath, +// extraArgs: ["--target", "BasicMixedTargetWithUmbrellaHeader"] +// ) +// XCTAssertSwiftTest( +// fixturePath, +// extraArgs: ["--filter", "BasicMixedTargetWithUmbrellaHeaderTests"] +// ) +// } +// } + + func testMixedTargetWithResources() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithResources"] + ) + } + } + + func testMixedTargetWithCustomModuleMap() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithCustomModuleMap"] + ) + + for value in [0, 1] { + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithCustomModuleMapTests"], + Xcc: ["-DTEST_MODULE_IMPORTS=\(value)"] + ) + } + } + } + + func testMixedTargetWithInvalidCustomModuleMap() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + // An invalid module map will cause the whole package to fail to + // build. To work around this, the module map is made invalid + // during the actual test. + let moduleMapPath = fixturePath.appending( + .init("Sources/MixedTargetWithInvalidCustomModuleMap/include/module.modulemap") + ) + + // In this case, an invalid module map is one that include a + // submodule of the form `$(ModuleName).Swift`. This is invalid + // because it collides with the submodule that SwiftPM will generate. + try """ + module MixedTargetWithInvalidCustomModuleMap { + header "Foo.h" + } + + module MixedTargetWithInvalidCustomModuleMap.Swift {} + """.write(to: moduleMapPath.asURL, atomically: true, encoding: .utf8) + + let commandExecutionError = try XCTUnwrap( + XCTAssertBuildFails( + fixturePath, + extraArgs: ["--target", "MixedTargetWithInvalidCustomModuleMap"] + ) + ) + + XCTAssert( + commandExecutionError.stderr.contains( + "error: The target's module map may not contain a Swift " + + "submodule for the module MixedTargetWithInvalidCustomModuleMap." + ) + ) + } + } + + func testMixedTargetWithCustomModuleMapAndResources() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: [ + "--filter", "MixedTargetWithCustomModuleMapAndResourcesTests" +// FIXME(ncooke3): Blocked by fix for #5728. Even though #5728 regression was +// addressed in #6055, #5728 is guarded on Swift Tools Version `.vNext`– which +// is also how the mixed language support is guarded. This comment should be +// resolved once the mixed language test feature is staged for release and the +// mixed language Fixture test targets have a swift-tools-version matching the +// expected tools version that the mixed language test targets will release in. +// ], +// // Surface warning where custom umbrella header does not +// // include `resource_bundle_accessor.h` in `build` directory. +// Xswiftc: [ +// "-warnings-as-errors" + ] + ) + } + } + + func testMixedTargetWithCXX() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithCXXTests"] + ) + } + } + + func testMixedTargetWithCXXAndCustomModuleMap() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithCXXAndCustomModuleMap"] + ) + } + } + + func testMixedTargetWithCXXPublicAPI() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithCXXPublicAPI"] + ) + for value in [0, 1] { + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithCXXPublicAPITests"], + Xcc: ["-DTEST_MODULE_IMPORTS=\(value)"] + ) + } + } + } + + func testMixedTargetWithCXXPublicAPIAndCustomModuleMap() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithCXXPublicAPIAndCustomModuleMap"] + ) + for value in [0, 1] { + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithCXXPublicAPIAndCustomModuleMapTests"], + Xcc: ["-DTEST_MODULE_IMPORTS=\(value)"] + ) + } + } + } + + + func testMixedTargetWithC() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithC"] + ) + } + } + + func testMixedTargetWithNoPublicObjectiveCHeaders() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTargetWithNoPublicObjectiveCHeadersTests"] + ) + + XCTAssertBuildFails( + fixturePath, + extraArgs: ["--target", "MixedTargetWithNoPublicObjectiveCHeadersTests"], + Xcc: ["EXPECT_FAILURE"] + ) + } + } + + func testMixedTargetWithNoObjectiveCCompatibleSwiftAPI() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithNoObjectiveCCompatibleSwiftAPI"] + ) + + XCTAssertBuildFails( + fixturePath, + extraArgs: ["--target", "MixedTargetWithNoObjectiveCCompatibleSwiftAPI"], + Xcc: ["EXPECT_FAILURE"] + ) + } + } + + func testNonPublicHeadersAreVisibleFromSwiftPartOfMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuildFails( + fixturePath, + extraArgs: ["--target", "MixedTargetWithNonPublicHeaders"], + // Without selectively enabling the tests with the below macro, + // the intentional build failure will break other unit tests + // since all targets in the package are build when running + // `swift test`. + Xswiftc: ["EXPECT_FAILURE"] + ) + } + } + + func testMixedTargetWithCustomPaths() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithCustomPaths"] + ) + } + + } + + func testMixedTargetBuildsInReleaseMode() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: [ + "--target", "BasicMixedTarget", + "--configuration", "release" + ] + ) + } + } + + func testStaticallyLinkedMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--product", "StaticallyLinkedBasicMixedTarget"] + ) + } + + // Test that statically linked mixed library is successfully + // integrated into an Objective-C executable. + try fixture(name: "MixedTargets") { fixturePath in + let (stdout, _) = try executeSwiftRun( + fixturePath.appending(component: "DummyTargets"), + "ClangExecutableDependsOnStaticallyLinkedMixedTarget" + ) + // The program should print "Hello, world!" + XCTAssert(stdout.contains("Hello, world!")) + } + + // Test that statically linked mixed library is successfully + // integrated into a Swift executable. + try fixture(name: "MixedTargets") { fixturePath in + let (stdout, _) = try executeSwiftRun( + fixturePath.appending(component: "DummyTargets"), + "SwiftExecutableDependsOnStaticallyLinkedMixedTarget" + ) + // The program should print "Hello, world!" + XCTAssert(stdout.contains("Hello, world!")) + } + } + + func testDynamicallyLinkedMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--product", "DynamicallyLinkedBasicMixedTarget"] + ) + } + + // Test that dynamically linked mixed library is successfully + // integrated into an Objective-C executable. + try fixture(name: "MixedTargets") { fixturePath in + let (stdout, _) = try executeSwiftRun( + fixturePath.appending(component: "DummyTargets"), + "ClangExecutableDependsOnDynamicallyLinkedMixedTarget" + ) + // The program should print "Hello, world!" + XCTAssert(stdout.contains("Hello, world!")) + } + + // Test that dynamically linked mixed library is successfully + // integrated into a Swift executable. + try fixture(name: "MixedTargets") { fixturePath in + let (stdout, _) = try executeSwiftRun( + fixturePath.appending(component: "DummyTargets"), + "SwiftExecutableDependsOnDynamicallyLinkedMixedTarget" + ) + // The program should print "Hello, world!" + XCTAssert(stdout.contains("Hello, world!")) + } + } + + func testMixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource() throws { + // Consider a mixed target with the following structure: + // + // MixedTarget + // ├── NewCar.swift + // ├── OldCar.m + // └── include + // └── OldCar.h + // + // Within the `OldCar.m` implementation, the `OldCar.h` header should + // be able to be imported via the following import statements: + // - #import "OldCar.h" + // - #import "include/OldCar.h" + // + // This aligns with the behavior of a Clang-only target. + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: [ + "--target", + "MixedTargetsPublicHeadersAreIncludedInHeaderSearchPathsForObjcSource" + ] + ) + } + } + + func testMixedTargetWithNestedPublicHeaders() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetWithNestedPublicHeaders"] + ) + } + } + + func testMixedTargetWithNestedPublicHeadersAndCustomModuleMap() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: [ + "--target", + "MixedTargetWithNestedPublicHeadersAndCustomModuleMap" + ] + ) + } + } + + // MARK: - Testing Mixed *Test* Targets + + func testMixedTestTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + for value in [0, 1] { + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "BasicMixedTargetTests"], + Xcc: ["-DTEST_MODULE_IMPORTS=\(value)"] + ) + } + } + } + + func testTestUtilitiesCanBeSharedAcrossSwiftAndObjcTestFiles() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "MixedTestTargetWithSharedUtilitiesTests"] + ) + } + } + + func testPrivateHeadersCanBeTestedViaHeaderSearchPaths() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertSwiftTest( + fixturePath, + extraArgs: ["--filter", "PrivateHeadersCanBeTestedViaHeaderSearchPathsTests"] + ) + } + } + + // MARK: - Integrating Mixed Target with other Targets + + func testClangTargetDependsOnMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + for value in [0, 1] { + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "ClangTargetDependsOnMixedTarget"], + Xcc: ["-DTEST_MODULE_IMPORTS=\(value)"] + ) + } + } + } + + func testSwiftTargetDependsOnMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "SwiftTargetDependsOnMixedTarget"] + ) + } + } + + func testMixedTargetDependsOnOtherMixedTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + for value in [0, 1] { + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetDependsOnMixedTarget"], + Xcc: ["-DTEST_MODULE_IMPORTS=\(value)"] + ) + } + } + } + + func testMixedTargetDependsOnClangTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetDependsOnClangTarget"] + ) + } + } + + func testMixedTargetDependsOnSwiftTarget() throws { + try fixture(name: "MixedTargets/MixedTargetsWithObjC") { fixturePath in + XCTAssertBuilds( + fixturePath, + extraArgs: ["--target", "MixedTargetDependsOnSwiftTarget"] + ) + } + } + #endif // os(macOS) +} diff --git a/Tests/FunctionalTests/PluginTests.swift b/Tests/FunctionalTests/PluginTests.swift index e22eef6f0c4..3dfbd372ba2 100644 --- a/Tests/FunctionalTests/PluginTests.swift +++ b/Tests/FunctionalTests/PluginTests.swift @@ -736,7 +736,7 @@ final class PluginTests: XCTestCase { XCTAssertNoDiagnostics(observability.diagnostics) XCTAssert(packageGraph.packages.count == 1, "\(packageGraph.packages)") XCTAssert(packageGraph.rootPackages.count == 1, "\(packageGraph.rootPackages)") - let package: ResolvedPackage = try XCTUnwrap(packageGraph.rootPackages.first) + let package = try XCTUnwrap(packageGraph.rootPackages.first) // Find the regular target in our test package. let libraryTarget = try XCTUnwrap( diff --git a/Tests/PackageLoadingTests/PackageBuilderTests.swift b/Tests/PackageLoadingTests/PackageBuilderTests.swift index d42e401f7c7..8284cafeff2 100644 --- a/Tests/PackageLoadingTests/PackageBuilderTests.swift +++ b/Tests/PackageLoadingTests/PackageBuilderTests.swift @@ -40,23 +40,113 @@ final class PackageBuilderTests: XCTestCase { } } + func testMixedSourcesWhenUnsupportedToolsVersion() throws { + let foo: AbsolutePath = AbsolutePath("/Sources/foo") + + let fs = InMemoryFileSystem(emptyFiles: + foo.appending(components: "Foo.swift").pathString, + foo.appending(components: "bar.c").pathString + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + path: .root, + // Use older tools version where mixed targets are not supported. + toolsVersion: .v5, + targets: [ + try TargetDescription(name: "foo"), + ] + ) + PackageBuilderTester(manifest, in: fs) { _, diagnostics in + // FIXME(ncooke3): Update error message with support version. + diagnostics.check( + diagnostic: "target at '\(foo)' contains mixed language source files; feature not supported until tools version XX", + severity: .error + ) + } + } + func testMixedSources() throws { let foo: AbsolutePath = "/Sources/foo" let fs = InMemoryFileSystem(emptyFiles: - foo.appending(components: "main.swift").pathString, - foo.appending(components: "main.c").pathString + foo.appending(components: "Foo.swift").pathString, + foo.appending(components: "include", "Bar.h").pathString, + foo.appending(components: "Bar.m").pathString, + foo.appending(components: "include", "baz.h").pathString, + foo.appending(components: "baz.c").pathString ) let manifest = Manifest.createRootManifest( displayName: "pkg", path: .root, + // FIXME(ncooke3): Update with next version of SPM. + toolsVersion: .vNext, targets: [ try TargetDescription(name: "foo"), ] ) + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("foo") { module in + module.check(c99name: "foo", type: .library) + module.checkSources(root: foo.pathString, paths: "Foo.swift", "Bar.m", "baz.c") + module.check(includeDir: foo.appending(component: "include").pathString) + module.check(moduleMapType: .umbrellaDirectory(foo.appending(component: "include"))) + } + } + } + + func testMixedSourcesWithCustomModuleMap() throws { + let foo: AbsolutePath = AbsolutePath("/Sources/foo") + + let fs = InMemoryFileSystem(emptyFiles: + foo.appending(components: "Foo.swift").pathString, + foo.appending(components: "include", "Bar.h").pathString, + foo.appending(components: "Bar.m").pathString, + foo.appending(components: "include", "module.modulemap").pathString + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + path: .root, + // FIXME(ncooke3): Update with next version of SPM. + toolsVersion: .vNext, + targets: [ + try TargetDescription(name: "foo"), + ] + ) + PackageBuilderTester(manifest, in: fs) { package, _ in + package.checkModule("foo") { module in + module.check(c99name: "foo", type: .library) + module.checkSources(root: foo.pathString, paths: "Foo.swift", "Bar.m") + module.check(includeDir: foo.appending(component: "include").pathString) + module.check(moduleMapType: .custom(foo.appending(components: "include", "module.modulemap"))) + } + } + } + + func testMixedTargetsDoNotSupportExecutables() throws { + let foo: AbsolutePath = AbsolutePath("/Sources/foo") + + let fs = InMemoryFileSystem(emptyFiles: + foo.appending(components: "Foo.swift").pathString, + foo.appending(components: "main.c").pathString + ) + + let manifest = Manifest.createRootManifest( + displayName: "pkg", + path: .root, + // FIXME(ncooke3): Update with next version of SPM. + toolsVersion: .vNext, + targets: [ + try TargetDescription(name: "foo", type: .executable), + ] + ) PackageBuilderTester(manifest, in: fs) { _, diagnostics in - diagnostics.check(diagnostic: "target at '\(foo)' contains mixed language source files; feature not supported", severity: .error) + diagnostics.check( + diagnostic: "Target with mixed sources at '\(foo)' is a \(Target.Kind.executable) target; targets with mixed language sources are only supported for library and test targets.", + severity: .error + ) } } @@ -3222,17 +3312,23 @@ final class PackageBuilderTester { } func check(includeDir: String, file: StaticString = #file, line: UInt = #line) { - guard case let target as ClangTarget = target else { - return XCTFail("Include directory is being checked on a non clang target", file: file, line: line) + if case let target as ClangTarget = target { + XCTAssertEqual(target.includeDir.pathString, includeDir, file: file, line: line) + } else if case let target as MixedTarget = target { + XCTAssertEqual(target.clangTarget.includeDir.pathString, includeDir, file: file, line: line) + } else { + return XCTFail("Include directory is being checked on a non-clang or mixed target", file: file, line: line) } - XCTAssertEqual(target.includeDir.pathString, includeDir, file: file, line: line) } func check(moduleMapType: ModuleMapType, file: StaticString = #file, line: UInt = #line) { - guard case let target as ClangTarget = target else { - return XCTFail("Module map type is being checked on a non-Clang target", file: file, line: line) + if case let target as ClangTarget = target { + XCTAssertEqual(target.moduleMapType, moduleMapType, file: file, line: line) + } else if case let target as MixedTarget = target { + XCTAssertEqual(target.clangTarget.moduleMapType, moduleMapType, file: file, line: line) + } else { + return XCTFail("Module map type is being checked on a non-clang or mixed target", file: file, line: line) } - XCTAssertEqual(target.moduleMapType, moduleMapType, file: file, line: line) } func check(c99name: String? = nil, type: PackageModel.Target.Kind? = nil, file: StaticString = #file, line: UInt = #line) {