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

Add XCBuildConfiguration.append public method #450

Merged
merged 4 commits into from Jun 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 7 additions & 6 deletions CHANGELOG.md
Expand Up @@ -4,28 +4,29 @@

### Changed

- **Breaking** Change the UUID generation logic to generate ids with a length of 24 https://github.com/tuist/xcodeproj/pull/432 by @pepibumur.
- **Breaking** Renamed module from `xcodeproj` to `XcodeProj` https://github.com/tuist/xcodeproj/pull/398 by @pepibumur.
- Add `override` flag to `PBXGroup.addFile(at:,sourceTree:,sourceRoot:)` https://github.com/tuist/xcodeproj/pull/410 by @mrylmz
- Rename some internal variables to have a more representative name https://github.com/tuist/xcodeproj/pull/415 by @pepibumur.
- **Breaking** Change the UUID generation logic to generate ids with a length of 24 https://github.com/tuist/xcodeproj/pull/432 by @pepibumur.

### Added

- **Breaking** Add `SWIFT_COMPILATION_MODE` and `CODE_SIGN_IDENTITY` build settings, remove `DEBUG` flag for Release https://github.com/tuist/xcodeproj/pull/417 @dangthaison91
- **Breaking** Added throwing an error in case group path can't be resolved by @damirdavletov
- **Breaking** Added remote project support to PBXContainerItemProxy by @damirdavletov
- **Breaking** Add support for `RemoteRunnable` https://github.com/tuist/xcodeproj/pull/400 by @pepibumur.
- **Breaking** Swift 5 support https://github.com/tuist/xcodeproj/pull/397 by @pepibumur.
- Added `com.apple.product-type.application.watchapp2-container` to `PBXProductType`. https://github.com/tuist/xcodeproj/pull/441 by @leogdion.
- Add BatchUpdater to quickly add files to the group https://github.com/tuist/xcodeproj/pull/388 by @CognitiveDisson.
- **Breaking** Swift 5 support https://github.com/tuist/xcodeproj/pull/397 by @pepibumur.
- `WorkspaceSettings.autoCreateSchemes` attribute https://github.com/tuist/xcodeproj/pull/399 by @pepibumur
- Additional Swift 5 fixes: https://github.com/tuist/xcodeproj/pull/402 by @samisuteria
- Make build phase name public by @llinardos.
- Can access embed frameworks build phase for a target by @llinardos.
- Added `com.apple.product-type.framework.static` to `PBXProductType`. https://github.com/tuist/xcodeproj/pull/347 by @ileitch.
- Can add a not existing file to a group https://github.com/tuist/xcodeproj/pull/418 by @llinardos.
- **Breaking** Add `SWIFT_COMPILATION_MODE` and `CODE_SIGN_IDENTITY` build settings, remove `DEBUG` flag for Release https://github.com/tuist/xcodeproj/pull/417 @dangthaison91
- **Breaking** Added throwing an error in case group path can't be resolved by @damirdavletov
- **Breaking** Added remote project support to PBXContainerItemProxy by @damirdavletov
- **Breaking** Add support for `RemoteRunnable` https://github.com/tuist/xcodeproj/pull/400 by @pepibumur.
- Support for Swift PM Packages https://github.com/tuist/xcodeproj/pull/439 https://github.com/tuist/xcodeproj/pull/444 by @pepibumur @yonaskolb.
- `LaunchAction.customLaunchCommand` attribute https://github.com/tuist/xcodeproj/pull/451 by @pepibumur.
- `XCBuildConfiguration.append` method https://github.com/tuist/xcodeproj/pull/450 by @pepibumur.

### Fixed

Expand Down
14 changes: 14 additions & 0 deletions Sources/xcodeproj/Extensions/Array+Extras.swift
@@ -0,0 +1,14 @@
import Foundation

extension Array where Element: Hashable {
/// Return the array with all duplicates removed.
///
/// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234, as
/// per @Alexander's comment.
public func uniqued() -> [Element] {
var seen = Set<Element>()
return filter { seen.insert($0).inserted }
}
}
26 changes: 26 additions & 0 deletions Sources/xcodeproj/Objects/Configuration/XCBuildConfiguration.swift
Expand Up @@ -63,6 +63,32 @@ public final class XCBuildConfiguration: PBXObject {
name = try container.decode(.name)
try super.init(from: decoder)
}

// MARK: - Public

/// Appends a value to the given setting.
/// If the setting doesn't exist, it initializes it with the $(inherited) value and appends the given value to it.
///
/// - Parameters:
/// - name: Setting to which the value will be appended.
/// - value: Value to be appended.
public func append(setting name: String, value: String) {
guard !value.isEmpty else { return }

let existing: Any = buildSettings[name] ?? "$(inherited)"

switch existing {
case let string as String where string != value:
let newValue = [string, value].joined(separator: " ")
buildSettings[name] = newValue
case let array as [String]:
var newValue = array
newValue.append(value)
buildSettings[name] = newValue.uniqued()
default:
break
}
}
}

// MARK: - PlistSerializable
Expand Down
20 changes: 10 additions & 10 deletions Sources/xcodeproj/Objects/Files/PBXContainerItemProxy.swift
Expand Up @@ -20,20 +20,20 @@ public final class PBXContainerItemProxy: PBXObject {

var uuid: String {
switch self {
case .reference(let reference): return reference.value
case .string(let string): return string
case let .reference(reference): return reference.value
case let .string(string): return string
}
}

var id: RemoteGlobalID {
switch self {
case .reference(let reference):
case let .reference(reference):
if let object = reference.getObject() {
return .object(object)
} else {
return .string(reference.value)
}
case .string(let string): return .string(string)
case let .string(string): return .string(string)
}
}
}
Expand All @@ -44,15 +44,15 @@ public final class PBXContainerItemProxy: PBXObject {

var uuid: String {
switch self {
case .object(let object): return object.uuid
case .string(let string): return string
case let .object(object): return object.uuid
case let .string(string): return string
}
}

var reference: RemoteGlobalIDReference {
switch self {
case .object(let object): return .reference(object.reference)
case .string(let string): return .string(string)
case let .object(object): return .reference(object.reference)
case let .string(string): return .string(string)
}
}
}
Expand Down Expand Up @@ -103,7 +103,7 @@ public final class PBXContainerItemProxy: PBXObject {
remoteInfo: String? = nil) {
guard let containerPortalReference = containerPortal.reference else { fatalError("Container portal is mandatory field that has to be set to a known value instead of: \(containerPortal)") }
self.containerPortalReference = containerPortalReference
self.remoteGlobalIDReference = remoteGlobalID?.reference
remoteGlobalIDReference = remoteGlobalID?.reference
self.remoteInfo = remoteInfo
self.proxyType = proxyType
super.init()
Expand All @@ -129,7 +129,7 @@ public final class PBXContainerItemProxy: PBXObject {
proxyType = try container.decodeIntIfPresent(.proxyType).flatMap(ProxyType.init)
if let remoteGlobalIDString: String = try container.decodeIfPresent(.remoteGlobalIDString) {
let remoteGlobalReference = objectReferenceRepository.getOrCreate(reference: remoteGlobalIDString,
objects: objects)
objects: objects)
remoteGlobalIDReference = .reference(remoteGlobalReference)
}
remoteInfo = try container.decodeIfPresent(.remoteInfo)
Expand Down
Expand Up @@ -103,7 +103,7 @@ public class XCRemoteSwiftPackageReference: PBXContainerItem, PlistSerializable
/// - repositoryURL: Package repository url.
/// - versionRequirement: Package version rules.
public init(repositoryURL: String,
versionRequirement: VersionRequirement? = nil) {
versionRequirement: VersionRequirement? = nil) {
self.repositoryURL = repositoryURL
self.versionRequirement = versionRequirement
super.init()
Expand Down
Expand Up @@ -21,7 +21,7 @@ public class XCSwiftPackageProductDependency: PBXContainerItem, PlistSerializabl
// MARK: - Init

public init(productName: String,
package: XCRemoteSwiftPackageReference? = nil) {
package: XCRemoteSwiftPackageReference? = nil) {
self.productName = productName
packageReference = package?.reference
super.init()
Expand Down
2 changes: 1 addition & 1 deletion Sources/xcodeproj/Objects/Targets/PBXTarget.swift
Expand Up @@ -113,7 +113,7 @@ public class PBXTarget: PBXContainerItem {
buildPhaseReferences = buildPhases.references()
buildRuleReferences = buildRules.references()
dependencyReferences = dependencies.references()
self.packageProductDependencyReferences = packageProductDependencies.references()
packageProductDependencyReferences = packageProductDependencies.references()
self.name = name
self.productName = productName
productReference = product?.reference
Expand Down
4 changes: 2 additions & 2 deletions Sources/xcodeproj/Project/XcodeProj.swift
Expand Up @@ -31,9 +31,9 @@ public final class XcodeProj: Equatable {
let context = ProjectDecodingContext(
pbxProjValueReader: { key in
pbxProjDictionary[key]
}
}
)

let plistDecoder = XcodeprojPropertyListDecoder(context: context)
pbxproj = try plistDecoder.decode(PBXProj.self, from: pbxProjData)
try pbxproj.updateProjectName(path: pbxprojPaths.first!)
Expand Down
4 changes: 2 additions & 2 deletions Sources/xcodeproj/Scheme/XCScheme+LaunchAction.swift
Expand Up @@ -193,7 +193,7 @@ extension XCScheme {
region = element.attributes["region"]
launchAutomaticallySubstyle = element.attributes["launchAutomaticallySubstyle"]
customLaunchCommand = element.attributes["customLaunchCommand"]

try super.init(element: element)
}

Expand Down Expand Up @@ -282,7 +282,7 @@ extension XCScheme {
if let launchAutomaticallySubstyle = launchAutomaticallySubstyle {
element.attributes["launchAutomaticallySubstyle"] = launchAutomaticallySubstyle
}

if let customLaunchCommand = customLaunchCommand {
element.attributes["customLaunchCommand"] = customLaunchCommand
}
Expand Down
Expand Up @@ -18,6 +18,73 @@ final class XCBuildConfigurationTests: XCTestCase {
XCTAssertEqual(XCBuildConfiguration.isa, "XCBuildConfiguration")
}

func test_append_when_theSettingDoesntExist() {
// Given
let subject = XCBuildConfiguration(name: "Debug",
baseConfiguration: nil,
buildSettings: [:])

// When
subject.append(setting: "PRODUCT_NAME", value: "$(TARGET_NAME:c99extidentifier)")

// Then
XCTAssertEqual(subject.buildSettings["PRODUCT_NAME"] as? String, "$(inherited) $(TARGET_NAME:c99extidentifier)")
}

func test_append_when_theSettingExists() {
// Given
let subject = XCBuildConfiguration(name: "Debug",
baseConfiguration: nil,
buildSettings: ["OTHER_LDFLAGS": "flag1"])

// When
subject.append(setting: "OTHER_LDFLAGS", value: "flag2")

// Then
XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? String, "flag1 flag2")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I noticed this failed locally for me

XCTAssertEqual failed: ("Optional("flag2 flag1")") is not equal to ("Optional("flag1 flag2")")

It's due to using a Set to reconstruct the setting. I think this test itself is correct as it is, appending settings should append the new setting to the end.

}

func test_append_when_duplicateSettingExists() {
// Given
let subject = XCBuildConfiguration(name: "Debug",
baseConfiguration: nil,
buildSettings: ["OTHER_LDFLAGS": "flag1"])

// When
subject.append(setting: "OTHER_LDFLAGS", value: "flag1")

// Then
XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? String, "flag1")
}

func test_append_removesDuplicates_when_theSettingIsAnArray() {
// Given
let subject = XCBuildConfiguration(name: "Debug",
baseConfiguration: nil,
buildSettings: [
"OTHER_LDFLAGS": ["flag1", "flag2"],
])

// When
subject.append(setting: "OTHER_LDFLAGS", value: "flag1")

// Then
XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? [String], ["flag1", "flag2"])
}

func test_append_when_theSettingExistsAsAnArray() {
// Given
let subject = XCBuildConfiguration(name: "Debug",
baseConfiguration: nil,
buildSettings: ["OTHER_LDFLAGS": ["flag1", "flag2"]])

// When
subject.append(setting: "OTHER_LDFLAGS", value: "flag3")

// Then
XCTAssertEqual(subject.buildSettings["OTHER_LDFLAGS"] as? [String], ["flag1", "flag2", "flag3"])
}

pepicrft marked this conversation as resolved.
Show resolved Hide resolved
private func testDictionary() -> [String: Any] {
return [
"baseConfigurationReference": "baseConfigurationReference",
Expand Down
2 changes: 1 addition & 1 deletion Tests/xcodeprojTests/Utils/PBXBatchUpdaterTests.swift
Expand Up @@ -170,7 +170,7 @@ class PBXBatchUpdaterTests: XCTestCase {
proj.add(object: project)
return proj
}

private func createFile(at filePath: Path) throws {
let directoryURL = filePath.url.deletingLastPathComponent()
try FileManager.default.createDirectory(
Expand Down