Merge branch 'yonaskolb:master' into master

This commit is contained in:
Thanh Vu 2023-10-31 20:53:08 +07:00 committed by GitHub
commit 92c4092596
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1668 additions and 235 deletions

View File

@ -2,13 +2,22 @@
## Next Version
## 2.38.0
### Added
- [Multi-destination targets](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md#supported-destinations) #1336 @amatig
- Added `supportedDestinations` to target
- Added optional new `platform` value of `auto` when using `supportedDestinations`
- Added `destinationFilters` for sources and dependencies
- Added `inferDestinationFiltersByPath`, a convenience filter for sources
- `.mlpackage` files now default to being a source type #1398 @aaron-foreflight
- Added support for `Build Tool Plug-ins` in `AggregateTarget` #1390 @BarredEwe
### Fixed
- Fixed source file `includes` not working when no paths were found #1337 @shnhrrsn
- Supports specifying multiple package products #1395 @simonbs
## 2.37.0
@ -359,7 +368,7 @@ Some support for Xcode Test Plans has been added. For now test plans are not gen
#### Fixed
- Fixed issue when linking and embeding static frameworks: they should be linked and NOT embed. #820 @acecilia
- Fixed issue when linking and embedding static frameworks: they should be linked and NOT embed. #820 @acecilia
- Fixed issue when generating projects for paths with a dot in the folder for swift sources. #826 @asifmohd
- Prefix static library target filenames with 'lib' to match Xcode. #831 @ileitch
- Fixed duplicate addition of carthage static frameworks. #829 @funzin

View File

@ -22,6 +22,7 @@ You can also use environment variables in your configuration file, by using `${S
- [Target](#target)
- [Product Type](#product-type)
- [Platform](#platform)
- [Supported Destinations](#supported-destinations)
- [Sources](#sources)
- [Target Source](#target-source)
- [Dependency](#dependency)
@ -69,7 +70,7 @@ You can also use environment variables in your configuration file, by using `${S
- [ ] **targets**: **[String: [Target](#target)]** - The list of targets in the project mapped by name
- [ ] **fileGroups**: **[String]** - A list of paths to add to the root of the project. These aren't files that will be included in your targets, but that you'd like to include in the project hierarchy anyway. For example a folder of xcconfig files that aren't already added by any target sources, or a Readme file.
- [ ] **schemes**: **[Scheme](#scheme)** - A list of schemes by name. This allows more control over what is found in [Target Scheme](#target-scheme)
- [ ] **schemeTemplates**: **[String: [Scheme Template](#scheme-template)]** - a list of schemes that can be used as templates for actual schems which reference them via a `template` property. They can be used to extract common scheme settings. Works great in combination with `include`.
- [ ] **schemeTemplates**: **[String: [Scheme Template](#scheme-template)]** - a list of schemes that can be used as templates for actual schemes which reference them via a `template` property. They can be used to extract common scheme settings. Works great in combination with `include`.
- [ ] **targetTemplates**: **[String: [Target Template](#target-template)]** - a list of targets that can be used as templates for actual targets which reference them via a `template` property. They can be used to extract common target settings. Works great in combination with `include`.
- [ ] **packages**: **[String: [Swift Package](#swift-package)]** - a map of Swift packages by name.
- [ ] **projectReferences**: **[String: [Project Reference](#project-reference)]** - a map of project references by name
@ -357,6 +358,7 @@ Settings are merged in the following order: `groups`, `base`, `configs` (simple
- [x] **type**: **[Product Type](#product-type)** - Product type of the target
- [x] **platform**: **[Platform](#platform)** - Platform of the target
- [ ] **supportedDestinations**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations for the target.
- [ ] **deploymentTarget**: **String** - The deployment target (eg `9.2`). If this is not specified the value from the project set in [Options](#options)`.deploymentTarget.PLATFORM` will be used.
- [ ] **sources**: **[Sources](#sources)** - Source directories of the target
- [ ] **configFiles**: **[Config Files](#config-files)** - `.xcconfig` files per config
@ -434,12 +436,15 @@ This will provide default build settings for a certain product type. It can be a
This will provide default build settings for a certain platform. It can be any of the following:
- `auto` (available only when we use `supportedDestinations`)
- `iOS`
- `macOS`
- `tvOS`
- `macOS`
- `watchOS`
- `visionOS` (`visionOS` doesn't support Carthage usage)
Note that when we use supported destinations with Xcode 14+ we can avoid the definition of platform that fallbacks to the `auto` value.
**Multi Platform targets**
You can also specify an array of platforms. This will generate a target for each platform.
@ -470,6 +475,33 @@ targets:
The above will generate 2 targets named `MyFramework_iOS` and `MyFramework_tvOS`, with all the relevant platform build settings. They will both have a `PRODUCT_NAME` of `MyFramework`
### Supported Destinations
This will provide a mix of default build settings for the chosen platform destinations. It can be any of the following:
- `iOS`
- `tvOS`
- `macOS`
- `macCatalyst`
- `visionOS`
```yaml
targets:
MyFramework:
type: framework
supportedDestinations: [iOS, tvOS]
deploymentTarget:
iOS: 9.0
tvOS: 10.0
sources:
- path: MySources
inferDestinationFiltersByPath: true
- path: OtherSources
destinationFilters: [iOS]
```
Note that the definition of supported destinations can be applied to every type of bundle making everything more easy to manage (app targets, unit tests, UI tests etc).
### Sources
Specifies the source directories for a target. This can either be a single source or a list of sources. Applicable source files, resources, headers, and `.lproj` files will be parsed appropriately.
@ -484,7 +516,9 @@ A source can be provided via a string (the path) or an object of the form:
- [ ] **compilerFlags**: **[String]** or **String** - A list of compilerFlags to add to files under this specific path provided as a list or a space delimited string. Defaults to empty.
- [ ] **excludes**: **[String]** - A list of [global patterns](https://en.wikipedia.org/wiki/Glob_(programming)) representing the files to exclude. These rules are relative to `path` and _not the directory where `project.yml` resides_. XcodeGen uses Bash 4's Glob behaviors where globstar (**) is enabled.
- [ ] **includes**: **[String]** - A list of global patterns in the same format as `excludes` representing the files to include. These rules are relative to `path` and _not the directory where `project.yml` resides_. If **excludes** is present and file conflicts with **includes**, **excludes** will override the **includes** behavior.
- [ ] **createIntermediateGroups**: **Bool** - This overrides the value in [Options](#options)
- [ ] **destinationFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations the files should filter to. Defaults to all supported destinations.
- [ ] **inferDestinationFiltersByPath**: **Bool** - This is a convenience filter that helps you to filter the files if their paths match these patterns `**/<supportedDestination>/*` or `*_<supportedDestination>.swift`. Note, if you use `destinationFilters` this flag will be ignored.
- [ ] **createIntermediateGroups**: **Bool** - This overrides the value in [Options](#options).
- [ ] **optional**: **Bool** - Disable missing path check. Defaults to false.
- [ ] **buildPhase**: **String** - This manually sets the build phase this file or files in this directory will be added to, otherwise XcodeGen will guess based on the file extension. Note that `Info.plist` files will never be added to any build phases, no matter what this setting is. Possible values are:
- `sources` - Compile Sources phase
@ -520,9 +554,11 @@ targets:
MyTarget:
sources: MyTargetSource
MyOtherTarget:
supportedDestinations: [iOS, tvOS]
sources:
- MyOtherTargetSource1
- path: MyOtherTargetSource2
inferDestinationFiltersByPath: true
name: MyNewName
excludes:
- "ios/*.[mh]"
@ -534,6 +570,7 @@ targets:
- "-Werror"
- "-Wextra"
- path: MyOtherTargetSource3
destinationFilters: [iOS]
compilerFlags: "-Werror -Wextra"
- path: ModuleMaps
buildPhase:
@ -561,10 +598,11 @@ A dependency can be one of a 6 types:
- [ ] **embed**: **Bool** - Whether to embed the dependency. Defaults to true for application target and false for non application targets.
- [ ] **link**: **Bool** - Whether to link the dependency. Defaults to `true` depending on the type of the dependency and the type of the target (e.g. static libraries will only link to executables by default).
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false
- [ ] **platformFilter**: **String** - This field is specific to Mac Catalyst. It corresponds to the "Platforms" dropdown in the Frameworks & Libraries section of Target settings in Xcode. Available options are: **iOS**, **macOS** and **all**. Defaults is **all**
- [ ] **codeSign**: **Bool** - Whether the `codeSignOnCopy` setting is applied when embedding framework. Defaults to true.
- [ ] **removeHeaders**: **Bool** - Whether the `removeHeadersOnCopy` setting is applied when embedding the framework. Defaults to true.
- [ ] **weak**: **Bool** - Whether the `Weak` setting is applied when linking the framework. Defaults to false.
- [ ] **platformFilter**: **String** - This field is specific to Mac Catalyst. It corresponds to the "Platforms" dropdown in the Frameworks & Libraries section of Target settings in Xcode. Available options are: **iOS**, **macOS** and **all**. Defaults is **all**.
- [ ] **destinationFilters**: **[[Supported Destinations](#supported-destinations)]** - List of supported platform destinations this dependency should filter to. Defaults to all supported destinations.
- [ ] **platforms**: **[[Platform](#platform)]** - List of platforms this dependency should apply to. Defaults to all applicable platforms.
- **copy** - Copy Files Phase for this dependency. This only applies when `embed` is true. Must be specified as an object with the following fields:
- [x] **destination**: **String** - Destination of the Copy Files phase. This can be one of the following values:
@ -614,13 +652,17 @@ projectReferences:
path: path/to/FooLib.xcodeproj
targets:
MyTarget:
supportedDestinations: [iOS, tvOS]
dependencies:
- target: MyFramework
destinationFilters: [iOS]
- target: FooLib/FooTarget
- framework: path/to/framework.framework
destinationFilters: [tvOS]
- carthage: Result
findFrameworks: false
linkType: static
destinationFilters: [iOS]
- sdk: Contacts.framework
- sdk: libc++.tbd
- sdk: libz.dylib
@ -646,7 +688,8 @@ targets:
```
**Package dependency**
- [ ] **product**: **String** - The product to use from the package. This defaults to the package name, so is only required if a Package has multiple libraries or a library with a differing name
- [ ] **product**: **String** - The product to use from the package. This defaults to the package name, so is only required if a Package has multiple libraries or a library with a differing name. Use this over `products` when you want to define different linking options per product.
- [ ] **products**: **String** - A list of products to use from the package. This can be used when depending on multiple products from a package.
```yaml
packages:
@ -664,6 +707,21 @@ targets:
product: SPMUtility
```
Depending on multiple products from a package:
```yaml
packages:
FooFeature:
path: Packages/FooFeature
targets:
App:
dependencies:
- package: FooFeature
products:
- FooDomain
- FooUI
```
### Config Files
Specifies `.xcconfig` files for each configuration.

View File

@ -1,6 +1,6 @@
TOOL_NAME = XcodeGen
export EXECUTABLE_NAME = xcodegen
VERSION = 2.37.0
VERSION = 2.38.0
PREFIX = /usr/local
INSTALL_PATH = $(PREFIX)/bin/$(EXECUTABLE_NAME)

View File

@ -113,7 +113,7 @@ swift run xcodegen
Add the following to your Package.swift file's dependencies:
```swift
.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.37.0"),
.package(url: "https://github.com/yonaskolb/XcodeGen.git", from: "2.38.0"),
```
And then import wherever needed: `import XcodeGenKit`

View File

@ -1,3 +1,3 @@
TARGETED_DEVICE_FAMILY: 3
LD_RUNPATH_SEARCH_PATHS: ["$(inherited)", "@executable_path/Frameworks"]
SDKROOT: appletvos
TARGETED_DEVICE_FAMILY: 3

View File

@ -0,0 +1,5 @@
SUPPORTED_PLATFORMS: iphoneos iphonesimulator
TARGETED_DEVICE_FAMILY: '1,2'
SUPPORTS_MACCATALYST: NO
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: YES
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: YES

View File

@ -0,0 +1,2 @@
SUPPORTS_MACCATALYST: YES
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO

View File

@ -0,0 +1,3 @@
SUPPORTED_PLATFORMS: macosx
SUPPORTS_MACCATALYST: NO
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO

View File

@ -0,0 +1,2 @@
SUPPORTED_PLATFORMS: appletvos appletvsimulator
TARGETED_DEVICE_FAMILY: '3'

View File

@ -0,0 +1,3 @@
SUPPORTED_PLATFORMS: xros xrsimulator
TARGETED_DEVICE_FAMILY: '7'
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO

View File

@ -8,6 +8,7 @@ public struct AggregateTarget: ProjectTarget {
public var targets: [String]
public var settings: Settings
public var buildScripts: [BuildScript]
public var buildToolPlugins: [BuildToolPlugin]
public var configFiles: [String: String]
public var scheme: TargetScheme?
public var attributes: [String: Any]
@ -18,6 +19,7 @@ public struct AggregateTarget: ProjectTarget {
settings: Settings = .empty,
configFiles: [String: String] = [:],
buildScripts: [BuildScript] = [],
buildToolPlugins: [BuildToolPlugin] = [],
scheme: TargetScheme? = nil,
attributes: [String: Any] = [:]
) {
@ -26,6 +28,7 @@ public struct AggregateTarget: ProjectTarget {
self.settings = settings
self.configFiles = configFiles
self.buildScripts = buildScripts
self.buildToolPlugins = buildToolPlugins
self.scheme = scheme
self.attributes = attributes
}
@ -46,6 +49,7 @@ extension AggregateTarget: Equatable {
lhs.settings == rhs.settings &&
lhs.configFiles == rhs.configFiles &&
lhs.buildScripts == rhs.buildScripts &&
lhs.buildToolPlugins == rhs.buildToolPlugins &&
lhs.scheme == rhs.scheme &&
NSDictionary(dictionary: lhs.attributes).isEqual(to: rhs.attributes)
}
@ -59,6 +63,7 @@ extension AggregateTarget: NamedJSONDictionaryConvertible {
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
buildScripts = jsonDictionary.json(atKeyPath: "buildScripts") ?? []
buildToolPlugins = jsonDictionary.json(atKeyPath: "buildToolPlugins") ?? []
scheme = jsonDictionary.json(atKeyPath: "scheme")
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
}
@ -72,6 +77,7 @@ extension AggregateTarget: JSONEncodable {
"configFiles": configFiles,
"attributes": attributes,
"buildScripts": buildScripts.map { $0.toJSONValue() },
"buildToolPlugins": buildToolPlugins.map { $0.toJSONValue() },
"scheme": scheme?.toJSONValue(),
] as [String: Any?]
}

View File

@ -16,6 +16,7 @@ public struct Dependency: Equatable {
public var implicit: Bool = implicitDefault
public var weakLink: Bool = weakLinkDefault
public var platformFilter: PlatformFilter = platformFilterDefault
public var destinationFilters: [SupportedDestination]?
public var platforms: Set<Platform>?
public var copyPhase: BuildPhaseSpec.CopyFilesSettings?
@ -28,6 +29,7 @@ public struct Dependency: Equatable {
implicit: Bool = implicitDefault,
weakLink: Bool = weakLinkDefault,
platformFilter: PlatformFilter = platformFilterDefault,
destinationFilters: [SupportedDestination]? = nil,
platforms: Set<Platform>? = nil,
copyPhase: BuildPhaseSpec.CopyFilesSettings? = nil
) {
@ -39,6 +41,7 @@ public struct Dependency: Equatable {
self.implicit = implicit
self.weakLink = weakLink
self.platformFilter = platformFilter
self.destinationFilters = destinationFilters
self.platforms = platforms
self.copyPhase = copyPhase
}
@ -48,7 +51,7 @@ public struct Dependency: Equatable {
case iOS
case macOS
}
public enum CarthageLinkType: String {
case dynamic
case `static`
@ -61,7 +64,7 @@ public struct Dependency: Equatable {
case framework
case carthage(findFrameworks: Bool?, linkType: CarthageLinkType)
case sdk(root: String?)
case package(product: String?)
case package(products: [String])
case bundle
}
}
@ -69,9 +72,9 @@ public struct Dependency: Equatable {
extension Dependency {
public var uniqueID: String {
switch type {
case .package(let product):
if let product = product {
return "\(reference)/\(product)"
case .package(let products):
if !products.isEmpty {
return "\(reference)/\(products.joined(separator: ","))"
} else {
return reference
}
@ -106,9 +109,16 @@ extension Dependency: JSONObjectConvertible {
type = .sdk(root: sdkRoot)
reference = sdk
} else if let package: String = jsonDictionary.json(atKeyPath: "package") {
let product: String? = jsonDictionary.json(atKeyPath: "product")
type = .package(product: product)
reference = package
if let products: [String] = jsonDictionary.json(atKeyPath: "products") {
type = .package(products: products)
reference = package
} else if let product: String = jsonDictionary.json(atKeyPath: "product") {
type = .package(products: [product])
reference = package
} else {
type = .package(products: [])
reference = package
}
} else if let bundle: String = jsonDictionary.json(atKeyPath: "bundle") {
type = .bundle
reference = bundle
@ -135,7 +145,11 @@ extension Dependency: JSONObjectConvertible {
} else {
self.platformFilter = .all
}
if let destinationFilters: [SupportedDestination] = jsonDictionary.json(atKeyPath: "destinationFilters") {
self.destinationFilters = destinationFilters
}
if let platforms: [ProjectSpec.Platform] = jsonDictionary.json(atKeyPath: "platforms") {
self.platforms = Set(platforms)
}
@ -154,6 +168,7 @@ extension Dependency: JSONEncodable {
"link": link,
"platforms": platforms?.map(\.rawValue).sorted(),
"copy": copyPhase?.toJSONValue(),
"destinationFilters": destinationFilters?.map { $0.rawValue },
]
if removeHeaders != Dependency.removeHeadersDefault {

View File

@ -26,6 +26,7 @@ public struct DeploymentTarget: Equatable {
public func version(for platform: Platform) -> Version? {
switch platform {
case .auto: return nil
case .iOS: return iOS
case .tvOS: return tvOS
case .watchOS: return watchOS
@ -39,6 +40,7 @@ extension Platform {
public var deploymentTargetSetting: String {
switch self {
case .auto: return ""
case .iOS: return "IPHONEOS_DEPLOYMENT_TARGET"
case .tvOS: return "TVOS_DEPLOYMENT_TARGET"
case .watchOS: return "WATCHOS_DEPLOYMENT_TARGET"
@ -49,6 +51,7 @@ extension Platform {
public var sdkRoot: String {
switch self {
case .auto: return "auto"
case .iOS: return "iphoneos"
case .tvOS: return "appletvos"
case .watchOS: return "watchos"

View File

@ -1,9 +1,10 @@
import Foundation
public enum Platform: String, Hashable, CaseIterable {
case auto
case iOS
case watchOS
case tvOS
case macOS
case watchOS
case visionOS
}

View File

@ -30,7 +30,7 @@ extension Plist: JSONEncodable {
[
"path": path,
"properties": properties,
]
] as [String : Any]
}
}

View File

@ -6,6 +6,7 @@ public protocol ProjectTarget: BuildSettingsContainer {
var name: String { get }
var type: PBXProductType { get }
var buildScripts: [BuildScript] { get }
var buildToolPlugins: [BuildToolPlugin] { get }
var scheme: TargetScheme? { get }
var attributes: [String: Any] { get }
}

View File

@ -113,7 +113,7 @@ extension Settings: JSONEncodable {
"base": buildSettings,
"groups": groups,
"configs": configSettings.mapValues { $0.toJSONValue() },
]
] as [String : Any]
}
return buildSettings
}

View File

@ -7,6 +7,7 @@ public enum SpecParsingError: Error, CustomStringConvertible {
case unknownPackageRequirement([String: Any])
case invalidSourceBuildPhase(String)
case invalidTargetReference(String)
case invalidTargetPlatformAsArray
case invalidVersion(String)
case unknownBreakpointType(String)
case unknownBreakpointScope(String)
@ -27,6 +28,8 @@ public enum SpecParsingError: Error, CustomStringConvertible {
return "Invalid Source Build Phase: \(error)"
case let .invalidTargetReference(targetReference):
return "Invalid Target Reference Syntax: \(targetReference)"
case .invalidTargetPlatformAsArray:
return "Invalid Target platform: Array not allowed with supported destinations"
case let .invalidVersion(version):
return "Invalid version: \(version)"
case let .unknownPackageRequirement(package):

View File

@ -144,6 +144,12 @@ extension Project {
}
errors += validateSettings(target.settings)
for buildToolPlugin in target.buildToolPlugins {
if packages[buildToolPlugin.package] == nil {
errors.append(.invalidPluginPackageReference(plugin: buildToolPlugin.plugin, package: buildToolPlugin.package))
}
}
}
for target in aggregateTargets {
@ -174,11 +180,28 @@ extension Project {
errors.append(.invalidTargetSource(target: target.name, source: sourcePath.string))
}
}
for buildToolPlugin in target.buildToolPlugins {
if packages[buildToolPlugin.package] == nil {
errors.append(.invalidPluginPackageReference(plugin: buildToolPlugin.plugin, package: buildToolPlugin.package))
}
if target.supportedDestinations != nil, target.platform == .watchOS {
errors.append(.unexpectedTargetPlatformForSupportedDestinations(target: target.name, platform: target.platform))
}
if target.supportedDestinations?.contains(.macOS) == true,
target.supportedDestinations?.contains(.macCatalyst) == true {
errors.append(.multipleMacPlatformsInSupportedDestinations(target: target.name))
}
if target.supportedDestinations?.contains(.macCatalyst) == true,
target.platform != .iOS, target.platform != .auto {
errors.append(.invalidTargetPlatformForSupportedDestinations(target: target.name))
}
if target.platform != .auto, target.platform != .watchOS,
let supportedDestination = SupportedDestination(rawValue: target.platform.rawValue),
target.supportedDestinations?.contains(supportedDestination) == false {
errors.append(.missingTargetPlatformInSupportedDestinations(target: target.name, platform: target.platform))
}
}

View File

@ -17,6 +17,10 @@ public struct SpecValidationError: Error, CustomStringConvertible {
case invalidTargetConfigFile(target: String, configFile: String, config: String)
case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType)
case invalidTargetSchemeTest(target: String, testTarget: String)
case invalidTargetPlatformForSupportedDestinations(target: String)
case unexpectedTargetPlatformForSupportedDestinations(target: String, platform: Platform)
case multipleMacPlatformsInSupportedDestinations(target: String)
case missingTargetPlatformInSupportedDestinations(target: String, platform: Platform)
case invalidSchemeTarget(scheme: String, target: String, action: String)
case invalidSchemeConfig(scheme: String, config: String)
case invalidSwiftPackage(name: String, target: String)
@ -54,6 +58,14 @@ public struct SpecValidationError: Error, CustomStringConvertible {
return "Target \(target.quoted) has an invalid scheme config variant which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)"
case let .invalidTargetSchemeTest(target, test):
return "Target \(target.quoted) scheme has invalid test \(test.quoted)"
case let .invalidTargetPlatformForSupportedDestinations(target):
return "Target \(target.quoted) has supported destinations that require a target platform iOS or auto"
case let .unexpectedTargetPlatformForSupportedDestinations(target, platform):
return "Target \(target.quoted) has platform \(platform.rawValue.quoted) that does not expect supported destinations"
case let .multipleMacPlatformsInSupportedDestinations(target):
return "Target \(target.quoted) has multiple definitions of mac platforms in supported destinations"
case let .missingTargetPlatformInSupportedDestinations(target, platform):
return "Target \(target.quoted) has platform \(platform.rawValue.quoted) that is missing in supported destinations"
case let .invalidConfigFile(configFile, config):
return "Invalid config file \(configFile.quoted) for config \(config.quoted)"
case let .invalidSchemeTarget(scheme, target, action):

View File

@ -0,0 +1,45 @@
import Foundation
public enum SupportedDestination: String, CaseIterable {
case iOS
case tvOS
case macOS
case macCatalyst
case visionOS
}
extension SupportedDestination {
public var string: String {
switch self {
case .iOS:
return "ios"
case .tvOS:
return "tvos"
case .macOS:
return "macos"
case .macCatalyst:
return "maccatalyst"
case .visionOS:
return "xros"
}
}
// This is used to:
// 1. Get the first one and apply SettingPresets 'Platforms' and 'Product_Platform' if the platform is 'auto'
// 2. Sort, loop and merge together SettingPresets 'SupportedDestinations'
public var priority: Int {
switch self {
case .iOS:
return 0
case .tvOS:
return 1
case .visionOS:
return 2
case .macOS:
return 3
case .macCatalyst:
return 4
}
}
}

View File

@ -37,6 +37,7 @@ public struct Target: ProjectTarget {
public var name: String
public var type: PBXProductType
public var platform: Platform
public var supportedDestinations: [SupportedDestination]?
public var settings: Settings
public var sources: [TargetSource]
public var dependencies: [Dependency]
@ -58,7 +59,7 @@ public struct Target: ProjectTarget {
public var productName: String
public var onlyCopyFilesOnInstall: Bool
public var putResourcesBeforeSourcesBuildPhase: Bool
public var isLegacy: Bool {
legacy != nil
}
@ -78,6 +79,7 @@ public struct Target: ProjectTarget {
name: String,
type: PBXProductType,
platform: Platform,
supportedDestinations: [SupportedDestination]? = nil,
productName: String? = nil,
deploymentTarget: Version? = nil,
settings: Settings = .empty,
@ -103,6 +105,7 @@ public struct Target: ProjectTarget {
self.name = name
self.type = type
self.platform = platform
self.supportedDestinations = supportedDestinations
self.deploymentTarget = deploymentTarget
self.productName = productName ?? name
self.settings = settings
@ -162,15 +165,17 @@ extension Target {
guard let targetsDictionary: [String: JSONDictionary] = jsonDictionary["targets"] as? [String: JSONDictionary] else {
return jsonDictionary
}
var crossPlatformTargets: [String: JSONDictionary] = [:]
for (targetName, target) in targetsDictionary {
if let platforms = target["platform"] as? [String] {
for platform in platforms {
var platformTarget = target
/// This value is set to help us to check, in Target init, that there are no conflicts in the definition of the platforms. We want to ensure that the user didn't define, at the same time,
/// the new Xcode 14 supported destinations and the XcodeGen generation of Multiple Platform Targets (when you define the platform field as an array).
platformTarget["isMultiPlatformTarget"] = true
platformTarget = platformTarget.expand(variables: ["platform": platform])
@ -202,8 +207,8 @@ extension Target {
crossPlatformTargets[targetName] = target
}
}
var merged = jsonDictionary
merged["targets"] = crossPlatformTargets
return merged
}
@ -268,19 +273,41 @@ extension Target: NamedJSONDictionaryConvertible {
let resolvedName: String = jsonDictionary.json(atKeyPath: "name") ?? name
self.name = resolvedName
productName = jsonDictionary.json(atKeyPath: "productName") ?? resolvedName
let typeString: String = try jsonDictionary.json(atKeyPath: "type")
let typeString: String = jsonDictionary.json(atKeyPath: "type") ?? ""
if let type = PBXProductType(string: typeString) {
self.type = type
} else {
throw SpecParsingError.unknownTargetType(typeString)
}
let platformString: String = try jsonDictionary.json(atKeyPath: "platform")
if let supportedDestinations: [SupportedDestination] = jsonDictionary.json(atKeyPath: "supportedDestinations") {
self.supportedDestinations = supportedDestinations
}
let isResolved = jsonDictionary.json(atKeyPath: "isMultiPlatformTarget") ?? false
if isResolved, supportedDestinations != nil {
throw SpecParsingError.invalidTargetPlatformAsArray
}
var platformString: String = jsonDictionary.json(atKeyPath: "platform") ?? ""
// platform defaults to 'auto' if it is empty and we are using supported destinations
if supportedDestinations != nil, platformString.isEmpty {
platformString = Platform.auto.rawValue
}
// we add 'iOS' in supported destinations if it contains only 'macCatalyst'
if supportedDestinations?.contains(.macCatalyst) == true,
supportedDestinations?.contains(.iOS) == false {
supportedDestinations?.append(.iOS)
}
if let platform = Platform(rawValue: platformString) {
self.platform = platform
} else {
throw SpecParsingError.unknownTargetPlatform(platformString)
}
if let string: String = jsonDictionary.json(atKeyPath: "deploymentTarget") {
deploymentTarget = try Version.parse(string)
} else if let double: Double = jsonDictionary.json(atKeyPath: "deploymentTarget") {
@ -351,6 +378,7 @@ extension Target: JSONEncodable {
var dict: [String: Any?] = [
"type": type.name,
"platform": platform.rawValue,
"supportedDestinations": supportedDestinations?.map { $0.rawValue },
"settings": settings.toJSONValue(),
"configFiles": configFiles,
"attributes": attributes,

View File

@ -23,6 +23,8 @@ public struct TargetSource: Equatable {
public var createIntermediateGroups: Bool?
public var attributes: [String]
public var resourceTags: [String]
public var inferDestinationFiltersByPath: Bool?
public var destinationFilters: [SupportedDestination]?
public enum HeaderVisibility: String {
case `public`
@ -51,7 +53,9 @@ public struct TargetSource: Equatable {
headerVisibility: HeaderVisibility? = nil,
createIntermediateGroups: Bool? = nil,
attributes: [String] = [],
resourceTags: [String] = []
resourceTags: [String] = [],
inferDestinationFiltersByPath: Bool? = nil,
destinationFilters: [SupportedDestination]? = nil
) {
self.path = (path as NSString).standardizingPath
self.name = name
@ -66,6 +70,8 @@ public struct TargetSource: Equatable {
self.createIntermediateGroups = createIntermediateGroups
self.attributes = attributes
self.resourceTags = resourceTags
self.inferDestinationFiltersByPath = inferDestinationFiltersByPath
self.destinationFilters = destinationFilters
}
}
@ -112,6 +118,12 @@ extension TargetSource: JSONObjectConvertible {
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups")
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? []
resourceTags = jsonDictionary.json(atKeyPath: "resourceTags") ?? []
inferDestinationFiltersByPath = jsonDictionary.json(atKeyPath: "inferDestinationFiltersByPath")
if let destinationFilters: [SupportedDestination] = jsonDictionary.json(atKeyPath: "destinationFilters") {
self.destinationFilters = destinationFilters
}
}
}
@ -129,6 +141,8 @@ extension TargetSource: JSONEncodable {
"createIntermediateGroups": createIntermediateGroups,
"resourceTags": resourceTags,
"path": path,
"inferDestinationFiltersByPath": inferDestinationFiltersByPath,
"destinationFilters": destinationFilters?.map { $0.rawValue },
]
if optional != TargetSource.optionalDefault {

View File

@ -25,7 +25,7 @@ extension TestPlan: JSONEncodable {
[
"path": path,
"defaultPlan": defaultPlan,
]
] as [String : Any]
}
}

View File

@ -82,6 +82,7 @@ extension Platform {
public var emoji: String {
switch self {
case .auto: return "🤖"
case .iOS: return "📱"
case .watchOS: return "⌚️"
case .tvOS: return "📺"

View File

@ -3,6 +3,6 @@ import ProjectSpec
import XcodeGenCLI
import Version
let version = Version("2.37.0")
let version = Version("2.38.0")
let cli = XcodeGenCLI(version: version)
cli.execute()

View File

@ -151,6 +151,9 @@ extension Platform {
public var carthageName: String {
switch self {
case .auto:
// This is a dummy value
return "auto"
case .iOS:
return "iOS"
case .tvOS:

View File

@ -30,6 +30,7 @@ public class PBXProjGenerator {
var generated = false
private var projects: [ProjectReference: PBXProj] = [:]
lazy private var localPackageReferences: [String] = project.packages.compactMap { $0.value.isLocal ? $0.key : nil }
public init(project: Project, projectDirectory: Path? = nil) {
self.project = project
@ -339,7 +340,7 @@ public class PBXProjGenerator {
return addObject(buildConfig)
}
let dependencies = target.targets.map { generateTargetDependency(from: target.name, to: $0, platform: nil) }
var dependencies = target.targets.map { generateTargetDependency(from: target.name, to: $0, platform: nil, platforms: nil) }
let defaultConfigurationName = project.options.defaultConfig ?? project.configs.first?.name ?? ""
let buildConfigList = addObject(XCConfigurationList(
@ -350,12 +351,15 @@ public class PBXProjGenerator {
var buildPhases: [PBXBuildPhase] = []
buildPhases += try target.buildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
let packagePluginDependencies = makePackagePluginDependency(for: target)
dependencies.append(contentsOf: packagePluginDependencies)
aggregateTarget.buildPhases = buildPhases
aggregateTarget.buildConfigurationList = buildConfigList
aggregateTarget.dependencies = dependencies
}
func generateTargetDependency(from: String, to target: String, platform: String?) -> PBXTargetDependency {
func generateTargetDependency(from: String, to target: String, platform: String?, platforms: [String]?) -> PBXTargetDependency {
guard let targetObject = targetObjects[target] ?? targetAggregateObjects[target] else {
fatalError("Target dependency not found: from ( \(from) ) to ( \(target) )")
}
@ -372,6 +376,7 @@ public class PBXProjGenerator {
let targetDependency = addObject(
PBXTargetDependency(
platformFilter: platform,
platformFilters: platforms,
target: targetObject,
targetProxy: targetProxy
)
@ -692,7 +697,6 @@ public class PBXProjGenerator {
var systemExtensions: [PBXBuildFile] = []
var appClips: [PBXBuildFile] = []
var carthageFrameworksToEmbed: [String] = []
let localPackageReferences: [String] = project.packages.compactMap { $0.value.isLocal ? $0.key : nil }
let targetDependencies = (target.transitivelyLinkDependencies ?? project.options.transitivelyLinkDependencies) ?
getAllDependenciesPlusTransitiveNeedingEmbedding(target: target) : target.dependencies
@ -724,7 +728,7 @@ public class PBXProjGenerator {
return !linkingAttributes.isEmpty ? ["ATTRIBUTES": linkingAttributes] : nil
}
func processTargetDependency(_ dependency: Dependency, dependencyTarget: Target, embedFileReference: PBXFileElement?, platform: String?) {
func processTargetDependency(_ dependency: Dependency, dependencyTarget: Target, embedFileReference: PBXFileElement?, platform: String?, platforms: [String]?) {
let dependencyLinkage = dependencyTarget.defaultLinkage
let link = dependency.link ??
((dependencyLinkage == .dynamic && target.type != .staticLibrary) ||
@ -733,6 +737,7 @@ public class PBXProjGenerator {
if link, let dependencyFile = embedFileReference {
let pbxBuildFile = PBXBuildFile(file: dependencyFile, settings: getDependencyFrameworkSettings(dependency: dependency))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
@ -749,6 +754,7 @@ public class PBXProjGenerator {
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? !dependencyTarget.type.isExecutable)
)
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -784,6 +790,7 @@ public class PBXProjGenerator {
let embed = dependency.embed ?? target.shouldEmbedDependencies
let platform = makePlatformFilter(for: dependency.platformFilter)
let platforms = makeDestinationFilters(for: dependency.destinationFilters)
switch dependency.type {
case .target:
@ -792,15 +799,15 @@ public class PBXProjGenerator {
switch dependencyTargetReference.location {
case .local:
let dependencyTargetName = dependency.reference
let targetDependency = generateTargetDependency(from: target.name, to: dependencyTargetName, platform: platform)
let targetDependency = generateTargetDependency(from: target.name, to: dependencyTargetName, platform: platform, platforms: platforms)
dependencies.append(targetDependency)
guard let dependencyTarget = project.getTarget(dependencyTargetName) else { continue }
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: targetFileReferences[dependencyTarget.name], platform: platform)
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: targetFileReferences[dependencyTarget.name], platform: platform, platforms: platforms)
case .project(let dependencyProjectName):
let dependencyTargetName = dependencyTargetReference.name
let (targetDependency, dependencyTarget, dependencyProductProxy) = try generateExternalTargetDependency(from: target.name, to: dependencyTargetName, in: dependencyProjectName, platform: target.platform)
dependencies.append(targetDependency)
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: dependencyProductProxy, platform: platform)
processTargetDependency(dependency, dependencyTarget: dependencyTarget, embedFileReference: dependencyProductProxy, platform: platform, platforms: platforms)
}
case .framework:
@ -826,6 +833,7 @@ public class PBXProjGenerator {
if dependency.link ?? (target.type != .staticLibrary) {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getDependencyFrameworkSettings(dependency: dependency))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
@ -838,6 +846,7 @@ public class PBXProjGenerator {
if embed {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -888,12 +897,14 @@ public class PBXProjGenerator {
settings: getDependencyFrameworkSettings(dependency: dependency)
)
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
if dependency.embed == true {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
@ -923,12 +934,13 @@ public class PBXProjGenerator {
if dependency.link ?? (!isStaticLibrary && !isCarthageStaticLink) {
let pbxBuildFile = PBXBuildFile(file: fileReference, settings: getDependencyFrameworkSettings(dependency: dependency))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
targetFrameworkBuildFiles.append(buildFile)
}
}
// Embedding handled by iterating over `carthageDependencies` below
case .package(let product):
case .package(let products):
let packageReference = packageReferences[dependency.reference]
// If package's reference is none and there is no specified package in localPackages,
@ -937,40 +949,51 @@ public class PBXProjGenerator {
continue
}
let productName = product ?? dependency.reference
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: productName, package: packageReference)
)
// Add package dependency if linking is true.
if dependency.link ?? true {
packageDependencies.append(packageDependency)
}
let link = dependency.link ?? (target.type != .staticLibrary)
if link {
let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency))
file.platformFilter = platform
let buildFile = addObject(file)
targetFrameworkBuildFiles.append(buildFile)
} else {
let targetDependency = addObject(
PBXTargetDependency(platformFilter: platform, product: packageDependency)
func addPackageProductDependency(named productName: String) {
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: productName, package: packageReference)
)
dependencies.append(targetDependency)
// Add package dependency if linking is true.
if dependency.link ?? true {
packageDependencies.append(packageDependency)
}
let link = dependency.link ?? (target.type != .staticLibrary)
if link {
let file = PBXBuildFile(product: packageDependency, settings: getDependencyFrameworkSettings(dependency: dependency))
file.platformFilter = platform
file.platformFilters = platforms
let buildFile = addObject(file)
targetFrameworkBuildFiles.append(buildFile)
} else {
let targetDependency = addObject(
PBXTargetDependency(platformFilter: platform, platformFilters: platforms, product: packageDependency)
)
dependencies.append(targetDependency)
}
if dependency.embed == true {
let pbxBuildFile = PBXBuildFile(product: packageDependency,
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
customCopyDependenciesReferences.append(embedFile)
} else {
copyFrameworksReferences.append(embedFile)
}
}
}
if dependency.embed == true {
let pbxBuildFile = PBXBuildFile(product: packageDependency,
settings: getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true))
pbxBuildFile.platformFilter = platform
let embedFile = addObject(pbxBuildFile)
if dependency.copyPhase != nil {
customCopyDependenciesReferences.append(embedFile)
} else {
copyFrameworksReferences.append(embedFile)
if !products.isEmpty {
for product in products {
addPackageProductDependency(named: product)
}
} else {
addPackageProductDependency(named: dependency.reference)
}
case .bundle:
// Static and dynamic libraries can't copy resources
@ -987,6 +1010,7 @@ public class PBXProjGenerator {
settings: embed ? getEmbedSettings(dependency: dependency, codeSign: dependency.codeSign ?? true) : nil
)
pbxBuildFile.platformFilter = platform
pbxBuildFile.platformFilters = platforms
let buildFile = addObject(pbxBuildFile)
copyBundlesReferences.append(buildFile)
@ -1032,22 +1056,8 @@ public class PBXProjGenerator {
carthageFrameworksToEmbed = carthageFrameworksToEmbed.uniqued()
// Adding `Build Tools Plug-ins` as a dependency to the target
for buildToolPlugin in target.buildToolPlugins {
let packageReference = packageReferences[buildToolPlugin.package]
if packageReference == nil, !localPackageReferences.contains(buildToolPlugin.package) {
continue
}
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: buildToolPlugin.plugin, package: packageReference, isPlugin: true)
)
let targetDependency = addObject(
PBXTargetDependency(product: packageDependency)
)
dependencies.append(targetDependency)
}
let packagePluginDependencies = makePackagePluginDependency(for: target)
dependencies.append(contentsOf: packagePluginDependencies)
var buildPhases: [PBXBuildPhase] = []
@ -1447,7 +1457,33 @@ public class PBXProjGenerator {
return "ios"
}
}
private func makeDestinationFilters(for filters: [SupportedDestination]?) -> [String]? {
guard let filters = filters, !filters.isEmpty else { return nil }
return filters.map { $0.string }
}
/// Make `Build Tools Plug-ins` as a dependency to the target
/// - Parameter target: ProjectTarget
/// - Returns: Elements for referencing other targets through content proxies.
func makePackagePluginDependency(for target: ProjectTarget) -> [PBXTargetDependency] {
target.buildToolPlugins.compactMap { buildToolPlugin in
let packageReference = packageReferences[buildToolPlugin.package]
if packageReference == nil, !localPackageReferences.contains(buildToolPlugin.package) {
return nil
}
let packageDependency = addObject(
XCSwiftPackageProductDependency(productName: buildToolPlugin.plugin, package: packageReference, isPlugin: true)
)
let targetDependency = addObject(
PBXTargetDependency(product: packageDependency)
)
return targetDependency
}
}
func getInfoPlists(for target: Target) -> [Config: String] {
var searchForDefaultInfoPlist: Bool = true
var defaultInfoPlist: String?
@ -1581,7 +1617,7 @@ extension Platform {
/// - returns: `true` for platforms that the app store requires simulator slices to be stripped.
public var requiresSimulatorStripping: Bool {
switch self {
case .iOS, .tvOS, .watchOS, .visionOS:
case .auto, .iOS, .tvOS, .watchOS, .visionOS:
return true
case .macOS:
return false

View File

@ -41,16 +41,63 @@ extension Project {
public func getTargetBuildSettings(target: Target, config: Config) -> BuildSettings {
var buildSettings = BuildSettings()
// list of supported destination sorted by priority
let specSupportedDestinations = target.supportedDestinations?.sorted(by: { $0.priority < $1.priority }) ?? []
if options.settingPresets.applyTarget {
buildSettings += SettingsPresetFile.platform(target.platform).getBuildSettings()
let platform: Platform
if target.platform == .auto,
let firstDestination = specSupportedDestinations.first,
let firstDestinationPlatform = Platform(rawValue: firstDestination.rawValue) {
platform = firstDestinationPlatform
} else {
platform = target.platform
}
buildSettings += SettingsPresetFile.platform(platform).getBuildSettings()
buildSettings += SettingsPresetFile.product(target.type).getBuildSettings()
buildSettings += SettingsPresetFile.productPlatform(target.type, target.platform).getBuildSettings()
buildSettings += SettingsPresetFile.productPlatform(target.type, platform).getBuildSettings()
if target.platform == .auto {
// this fix is necessary because the platform preset overrides the original value
buildSettings["SDKROOT"] = Platform.auto.rawValue
}
}
if !specSupportedDestinations.isEmpty {
var supportedPlatforms: [String] = []
var targetedDeviceFamily: [String] = []
for supportedDestination in specSupportedDestinations {
let supportedPlatformBuildSettings = SettingsPresetFile.supportedDestination(supportedDestination).getBuildSettings()
buildSettings += supportedPlatformBuildSettings
if let value = supportedPlatformBuildSettings?["SUPPORTED_PLATFORMS"] as? String {
supportedPlatforms += value.components(separatedBy: " ")
}
if let value = supportedPlatformBuildSettings?["TARGETED_DEVICE_FAMILY"] as? String {
targetedDeviceFamily += value.components(separatedBy: ",")
}
}
buildSettings["SUPPORTED_PLATFORMS"] = supportedPlatforms.joined(separator: " ")
buildSettings["TARGETED_DEVICE_FAMILY"] = targetedDeviceFamily.joined(separator: ",")
}
// apply custom platform version
if let version = target.deploymentTarget {
buildSettings[target.platform.deploymentTargetSetting] = version.deploymentTarget
if !specSupportedDestinations.isEmpty {
for supportedDestination in specSupportedDestinations {
if let platform = Platform(rawValue: supportedDestination.rawValue) {
buildSettings[platform.deploymentTargetSetting] = version.deploymentTarget
}
}
} else {
buildSettings[target.platform.deploymentTargetSetting] = version.deploymentTarget
}
}
// Prevent setting presets from overrwriting settings in target xcconfig files
@ -205,7 +252,7 @@ extension SettingsPresetFile {
guard let settingsPath = possibleSettingsPaths.first(where: { $0.exists }) else {
switch self {
case .base, .config, .platform:
case .base, .config, .platform, .supportedDestination:
print("No \"\(name)\" settings found")
case .product, .productPlatform:
break

View File

@ -5,6 +5,7 @@ import XcodeProj
public enum SettingsPresetFile {
case config(ConfigType)
case platform(Platform)
case supportedDestination(SupportedDestination)
case product(PBXProductType)
case productPlatform(PBXProductType, Platform)
case base
@ -13,6 +14,7 @@ public enum SettingsPresetFile {
switch self {
case let .config(config): return "Configs/\(config.rawValue)"
case let .platform(platform): return "Platforms/\(platform.rawValue)"
case let .supportedDestination(supportedDestination): return "SupportedDestinations/\(supportedDestination.rawValue)"
case let .product(product): return "Products/\(product.name)"
case let .productPlatform(product, platform): return "Product_Platform/\(product.name)_\(platform.rawValue)"
case .base: return "base"
@ -23,6 +25,7 @@ public enum SettingsPresetFile {
switch self {
case let .config(config): return "\(config.rawValue) config"
case let .platform(platform): return platform.rawValue
case let .supportedDestination(supportedDestination): return supportedDestination.rawValue
case let .product(product): return product.name
case let .productPlatform(product, platform): return "\(platform) \(product)"
case .base: return "base"

View File

@ -109,7 +109,23 @@ class SourceGenerator {
return nil
}
}
private func makeDestinationFilters(for path: Path, with filters: [SupportedDestination]?, or inferDestinationFiltersByPath: Bool?) -> [String]? {
if let filters = filters, !filters.isEmpty {
return filters.map { $0.string }
} else if inferDestinationFiltersByPath == true {
for supportedDestination in SupportedDestination.allCases {
let regex1 = try? NSRegularExpression(pattern: "\\/\(supportedDestination)\\/", options: .caseInsensitive)
let regex2 = try? NSRegularExpression(pattern: "\\_\(supportedDestination)\\.swift$", options: .caseInsensitive)
if regex1?.isMatch(to: path.string) == true || regex2?.isMatch(to: path.string) == true {
return [supportedDestination.string]
}
}
}
return nil
}
func generateSourceFile(targetType: PBXProductType, targetSource: TargetSource, path: Path, fileReference: PBXFileElement? = nil, buildPhases: [Path: BuildPhaseSpec]) -> SourceFile {
let fileReference = fileReference ?? fileReferencesByPath[path.string.lowercased()]!
var settings: [String: Any] = [:]
@ -174,8 +190,10 @@ class SourceGenerator {
if chosenBuildPhase == .resources && !assetTags.isEmpty {
settings["ASSET_TAGS"] = assetTags
}
let buildFile = PBXBuildFile(file: fileReference, settings: settings.isEmpty ? nil : settings)
let platforms = makeDestinationFilters(for: path, with: targetSource.destinationFilters, or: targetSource.inferDestinationFiltersByPath)
let buildFile = PBXBuildFile(file: fileReference, settings: settings.isEmpty ? nil : settings, platformFilters: platforms)
return SourceFile(
path: path,
fileReference: fileReference,

View File

@ -0,0 +1,20 @@
// swift-tools-version: 5.8
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "FooFeature",
products: [
.library(name: "FooDomain", targets: [
"FooDomain"
]),
.library(name: "FooUI", targets: [
"FooUI"
])
],
targets: [
.target(name: "FooDomain"),
.target(name: "FooUI")
]
)

View File

@ -0,0 +1 @@
public struct FooDomain {}

View File

@ -0,0 +1 @@
public struct FooUI {}

View File

@ -6,13 +6,29 @@
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
ADD3CE771A0D5E996031A193 /* AggTarget */ = {
isa = PBXAggregateTarget;
buildConfigurationList = A7ABF1B35D9170092F822790 /* Build configuration list for PBXAggregateTarget "AggTarget" */;
buildPhases = (
);
dependencies = (
D287BAAB664D1A024D9DD57E /* PBXTargetDependency */,
);
name = AggTarget;
productName = AggTarget;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
23C6626698DE560017A89F2F /* XcodeGen in Frameworks */ = {isa = PBXBuildFile; productRef = 6F7DEA2D82649EDF903FBDBD /* XcodeGen */; };
2DA7998902987953B119E4CE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26F7EFEE613987D1E1258A60 /* AppDelegate.swift */; };
36CE2E6187D9709BAD9EF807 /* FooUI in Frameworks */ = {isa = PBXBuildFile; productRef = 927CB19D94339CC9960E930C /* FooUI */; };
3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; };
4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */; };
9AD886A88D3E4A1B5E900687 /* SPMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7970A2253B14A9B27C307FAC /* SPMTests.swift */; };
9C4AD0711D706FD3ED0E436D /* StaticLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */; };
AF8E362713B9D28EA9A5C9FC /* FooDomain in Frameworks */ = {isa = PBXBuildFile; productRef = 8D2DC638BEF7FDF23907E134 /* FooDomain */; };
B89EA0F3859878A1DCF7BAFD /* SwiftRoaringDynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CE46CBA5671B951B546C8673 /* Codability in Frameworks */ = {isa = PBXBuildFile; productRef = 16E6FE01D5BD99F78D4A17E2 /* Codability */; settings = {ATTRIBUTES = (Weak, ); }; };
E368431019ABC696E4FFC0CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */; };
@ -57,6 +73,7 @@
4E22B8BCC18A29EFE1DE3BE4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
61C17B77601A9D1B7895AB42 /* StaticLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLibrary.swift; sourceTree = "<group>"; };
7970A2253B14A9B27C307FAC /* SPMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPMTests.swift; sourceTree = "<group>"; };
979AE1767E2AF6B3B9D7F13D /* FooFeature */ = {isa = PBXFileReference; lastKnownFileType = folder; name = FooFeature; path = FooFeature; sourceTree = SOURCE_ROOT; };
A9601593D0AD02931266A4E5 /* App.xctestplan */ = {isa = PBXFileReference; path = App.xctestplan; sourceTree = "<group>"; };
CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = archive.ar; path = libStaticLibrary.a; sourceTree = BUILT_PRODUCTS_DIR; };
ED284AB7C13DCC0A95DAA680 /* XcodeGen */ = {isa = PBXFileReference; lastKnownFileType = folder; name = XcodeGen; path = ../../..; sourceTree = SOURCE_ROOT; };
@ -71,6 +88,8 @@
3986ED6965842721C46C0452 /* SwiftRoaringDynamic in Frameworks */,
4CC663B42B270404EF5FD037 /* libStaticLibrary.a in Frameworks */,
23C6626698DE560017A89F2F /* XcodeGen in Frameworks */,
AF8E362713B9D28EA9A5C9FC /* FooDomain in Frameworks */,
36CE2E6187D9709BAD9EF807 /* FooUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -100,6 +119,7 @@
218F6C96DF9E182F526258CF = {
isa = PBXGroup;
children = (
AD0F3623091EEA8D1EA3DFF8 /* Packages */,
17DD374CC81D710476AFF41C /* SPM */,
CF3BD77AEAA56553289456BA /* SPMTests */,
1FA59BFD192FB5A68D5F587C /* StaticLibrary */,
@ -117,6 +137,14 @@
name = Products;
sourceTree = "<group>";
};
AD0F3623091EEA8D1EA3DFF8 /* Packages */ = {
isa = PBXGroup;
children = (
979AE1767E2AF6B3B9D7F13D /* FooFeature */,
);
name = Packages;
sourceTree = SOURCE_ROOT;
};
CF3BD77AEAA56553289456BA /* SPMTests */ = {
isa = PBXGroup;
children = (
@ -157,12 +185,16 @@
D85FFB99444DD260A72DDDA7 /* PBXTargetDependency */,
DDD17C561AD5065DF4FA4072 /* PBXTargetDependency */,
C6360997FFC102F6725099D4 /* PBXTargetDependency */,
00B467060F3DEC027711F9C2 /* PBXTargetDependency */,
7EB17E90A4D8F26FEABEEDF6 /* PBXTargetDependency */,
);
name = StaticLibrary;
packageProductDependencies = (
AF233B61592982A7F6431FC6 /* Codability */,
C816AEB28ED71C3C47F31B98 /* SwiftRoaringDynamic */,
5A36E2FE69703FCAC0BE8064 /* XcodeGen */,
6B8A6E1EA485E607A1D1DCD1 /* FooDomain */,
15DB49096E2978F6BCA8D604 /* FooUI */,
);
productName = StaticLibrary;
productReference = CAB5625F6FEA668410ED5482 /* libStaticLibrary.a */;
@ -188,6 +220,8 @@
16E6FE01D5BD99F78D4A17E2 /* Codability */,
DC73B269C8B0C0BF6912842C /* SwiftRoaringDynamic */,
6F7DEA2D82649EDF903FBDBD /* XcodeGen */,
8D2DC638BEF7FDF23907E134 /* FooDomain */,
927CB19D94339CC9960E930C /* FooUI */,
);
productName = App;
productReference = 097F2DB5622B591E21BC3C73 /* App.app */;
@ -221,6 +255,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
ADD3CE771A0D5E996031A193 /* AggTarget */,
C99E3C420D63D5219CE57E33 /* App */,
3F8D94C4EFC431F646AAFB28 /* StaticLibrary */,
339863E54E2D955C00B56802 /* Tests */,
@ -286,11 +321,19 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
00B467060F3DEC027711F9C2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 6B8A6E1EA485E607A1D1DCD1 /* FooDomain */;
};
078202CF7B08B66ACF7FEC23 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 3F8D94C4EFC431F646AAFB28 /* StaticLibrary */;
targetProxy = 29147E1DDAEB1AAC20CB0CF9 /* PBXContainerItemProxy */;
};
7EB17E90A4D8F26FEABEEDF6 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 15DB49096E2978F6BCA8D604 /* FooUI */;
};
8693351DA9DBE579AC9DD513 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C99E3C420D63D5219CE57E33 /* App */;
@ -300,6 +343,10 @@
isa = PBXTargetDependency;
productRef = 5A36E2FE69703FCAC0BE8064 /* XcodeGen */;
};
D287BAAB664D1A024D9DD57E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = 896D1E2063A93D40F04D7864 /* PrefirePlaybookPlugin */;
};
D85FFB99444DD260A72DDDA7 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
productRef = AF233B61592982A7F6431FC6 /* Codability */;
@ -315,6 +362,12 @@
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
0C023F1AE037C42683029CE9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
0CCC06807E5CD8361D899B7F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -360,6 +413,12 @@
};
name = Debug;
};
5E087A904FBC0E623E672507 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Release;
};
7A384B9B9CF42FCF9EF02057 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -565,6 +624,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
A7ABF1B35D9170092F822790 /* Build configuration list for PBXAggregateTarget "AggTarget" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0C023F1AE037C42683029CE9 /* Debug */,
5E087A904FBC0E623E672507 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
@ -595,6 +663,10 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
15DB49096E2978F6BCA8D604 /* FooUI */ = {
isa = XCSwiftPackageProductDependency;
productName = FooUI;
};
16E6FE01D5BD99F78D4A17E2 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;
@ -604,10 +676,27 @@
isa = XCSwiftPackageProductDependency;
productName = XcodeGen;
};
6B8A6E1EA485E607A1D1DCD1 /* FooDomain */ = {
isa = XCSwiftPackageProductDependency;
productName = FooDomain;
};
6F7DEA2D82649EDF903FBDBD /* XcodeGen */ = {
isa = XCSwiftPackageProductDependency;
productName = XcodeGen;
};
896D1E2063A93D40F04D7864 /* PrefirePlaybookPlugin */ = {
isa = XCSwiftPackageProductDependency;
package = 348C81C327DB1710B742C370 /* XCRemoteSwiftPackageReference "Prefire" */;
productName = "plugin:PrefirePlaybookPlugin";
};
8D2DC638BEF7FDF23907E134 /* FooDomain */ = {
isa = XCSwiftPackageProductDependency;
productName = FooDomain;
};
927CB19D94339CC9960E930C /* FooUI */ = {
isa = XCSwiftPackageProductDependency;
productName = FooUI;
};
AF233B61592982A7F6431FC6 /* Codability */ = {
isa = XCSwiftPackageProductDependency;
package = 5BA91390AE78D2EE15C60091 /* XCRemoteSwiftPackageReference "Codability" */;

View File

@ -1,106 +1,122 @@
{
"object": {
"pins": [
{
"package": "AEXML",
"repositoryURL": "https://github.com/tadija/AEXML",
"state": {
"branch": null,
"revision": "e4d517844dd03dac557e35d77a8e9ab438de91a6",
"version": "4.4.0"
}
},
{
"package": "Codability",
"repositoryURL": "https://github.com/yonaskolb/Codability",
"state": {
"branch": null,
"revision": "eb5bac78e0679f521c3f058c1eb9be0dd657dadd",
"version": "0.2.1"
}
},
{
"package": "JSONUtilities",
"repositoryURL": "https://github.com/yonaskolb/JSONUtilities.git",
"state": {
"branch": null,
"revision": "128d2ffc22467f69569ef8ff971683e2393191a0",
"version": "4.2.0"
}
},
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit.git",
"state": {
"branch": null,
"revision": "73f8e9dca9b7a3078cb79128217dc8f2e585a511",
"version": "1.0.0"
}
},
{
"package": "Rainbow",
"repositoryURL": "https://github.com/onevcat/Rainbow.git",
"state": {
"branch": null,
"revision": "9c52c1952e9b2305d4507cf473392ac2d7c9b155",
"version": "3.1.5"
}
},
{
"package": "Spectre",
"repositoryURL": "https://github.com/kylef/Spectre.git",
"state": {
"branch": null,
"revision": "f14ff47f45642aa5703900980b014c2e9394b6e5",
"version": "0.9.0"
}
},
{
"package": "SwiftCLI",
"repositoryURL": "https://github.com/jakeheis/SwiftCLI.git",
"state": {
"branch": null,
"revision": "c72c4564f8c0a24700a59824880536aca45a4cae",
"version": "6.0.1"
}
},
{
"package": "SwiftRoaring",
"repositoryURL": "https://github.com/piotte13/SwiftRoaring",
"state": {
"branch": null,
"revision": "9104cf3f35e7a38c9fb633084c7adb63a9f27f8b",
"version": "1.0.4"
}
},
{
"package": "Version",
"repositoryURL": "https://github.com/mxcl/Version",
"state": {
"branch": null,
"revision": "a94b48f36763c05629fc102837398505032dead9",
"version": "2.0.0"
}
},
{
"package": "XcodeProj",
"repositoryURL": "https://github.com/tuist/XcodeProj.git",
"state": {
"branch": null,
"revision": "f32704e01d2752bdc17cde3963bee4256c1f4df4",
"version": "7.8.0"
}
},
{
"package": "Yams",
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "c947a306d2e80ecb2c0859047b35c73b8e1ca27f",
"version": "2.0.0"
}
"pins" : [
{
"identity" : "aexml",
"kind" : "remoteSourceControl",
"location" : "https://github.com/tadija/AEXML.git",
"state" : {
"revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3",
"version" : "4.6.1"
}
]
},
"version": 1
},
{
"identity" : "codability",
"kind" : "remoteSourceControl",
"location" : "https://github.com/yonaskolb/Codability",
"state" : {
"revision" : "eb5bac78e0679f521c3f058c1eb9be0dd657dadd",
"version" : "0.2.1"
}
},
{
"identity" : "graphviz",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SwiftDocOrg/GraphViz.git",
"state" : {
"revision" : "70bebcf4597b9ce33e19816d6bbd4ba9b7bdf038",
"version" : "0.2.0"
}
},
{
"identity" : "jsonutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/yonaskolb/JSONUtilities.git",
"state" : {
"revision" : "128d2ffc22467f69569ef8ff971683e2393191a0",
"version" : "4.2.0"
}
},
{
"identity" : "pathkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/PathKit.git",
"state" : {
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
"version" : "1.0.1"
}
},
{
"identity" : "prefire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/BarredEwe/Prefire",
"state" : {
"revision" : "abb8dfa44391b4f47edb4937a4ba124e76270a87",
"version" : "1.4.1"
}
},
{
"identity" : "rainbow",
"kind" : "remoteSourceControl",
"location" : "https://github.com/onevcat/Rainbow.git",
"state" : {
"revision" : "626c3d4b6b55354b4af3aa309f998fae9b31a3d9",
"version" : "3.2.0"
}
},
{
"identity" : "spectre",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/Spectre.git",
"state" : {
"revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
"version" : "0.10.1"
}
},
{
"identity" : "swiftcli",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jakeheis/SwiftCLI.git",
"state" : {
"revision" : "2e949055d9797c1a6bddcda0e58dada16cc8e970",
"version" : "6.0.3"
}
},
{
"identity" : "swiftroaring",
"kind" : "remoteSourceControl",
"location" : "https://github.com/piotte13/SwiftRoaring",
"state" : {
"revision" : "9104cf3f35e7a38c9fb633084c7adb63a9f27f8b",
"version" : "1.0.4"
}
},
{
"identity" : "version",
"kind" : "remoteSourceControl",
"location" : "https://github.com/mxcl/Version",
"state" : {
"revision" : "1fe824b80d89201652e7eca7c9252269a1d85e25",
"version" : "2.0.1"
}
},
{
"identity" : "xcodeproj",
"kind" : "remoteSourceControl",
"location" : "https://github.com/tuist/XcodeProj.git",
"state" : {
"revision" : "6e60fb55271c80f83a186c9b1b4982fd991cfc0a",
"version" : "8.13.0"
}
},
{
"identity" : "yams",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git",
"state" : {
"revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3",
"version" : "5.0.6"
}
}
],
"version" : 2
}

View File

@ -12,6 +12,13 @@ packages:
XcodeGen:
path: ../../.. #XcodeGen itself
group: SPM
FooFeature:
path: FooFeature
aggregateTargets:
AggTarget:
buildToolPlugins:
- plugin: PrefirePlaybookPlugin
package: Prefire
targets:
App:
type: application
@ -32,6 +39,10 @@ targets:
embed: true
- target: StaticLibrary
- package: XcodeGen
- package: FooFeature
products:
- FooDomain
- FooUI
Tests:
type: bundle.unit-test
platform: iOS
@ -47,3 +58,7 @@ targets:
- package: SwiftRoaring
product: SwiftRoaringDynamic
- package: XcodeGen
- package: FooFeature
products:
- FooDomain
- FooUI

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>TestApp</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
</dict>
</plist>

View File

@ -0,0 +1,10 @@
import SwiftUI
@main
struct MyAppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world! on iOS")
}
.padding()
}
}

View File

@ -0,0 +1,13 @@
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "house")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world! tvOS")
}
.padding()
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -0,0 +1 @@
import Foundation

View File

@ -41,6 +41,7 @@
0F99AECCB4691803C791CDCE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2FC2A8A829CE71B1CF415FF7 /* Main.storyboard */; };
15129B8D9ED000BDA1FEEC27 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A2F16890ECF2EE3FED72AE /* AppDelegate.swift */; };
1551370B0ACAC632E15C853B /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF47010E7368583405AA50CB /* SwiftyJSON.framework */; };
1B485D6584C3B47AC58831C6 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18722C61B05FFF4CC63D5755 /* ContentView.swift */; platformFilters = (tvos, ); };
1BC891D89980D82738D963F3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74FBDFA5CB063F6001AD8ACD /* Main.storyboard */; };
1E03FC7312293997599C6435 /* Empty.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 068EDF47F0B087F6A4052AC0 /* Empty.h */; };
1E2A4D61E96521FF7123D7B0 /* XPC Service.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 22237B8EBD9E6BE8EBC8735F /* XPC Service.xpc */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -56,6 +57,7 @@
21CA04F29CD0DEB0DD27B808 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C5AC2545AE4D4F7F44E2E9B /* Result.framework */; };
262891CCD5F74316610437FA /* Framework2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EF21DF245F66BEF5446AAEF /* Framework2.framework */; platformFilter = ios; settings = {ATTRIBUTES = (Weak, ); }; };
265B6A05C0198FD2EB485173 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93C033648A37D95027845BD3 /* main.swift */; };
2698ED273D0A5820B28CAD20 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D52EC9AA9FFD3B690C355068 /* LaunchScreen.storyboard */; platformFilters = (ios, ); };
2730C6D0A35AED4ADD6EDF17 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0704B6CAFBB53E0EBB08F6B3 /* ViewController.swift */; settings = {COMPILER_FLAGS = "-Werror"; }; };
28A96EBC76D53817AABDA91C /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8AF20308873AEEEC4D8C45D1 /* Settings.bundle */; };
2A5356FCC03EE312F1738C61 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B82F603D981398F38D762E /* AppDelegate.swift */; };
@ -70,6 +72,7 @@
339578307B9266AB3D7722D9 /* File2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC56891DA7446EAC8C2F27EB /* File2.swift */; };
3535891EC86283BB5064E7E1 /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = 2E1E747C7BC434ADB80CC269 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; };
3788E1382B38DF4ACE3D2BB1 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A58A16491CDDF968B0D56DE /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
3A3BA9F91994D8B472C71F04 /* Swinject in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = C7F9B7EDE85527EFEA85D46D /* Swinject */; };
3BBCA6F76E5F212E9C55FB78 /* BundleX.bundle in Copy Bundle Resources */ = {isa = PBXBuildFile; fileRef = 45C12576F5AA694DD0CE2132 /* BundleX.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3C5134EE524310ACF7B7CD6E /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAF6C55B555E3E1352645B6 /* ExtensionDelegate.swift */; };
3DF22C477446669094AC7C8C /* ExternalTarget.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F6ADE654A3459AFDA2CC0CD3 /* ExternalTarget.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -148,6 +151,7 @@
B18C121B0A4D43ED8149D8E2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 79325B44B19B83EC6CEDBCC5 /* LaunchScreen.storyboard */; };
B20617116B230DED1F7AF5E5 /* libStaticLibrary_ObjC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B221F5A689AD7D3AD52F56B8 /* libStaticLibrary_ObjC.a */; };
B2D43A31C184E34EF9CB743C /* Framework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8A9274BE42A03DC5DA1FAD04 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B358AB913543E62237FCC4E3 /* MyAppApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A4363D659A58DA835DE8BA /* MyAppApp.swift */; };
B47F2629BFE5853767C8BB5E /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FDB2B6A77D39CD5602F2125F /* Contacts.framework */; };
B49D3A51787E362DE4D0E78A /* SomeXPCService.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = 70A8E15C81E454DC950C59F0 /* SomeXPCService.xpc */; };
B502EF8F7605CBD038298F23 /* CrossOverlayFramework.swiftcrossimport in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8DD7A61B07AD2F91BDECC255 /* CrossOverlayFramework.swiftcrossimport */; };
@ -163,6 +167,7 @@
C836F09B677937EFF69B1FCE /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C934C1F7A68CCD0AB6B38478 /* NotificationController.swift */; };
C88598A49087A212990F4E8B /* ResourceFolder in Resources */ = {isa = PBXBuildFile; fileRef = 6B1603BA83AA0C7B94E45168 /* ResourceFolder */; };
CAE18A2194B57C830A297F83 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6680EFE4E908CDBDCE405C8 /* main.swift */; };
CAF8470C7F1BF207DBE6AEE3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEEFDE76B5FEC833403C0869 /* ContentView.swift */; platformFilters = (ios, ); };
CCA17097382757012B58C17C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1BC32A813B80A53962A1F365 /* Assets.xcassets */; };
D058D241BDF5FB0C919BBECA /* CrossOverlayFramework.swiftcrossimport in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8DD7A61B07AD2F91BDECC255 /* CrossOverlayFramework.swiftcrossimport */; };
D5458D67C3596943114C3205 /* Standalone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F5BD97AF0F94A15A5B7DDB7 /* Standalone.swift */; };
@ -738,8 +743,10 @@
108BB29172D27BE3BD1E7F35 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
13EEAB58665D79C15184D9D0 /* App_iOS_UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = App_iOS_UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
148B7C933698BCC4F1DBA979 /* XPC_Service.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XPC_Service.m; sourceTree = "<group>"; };
15A4363D659A58DA835DE8BA /* MyAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppApp.swift; sourceTree = "<group>"; };
16AA52945B70B1BF9E246350 /* FilterDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterDataProvider.swift; sourceTree = "<group>"; };
16D662EE577E4CD6AFF39D66 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = config.xcconfig; sourceTree = "<group>"; };
18722C61B05FFF4CC63D5755 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
187E665975BB5611AF0F27E1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
1BC32A813B80A53962A1F365 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1D0C79A8C750EC0DE748C463 /* StaticLibrary_ObjC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StaticLibrary_ObjC.m; sourceTree = "<group>"; };
@ -766,6 +773,7 @@
3ED831531AA349CCC19B258B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3EF21DF245F66BEF5446AAEF /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3FC04772130400920D68A167 /* App_Clip_Tests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = App_Clip_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
407C3F0009FDCE5B1B7DC2A8 /* App_supportedDestinations.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = App_supportedDestinations.app; sourceTree = BUILT_PRODUCTS_DIR; };
40863AE6202CFCD0529D8438 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
41FC82ED1C4C3B7B3D7B2FB7 /* Framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
432E2C071A4B6B3757BEA13E /* Driver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Driver.cpp; sourceTree = "<group>"; };
@ -830,6 +838,7 @@
AAA49985DFFE797EE8416887 /* inputList.xcfilelist */ = {isa = PBXFileReference; lastKnownFileType = text.xcfilelist; path = inputList.xcfilelist; sourceTree = "<group>"; };
AB055761199DF36DB0C629A6 /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AEBCA8CFF769189C0D52031E /* App_iOS.xctestplan */ = {isa = PBXFileReference; path = App_iOS.xctestplan; sourceTree = "<group>"; };
AEEFDE76B5FEC833403C0869 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
B17B8D9C9B391332CD176A35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LocalizedStoryboard.storyboard; sourceTree = "<group>"; };
B198242976C3395E31FE000A /* MessagesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = "<group>"; };
B1C33BB070583BE3B0EC0E68 /* App_iOS.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = App_iOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -855,6 +864,7 @@
D21BB1B6FA5A025305B223BA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
D296BB7355994040E197A1EE /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
D51CC8BCCBD68A90E90A3207 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D52EC9AA9FFD3B690C355068 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
D629E142AB87C681D4EC90F7 /* iMessageExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = iMessageExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
D6C89D80B5458D8929F5C127 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
D70BE0C05E5779A077793BE6 /* Model 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 2.xcdatamodel"; sourceTree = "<group>"; };
@ -909,6 +919,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
36418B6CABA06BA9B206556E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3A3BA9F91994D8B472C71F04 /* Swinject in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5EFF61D0A49AA8EABD72DF44 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -1102,6 +1120,7 @@
0D039F2E62354C7C8E283BE6 /* App_iOS_UITests */,
EE78B4FBD0137D1975C47D76 /* App_macOS */,
6DE1C805DC13547F27FD86C6 /* App_macOS_Tests */,
D27117FBA2920408002F0B4C /* App_supportedDestinations */,
BAE6C12745737019DC9E98BF /* App_watchOS */,
795B8D70B674C850B57DD39D /* App_watchOS Extension */,
6DBE0EE90642BB3F6E58AD43 /* Configs */,
@ -1194,6 +1213,23 @@
path = UnderFileGroup;
sourceTree = "<group>";
};
69C61547C081D04364A5DE42 /* Storyboards */ = {
isa = PBXGroup;
children = (
D52EC9AA9FFD3B690C355068 /* LaunchScreen.storyboard */,
);
name = Storyboards;
path = Storyboards;
sourceTree = "<group>";
};
6A90AFD865B13D26DA108CAB /* tvOS */ = {
isa = PBXGroup;
children = (
18722C61B05FFF4CC63D5755 /* ContentView.swift */,
);
path = tvOS;
sourceTree = "<group>";
};
6BD8F0932CCAD4BBE752866B /* App_Clip_UITests */ = {
isa = PBXGroup;
children = (
@ -1266,6 +1302,17 @@
path = "XPC Service";
sourceTree = "<group>";
};
85FEBB7D2103B020423407A2 /* Sources */ = {
isa = PBXGroup;
children = (
9E5249A284275B0CDF4E5DDA /* iOS */,
6A90AFD865B13D26DA108CAB /* tvOS */,
15A4363D659A58DA835DE8BA /* MyAppApp.swift */,
);
name = Sources;
path = Sources;
sourceTree = "<group>";
};
8CFD8AD4820FAB9265663F92 /* Tool */ = {
isa = PBXGroup;
children = (
@ -1301,6 +1348,14 @@
path = StandaloneFiles;
sourceTree = "<group>";
};
9E5249A284275B0CDF4E5DDA /* iOS */ = {
isa = PBXGroup;
children = (
AEEFDE76B5FEC833403C0869 /* ContentView.swift */,
);
path = iOS;
sourceTree = "<group>";
};
9EDF27BB8A57733E6639D36D /* Resources */ = {
isa = PBXGroup;
children = (
@ -1343,6 +1398,7 @@
B1C33BB070583BE3B0EC0E68 /* App_iOS.app */,
0B9D98D935F2C69A1F5BA539 /* App_macOS_Tests.xctest */,
33F6DCDC37D2E66543D4965D /* App_macOS.app */,
407C3F0009FDCE5B1B7DC2A8 /* App_supportedDestinations.app */,
0D09D243DBCF9D32E239F1E8 /* App_watchOS Extension.appex */,
A680BE9F68A255B0FB291AE6 /* App_watchOS.app */,
84317819C92F78425870E483 /* BundleX.bundle */,
@ -1432,6 +1488,15 @@
path = StaticLibrary_Swift;
sourceTree = "<group>";
};
D27117FBA2920408002F0B4C /* App_supportedDestinations */ = {
isa = PBXGroup;
children = (
85FEBB7D2103B020423407A2 /* Sources */,
69C61547C081D04364A5DE42 /* Storyboards */,
);
path = App_supportedDestinations;
sourceTree = "<group>";
};
D557819B1EE5B42A0A3DD4D1 /* tvOS */ = {
isa = PBXGroup;
children = (
@ -2205,6 +2270,26 @@
productReference = 7D700FA699849D2F95216883 /* EntitledApp.app */;
productType = "com.apple.product-type.application";
};
C0570E2FB50D830D8D423396 /* App_supportedDestinations */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5EC789CCE1928A4CDA00DD1E /* Build configuration list for PBXNativeTarget "App_supportedDestinations" */;
buildPhases = (
CF8BAE171BAAFB2E5DDB9C19 /* Sources */,
97119CD9F01D9A9522EF3526 /* Resources */,
36418B6CABA06BA9B206556E /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = App_supportedDestinations;
packageProductDependencies = (
C7F9B7EDE85527EFEA85D46D /* Swinject */,
);
productName = App_supportedDestinations;
productReference = 407C3F0009FDCE5B1B7DC2A8 /* App_supportedDestinations.app */;
productType = "com.apple.product-type.application";
};
C7F90FD0FAAF232B3E015D38 /* CrossOverlayFramework_watchOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = C483BD5456B09C276DE6EFC1 /* Build configuration list for PBXNativeTarget "CrossOverlayFramework_watchOS" */;
@ -2415,6 +2500,7 @@
F674B2CFC4738EEC49BAD0DA /* App_iOS_UITests */,
020A320BB3736FCDE6CC4E70 /* App_macOS */,
71E2BDAC4B8E8FC2BBF75C55 /* App_macOS_Tests */,
C0570E2FB50D830D8D423396 /* App_supportedDestinations */,
208179651927D1138D19B5AD /* App_watchOS */,
307AE3FA155FFD09B74AE351 /* App_watchOS Extension */,
DA40AB367B606CCE2FDD398D /* BundleX */,
@ -2520,6 +2606,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
97119CD9F01D9A9522EF3526 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2698ED273D0A5820B28CAD20 /* LaunchScreen.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AA12B5909FEE45016F469C78 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -3000,6 +3094,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
CF8BAE171BAAFB2E5DDB9C19 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CAF8470C7F1BF207DBE6AEE3 /* ContentView.swift in Sources */,
1B485D6584C3B47AC58831C6 /* ContentView.swift in Sources */,
B358AB913543E62237FCC4E3 /* MyAppApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D1F422E9C4DD531AA88418C9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -4108,6 +4212,26 @@
};
name = "Staging Debug";
};
24CFBB3ABB9E14E85035B892 /* Production Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Production Debug";
};
2569D399CA3C4828EF87AD78 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -4815,6 +4939,26 @@
};
name = "Production Release";
};
4C11A3E7EA48B5FB556D4614 /* Staging Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Staging Debug";
};
4D5DC2028DC046B8AF0B9B83 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5558,6 +5702,26 @@
};
name = "Staging Debug";
};
75B3C83C754AA9C12ABF5E54 /* Staging Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Staging Release";
};
77B8B41EBA5D778EB3AF89DC /* Production Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5757,6 +5921,26 @@
};
name = "Production Release";
};
7F3F6A813F8B126258780E8D /* Production Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Production Release";
};
7F86E00770E76CA3412A03BD /* Staging Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -6288,6 +6472,26 @@
};
name = "Staging Debug";
};
98EE00BF46FAE62F16C25E9C /* Test Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Test Release";
};
9A891313A139893990989BDD /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -6469,6 +6673,26 @@
};
name = "Production Debug";
};
A4015127D0A01FE60CF5621B /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
INFOPLIST_FILE = App_supportedDestinations/Info.generated.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.test.TestApp;
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = YES;
TARGETED_DEVICE_FAMILY = "1,2,3";
};
name = "Test Debug";
};
A59DDFBFCF18C44A993CFB00 /* Test Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -8721,6 +8945,19 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Production Debug";
};
5EC789CCE1928A4CDA00DD1E /* Build configuration list for PBXNativeTarget "App_supportedDestinations" */ = {
isa = XCConfigurationList;
buildConfigurations = (
24CFBB3ABB9E14E85035B892 /* Production Debug */,
7F3F6A813F8B126258780E8D /* Production Release */,
4C11A3E7EA48B5FB556D4614 /* Staging Debug */,
75B3C83C754AA9C12ABF5E54 /* Staging Release */,
A4015127D0A01FE60CF5621B /* Test Debug */,
98EE00BF46FAE62F16C25E9C /* Test Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = "Production Debug";
};
62C52A55CB8D3BD9A055FD14 /* Build configuration list for PBXNativeTarget "App_macOS_Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -9125,6 +9362,11 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C7F9B7EDE85527EFEA85D46D /* Swinject */ = {
isa = XCSwiftPackageProductDependency;
package = 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */;
productName = Swinject;
};
D7917D10F77DA9D69937D493 /* Swinject */ = {
isa = XCSwiftPackageProductDependency;
package = 4EDA79334592CBBA0E507AD2 /* XCRemoteSwiftPackageReference "Swinject" */;

View File

@ -239,6 +239,29 @@ targets:
properties:
com.apple.security.application-groups: group.com.app
App_supportedDestinations:
type: application
supportedDestinations: [iOS, tvOS]
info:
path: App_supportedDestinations/Info.generated.plist
properties:
CFBundleDisplayName: "TestApp"
CFBundleVersion: "1.0.0"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.test.TestApp
sources:
- path: App_supportedDestinations/Sources
group: App_supportedDestinations
inferDestinationFiltersByPath: true
- path: App_supportedDestinations/Storyboards
group: App_supportedDestinations
destinationFilters: [iOS]
dependencies:
- package: Swinject
product: Swinject
destinationFilters: [tvOS]
App_watchOS:
type: application.watchapp2
platform: watchOS
@ -489,7 +512,7 @@ schemes:
enableGPUFrameCaptureMode: "disabled"
enableGPUValidationMode: "disabled"
storeKitConfiguration: "App_iOS/Configuration.storekit"
macroExpansion: App_iOS
macroExpansion: App_iOS
test:
gatherCoverageData: true
targets:

View File

@ -73,12 +73,23 @@ class ProjectSpecTests: XCTestCase {
describe {
$0.it("has correct build setting") {
try expect(Platform.auto.deploymentTargetSetting) == ""
try expect(Platform.iOS.deploymentTargetSetting) == "IPHONEOS_DEPLOYMENT_TARGET"
try expect(Platform.tvOS.deploymentTargetSetting) == "TVOS_DEPLOYMENT_TARGET"
try expect(Platform.watchOS.deploymentTargetSetting) == "WATCHOS_DEPLOYMENT_TARGET"
try expect(Platform.macOS.deploymentTargetSetting) == "MACOSX_DEPLOYMENT_TARGET"
try expect(Platform.visionOS.deploymentTargetSetting) == "XROS_DEPLOYMENT_TARGET"
}
$0.it("has correct sdk root") {
try expect(Platform.auto.sdkRoot) == "auto"
try expect(Platform.iOS.sdkRoot) == "iphoneos"
try expect(Platform.tvOS.sdkRoot) == "appletvos"
try expect(Platform.watchOS.sdkRoot) == "watchos"
try expect(Platform.macOS.sdkRoot) == "macosx"
try expect(Platform.visionOS.sdkRoot) == "xros"
}
$0.it("parses version correctly") {
try expect(Version.parse("2").deploymentTarget) == "2.0"
try expect(Version.parse("2.0").deploymentTarget) == "2.0"
@ -156,8 +167,8 @@ class ProjectSpecTests: XCTestCase {
Dependency(type: .framework, reference: "dependency2"),
// multiple package dependencies with different products should be allowed
Dependency(type: .package(product: "one"), reference: "package1"),
Dependency(type: .package(product: "two"), reference: "package1"),
Dependency(type: .package(products: ["one"]), reference: "package1"),
Dependency(type: .package(products: ["two"]), reference: "package1"),
]
),
Target(
@ -178,7 +189,59 @@ class ProjectSpecTests: XCTestCase {
try expectNoValidationError(project, .duplicateDependencies(target: "target1", dependencyReference: "package1"))
}
$0.it("unexpected supported destinations for watch app") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .watchOS,
supportedDestinations: [.macOS]
)
]
try expectValidationError(project, .unexpectedTargetPlatformForSupportedDestinations(target: "target1", platform: .watchOS))
}
$0.it("multiple definitions of mac platform in supported destinations") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .iOS,
supportedDestinations: [.macOS, .macCatalyst]
)
]
try expectValidationError(project, .multipleMacPlatformsInSupportedDestinations(target: "target1"))
}
$0.it("invalid target platform for macCatalyst supported destinations") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .tvOS,
supportedDestinations: [.tvOS, .macCatalyst]
)
]
try expectValidationError(project, .invalidTargetPlatformForSupportedDestinations(target: "target1"))
}
$0.it("missing target platform in supported destinations") {
var project = baseProject
project.targets = [
Target(
name: "target1",
type: .application,
platform: .iOS,
supportedDestinations: [.tvOS]
)
]
try expectValidationError(project, .missingTargetPlatformInSupportedDestinations(target: "target1", platform: .iOS))
}
$0.it("allows non-existent configurations") {
var project = baseProject
project.options = SpecOptions(disabledValidations: [.missingConfigs])
@ -205,7 +268,7 @@ class ProjectSpecTests: XCTestCase {
sources: ["invalidSource"],
dependencies: [
Dependency(type: .target, reference: "invalidDependency"),
Dependency(type: .package(product: nil), reference: "invalidPackage"),
Dependency(type: .package(products: []), reference: "invalidPackage"),
],
preBuildScripts: [BuildScript(script: .path("invalidPreBuildScript"), name: "preBuildScript1")],
postCompileScripts: [BuildScript(script: .path("invalidPostCompileScript"))],

View File

@ -38,7 +38,7 @@ class SpecLoadingTests: XCTestCase {
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(product: nil), reference: "Yams")]),
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(products: []), reference: "Yams")]),
Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]),
]
}
@ -54,7 +54,7 @@ class SpecLoadingTests: XCTestCase {
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(product: nil), reference: "SwiftPM"), Dependency(type: .package(product: nil), reference: "Yams")]),
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(products: []), reference: "SwiftPM"), Dependency(type: .package(products: []), reference: "Yams")]),
Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]),
]
}
@ -70,7 +70,7 @@ class SpecLoadingTests: XCTestCase {
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(product: nil), reference: "Yams")]),
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(products: []), reference: "Yams")]),
Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]),
]
}
@ -613,10 +613,66 @@ class SpecLoadingTests: XCTestCase {
target_iOS.deploymentTarget = Version(major: 9, minor: 0, patch: 0)
target_tvOS.deploymentTarget = Version(major: 10, minor: 0, patch: 0)
try expect(project.targets.count) == 2
try expect(project.targets) == [target_iOS, target_tvOS]
}
$0.it("parses no platform fallbacks to auto if we are using supported destinations") {
let targetDictionary: [String: Any] = [
"type": "framework",
"supportedDestinations": ["iOS", "tvOS"]
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
let target = Target(name: "Framework", type: .framework, platform: .auto)
try expect(project.targets) == [target]
}
$0.it("parses no platform fails if we are not using supported destinations") {
let expectedError = SpecParsingError.unknownTargetPlatform("")
let projectDictionary: [String: Any] = [
"name": "test",
"targets": ["target1": [
"type": "application"
] as [String : Any]]
]
try expectError(expectedError) {
_ = try Project(jsonDictionary: projectDictionary)
}
}
$0.it("parses supported destinations with macCatalyst but not iOS, we add iOS") {
let targetDictionary: [String: Any] = [
"type": "framework",
"supportedDestinations": ["macCatalyst"]
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
let target = Target(name: "Framework", type: .framework, platform: .auto)
try expect(project.targets) == [target]
try expect(project.targets.first?.supportedDestinations) == [.macCatalyst, .iOS]
}
$0.it("invalid target platform because platform is an array and supported destinations is in use") {
let expectedError = SpecParsingError.invalidTargetPlatformAsArray
let projectDictionary: [String: Any] = [
"name": "test",
"targets": ["target1": [
"type": "application",
"platform": ["iOS", "tvOS"],
"supportedDestinations": ["iOS", "tvOS"]
] as [String : Any]]
]
try expectError(expectedError) {
_ = try Project(jsonDictionary: projectDictionary)
}
}
$0.it("parses target templates") {
let targetDictionary: [String: Any] = [

View File

@ -14,7 +14,7 @@ private let app = Target(
Dependency(type: .carthage(findFrameworks: true, linkType: .static), reference: "MyStaticFramework"),
Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "MyDynamicFramework"),
Dependency(type: .framework, reference: "MyExternalFramework"),
Dependency(type: .package(product: "MyPackage"), reference: "MyPackage"),
Dependency(type: .package(products: ["MyPackage"]), reference: "MyPackage"),
Dependency(type: .sdk(root: "MySDK"), reference: "MySDK"),
]
)

View File

@ -357,7 +357,7 @@ class PBXProjGeneratorTests: XCTestCase {
let dependency1 = Dependency(type: .target, reference: "TestAll", platformFilter: .all)
let dependency2 = Dependency(type: .target, reference: "TestiOS", platformFilter: .iOS)
let dependency3 = Dependency(type: .target, reference: "TestmacOS", platformFilter: .macOS)
let dependency4 = Dependency(type: .package(product: "Swinject"), reference: "Swinject", platformFilter: .iOS)
let dependency4 = Dependency(type: .package(products: ["Swinject"]), reference: "Swinject", platformFilter: .iOS)
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"], dependencies: [dependency1, dependency2, dependency3, dependency4])
let swinjectPackage = SwiftPackage.remote(url: "https://github.com/Swinject/Swinject", versionRequirement: .exact("2.8.0"))
let project = Project(basePath: directoryPath, name: "Test", targets: [target, target1, target2, target3], packages: ["Swinject": swinjectPackage])

View File

@ -335,7 +335,156 @@ class ProjectGeneratorTests: XCTestCase {
try expect(targetConfig.buildSettings["WATCHOS_DEPLOYMENT_TARGET"] as? String) == "2.0"
try expect(targetConfig.buildSettings["TVOS_DEPLOYMENT_TARGET"]).beNil()
}
$0.it("supportedPlaforms merges settings - iOS, tvOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.tvOS, .iOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator appletvos appletvsimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, visionOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.visionOS, .iOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator xros xrsimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,7"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, tvOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .tvOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator appletvos appletvsimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, tvOS, macCatalyst") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .tvOS, .macCatalyst])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator appletvos appletvsimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2,3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - iOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("supportedPlaforms merges settings - tvOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.tvOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "appletvos appletvsimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "3"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "App Icon & Top Shelf Image"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME"] as? String) == "LaunchImage"
}
$0.it("supportedPlaforms merges settings - visionOS, macOS") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.visionOS, .macOS])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "xros xrsimulator macosx"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "7"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
}
$0.it("supportedPlaforms merges settings - iOS, macCatalyst") {
let target = Target(name: "Target", type: .application, platform: .auto, supportedDestinations: [.iOS, .macCatalyst])
let project = Project(name: "", targets: [target])
let pbxProject = try project.generatePbxProj()
let targetConfig1 = try unwrap(pbxProject.nativeTargets.first?.buildConfigurationList?.buildConfigurations.first)
try expect(targetConfig1.buildSettings["SUPPORTED_PLATFORMS"] as? String) == "iphoneos iphonesimulator"
try expect(targetConfig1.buildSettings["TARGETED_DEVICE_FAMILY"] as? String) == "1,2"
try expect(targetConfig1.buildSettings["SUPPORTS_MACCATALYST"] as? Bool) == true
try expect(targetConfig1.buildSettings["SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == false
try expect(targetConfig1.buildSettings["SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD"] as? Bool) == true
try expect(targetConfig1.buildSettings["LD_RUNPATH_SEARCH_PATHS"] as? [String]) == ["$(inherited)", "@executable_path/Frameworks"]
try expect(targetConfig1.buildSettings["SDKROOT"] as? String) == "auto"
try expect(targetConfig1.buildSettings["ASSETCATALOG_COMPILER_APPICON_NAME"] as? String) == "AppIcon"
try expect(targetConfig1.buildSettings["CODE_SIGN_IDENTITY"] as? String) == "iPhone Developer"
}
$0.it("generates dependencies") {
let pbxProject = try project.generatePbxProj()
@ -555,12 +704,12 @@ class ProjectGeneratorTests: XCTestCase {
Dependency(type: .target, reference: resourceBundle.name),
Dependency(type: .framework, reference: "FrameworkC.framework"),
Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "CarthageA"),
Dependency(type: .package(product: "RxSwift"), reference: "RxSwift"),
Dependency(type: .package(product: "RxCocoa"), reference: "RxSwift"),
Dependency(type: .package(product: "RxRelay"), reference: "RxSwift"),
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift"),
Dependency(type: .package(products: ["RxCocoa"]), reference: "RxSwift"),
Dependency(type: .package(products: ["RxRelay"]), reference: "RxSwift"),
// Validate - Do not link package
Dependency(type: .package(product: "KeychainAccess"), reference: "KeychainAccess", link: false),
Dependency(type: .package(products: ["KeychainAccess"]), reference: "KeychainAccess", link: false),
// Statically linked, so don't embed into test
Dependency(type: .target, reference: staticLibrary.name),
@ -1066,7 +1215,108 @@ class ProjectGeneratorTests: XCTestCase {
try expect(app2OtherLinkerSettings.contains("-ObjC")) == false
try expect(app3OtherLinkerSettings.contains("-ObjC")) == true
}
$0.it("filter sources with inferDestinationFiltersByPath") {
let sourceFiles = TargetSource(path: "App_supportedDestinations/TestResources", inferDestinationFiltersByPath: true)
let target = Target(
name: "test",
type: .application,
platform: .auto,
sources: [sourceFiles],
dependencies: []
)
let project = Project(
basePath: fixturePath + "TestProject",
name: "test",
targets: [target]
)
let pbxProject = try project.generatePbxProj()
let buildFiles = pbxProject.buildFiles
try expect(buildFiles.count) == 8
for buildFile in buildFiles {
let name = buildFile.file?.nameOrPath
if buildFile.platformFilters == [SupportedDestination.iOS.string] &&
(name == "File_ios.swift" || name == "File_A.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.tvOS.string] &&
(name == "File_tvOs.swift" || name == "File_B.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macOS.string] &&
(name == "File_macOS.swift" || name == "File_C.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macCatalyst.string] &&
(name == "File_MACCATALYST.swift" || name == "File_D.swift") {
continue
}
throw failure("Unexpected source file / destinationFilters")
}
}
$0.it("filter sources with destinationFilters") {
let sourceFile1 = TargetSource(path: "App_supportedDestinations/TestResources/iOs",
destinationFilters: [.iOS])
let sourceFile2 = TargetSource(path: "App_supportedDestinations/TestResources/TVOS",
destinationFilters: [.tvOS])
let sourceFile3 = TargetSource(path: "App_supportedDestinations/TestResources/macos",
destinationFilters: [.macOS, .macCatalyst])
let sourceFile4 = TargetSource(path: "App_supportedDestinations/TestResources/macCatalyst",
destinationFilters: [.macOS, .macCatalyst])
let sourceFile5 = TargetSource(path: "App_supportedDestinations/TestResources/File_ios.swift",
destinationFilters: [.iOS])
let sourceFile6 = TargetSource(path: "App_supportedDestinations/TestResources/File_tvOs.swift",
destinationFilters: [.tvOS])
let sourceFile7 = TargetSource(path: "App_supportedDestinations/TestResources/File_macOS.swift",
destinationFilters: [.macOS, .macCatalyst])
let sourceFile8 = TargetSource(path: "App_supportedDestinations/TestResources/File_MACCATALYST.swift",
destinationFilters: [.macOS, .macCatalyst])
let target = Target(
name: "test",
type: .application,
platform: .auto,
sources: [sourceFile1, sourceFile2, sourceFile3, sourceFile4, sourceFile5, sourceFile6, sourceFile7, sourceFile8],
dependencies: []
)
let project = Project(
basePath: fixturePath + "TestProject",
name: "test",
targets: [target]
)
let pbxProject = try project.generatePbxProj()
let buildFiles = pbxProject.buildFiles
try expect(buildFiles.count) == 8
for buildFile in buildFiles {
let name = buildFile.file?.nameOrPath
if buildFile.platformFilters == [SupportedDestination.iOS.string] &&
(name == "File_ios.swift" || name == "File_A.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.tvOS.string] &&
(name == "File_tvOs.swift" || name == "File_B.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macOS.string, SupportedDestination.macCatalyst.string] &&
(name == "File_C.swift" || name == "File_D.swift") {
continue
} else if buildFile.platformFilters == [SupportedDestination.macOS.string, SupportedDestination.macCatalyst.string] &&
(name == "File_macOS.swift" || name == "File_MACCATALYST.swift") {
continue
}
throw failure("Unexpected source file / destinationFilters")
}
}
$0.it("copies Swift Objective-C Interface Header") {
let swiftStaticLibraryWithHeader = Target(
name: "swiftStaticLibraryWithHeader",
@ -1263,8 +1513,8 @@ class ProjectGeneratorTests: XCTestCase {
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(product: "ProjectSpec"), reference: "XcodeGen"),
Dependency(type: .package(product: nil), reference: "Codability"),
Dependency(type: .package(products: ["ProjectSpec"]), reference: "XcodeGen"),
Dependency(type: .package(products: []), reference: "Codability"),
]
)
@ -1300,7 +1550,7 @@ class ProjectGeneratorTests: XCTestCase {
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(product: nil), reference: "XcodeGen"),
Dependency(type: .package(products: []), reference: "XcodeGen"),
]
)
@ -1324,14 +1574,13 @@ class ProjectGeneratorTests: XCTestCase {
try expect(file.product?.productName) == "XcodeGen"
}
$0.it("generates local swift packages with custom xcode path") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(product: nil), reference: "XcodeGen"),
Dependency(type: .package(products: []), reference: "XcodeGen"),
]
)
@ -1495,6 +1744,40 @@ class ProjectGeneratorTests: XCTestCase {
try expect(NSDictionary(dictionary: expectedInfoPlist).isEqual(to: infoPlist)).beTrue()
}
$0.it("generates local swift packages with multiple products") {
let app = Target(
name: "MyApp",
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(products: ["FooDomain", "FooUI"]), reference: "FooFeature")
]
)
let project = Project(name: "test", targets: [app], packages: [
"FooFeature": .local(path: "../FooFeature", group: nil)
], options: .init(localPackagesGroup: "MyPackages"))
let pbxProject = try project.generatePbxProj(specValidate: false)
let nativeTarget = try unwrap(pbxProject.nativeTargets.first(where: { $0.name == app.name }))
let localPackageFile = try unwrap(pbxProject.fileReferences.first(where: { $0.path == "../FooFeature" }))
try expect(localPackageFile.lastKnownFileType) == "folder"
let frameworkPhases = nativeTarget.buildPhases.compactMap { $0 as? PBXFrameworksBuildPhase }
guard let frameworkPhase = frameworkPhases.first else {
return XCTFail("frameworkPhases should have more than one")
}
guard let files = frameworkPhase.files, files.count == 2 else {
return XCTFail("frameworkPhase should have exactly two files")
}
let productNames = files.compactMap(\.product?.productName)
try expect(productNames).contains { $0 == "FooDomain" }
try expect(productNames).contains { $0 == "FooUI" }
}
}
}
@ -1612,7 +1895,197 @@ class ProjectGeneratorTests: XCTestCase {
}
}
}
func testGenerateXcodeProjectWithPlatformFilteredDependencies() throws {
describe("generateXcodeProject with destinationFilters") {
func generateProjectForApp(withDependencies: [Dependency], targets: [Target], packages: [String: SwiftPackage] = [:]) throws -> PBXProj {
let app = Target(
name: "App",
type: .application,
platform: .iOS,
dependencies: withDependencies
)
let project = Project(
name: "test",
targets: targets + [app],
packages: packages
)
return try project.generatePbxProj()
}
func expectLinkedDependecies(_ expectedLinkedFiles: [String: [String]], in project: PBXProj) throws {
let buildPhases = project.buildPhases
let frameworkPhases = project.frameworksBuildPhases.filter { buildPhases.contains($0) }
var linkedFiles: [String: [String]] = [:]
for link in frameworkPhases[0].files ?? [] {
if let name = link.file?.nameOrPath ?? link.product?.productName {
linkedFiles[name] = link.platformFilters
}
}
try expect(linkedFiles) == expectedLinkedFiles
}
func expectCopiedBundles(_ expectedCopiedBundleFiles: [String: [String]], in project: PBXProj) throws {
let buildPhases = project.buildPhases
let copyBundlesPhase = project.copyFilesBuildPhases.filter { buildPhases.contains($0) }
var copiedFiles: [String: [String]] = [:]
for copy in copyBundlesPhase[0].files ?? [] {
if let name = copy.file?.nameOrPath {
copiedFiles[name] = copy.platformFilters
}
}
try expect(copiedFiles) == expectedCopiedBundleFiles
}
$0.it("target dependencies") {
let frameworkA = Target(
name: "frameworkA",
type: .framework,
platform: .iOS
)
let frameworkB = Target(
name: "frameworkB",
type: .framework,
platform: .iOS
)
let expectedLinkedFiles = [
"frameworkA.framework": [SupportedDestination.iOS.string],
"frameworkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .target, reference: frameworkA.name, destinationFilters: [.iOS]),
Dependency(type: .target, reference: frameworkB.name, destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [frameworkA, frameworkB])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("framework dependencies") {
let expectedLinkedFiles = [
"frameworkA.framework": [SupportedDestination.iOS.string],
"frameworkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .framework, reference: "frameworkA.framework", destinationFilters: [.iOS]),
Dependency(type: .framework, reference: "frameworkB.framework", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("carthage dependencies") {
let expectedLinkedFiles = [
"frameworkA.framework": [SupportedDestination.iOS.string],
"frameworkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkA.framework", destinationFilters: [.iOS]),
Dependency(type: .carthage(findFrameworks: false, linkType: .dynamic), reference: "frameworkB.framework", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("sdk dependencies") {
let expectedLinkedFiles = [
"sdkA.framework": [SupportedDestination.iOS.string],
"sdkB.framework": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .sdk(root: nil), reference: "sdkA.framework", destinationFilters: [.iOS]),
Dependency(type: .sdk(root: nil), reference: "sdkB.framework", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("package dependencies") {
let packages: [String: SwiftPackage] = [
"RxSwift": .remote(url: "https://github.com/ReactiveX/RxSwift", versionRequirement: .upToNextMajorVersion("5.1.1")),
]
let expectedLinkedFiles = [
"RxSwift": [SupportedDestination.iOS.string],
"RxCocoa": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift", destinationFilters: [.iOS]),
Dependency(type: .package(products: ["RxCocoa"]), reference: "RxSwift", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [], packages: packages)
// then ensure that everything is linked
try expectLinkedDependecies(expectedLinkedFiles, in: pbxProject)
}
$0.it("bundle dependencies") {
let expectedCopiedBundleFiles = [
"bundleA.bundle": [SupportedDestination.iOS.string],
"bundleB.bundle": [SupportedDestination.iOS.string, SupportedDestination.tvOS.string]
]
// given
let dependencies = [
Dependency(type: .bundle, reference: "bundleA.bundle", destinationFilters: [.iOS]),
Dependency(type: .bundle, reference: "bundleB.bundle", destinationFilters: [.iOS, .tvOS]),
]
// when
let pbxProject = try generateProjectForApp(withDependencies: dependencies, targets: [])
// then ensure that everything is linked
try expectCopiedBundles(expectedCopiedBundleFiles, in: pbxProject)
}
}
}
func testGenerateXcodeProjectWithCustomDependencyDestinations() throws {
describe("generateXcodeProject") {
@ -2978,8 +3451,8 @@ class ProjectGeneratorTests: XCTestCase {
// given
let dependencies = [
Dependency(type: .package(product: "RxSwift"), reference: "RxSwift", embed: true),
Dependency(type: .package(product: "RxCocoa"), reference: "RxSwift", embed: false),
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift", embed: true),
Dependency(type: .package(products: ["RxCocoa"]), reference: "RxSwift", embed: false),
]
// when
@ -2993,8 +3466,8 @@ class ProjectGeneratorTests: XCTestCase {
// given
let dependencies = [
Dependency(type: .package(product: "RxSwift"), reference: "RxSwift", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)),
Dependency(type: .package(product: "RxCocoa"), reference: "RxSwift", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)),
Dependency(type: .package(products: ["RxSwift"]), reference: "RxSwift", embed: true, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)),
Dependency(type: .package(products: ["RxCocoa"]), reference: "RxSwift", embed: false, copyPhase: BuildPhaseSpec.CopyFilesSettings(destination: .plugins, subpath: "test", phaseOrder: .postCompile)),
]
// when

View File

@ -487,7 +487,7 @@ class SchemeGeneratorTests: XCTestCase {
type: .application,
platform: .iOS,
dependencies: [
Dependency(type: .package(product: nil), reference: "XcodeGen")
Dependency(type: .package(products: []), reference: "XcodeGen")
],
scheme: targetScheme
)