Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiplatform Phase 3 - Expose support in ProjectDescription #5381

Closed
wants to merge 65 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
8e25b91
Introduce `edges` to `Graph`
waltflanagan Nov 10, 2023
34daa55
Resolve all `GraphDependency` -> `GraphDependencyReference` via `depe…
waltflanagan Nov 10, 2023
e4c70da
Add `platformFilters` to `GraphDependencyReference`
waltflanagan Nov 10, 2023
518ec6f
Update generators to apply filters when present
waltflanagan Nov 10, 2023
63096b3
Linting
waltflanagan Nov 10, 2023
142585d
Add test to validate transitive platform filters are applied
waltflanagan Nov 13, 2023
83d757a
Add logic to apply intermediate filters.
waltflanagan Nov 13, 2023
e8f272d
Linting
waltflanagan Nov 13, 2023
902a770
Adding one more platformfilters test
waltflanagan Nov 14, 2023
19038a6
Linting
waltflanagan Nov 14, 2023
97de5c8
Update name of edges to be more clear
waltflanagan Nov 14, 2023
1f16419
Return `nil` instead of `[]` in these two terminal cases
waltflanagan Nov 15, 2023
5e34346
Linting
waltflanagan Nov 15, 2023
8ffc653
Add `all` to `PlatformFilters`
waltflanagan Nov 15, 2023
8d0a6f9
Update traversal logic to account for `.all`
waltflanagan Nov 15, 2023
b0dee9c
Extra tests for `applyPlaftformFilters`
waltflanagan Nov 15, 2023
66037e0
Linting
waltflanagan Nov 16, 2023
fccb9b6
Refactor `dependencyReference(to:from:)` to be source of truth for ma…
waltflanagan Nov 17, 2023
bcb639a
Rename method for resolving platformFilters
waltflanagan Nov 17, 2023
02292d8
Add test to validate transitive platform filters are applied
waltflanagan Nov 13, 2023
a10c5a1
Add logic to apply intermediate filters.
waltflanagan Nov 13, 2023
6f19aa8
Adding one more platformfilters test
waltflanagan Nov 14, 2023
081f81f
Linting
waltflanagan Nov 14, 2023
90326ad
Adding multiplatform types to ProjectDescription
waltflanagan Aug 26, 2023
c4a1e9e
Adding multiplatform namespace
waltflanagan Aug 27, 2023
28d539a
Initial addition of multiplatform support in ProjectDescription
waltflanagan Aug 28, 2023
ebad21c
Adding simple multiplatform fixture
waltflanagan Sep 12, 2023
0bca818
Edges Checkpoint
waltflanagan Oct 11, 2023
8fe4e9e
it compiles
waltflanagan Oct 12, 2023
cc4c302
Connect `ProjectDescription` and `TuistGraph` Platformfilters
waltflanagan Oct 12, 2023
e870f75
Fix DependenciesGraphControllerTests
waltflanagan Oct 13, 2023
6090a70
Fix DumpServiceIntegrationTests
waltflanagan Oct 13, 2023
9fa0794
Dependencies WIP
waltflanagan Oct 22, 2023
dc3f592
Incorporate filters for external dependencies
waltflanagan Oct 23, 2023
0cae51d
Fix deploymentTargets resolution for packages.
waltflanagan Oct 26, 2023
b82d6eb
Traverse our graph edges to find transient platform filters
waltflanagan Oct 27, 2023
161bb39
Fix a typo
waltflanagan Oct 27, 2023
7223a33
Only use `platformFilters` field when adding them
waltflanagan Oct 27, 2023
d874718
Move overlay logic to an extension instead of `SettingsHelper`
waltflanagan Nov 4, 2023
a5b095c
Fix custom setting mapping to work with multiplatform dependencies
waltflanagan Nov 6, 2023
c00cd98
One last bit from the rebase
waltflanagan Nov 7, 2023
842eb48
Resolve merge issue from rebase
waltflanagan Nov 7, 2023
baed57a
Update `directStaticDependencies` to use `dependencyReference(to: fro…
waltflanagan Nov 9, 2023
8779722
Fix settings resolution to correctly merge settings dictionaries
waltflanagan Nov 9, 2023
f5372d5
Fix rebase issue with test data
waltflanagan Nov 9, 2023
170a041
Fix `tuist generate -p` to include multiplatform targets
waltflanagan Nov 9, 2023
3883901
Use `ProjectDescription` platforms to ease use in `PackageInfoMapperT…
waltflanagan Nov 9, 2023
f16713f
Add missingn linting on multiplatform targets
waltflanagan Nov 9, 2023
dc7abcc
Update tests for multiplatform dependencies
waltflanagan Nov 9, 2023
e50be1c
Linting
waltflanagan Nov 9, 2023
46c653f
Remove unneeded parameters for `platforms`
waltflanagan Nov 9, 2023
f7f6339
Skip dependencies that are targeting unsupported platforms
waltflanagan Nov 9, 2023
d4de36d
FIx typo
waltflanagan Nov 10, 2023
671bff1
Fixing rebase issues
waltflanagan Nov 10, 2023
2a4192f
Update multiplatform fixture location and add multiplatform dependency
waltflanagan Nov 10, 2023
298d917
Fix invalid dependency error to work with multiplatform dependencies
waltflanagan Nov 10, 2023
7b3a04f
Remove things that should be ignored
waltflanagan Nov 10, 2023
5a234c7
Ignore dependencies artifacts
waltflanagan Nov 10, 2023
f3dc7ee
Rebase fixes
waltflanagan Nov 16, 2023
2238b7a
Fix rebase and use `.all` instead of `[]`
waltflanagan Nov 16, 2023
0c16654
Adding TargetDependency filter tests
waltflanagan Nov 18, 2023
71f625f
Update dependency finding logic to match only on name
waltflanagan Nov 20, 2023
7890331
Add missed packagePlugin filter application
waltflanagan Nov 20, 2023
68158ec
Match resource filter to targets filters
waltflanagan Nov 20, 2023
b0840d7
Fix small issue mapping that could have been dropping filters
waltflanagan Nov 20, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/ProjectDescription/Dependencies/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public struct Dependencies: Codable, Equatable {
public let swiftPackageManager: SwiftPackageManagerDependencies?

/// List of platforms for which you want to install dependencies.
public let platforms: Set<Platform>
public let platforms: Set<PackagePlatform>

/// Creates a new `Dependencies` manifest instance.
/// - Parameters:
Expand All @@ -37,7 +37,7 @@ public struct Dependencies: Codable, Equatable {
public init(
carthage: CarthageDependencies? = nil,
swiftPackageManager: SwiftPackageManagerDependencies? = nil,
platforms: Set<Platform> = Set(Platform.allCases)
platforms: Set<PackagePlatform> = Set(PackagePlatform.allCases)
) {
self.carthage = carthage
self.swiftPackageManager = swiftPackageManager
Expand Down
1 change: 1 addition & 0 deletions Sources/ProjectDescription/DeploymentDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
// MARK: - DeploymentDevice

/// A supported deployment device.
@available(*, deprecated)
public struct DeploymentDevice: OptionSet, Codable, Hashable {
/// An iPhone device.
public static let iphone = DeploymentDevice(rawValue: 1 << 0)
Expand Down
1 change: 1 addition & 0 deletions Sources/ProjectDescription/DeploymentTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
// MARK: - DeploymentTarget

/// A supported minimum deployment target.
@available(*, deprecated)
public enum DeploymentTarget: Codable, Hashable {
/// The minimum iOS version, the list of devices your product will support, and whether or not the target should run on mac
/// devices.
Expand Down
54 changes: 54 additions & 0 deletions Sources/ProjectDescription/DeploymentTargets.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation

// MARK: - DeploymentTargets

public struct DeploymentTargets: Hashable, Codable {
public let iOS: String?
public let macOS: String?
public let watchOS: String?
public let tvOS: String?
public let visionOS: String?

public init(iOS: String? = nil, macOS: String? = nil, watchOS: String? = nil, tvOS: String? = nil, visionOS: String? = nil) {
self.iOS = iOS
self.macOS = macOS
self.watchOS = watchOS
self.tvOS = tvOS
self.visionOS = visionOS
}

public subscript(platform: Platform) -> String? {
switch platform {
case .iOS:
return iOS
case .macOS:
return macOS
case .watchOS:
return watchOS
case .tvOS:
return tvOS
case .visionOS:
return visionOS
}
}

public static func iOS(_ version: String) -> DeploymentTargets {
DeploymentTargets(iOS: version)
}

public static func macOS(_ version: String) -> DeploymentTargets {
DeploymentTargets(macOS: version)
}

public static func watchOS(_ version: String) -> DeploymentTargets {
DeploymentTargets(watchOS: version)
}

public static func tvOS(_ version: String) -> DeploymentTargets {
DeploymentTargets(tvOS: version)
}

public static func visionOS(_ version: String) -> DeploymentTargets {
DeploymentTargets(visionOS: version)
}
}
46 changes: 46 additions & 0 deletions Sources/ProjectDescription/Destination.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Foundation

public typealias Destinations = Set<Destination>

extension Destinations {
public static var watchOS: Destinations = [.appleWatch]
public static var iOS: Destinations = [.iPhone, .iPad, .macWithiPadDesign]
public static var macOS: Destinations = [.mac]
public static var tvOS: Destinations = [.appleTv]
public static var visionOS: Destinations = [.appleVision]
}

extension Destinations {
public var platforms: Set<Platform> {
let platforms = map(\.platform)
return Set<Platform>(platforms)
}
}

/// A supported platform representation.
public enum Destination: String, Codable, Equatable, CaseIterable {
case iPhone
case iPad
case mac
case macWithiPadDesign
case macCatalyst
case appleWatch
case appleTv
case appleVision
case appleVisionWithiPadDesign

public var platform: Platform {
switch self {
case .iPad, .iPhone, .macCatalyst, .macWithiPadDesign, .appleVisionWithiPadDesign:
return .iOS
case .mac:
return .macOS
case .appleTv:
return .tvOS
case .appleWatch:
return .watchOS
case .appleVision:
return .visionOS
}
}
}
3 changes: 3 additions & 0 deletions Sources/ProjectDescription/Multiplatform.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Foundation

public enum Multiplatform {}
112 changes: 112 additions & 0 deletions Sources/ProjectDescription/MultiplatformTarget.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import Foundation

extension Multiplatform {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach you've taken here is much cleaner though so I see the value in that, main concern is maintaining two duplicate Targets

Would it be possible to make this the main Target structure but map the the legacy properties to the appropriate underlying new properties?

e.g.

init(
// ...
  platform: Platform,
  deploymentTarget: DeploymentTarget,
// ...
) {
  self.init(
   // ...
     destinations: [Destination.from(platform)],
     deploymentTargets: DeploymentTargets.from(deploymentTarget)
   // ...
  )
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we can and should. this approach was taken to continue to support source compatibility for current Tuist users. given that platform is a public property of Target, I didn't feel safe changing how things like that are exposed at this point in time.

If we're open to breaking changes, we can very much adjust the main Target type for our new needs as well as provide a convenience initializer that makes migration simpler for folks that dont need multiplatform support.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to maintain source compatibility.

My thinking is we may be able to map / derive the old properties to prevent breaking changes, however if that proves to be too much hassle the approach you have taken is much cleaner.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on the fence here. While supporting source compatibility is important, the cost of maintaining two similar structures in the codebase creates room for inconsistencies. I'm not a big fan of breaking changes, but multi-platform support is an important change for Tuist and might warrant going with a major release, in which we can batch other deprecated APIs. If I'm not wrong here, the changes developers would have to make are not that significant, so even if it's a breaking change, which is always undesirable, the process "should" be straightforward.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main thought for this was that it would be "short" lived. We could publish the source compatible version for a while, allow users to migrate at their own pace and incrementally if they have many targets to migrate. After enough time we can make the source breaking change to remove the old deprecated target implementation and promote the multiplatform one to the only one.

/// A target which supports multiple platforms
public struct Target: Codable, Equatable {
/// The name of the target. Also, the product name if not specified with ``productName``.
public let name: String

/// The destinations this target supports
public let destinations: Destinations

/// The type of build product this target will output.
public let product: Product

/// The built product name. If nil, it will be equal to ``name``.
public let productName: String?

/// The product bundle identifier.
public let bundleId: String

/// The minimum deployment target your product will support.
public let deploymentTargets: DeploymentTargets?

/// The Info.plist representation.
public let infoPlist: InfoPlist?

/// The source files of the target.
/// Note: any playgrounds matched by the globs used in this property will be automatically added.
public let sources: SourceFilesList?

/// The resource files of target.
/// Note: localizable files, `*.lproj`, are supported.
public let resources: ResourceFileElements?

/// The build phase copy files actions for the target.
public let copyFiles: [CopyFilesAction]?

/// The headers for the target.
public let headers: Headers?

/// The entitlements file path for the target.
public let entitlements: Entitlements?

/// The build phase scripts actions for the target.
public let scripts: [TargetScript]

/// The target's dependencies.
public let dependencies: [TargetDependency]

/// The target's settings.
public let settings: Settings?

/// The Core Data models.
public let coreDataModels: [CoreDataModel]

/// The environment variables. Used by autogenerated schemes for the target.
public let environmentVariables: [String: EnvironmentVariable]

/// The launch arguments. Used by autogenerated schemes for the target.
public let launchArguments: [LaunchArgument]

/// The additional files for the target. For project's additional files, see ``Project/additionalFiles``.
public let additionalFiles: [FileElement]

/// The build rules used for transformation of source files during compilation.
public let buildRules: [BuildRule]

public init(
name: String,
destinations: Destinations,
product: Product,
productName: String? = nil,
bundleId: String,
deploymentTargets: DeploymentTargets? = nil,
infoPlist: InfoPlist? = .default,
sources: SourceFilesList? = nil,
resources: ResourceFileElements? = nil,
copyFiles: [CopyFilesAction]? = nil,
headers: Headers? = nil,
entitlements: Entitlements? = nil,
scripts: [TargetScript] = [],
dependencies: [TargetDependency] = [],
settings: Settings? = nil,
coreDataModels: [CoreDataModel] = [],
environmentVariables: [String: EnvironmentVariable] = [:],
launchArguments: [LaunchArgument] = [],
additionalFiles: [FileElement] = [],
buildRules: [BuildRule] = []
) {
self.name = name
self.destinations = destinations
self.bundleId = bundleId
self.productName = productName
self.product = product
self.infoPlist = infoPlist
self.entitlements = entitlements
self.dependencies = dependencies
self.settings = settings
self.sources = sources
self.resources = resources
self.copyFiles = copyFiles
self.headers = headers
self.scripts = scripts
self.coreDataModels = coreDataModels
self.environmentVariables = environmentVariables
self.launchArguments = launchArguments
self.deploymentTargets = deploymentTargets
self.additionalFiles = additionalFiles
self.buildRules = buildRules
}
}
}
16 changes: 16 additions & 0 deletions Sources/ProjectDescription/Platform.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,19 @@ public enum Platform: String, Codable, Equatable, CaseIterable {
/// The visionOS platform
case visionOS = "visionos"
}

/// A supported platform representation.
public enum PackagePlatform: String, Codable, Equatable, CaseIterable {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was introduced because SPM includes catalyst as a platform while many other parts of our system map catalyst to iOS. This was a necessary distinction when inferring destinations from a SPM package.

/// The iOS platform
case iOS = "ios"
/// The macOS platform
case macOS = "macos"
/// The macOS platform
case macCatalyst = "maccatalyst"
/// The watchOS platform
case watchOS = "watchos"
/// The tvOS platform
case tvOS = "tvos"
/// The visionOS platform
case visionOS = "visionos"
}
15 changes: 15 additions & 0 deletions Sources/ProjectDescription/PlatformFilter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
public typealias PlatformFilters = Set<PlatformFilter>

extension PlatformFilters {
public static let all = Set(PlatformFilter.allCases)
}

public enum PlatformFilter: Comparable, Hashable, Codable, CaseIterable {
case ios
case macos
case tvos
case catalyst
case driverkit
case watchos
case visionos
}
4 changes: 4 additions & 0 deletions Sources/ProjectDescription/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public struct Project: Codable, Equatable {
public let packages: [Package]
/// The targets of the project.
public let targets: [Target]
/// The targets of the project.
public let multiplatformTargets: [Multiplatform.Target]
/// The custom schemes for the project. Default schemes for each target are generated by default.
public let schemes: [Scheme]
/// The build settings and configuration for the project.
Expand All @@ -89,6 +91,7 @@ public struct Project: Codable, Equatable {
packages: [Package] = [],
settings: Settings? = nil,
targets: [Target] = [],
multiplatformTargets: [Multiplatform.Target] = [],
schemes: [Scheme] = [],
fileHeaderTemplate: FileHeaderTemplate? = nil,
additionalFiles: [FileElement] = [],
Expand All @@ -99,6 +102,7 @@ public struct Project: Codable, Equatable {
self.options = options
self.packages = packages
self.targets = targets
self.multiplatformTargets = multiplatformTargets
self.schemes = schemes
self.settings = settings
self.additionalFiles = additionalFiles
Expand Down
Loading
Loading