Ensuring target product names are consistent with Xcode (#323)

Resolves https://github.com/tuist/tuist/issues/254

- When creating new Xcode projects manually from the UI, the product names do not include the extension
- Renaming existing `productName` references to `productNameWithExtension`

Test Plan:

- Ensure unit tests pass via `swift test`
- Ensure acceptance tests pass via `bundle exec rake features`
This commit is contained in:
Kas 2019-04-12 19:39:31 +01:00 committed by GitHub
parent a03a4e932b
commit fcbbfb14ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 128 additions and 50 deletions

View File

@ -16,6 +16,8 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
### Fixed
- Ensuring target product names are consistent with Xcode https://github.com/tuist/tuist/pull/323 by @kwridan
## 0.13.0
### Added

View File

@ -179,7 +179,7 @@ final class ConfigGenerator: ConfigGenerating {
settings["TEST_TARGET_NAME"] = "\(app.target.name)"
if target.product == .unitTests {
settings["TEST_HOST"] = "$(BUILT_PRODUCTS_DIR)/\(app.target.productName)/\(app.target.name)"
settings["TEST_HOST"] = "$(BUILT_PRODUCTS_DIR)/\(app.target.productNameWithExtension)/\(app.target.name)"
settings["BUNDLE_LOADER"] = "$(TEST_HOST)"
}
}

View File

@ -115,7 +115,7 @@ class ProjectFileElements {
func targetProducts(target: Target) -> Set<String> {
var products: Set<String> = Set()
products.insert(target.productName)
products.insert(target.productNameWithExtension)
return products
}
@ -213,7 +213,7 @@ class ProjectFileElements {
try dependencies.forEach { node in
if let targetNode = node as? TargetNode {
// Product name
let productName = targetNode.target.productName
let productName = targetNode.target.productNameWithExtension
if self.products[productName] != nil { return }
/// The dependency belongs to the same project and its product

View File

@ -254,7 +254,7 @@ final class SchemesGenerator: SchemesGenerating {
func targetBuildableReference(target: Target, pbxTarget: PBXNativeTarget, projectName: String) -> XCScheme.BuildableReference {
return XCScheme.BuildableReference(referencedContainer: "container:\(projectName)",
blueprint: pbxTarget,
buildableName: target.productName,
buildableName: target.productNameWithExtension,
blueprintName: target.name,
buildableIdentifier: "primary")
}

View File

@ -54,7 +54,7 @@ final class TargetGenerator: TargetGenerating {
graph: Graphing,
system: Systeming = System()) throws -> PBXNativeTarget {
/// Products reference.
let productFileReference = fileElements.products[target.productName]!
let productFileReference = fileElements.products[target.productNameWithExtension]!
/// Target
let pbxTarget = PBXNativeTarget(name: target.name,
@ -63,7 +63,7 @@ final class TargetGenerator: TargetGenerating {
buildRules: [],
dependencies: [],
productInstallPath: nil,
productName: target.productName,
productName: target.name,
product: productFileReference,
productType: target.product.xcodeValue)
pbxproj.add(object: pbxTarget)

View File

@ -122,7 +122,7 @@ class Graph: Graphing {
return targetNode.targetDependencies
.filter(isStaticLibrary)
.map(\.target.productName)
.map(\.target.productNameWithExtension)
.map(DependencyReference.product)
}
@ -149,7 +149,7 @@ class Graph: Graphing {
let staticLibraries = findAll(targetNode: targetNode, test: isStaticLibrary, skip: isFramework)
.lazy
.map(\.target.productName)
.map(\.target.productNameWithExtension)
.map(DependencyReference.product)
references.append(contentsOf: staticLibraries)
@ -159,7 +159,7 @@ class Graph: Graphing {
let dynamicLibrariesAndFrameworks = targetNode.targetDependencies
.filter(or(isFramework, isDynamicLibrary))
.map(\.target.productName)
.map(\.target.productNameWithExtension)
.map(DependencyReference.product)
references.append(contentsOf: dynamicLibrariesAndFrameworks)
@ -224,7 +224,7 @@ class Graph: Graphing {
/// Other targets' frameworks.
let otherTargetFrameworks = findAll(targetNode: targetNode, test: isFramework)
.lazy
.map(\.target.productName)
.map(\.target.productNameWithExtension)
.map(DependencyReference.product)
references.append(contentsOf: otherTargetFrameworks)

View File

@ -68,7 +68,7 @@ public class Target: Equatable {
}
/// Returns the product name including the extension.
var productName: String {
var productNameWithExtension: String {
switch product {
case .staticLibrary, .dynamicLibrary:
return "lib\(name).\(product.xcodeValue.fileExtension!)"

View File

@ -39,19 +39,19 @@ final class SchemeGeneratorTests: XCTestCase {
XCTAssertEqual(appEntry.buildFor, [.analyzing, .archiving, .profiling, .running, .testing])
XCTAssertEqual(appEntry.buildableReference.referencedContainer, "container:project.xcodeproj")
XCTAssertEqual(appEntry.buildableReference.buildableName, app.productName)
XCTAssertEqual(appEntry.buildableReference.buildableName, app.productNameWithExtension)
XCTAssertEqual(appEntry.buildableReference.blueprintName, app.name)
XCTAssertEqual(appEntry.buildableReference.buildableIdentifier, "primary")
XCTAssertEqual(testsEntry.buildFor, [.testing])
XCTAssertEqual(testsEntry.buildableReference.referencedContainer, "container:project.xcodeproj")
XCTAssertEqual(testsEntry.buildableReference.buildableName, appTests.productName)
XCTAssertEqual(testsEntry.buildableReference.buildableName, appTests.productNameWithExtension)
XCTAssertEqual(testsEntry.buildableReference.blueprintName, appTests.name)
XCTAssertEqual(testsEntry.buildableReference.buildableIdentifier, "primary")
XCTAssertEqual(uiTestsEntry.buildFor, [.testing])
XCTAssertEqual(uiTestsEntry.buildableReference.referencedContainer, "container:project.xcodeproj")
XCTAssertEqual(uiTestsEntry.buildableReference.buildableName, appUITests.productName)
XCTAssertEqual(uiTestsEntry.buildableReference.buildableName, appUITests.productNameWithExtension)
XCTAssertEqual(uiTestsEntry.buildableReference.blueprintName, appUITests.name)
XCTAssertEqual(uiTestsEntry.buildableReference.buildableIdentifier, "primary")
}
@ -73,7 +73,7 @@ final class SchemeGeneratorTests: XCTestCase {
XCTAssertEqual(testable?.skipped, false)
XCTAssertEqual(testable?.buildableReference.referencedContainer, "container:project.xcodeproj")
XCTAssertEqual(testable?.buildableReference.buildableName, appTests.productName)
XCTAssertEqual(testable?.buildableReference.buildableName, appTests.productNameWithExtension)
XCTAssertEqual(testable?.buildableReference.blueprintName, appTests.name)
XCTAssertEqual(testable?.buildableReference.buildableIdentifier, "primary")
}
@ -186,7 +186,7 @@ final class SchemeGeneratorTests: XCTestCase {
XCTAssertNil(got?.macroExpansion)
XCTAssertEqual(got?.buildableProductRunnable?.runnableDebuggingMode, "0")
XCTAssertEqual(buildable?.referencedContainer, "container:project.xcodeproj")
XCTAssertEqual(buildable?.buildableName, target.productName)
XCTAssertEqual(buildable?.buildableName, target.productNameWithExtension)
XCTAssertEqual(buildable?.blueprintName, target.name)
XCTAssertEqual(buildable?.buildableIdentifier, "primary")

View File

@ -5,46 +5,69 @@ import XCTest
@testable import TuistGenerator
final class TargetGeneratorTests: XCTestCase {
var path: AbsolutePath!
var subject: TargetGenerator!
var pbxproj: PBXProj!
var pbxProject: PBXProject!
var fileElements: ProjectFileElements!
override func setUp() {
super.setUp()
path = AbsolutePath("/test")
pbxproj = PBXProj()
pbxProject = createPbxProject(pbxproj: pbxproj)
fileElements = ProjectFileElements([:], playgrounds: MockPlaygrounds())
subject = TargetGenerator()
}
func test_generateTarget_productName() throws {
// Given
let target = Target.test(name: "MyFramework",
product: .framework)
let project = Project.test(path: path, targets: [target])
let graph = Graph.test()
let groups = ProjectGroups.generate(project: project,
pbxproj: pbxproj,
sourceRootPath: path,
playgrounds: MockPlaygrounds())
try fileElements.generateProjectFiles(project: project,
graph: graph,
groups: groups,
pbxproj: pbxproj,
sourceRootPath: path)
// When
let generatedTarget = try subject.generateTarget(target: target,
pbxproj: pbxproj,
pbxProject: pbxProject,
groups: groups,
fileElements: fileElements,
path: path,
sourceRootPath: path,
options: GenerationOptions(),
graph: graph)
// Then
XCTAssertEqual(generatedTarget.productName, "MyFramework")
XCTAssertEqual(generatedTarget.productNameWithExtension(), "MyFramework.framework")
XCTAssertEqual(generatedTarget.productType, .framework)
}
func test_generateTargetDependencies() throws {
let pbxproj = PBXProj()
let path = AbsolutePath("/test")
// Given
let targetA = Target.test(name: "TargetA")
let targetB = Target.test(name: "TargetB")
let nativeTargetA = PBXNativeTarget(name: "TargetA")
let nativeTargetB = PBXNativeTarget(name: "TargetB")
pbxproj.add(object: nativeTargetA)
pbxproj.add(object: nativeTargetB)
let configList = XCConfigurationList(buildConfigurations: [])
pbxproj.add(object: configList)
let mainGroup = PBXGroup()
pbxproj.add(object: mainGroup)
let project = Project.test(path: path,
name: "Project",
targets: [targetA, targetB])
let pbxProject = PBXProject(name: "Project",
buildConfigurationList: configList,
compatibilityVersion: "0",
mainGroup: mainGroup)
pbxproj.add(object: pbxProject)
let graphCache = GraphLoaderCache()
let targetBNode = TargetNode(project: project,
target: targetA,
dependencies: [])
let targetANode = TargetNode(project: project,
target: targetA,
dependencies: [targetBNode])
let graph = Graph.test(cache: graphCache)
graphCache.targetNodes[path] = [
"TargetA": targetANode,
"TargetB": targetBNode,
]
let nativeTargetA = createNativeTarget(for: targetA)
let nativeTargetB = createNativeTarget(for: targetB)
let graph = createGraph(project: .test(path: path),
dependencies: [
(target: targetA, dependencies: [targetB]),
(target: targetB, dependencies: []),
])
// When
try subject.generateTargetDependencies(path: path,
targets: [targetA, targetB],
nativeTargets: [
@ -52,6 +75,59 @@ final class TargetGeneratorTests: XCTestCase {
"TargetB": nativeTargetB,
],
graph: graph)
XCTAssertNotNil(nativeTargetA.dependencies.first)
// Then
XCTAssertEqual(nativeTargetA.dependencies.map(\.name), [
"TargetB",
])
}
// MARK: - Helpers
private func createTargetNodes(project: Project,
dependencies: [(target: Target, dependencies: [Target])]) -> [TargetNode] {
let nodesCache = Dictionary(uniqueKeysWithValues: dependencies.map {
($0.target.name, TargetNode(project: project,
target: $0.target,
dependencies: []))
})
dependencies.forEach {
let node = nodesCache[$0.target.name]!
node.dependencies = $0.dependencies.map { nodesCache[$0.name]! }
}
return dependencies.map { nodesCache[$0.target.name]! }
}
private func createGraph(project: Project,
dependencies: [(target: Target, dependencies: [Target])]) -> Graph {
let targetNodes = createTargetNodes(project: project, dependencies: dependencies)
let cache = GraphLoaderCache()
let graph = Graph.test(cache: cache)
targetNodes.forEach { cache.add(targetNode: $0) }
return graph
}
private func createNativeTarget(for target: Target) -> PBXNativeTarget {
let nativeTarget = PBXNativeTarget(name: target.name)
pbxproj.add(object: nativeTarget)
return nativeTarget
}
private func createPbxProject(pbxproj: PBXProj) -> PBXProject {
let configList = XCConfigurationList(buildConfigurations: [])
pbxproj.add(object: configList)
let mainGroup = PBXGroup()
pbxproj.add(object: mainGroup)
let pbxProject = PBXProject(name: "Project",
buildConfigurationList: configList,
compatibilityVersion: "0",
mainGroup: mainGroup)
pbxproj.add(object: pbxProject)
return pbxProject
}
}

View File

@ -19,17 +19,17 @@ final class TargetTests: XCTestCase {
func test_productName_when_staticLibrary() {
let target = Target.test(name: "Test", product: .staticLibrary)
XCTAssertEqual(target.productName, "libTest.a")
XCTAssertEqual(target.productNameWithExtension, "libTest.a")
}
func test_productName_when_dynamicLibrary() {
let target = Target.test(name: "Test", product: .dynamicLibrary)
XCTAssertEqual(target.productName, "libTest.dylib")
XCTAssertEqual(target.productNameWithExtension, "libTest.dylib")
}
func test_productName_when_app() {
let target = Target.test(name: "Test", product: .app)
XCTAssertEqual(target.productName, "Test.app")
XCTAssertEqual(target.productNameWithExtension, "Test.app")
}
func test_sequence_testBundles() {