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 sdk dependency type #430

Merged
merged 2 commits into from
Nov 4, 2018
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Added `weak` linking setting for dependencies [#411](https://github.com/yonaskolb/XcodeGen/pull/411) @alvarhansen
- Added `info` to targets for generating an `Info.plist` [#415](https://github.com/yonaskolb/XcodeGen/pull/415) @yonaskolb
- Added `entitlements` to targets for generating an `.entitlement` file [#415](https://github.com/yonaskolb/XcodeGen/pull/415) @yonaskolb
- Added `sdk` dependency type for linking system frameworks within the SDK [#430](https://github.com/yonaskolb/XcodeGen/pull/430) @yonaskolb
- Added `parallelizable` and `randomExecutionOrder` to `Scheme` test targets in an expanded form [#434](https://github.com/yonaskolb/XcodeGen/pull/434) @yonaskolb
- Validate incorrect config setting definitions [#431](https://github.com/yonaskolb/XcodeGen/pull/431) @yonaskolb
- Automatically set project `SDKROOT` if there is only a single platform within the project [#433](https://github.com/yonaskolb/XcodeGen/pull/433) @yonaskolb
Expand Down
7 changes: 4 additions & 3 deletions Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,9 @@ A dependency can be one of a 3 types:
- `target: name` - links to another target
- `framework: path` - links to a framework
- `carthage: name` - helper for linking to a Carthage framework
- `sdk: name` - links to a dependency with the SDK. This can either be a relative path within the sdk root or a single filename that references a framework (.framework) or lib (.tbd)

**Embed options**:

These only applied to `target` and `framework` dependencies.
**Linking options**:

- [ ] **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).
Expand Down Expand Up @@ -363,6 +362,8 @@ targets:
- target: MyFramework
- framework: path/to/framework.framework
- carthage: Result
- sdk: Contacts.framework
- sdk: libc++.tbd
MyFramework:
type: framework
```
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ targets:
dependencies:
- target: MyFramework
- carthage: Alamofire
- framework: Vendor/MyFramework.framework
- sdk: Contacts.framework
- sdk: libc++.tbd
MyFramework:
type: framework
platform: iOS
Expand Down
4 changes: 4 additions & 0 deletions Sources/ProjectSpec/Dependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public struct Dependency: Equatable {
case target
case framework
case carthage
case sdk
}
}

Expand All @@ -49,6 +50,9 @@ extension Dependency: JSONObjectConvertible {
} else if let carthage: String = jsonDictionary.json(atKeyPath: "carthage") {
type = .carthage
reference = carthage
} else if let sdk: String = jsonDictionary.json(atKeyPath: "sdk") {
type = .sdk
reference = sdk
} else {
throw SpecParsingError.invalidDependency(jsonDictionary)
}
Expand Down
20 changes: 18 additions & 2 deletions Sources/ProjectSpec/SpecValidation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,25 @@ extension Project {

for target in targets {
for dependency in target.dependencies {
if dependency.type == .target, getProjectTarget(dependency.reference) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference))
switch dependency.type {
case .target:
if getProjectTarget(dependency.reference) == nil {
errors.append(.invalidTargetDependency(target: target.name, dependency: dependency.reference))
}
case .sdk:
let path = Path(dependency.reference)
if !dependency.reference.contains("/") {
switch path.extension {
case "framework"?,
"tbd"?:
break
default:
errors.append(.invalidSDKDependency(target: target.name, dependency: dependency.reference))
}
}
default: break
}

}

for source in target.sources {
Expand Down
3 changes: 3 additions & 0 deletions Sources/ProjectSpec/SpecValidationError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public struct SpecValidationError: Error, CustomStringConvertible {

public enum ValidationError: Error, CustomStringConvertible {
case invalidXcodeGenVersion(minimumVersion: Version, version: Version)
case invalidSDKDependency(target: String, dependency: String)
case invalidTargetDependency(target: String, dependency: String)
case invalidTargetSource(target: String, source: String)
case invalidTargetConfigFile(target: String, configFile: String, config: String)
Expand All @@ -27,6 +28,8 @@ public struct SpecValidationError: Error, CustomStringConvertible {
switch self {
case let .invalidXcodeGenVersion(minimumVersion, version):
return "XcodeGen version is \(version), but minimum required version specified as \(minimumVersion)"
case let .invalidSDKDependency(target, dependency):
return "Target \(target.quoted) has invalid sdk dependency: \(dependency.quoted). It must be a full path or have the following extensions: .framework, .dylib, .tbd"
case let .invalidTargetDependency(target, dependency):
return "Target \(target.quoted) has invalid dependency: \(dependency.quoted)"
case let .invalidTargetConfigFile(target, configFile, config):
Expand Down
35 changes: 35 additions & 0 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class PBXProjGenerator {
var targetObjects: [String: PBXTarget] = [:]
var targetAggregateObjects: [String: PBXAggregateTarget] = [:]
var targetFileReferences: [String: PBXFileReference] = [:]
var sdkFileReferences: [String: PBXFileReference] = [:]

var carthageFrameworksByPlatform: [String: Set<PBXFileElement>] = [:]
var frameworkFiles: [PBXFileElement] = []
Expand Down Expand Up @@ -522,6 +523,38 @@ public class PBXProjGenerator {

let buildPath = Path(dependency.reference).parent().string.quoted
frameworkBuildPaths.insert(buildPath)
case .sdk:

var dependencyPath = Path(dependency.reference)
if !dependency.reference.contains("/") {
switch dependencyPath.extension ?? "" {
case "framework":
dependencyPath = Path("System/Library/Frameworks") + dependencyPath
case "tbd":
dependencyPath = Path("usr/lib") + dependencyPath
default: break
}
}

let fileReference: PBXFileReference
if let existingFileReferences = sdkFileReferences[dependency.reference] {
fileReference = existingFileReferences
} else {
fileReference = addObject(
PBXFileReference(sourceTree: .sdkRoot,
name: dependencyPath.lastComponent,
lastKnownFileType: Xcode.fileType(path: dependencyPath),
path: dependencyPath.string)
)
sdkFileReferences[dependency.reference] = fileReference
frameworkFiles.append(fileReference)
}

let buildFile = addObject(
PBXBuildFile(file: fileReference,
settings: getDependencyFrameworkSettings(dependency: dependency))
)
targetFrameworkBuildFiles.append(buildFile)

case .carthage:
guard target.type != .staticLibrary else { break }
Expand Down Expand Up @@ -943,6 +976,8 @@ public class PBXProjGenerator {
// don't want a dependency if it's going to be embedded or statically linked in a non-top level target
// in .target check we filter out targets that will embed all of their dependencies
switch dependency.type {
case .sdk:
dependencies[dependency.reference] = dependency
case .framework, .carthage:
if isTopLevel || dependency.embed == nil {
dependencies[dependency.reference] = dependency
Expand Down
3 changes: 2 additions & 1 deletion Tests/Fixtures/TestProject/App_iOS/ViewController.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import UIKit
import Contacts

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
_ = CNContact()
}

override func didReceiveMemoryWarning() {
Expand Down
10 changes: 10 additions & 0 deletions Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
BF_47A75C8A7EF15658238E254C846C5C6B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG_30676EBEE9BE54AA26CAE69BE744CAE8 /* Main.storyboard */; };
BF_47FBEFEA08B9642C1F711EDA77FC8C89 /* LocalizedStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG_FE6D89DA2D7E7F58340D566590FF221C /* LocalizedStoryboard.storyboard */; };
BF_47FF83A37355E90F93C0F5B2CFBCE317 /* Standalone.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR_82E3C6C060C4487B5177509917C3FAE3 /* Standalone.swift */; };
BF_486398284252AEE9001DD72E5E710D93 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = FR_6A3D8067EBC18CB321826EB21FEBD094 /* libc++.tbd */; };
BF_4D56F3F4D081A77C11325B96DD34D8E1 /* FrameworkFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR_261C31660333EF514356EFCBDB368EAB /* FrameworkFile.swift */; };
BF_4EBBAD70FA73DDC89BD933866B90DD08 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = FR_A957DAE2193BE1E970F452BFEFF3EBF6 /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; };
BF_51D370314B5DA8E002A908021E459F50 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FR_A55A35F549FD72775C37ED05342812AA /* Assets.xcassets */; };
Expand All @@ -70,6 +71,7 @@
BF_90F0E9253F2768C312B65530931CD55A /* Empty.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FR_DBD29CA78CDBBEF69B2C39C6D23BBDA1 /* Empty.h */; };
BF_910C2D6055BD22643F042545CD21AA78 /* StaticLibrary_ObjC.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FR_D0BE69522DB875ADB041E9135E0767CA /* StaticLibrary_ObjC.h */; };
BF_93A4E1A93C7DB4289E526466D547E55E /* SomeFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FR_2F56FD7A1F7782467AC9F315B6133468 /* SomeFramework.framework */; };
BF_9743BCF98E3EF021AB384652DA126416 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FR_9A09F93850576AF57455BE58FD53C42F /* Contacts.framework */; };
BF_9830FFD35765AACD86D1B619E22830D6 /* Headers in Headers */ = {isa = PBXBuildFile; fileRef = FR_3DD4DE355C21662A9169EE41C44F73E3 /* Headers */; settings = {ATTRIBUTES = (Public, ); }; };
BF_98DEE7FD578AA439550E0023A2ECE8D0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR_25BDF725104C5E280D45CBF33F54C72C /* ViewController.swift */; };
BF_9A74AED05E2F61530BFA0F7D23AF0112 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = VG_EB676136B9F946373D4796800CC00AD4 /* Model.xcdatamodeld */; settings = {COMPILER_FLAGS = "-Werror"; }; };
Expand All @@ -91,6 +93,7 @@
BF_CD062A97959629BD672893FDAE89A1E9 /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR_BB49B398F9291781A60DA963A0BF168C /* NotificationController.swift */; };
BF_D0676C98017B6FDD96A733CB851645DE /* StaticLibrary_ObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = FR_C2A280C4FA602E6610BCFB820602B69E /* StaticLibrary_ObjC.m */; };
BF_D0D1D142403C75D11757CB7092E8D035 /* XPC Service.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = FR_1FA381C639CE4923ED6845790A5DF9D2 /* XPC Service.xpc */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BF_D2532BE2FA7D664875AD6E29A22269C0 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FR_9A09F93850576AF57455BE58FD53C42F /* Contacts.framework */; };
BF_D3D64E2595369BBDEF03E07543AE2779 /* FrameworkFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR_261C31660333EF514356EFCBDB368EAB /* FrameworkFile.swift */; };
BF_D6588CD7B83034816FFD3A7DB10DAD78 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FR_16B791CFA2095097A17CC216977DF6EF /* Assets.xcassets */; };
BF_D7E271A6820E0A908736F44F99341DE1 /* Framework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FR_EFD283107EDF836BF0D9F4EB3F9A0016 /* Framework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -440,6 +443,7 @@
FR_640ADF15D2B92FDC35556B2E0934C21C /* App_macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App_macOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
FR_654475F320EF1630562C841CA8B6938A /* Framework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FR_6643E0FE8DD9AAC71691A739070C6833 /* Model 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Model 3.xcdatamodel"; sourceTree = "<group>"; };
FR_6A3D8067EBC18CB321826EB21FEBD094 /* libc++.tbd */ = {isa = PBXFileReference; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
FR_752FB5DFFBC490CFB9742549A0C48527 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
FR_7532DD7B78451A5040048474AC4FBCCC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
FR_7C782E8FBF41DE8BB1E3789DF2C8C1F7 /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = "<group>"; };
Expand All @@ -451,6 +455,7 @@
FR_8B770F475242D91FC20289A3B35CD165 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = "<group>"; };
FR_96127F4D9D804B89024AB846F0961621 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.module; path = module.modulemap; sourceTree = "<group>"; };
FR_98BB4C8D33EB0E666C136ACD08B21EB3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
FR_9A09F93850576AF57455BE58FD53C42F /* Contacts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Contacts.framework; path = System/Library/Frameworks/Contacts.framework; sourceTree = SDKROOT; };
FR_9B4B00A3CDADD50167B2393562AEBAB2 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.module; path = module.modulemap; sourceTree = "<group>"; };
FR_A0867B127ACF3ED382EB2FD5133D3EA8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
FR_A0DA89632C14F8DF99F15196E6EBB7D5 /* Framework2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Framework2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -495,6 +500,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BF_D2532BE2FA7D664875AD6E29A22269C0 /* Contacts.framework in Frameworks */,
BF_F8D73622DA7CFF30DB9AD17C08C63655 /* Framework2.framework in Frameworks */,
BF_0378AD9857D61219363F74B2FA308B5A /* Framework.framework in Frameworks */,
BF_B8E824A58BFE0B81E16BB26087FDC8B4 /* Result.framework in Frameworks */,
Expand All @@ -515,9 +521,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BF_9743BCF98E3EF021AB384652DA126416 /* Contacts.framework in Frameworks */,
BF_6E2D4086A22C12D516A06AE66DC48D16 /* Framework.framework in Frameworks */,
BF_230439786C4C6849F488A5FADC6A42A5 /* Result.framework in Frameworks */,
BF_36BCFE51A59D5EAE19684491C9F5427F /* StaticLibrary_ObjC.a in Frameworks */,
BF_486398284252AEE9001DD72E5E710D93 /* libc++.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -658,6 +666,8 @@
isa = PBXGroup;
children = (
G_A1CE8BFBEAC6AFDC64D7068C3CE11421 /* Carthage */,
FR_9A09F93850576AF57455BE58FD53C42F /* Contacts.framework */,
FR_6A3D8067EBC18CB321826EB21FEBD094 /* libc++.tbd */,
FR_2F56FD7A1F7782467AC9F315B6133468 /* SomeFramework.framework */,
);
name = Frameworks;
Expand Down
3 changes: 3 additions & 0 deletions Tests/Fixtures/TestProject/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ targets:
dependencies:
- target: Framework_macOS
- target: XPC Service
- sdk: Contacts.framework
- sdk: libc++.tbd

App_iOS:
type: application
Expand Down Expand Up @@ -90,6 +92,7 @@ targets:
- target: App_watchOS
- target: iMessageApp
- framework: Vendor/SomeFramework.framework
- sdk: Contacts.framework
scheme:
testTargets:
- App_iOS_Tests
Expand Down
14 changes: 14 additions & 0 deletions Tests/XcodeGenKitTests/ProjectSpecTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,20 @@ class ProjectSpecTests: XCTestCase {
try expectValidationError(project, .invalidTargetSchemeConfigVariant(target: "target1", configVariant: "invalidVariant", configType: .debug))
}

$0.it("fails with invalid sdk dependency") {
var project = baseProject
project.targets = [Target(
name: "target1",
type: .application,
platform: .iOS,
dependencies: [Dependency(type: .sdk, reference: "invalidDependency")]
)
]

try expectValidationError(project, .invalidSDKDependency(target: "target1", dependency: "invalidDependency"))
}


$0.it("fails with invalid scheme") {
var project = baseProject
project.schemes = [Scheme(
Expand Down
4 changes: 3 additions & 1 deletion Tests/XcodeGenKitTests/SpecLoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,14 @@ class SpecLoadingTests: XCTestCase {
["target": "name", "embed": false],
["carthage": "name"],
["framework": "path", "weak": true],
["sdk": "Contacts.framework"],
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
try expect(target.dependencies.count) == 3
try expect(target.dependencies.count) == 4
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false)
try expect(target.dependencies[1]) == Dependency(type: .carthage, reference: "name")
try expect(target.dependencies[2]) == Dependency(type: .framework, reference: "path", weakLink: true)
try expect(target.dependencies[3]) == Dependency(type: .sdk, reference: "Contacts.framework")
}

$0.it("parses info plist") {
Expand Down