Skip to content

Commit

Permalink
Support linting multi-destination targets (#5441)
Browse files Browse the repository at this point in the history
- Updated the `GraphLinter` to account for multi-destination (multi-platform) targets
- Lint issues are now flagged only if there are no valid source and destination platform combination based on the existing rules rather than only using the `legacyPlatform` (single platform) to base the rules on

Test Plan:

- Existing unit tests should continue to pass
- New cases for valid and invalid combinations were addeed

Notes:

- This is a pre-requiste for #5381 which adds the public API for multi-desintations
  • Loading branch information
kwridan committed Sep 29, 2023
1 parent 08f5438 commit c7960bd
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 14 deletions.
38 changes: 24 additions & 14 deletions Sources/TuistGenerator/Linter/GraphLinter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,24 +109,34 @@ public class GraphLinter: GraphLinting {
}

private func lintDependency(from: GraphTarget, to: GraphTarget) -> [LintingIssue] {
let fromTarget = LintableTarget(
platform: from.target.legacyPlatform,
product: from.target.product
)
let toTarget = LintableTarget(
platform: to.target.legacyPlatform,
product: to.target.product
)
let fromPlatforms = from.target.supportedPlatforms
let toPlatforms = to.target.supportedPlatforms

var validLinksCount = 0
for fromPlatform in fromPlatforms {
let fromTarget = LintableTarget(
platform: fromPlatform,
product: from.target.product
)

guard let supportedTargets = GraphLinter.validLinks[fromTarget] else {
let reason =
"Target \(from.target.name) has platform '\(from.target.legacyPlatform)' and product '\(from.target.product)' which is an invalid or not supported yet combination."
return [LintingIssue(reason: reason, severity: .error)]
guard let supportedTargets = GraphLinter.validLinks[fromTarget] else {
let reason =
"Target \(from.target.name) has platform '\(fromPlatform)' and product '\(from.target.product)' which is an invalid or not yet supported combination."
return [LintingIssue(reason: reason, severity: .error)]
}

let toLintTargets = toPlatforms.map {
LintableTarget(platform: $0, product: to.target.product)
}

let validLinks = Set(toLintTargets).intersection(supportedTargets)
validLinksCount += validLinks.count
}

guard supportedTargets.contains(toTarget) else {
// Need to have at least one valid link based on common platforms
guard validLinksCount > 0 else {
let reason =
"Target \(from.target.name) has platform '\(from.target.legacyPlatform)' and product '\(from.target.product)' and depends on target \(to.target.name) of type \(to.target.product) and platform '\(to.target.legacyPlatform)' which is an invalid or not supported yet combination."
"Target \(from.target.name) has platforms '\(fromPlatforms.map(\.caseValue).listed())' and product '\(from.target.product)' and depends on target \(to.target.name) of type '\(to.target.product)' and platforms '\(toPlatforms.map(\.caseValue).listed())' which is an invalid or not yet supported combination."
return [LintingIssue(reason: reason, severity: .error)]
}

Expand Down
74 changes: 74 additions & 0 deletions Tests/TuistGeneratorTests/Linter/GraphLinterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1832,4 +1832,78 @@ final class GraphLinterTests: TuistUnitTestCase {
]
)
}

func test_lint_multiDestinationTarget_validLinks() throws {
// Given
let path = try self.temporaryPath()
let iOSAndMacTarget = Target.test(name: "IOSAndMacTarget", destinations: [.iPhone, .mac], product: .framework)
let macOnlyTarget = Target.test(name: "MacOnlyTarget", destinations: [.mac], product: .framework)

let project = Project.test(
path: path,
targets: [
iOSAndMacTarget,
macOnlyTarget,
]
)
let graph = Graph.test(
projects: [path: project],
targets: [
path: [
iOSAndMacTarget.name: iOSAndMacTarget,
macOnlyTarget.name: macOnlyTarget,
],
],
dependencies: [
.target(name: iOSAndMacTarget.name, path: path): [
.target(name: macOnlyTarget.name, path: path),
],
.target(name: macOnlyTarget.name, path: path): [],
]
)
let graphTraverser = GraphTraverser(graph: graph)

// When
let results = subject.lint(graphTraverser: graphTraverser)

// Then
XCTAssertTrue(results.isEmpty)
}

func test_lint_multiDestinationTarget_invalidLinks() throws {
// Given
let path = try self.temporaryPath()
let iOSAndMacTarget = Target.test(name: "IOSAndMacTarget", destinations: [.iPhone, .mac], product: .framework)
let watchOnlyTarget = Target.test(name: "WatchOnlyTarget", destinations: [.appleWatch], product: .framework)

let project = Project.test(
path: path,
targets: [
iOSAndMacTarget,
watchOnlyTarget,
]
)
let graph = Graph.test(
projects: [path: project],
targets: [
path: [
iOSAndMacTarget.name: iOSAndMacTarget,
watchOnlyTarget.name: watchOnlyTarget,
],
],
dependencies: [
.target(name: iOSAndMacTarget.name, path: path): [
.target(name: watchOnlyTarget.name, path: path),
],
.target(name: watchOnlyTarget.name, path: path): [],
]
)
let graphTraverser = GraphTraverser(graph: graph)

// When
let results = subject.lint(graphTraverser: graphTraverser)

// Then
XCTAssertFalse(results.isEmpty)
}
}

0 comments on commit c7960bd

Please sign in to comment.