diff --git a/CHANGELOG.md b/CHANGELOG.md index 119a371c..fa719390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ ## Master +### Added +- generate output files for Carthage copy-frameworks script [#84](https://github.com/yonaskolb/XcodeGen/pull/84) @mironal + +### Changed +- updated to xcproj 0.4.1 [#85](https://github.com/yonaskolb/XcodeGen/pull/85) @enmiller +- don't copy base settings if config type has been left out [#100](https://github.com/yonaskolb/XcodeGen/pull/100) @yonaskolb +- generate localised files under a single variant group [#70](https://github.com/yonaskolb/XcodeGen/pull/70) @ryohey + +### Fixed +- embed Carthage frameworks for macOS [#82](https://github.com/yonaskolb/XcodeGen/pull/82) @toshi0383 +- fixed copying of watchOS app resources [#96](https://github.com/yonaskolb/XcodeGen/pull/96) @keith +- automatically ignore more file types for a target's sources (entitlements, gpx, apns) [#94](https://github.com/yonaskolb/XcodeGen/pull/94) @keith +- change make build to a PHONY task [#98](https://github.com/yonaskolb/XcodeGen/pull/98) @keith +- allow copying of resource files from dependant targets [#95](https://github.com/yonaskolb/XcodeGen/pull/95) @keith +- fixed library linking [#93](https://github.com/yonaskolb/XcodeGen/pull/93) @keith + + + + + ## 1.2.4 ### Fixed diff --git a/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj b/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj index 14a7e34a..ce7e60b9 100644 --- a/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj +++ b/Fixtures/TestProject/GeneratedProject.xcodeproj/project.pbxproj @@ -1,8 +1,8 @@ // !$*UTF8*$! { archiveVersion = 1; - classes = ( - ); + classes = { + }; objectVersion = 46; objects = { @@ -10,13 +10,16 @@ BF1073850101 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR1332263601 /* AppDelegate.swift */; }; BF1462768401 = {isa = PBXBuildFile; fileRef = FR2653659501 /* TestProjectTests.xctest */; }; BF1744565901 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR6218091901 /* ViewController.swift */; }; - BF1911148401 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG6405436301 /* LaunchScreen.storyboard */; }; + BF2250910101 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG2043127501 /* Main.storyboard */; }; + BF2445564001 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG2858723001 /* LaunchScreen.storyboard */; }; + BF2513089601 /* LocalizedStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG3182922801 /* LocalizedStoryboard.storyboard */; }; BF2753556301 = {isa = PBXBuildFile; fileRef = FR2993497801 /* MyFramework.framework */; }; BF3154421201 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FR5980633301 /* Assets.xcassets */; }; BF3515549501 /* MyFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = FR7740960501 /* MyFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; - BF3862341101 /* MyFramework.framework in Copy Files */ = {isa = PBXBuildFile; fileRef = FR2993497801 /* MyFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - BF4946816301 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = VG1473702401 /* Main.storyboard */; }; + BF3862341101 /* MyFramework.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FR2993497801 /* MyFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BF5986511201 = {isa = PBXBuildFile; fileRef = FR6523263101 /* TestProject.app */; }; + BF6182896901 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FR9215298301 /* Result.framework */; }; + BF7015992001 /* MyFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FR2993497801 /* MyFramework.framework */; }; BF9001417701 /* TestProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR6877173101 /* TestProjectTests.swift */; }; BF9155249601 /* FrameworkFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = FR7078510801 /* FrameworkFile.swift */; }; /* End PBXBuildFile section */ @@ -45,7 +48,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - BF3862341101 /* MyFramework.framework in Copy Files */, + BF3862341101 /* MyFramework.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -56,21 +59,44 @@ FR1345298501 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; FR1345298502 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; FR1345298503 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FR1473702401 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; FR2653659501 /* TestProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = xctest; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = TestProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; FR2993497801 /* MyFramework.framework */ = {isa = PBXFileReference; explicitFileType = framework; includeInIndex = 0; lastKnownFileType = wrapper.framework; path = MyFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FR3546283901 /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; - FR3676338401 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - FR3676338402 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + FR4822987701 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LocalizedStoryboard.storyboard; sourceTree = ""; }; FR5980633301 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; FR6218091901 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; FR6334256101 /* config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = config.xcconfig; sourceTree = ""; }; + FR6405436301 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; FR6523263101 /* TestProject.app */ = {isa = PBXFileReference; explicitFileType = app; includeInIndex = 0; lastKnownFileType = wrapper.application; path = TestProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; FR6877173101 /* TestProjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestProjectTests.swift; sourceTree = ""; }; FR7078510801 /* FrameworkFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameworkFile.swift; sourceTree = ""; }; FR7740960501 /* MyFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyFramework.h; sourceTree = ""; }; + FR8182352201 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LocalizedStoryboard.strings; sourceTree = ""; }; + FR9215298301 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Result.framework; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFrameworksBuildPhase section */ + FBP652326301 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BF7015992001 /* MyFramework.framework in Frameworks */, + BF6182896901 /* Result.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + /* Begin PBXGroup section */ + G19527407101 /* Frameworks */ = { + isa = PBXGroup; + children = ( + G28836901501 /* Carthage */, + ); + name = Frameworks; + sourceTree = ""; + }; G26536595301 /* TestProjectTests */ = { isa = PBXGroup; children = ( @@ -81,6 +107,15 @@ path = TestProjectTests; sourceTree = ""; }; + G28836901501 /* Carthage */ = { + isa = PBXGroup; + children = ( + G47994500501 /* iOS */, + ); + name = Carthage; + path = Carthage/Build; + sourceTree = ""; + }; G29934978701 /* MyFramework */ = { isa = PBXGroup; children = ( @@ -92,6 +127,15 @@ path = MyFramework; sourceTree = ""; }; + G47994500501 /* iOS */ = { + isa = PBXGroup; + children = ( + FR9215298301 /* Result.framework */, + ); + name = iOS; + path = iOS; + sourceTree = ""; + }; G65232631501 /* TestProject */ = { isa = PBXGroup; children = ( @@ -99,8 +143,9 @@ FR5980633301 /* Assets.xcassets */, FR1345298502 /* Info.plist */, FR6218091901 /* ViewController.swift */, - VG6405436301 /* LaunchScreen.storyboard */, - VG1473702401 /* Main.storyboard */, + VG2858723001 /* LaunchScreen.storyboard */, + VG3182922801 /* LocalizedStoryboard.storyboard */, + VG2043127501 /* Main.storyboard */, ); name = TestProject; path = TestProject; @@ -124,6 +169,7 @@ G65232631501 /* TestProject */, G26536595301 /* TestProjectTests */, G86202385201 /* Products */, + G19527407101 /* Frameworks */, ); sourceTree = ""; }; @@ -206,7 +252,9 @@ SBP652326301 /* Sources */, RBP652326301 /* Resources */, HBP652326301 /* Headers */, + FBP652326301 /* Frameworks */, CFBP50493301 /* CopyFiles */, + SSBP58567701 /* Carthage */, SSBP24648001 /* Strip Unused Architectures from Frameworks */, SSBP19207501 /* Swiftlint */, ); @@ -227,7 +275,7 @@ attributes = { LastUpgradeCheck = 0900; }; - buildConfigurationList = XCCL81399401 /* Build configuration list for PBXProject */; + buildConfigurationList = XCCL81399401 /* Build configuration list for PBXProject "GeneratedProject" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; knownRegions = ( @@ -263,8 +311,9 @@ buildActionMask = 2147483647; files = ( BF3154421201 /* Assets.xcassets in Resources */, - BF1911148401 /* LaunchScreen.storyboard in Resources */, - BF4946816301 /* Main.storyboard in Resources */, + BF2445564001 /* LaunchScreen.storyboard in Resources */, + BF2513089601 /* LocalizedStoryboard.storyboard in Resources */, + BF2250910101 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -313,6 +362,22 @@ shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; + SSBP58567701 /* Carthage */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Carthage/Build/iOS/Result.framework", + ); + name = Carthage; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Result.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/local/bin/carthage copy-frameworks\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -357,22 +422,31 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ - VG1473702401 /* Main.storyboard */ = { + VG2043127501 /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( - FR3676338402 /* Base */, + FR1473702401 /* Base */, ); name = Main.storyboard; sourceTree = ""; }; - VG6405436301 /* LaunchScreen.storyboard */ = { + VG2858723001 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( - FR3676338401 /* Base */, + FR6405436301 /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; + VG3182922801 /* LocalizedStoryboard.storyboard */ = { + isa = PBXVariantGroup; + children = ( + FR4822987701 /* Base */, + FR8182352201 /* en */, + ); + name = LocalizedStoryboard.storyboard; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -409,6 +483,10 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = TestProject/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -482,6 +560,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = TestProjectTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -501,6 +583,10 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = TestProject/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -566,6 +652,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/iOS", + ); INFOPLIST_FILE = TestProjectTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -635,7 +725,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = ""; }; - XCCL81399401 /* Build configuration list for PBXProject */ = { + XCCL81399401 /* Build configuration list for PBXProject "GeneratedProject" */ = { isa = XCConfigurationList; buildConfigurations = ( XCBC47994501 /* Debug */, diff --git a/Fixtures/TestProject/TestProject/Base.lproj/LocalizedStoryboard.storyboard b/Fixtures/TestProject/TestProject/Base.lproj/LocalizedStoryboard.storyboard new file mode 100644 index 00000000..f9a048ed --- /dev/null +++ b/Fixtures/TestProject/TestProject/Base.lproj/LocalizedStoryboard.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Fixtures/TestProject/TestProject/en.lproj/LocalizedStoryboard.strings b/Fixtures/TestProject/TestProject/en.lproj/LocalizedStoryboard.strings new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Fixtures/TestProject/TestProject/en.lproj/LocalizedStoryboard.strings @@ -0,0 +1 @@ + diff --git a/Fixtures/TestProject/spec.yml b/Fixtures/TestProject/spec.yml index da59c0a8..05680d63 100644 --- a/Fixtures/TestProject/spec.yml +++ b/Fixtures/TestProject/spec.yml @@ -12,6 +12,7 @@ targets: INFOPLIST_FILE: TestProject/Info.plist dependencies: - target: MyFramework + - carthage: Result scheme: testTargets: - TestProjectTests diff --git a/Makefile b/Makefile index 1aab87e7..3fc7958a 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ install: build mkdir -p $(SHARE_PATH) cp -R $(CURRENT_PATH)/SettingPresets $(SHARE_PATH)/SettingPresets +.PHONY: build build: swift build --disable-sandbox -c release -Xswiftc -static-stdlib diff --git a/Package.resolved b/Package.resolved index 8a0e3bcf..58535b03 100644 --- a/Package.resolved +++ b/Package.resolved @@ -55,22 +55,13 @@ "version": "0.7.2" } }, - { - "package": "Unbox", - "repositoryURL": "https://github.com/JohnSundell/Unbox.git", - "state": { - "branch": null, - "revision": "17ad6aa1cc2f1efa27a0bbbdb66f79796708aa95", - "version": "2.5.0" - } - }, { "package": "Yams", "repositoryURL": "https://github.com/jpsim/Yams.git", "state": { "branch": null, - "revision": "5ac9248b378b5e12201c255ba74caf990d1e0f4b", - "version": "0.3.6" + "revision": "05921531200acff0d8d9caab5ec77b2bcf300b6b", + "version": "0.3.7" } }, { @@ -78,8 +69,8 @@ "repositoryURL": "https://github.com/xcodeswift/xcproj.git", "state": { "branch": null, - "revision": "e937b43cefa9c67a1787aba482ceb2f3ba172baf", - "version": "0.3.0" + "revision": "cd1c329c6e092fdf5afbddf10dd59da564293aa6", + "version": "0.4.1" } } ] diff --git a/README.md b/README.md index 9ac6b6dc..7242769a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ + + +

# XcodeGen @@ -108,7 +111,7 @@ $ xcodegen This will look for a project spec in the current directory called `project.yml` -Use `xcodegen help` to see the list of options: +Use `xcodegen --help` to see the list of options: - **--spec**: An optional path to a `.yml` or `.json` project spec - **--project**: An optional path to a directory where the project will be generated. By default this is the current directory diff --git a/Sources/ProjectSpec/ProjectExtensions.swift b/Sources/ProjectSpec/ProjectExtensions.swift index eedb2c79..93317b58 100644 --- a/Sources/ProjectSpec/ProjectExtensions.swift +++ b/Sources/ProjectSpec/ProjectExtensions.swift @@ -46,6 +46,14 @@ extension PBXProductType { } } + public var isFramework: Bool { + return self == .framework + } + + public var isLibrary: Bool { + return self == .staticLibrary || self == .dynamicLibrary + } + public var isExtension: Bool { return fileExtension == "appex" } diff --git a/Sources/ProjectSpec/Settings.swift b/Sources/ProjectSpec/Settings.swift index 6e84eeb8..3682a2f8 100644 --- a/Sources/ProjectSpec/Settings.swift +++ b/Sources/ProjectSpec/Settings.swift @@ -11,6 +11,7 @@ import JSONUtilities import xcproj import PathKit import Yams + public struct Settings: Equatable, JSONObjectConvertible, CustomStringConvertible { public let buildSettings: BuildSettings diff --git a/Sources/XcodeGenKit/PBXProjGenerator.swift b/Sources/XcodeGenKit/PBXProjGenerator.swift index cbe64f50..29963225 100644 --- a/Sources/XcodeGenKit/PBXProjGenerator.swift +++ b/Sources/XcodeGenKit/PBXProjGenerator.swift @@ -22,9 +22,10 @@ public class PBXProjGenerator { var fileReferencesByPath: [Path: String] = [:] var groupsByPath: [Path: PBXGroup] = [:] + var variantGroupsByPath: [Path: PBXVariantGroup] = [:] var targetNativeReferences: [String: String] = [:] - var targetBuildFileReferences: [String: String] = [:] + var targetBuildFiles: [String: PBXBuildFile] = [:] var targetFileReferences: [String: String] = [:] var topLevelGroups: [PBXGroup] = [] var carthageFrameworksByPlatform: [String: [String]] = [:] @@ -100,7 +101,7 @@ public class PBXProjGenerator { let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference.reference), fileRef: fileReference.reference) addObject(buildFile) - targetBuildFileReferences[target.name] = buildFile.reference + targetBuildFiles[target.name] = buildFile } let targets = try spec.targets.map(generateTarget) @@ -132,7 +133,8 @@ public class PBXProjGenerator { let knownRegions: [String] = ["en", "Base"] let projectAttributes: [String: Any] = ["LastUpgradeCheck": currentXcodeVersion].merged(spec.attributes) - let root = PBXProject(reference: project.rootObject, + let root = PBXProject(name: spec.name, + reference: project.rootObject, buildConfigurationList: buildConfigList.reference, compatibilityVersion: "Xcode 3.2", mainGroup: mainGroup.reference, @@ -158,7 +160,6 @@ public class PBXProjGenerator { settings = ["ATTRIBUTES": ["Public"]] } let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference), fileRef: fileReference, settings: settings) - addObject(buildFile) return SourceFile(path: path, fileReference: fileReference, buildFile: buildFile) } @@ -167,11 +168,11 @@ public class PBXProjGenerator { let carthageDependencies = getAllCarthageDependencies(target: target) let sourcePaths = target.sources.map { basePath + $0 } - var sourceFilePaths: [Path] = [] + var sourceFiles: [SourceFile] = [] for source in sourcePaths { let sourceGroups = try getGroups(path: source) - sourceFilePaths += sourceGroups.filePaths + sourceFiles += sourceGroups.sourceFiles } // find all Info.plist @@ -256,7 +257,9 @@ public class PBXProjGenerator { var dependencies: [String] = [] var targetFrameworkBuildFiles: [String] = [] - var copyFiles: [String] = [] + var copyFrameworksReferences: [String] = [] + var copyResourcesReferences: [String] = [] + var copyWatchReferences: [String] = [] var extensions: [String] = [] for dependency in target.dependencies { @@ -275,11 +278,15 @@ public class PBXProjGenerator { addObject(targetDependency) dependencies.append(targetDependency.reference) - // don't bother linking a target dependency - // let dependencyBuildFile = targetBuildFileReferences[dependencyTargetName]! - // targetFrameworkBuildFiles.append(dependencyBuildFile) + if dependencyTarget.type.isLibrary || dependencyTarget.type.isFramework { + let dependencyBuildFile = targetBuildFiles[dependencyTargetName]! + let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, dependencyBuildFile.reference + target.name), fileRef: dependencyBuildFile.fileRef) + addObject(buildFile) + targetFrameworkBuildFiles.append(buildFile.reference) + } + + if embed && !dependencyTarget.type.isLibrary { - if embed { let embedSettings = dependency.buildSettings let embedFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, dependencyFileReference + target.name), fileRef: dependencyFileReference, settings: embedSettings) addObject(embedFile) @@ -287,8 +294,12 @@ public class PBXProjGenerator { if dependencyTarget.type.isExtension { // embed app extension extensions.append(embedFile.reference) + } else if dependencyTarget.type.isFramework { + copyFrameworksReferences.append(embedFile.reference) + } else if dependencyTarget.type.isApp && dependencyTarget.platform == .watchOS { + copyWatchReferences.append(embedFile.reference) } else { - copyFiles.append(embedFile.reference) + copyResourcesReferences.append(embedFile.reference) } } @@ -307,7 +318,7 @@ public class PBXProjGenerator { if embed { let embedFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference, settings: dependency.buildSettings) addObject(embedFile) - copyFiles.append(embedFile.reference) + copyFrameworksReferences.append(embedFile.reference) } case .carthage: if carthageFrameworksByPlatform[target.platform.carthageDirectoryName] == nil { @@ -325,6 +336,11 @@ public class PBXProjGenerator { carthageFrameworksByPlatform[target.platform.carthageDirectoryName]?.append(fileReference) targetFrameworkBuildFiles.append(buildFile.reference) + if target.platform == .macOS && target.type.isApp { + let embedFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference + target.name), fileRef: fileReference, settings: dependency.buildSettings) + addObject(embedFile) + copyFrameworksReferences.append(embedFile.reference) + } } } @@ -332,7 +348,8 @@ public class PBXProjGenerator { var buildPhases: [String] = [] func getBuildFilesForPhase(_ buildPhase: BuildPhase) -> [String] { - let files = sourceFilePaths.filter { getBuildPhaseForPath($0) == buildPhase }.map(generateSourceFile) + let files = sourceFiles.filter { getBuildPhaseForPath($0.path) == buildPhase } + files.forEach { addObject($0.buildFile) } return files.map { $0.buildFile.reference } } @@ -366,7 +383,7 @@ public class PBXProjGenerator { addObject(sourcesBuildPhase) buildPhases.append(sourcesBuildPhase.reference) - let resourcesBuildPhase = PBXResourcesBuildPhase(reference: generateUUID(PBXResourcesBuildPhase.self, target.name), files: getBuildFilesForPhase(.resources)) + let resourcesBuildPhase = PBXResourcesBuildPhase(reference: generateUUID(PBXResourcesBuildPhase.self, target.name), files: getBuildFilesForPhase(.resources) + copyResourcesReferences) addObject(resourcesBuildPhase) buildPhases.append(resourcesBuildPhase.reference) @@ -397,26 +414,41 @@ public class PBXProjGenerator { buildPhases.append(copyFilesPhase.reference) } - if !copyFiles.isEmpty { + if !copyFrameworksReferences.isEmpty { let copyFilesPhase = PBXCopyFilesBuildPhase( reference: generateUUID(PBXCopyFilesBuildPhase.self, "embed frameworks" + target.name), dstPath: "", dstSubfolderSpec: .frameworks, - files: copyFiles) + files: copyFrameworksReferences) addObject(copyFilesPhase) buildPhases.append(copyFilesPhase.reference) } - let carthageFrameworksToEmbed = carthageDependencies - .filter { ($0.embed ?? true) } - .map { $0.reference } + if !copyWatchReferences.isEmpty { + + let copyFilesPhase = PBXCopyFilesBuildPhase( + reference: generateUUID(PBXCopyFilesBuildPhase.self, "embed watch content" + target.name), + dstPath: "$(CONTENTS_FOLDER_PATH)/Watch", + dstSubfolderSpec: .productsDirectory, + files: copyWatchReferences) + + addObject(copyFilesPhase) + buildPhases.append(copyFilesPhase.reference) + } + + let carthageFrameworksToEmbed = Array(Set(carthageDependencies + .filter { $0.embed ?? true } + .map { $0.reference })) + .sorted() + if !carthageFrameworksToEmbed.isEmpty { if target.type.isApp && target.platform != .macOS { - let inputPaths = Set(carthageFrameworksToEmbed).map { "$(SRCROOT)/\(carthageBuildPath)/\(target.platform)/\($0)\($0.contains(".") ? "" : ".framework")" } - let carthageScript = PBXShellScriptBuildPhase(reference: generateUUID(PBXShellScriptBuildPhase.self, "Carthage" + target.name), files: [], name: "Carthage", inputPaths: inputPaths, outputPaths: [], shellPath: "/bin/sh", shellScript: "/usr/local/bin/carthage copy-frameworks\n") + let inputPaths = carthageFrameworksToEmbed.map { "$(SRCROOT)/\(carthageBuildPath)/\(target.platform)/\($0)\($0.contains(".") ? "" : ".framework")" } + let outputPaths = carthageFrameworksToEmbed.map { "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/\($0)\($0.contains(".") ? "" : ".framework")" } + let carthageScript = PBXShellScriptBuildPhase(reference: generateUUID(PBXShellScriptBuildPhase.self, "Carthage" + target.name), files: [], name: "Carthage", inputPaths: inputPaths, outputPaths: outputPaths, shellPath: "/bin/sh", shellScript: "/usr/local/bin/carthage copy-frameworks\n") addObject(carthageScript) buildPhases.append(carthageScript.reference) } @@ -468,7 +500,7 @@ public class PBXProjGenerator { switch fileExtension { case "swift", "m", "cpp": return .sources case "h", "hh", "hpp", "ipp", "tpp", "hxx", "def": return .headers - case "xcconfig": return nil + case "xcconfig", "entitlements", "gpx", "lproj", "apns": return nil default: return .resources } } @@ -486,7 +518,7 @@ public class PBXProjGenerator { } } - func getGroups(path: Path, depth: Int = 0) throws -> (filePaths: [Path], groups: [PBXGroup]) { + func getGroups(path: Path, depth: Int = 0) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) { let excludedFiles: [String] = [".DS_Store"] @@ -503,34 +535,79 @@ public class PBXProjGenerator { .filter { $0.extension == "lproj" } .sorted { $0.lastComponent < $1.lastComponent } - var groupChildren: [String] = [] - var allFilePaths: [Path] = filePaths + var groupChildren: [String] = filePaths.map { getFileReference(path: $0, inPath: path) } + var allSourceFiles: [SourceFile] = filePaths.map { generateSourceFile(path: $0) } var groups: [PBXGroup] = [] for path in directories { let subGroups = try getGroups(path: path, depth: depth + 1) - allFilePaths += subGroups.filePaths + allSourceFiles += subGroups.sourceFiles groupChildren.append(subGroups.groups.first!.reference) groups += subGroups.groups } - for filePath in filePaths { - let fileReference = getFileReference(path: filePath, inPath: path) - groupChildren.append(fileReference) + // create variant groups of the base localisation first + var baseLocalisationVariantGroups:[PBXVariantGroup] = [] + if let baseLocalisedDirectory = localisedDirectories.first(where: { $0.lastComponent == "Base.lproj" }) { + for path in try baseLocalisedDirectory.children() { + let filePath = "\(baseLocalisedDirectory.lastComponent)/\(path.lastComponent)" + + let variantGroup: PBXVariantGroup + if let cachedGroup = variantGroupsByPath[path] { + variantGroup = cachedGroup + } else { + variantGroup = PBXVariantGroup(reference: generateUUID(PBXVariantGroup.self, filePath), + children: [], + name: path.lastComponent, + sourceTree: .group) + variantGroupsByPath[path] = variantGroup + + addObject(variantGroup) + groupChildren.append(variantGroup.reference) + } + + baseLocalisationVariantGroups.append(variantGroup) + + let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, variantGroup.reference), fileRef: variantGroup.reference, settings: nil) + allSourceFiles.append(SourceFile(path: path, fileReference: variantGroup.reference, buildFile: buildFile)) + } } + // add references to localised resources into base localisation variant groups for localisedDirectory in localisedDirectories { + let localisationName = localisedDirectory.lastComponentWithoutExtension for path in try localisedDirectory.children().sorted { $0.lastComponent < $1.lastComponent } { + + let filePath = "\(localisedDirectory.lastComponent)/\(path.lastComponent)" - let fileReference = PBXFileReference(reference: generateUUID(PBXFileReference.self, localisedDirectory.lastComponent), sourceTree: .group, name: localisedDirectory.lastComponentWithoutExtension, path: filePath) - addObject(fileReference) - let variantGroup = PBXVariantGroup(reference: generateUUID(PBXVariantGroup.self, path.lastComponent), children: [fileReference.reference], name: path.lastComponent, sourceTree: .group) - addObject(variantGroup) + // find base localisation variant group + let name = path.lastComponentWithoutExtension + let variantGroup = baseLocalisationVariantGroups.first { Path($0.name).lastComponentWithoutExtension == name } - fileReferencesByPath[path] = variantGroup.reference - groupChildren.append(variantGroup.reference) - allFilePaths.append(path) + let fileReference: String + if let cachedFileReference = fileReferencesByPath[path] { + fileReference = cachedFileReference + } else { + let reference = PBXFileReference(reference: generateUUID(PBXFileReference.self, path.lastComponent), + sourceTree: .group, + name: variantGroup != nil ? localisationName : path.lastComponent, + path: filePath) + addObject(reference) + fileReference = reference.reference + fileReferencesByPath[path] = fileReference + } + + if let variantGroup = variantGroup { + variantGroup.children.append(fileReference) + } else { + // add SourceFile to group if there is no Base.lproj directory + let buildFile = PBXBuildFile(reference: generateUUID(PBXBuildFile.self, fileReference), + fileRef: fileReference, + settings: nil) + allSourceFiles.append(SourceFile(path: path, fileReference: fileReference, buildFile: buildFile)) + groupChildren.append(fileReference) + } } } @@ -547,6 +624,6 @@ public class PBXProjGenerator { groupsByPath[path] = group } groups.insert(group, at: 0) - return (allFilePaths, groups) + return (allSourceFiles, groups) } } diff --git a/Sources/XcodeGenKit/SettingsBuilder.swift b/Sources/XcodeGenKit/SettingsBuilder.swift index 5cc62882..1e5214c6 100644 --- a/Sources/XcodeGenKit/SettingsBuilder.swift +++ b/Sources/XcodeGenKit/SettingsBuilder.swift @@ -16,11 +16,10 @@ import JSONUtilities extension ProjectSpec { public func getProjectBuildSettings(config: Config) -> BuildSettings { - var buildSettings: BuildSettings = [:] - buildSettings += SettingsPresetFile.base.getBuildSettings() if let type = config.type { + buildSettings += SettingsPresetFile.base.getBuildSettings() buildSettings += SettingsPresetFile.config(type).getBuildSettings() } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 9df44975..7d5c97d1 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -5,4 +5,5 @@ XCTMain([ testCase(GeneratorTests.allTests), testCase(SpecLoadingTests.allTests), testCase(FixtureTests.allTests), + testCase(ProjectSpecTests.allTests), ]) diff --git a/Tests/XcodeGenKitTests/FixtureTests.swift b/Tests/XcodeGenKitTests/FixtureTests.swift index 644319af..8fad8542 100644 --- a/Tests/XcodeGenKitTests/FixtureTests.swift +++ b/Tests/XcodeGenKitTests/FixtureTests.swift @@ -6,7 +6,7 @@ import ProjectSpec let fixturePath = Path(#file).parent().parent().parent() + "Fixtures" -func generate(specPath: Path, projectPath: Path) throws { +func generate(specPath: Path, projectPath: Path) throws -> XcodeProj { let spec = try ProjectSpec(path: specPath) let generator = ProjectGenerator(spec: spec, path: specPath.parent()) let project = try generator.generateProject() @@ -17,13 +17,47 @@ func generate(specPath: Path, projectPath: Path) throws { if newProject != oldProject { throw failure("\(projectPath.string) has changed. If change is legitimate commit the change and run test again") } + + return newProject } func fixtureTests() { describe("Test Project") { + var project: XcodeProj? + $0.it("generates") { - try generate(specPath: fixturePath + "TestProject/spec.yml", projectPath: fixturePath + "TestProject/GeneratedProject.xcodeproj") + project = try generate(specPath: fixturePath + "TestProject/spec.yml", projectPath: fixturePath + "TestProject/GeneratedProject.xcodeproj") + } + + $0.it("generates variant group") { + guard let project = project else { throw failure("Project is not generated") } + + func getFileReferences(_ path: String) -> [PBXFileReference] { + return project.pbxproj.fileReferences.filter { $0.path == path } + } + + func getVariableGroups(_ name: String?) -> [PBXVariantGroup] { + return project.pbxproj.variantGroups.filter { $0.name == name } + } + + let resourceName = "LocalizedStoryboard.storyboard" + let baseResource = "Base.lproj/LocalizedStoryboard.storyboard" + let localizedResource = "en.lproj/LocalizedStoryboard.strings" + + guard let variableGroup = getVariableGroups(resourceName).first else { throw failure("Couldn't find the variable group") } + + do { + let refs = getFileReferences(baseResource) + try expect(refs.count) == 1 + try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1 + } + + do { + let refs = getFileReferences(localizedResource) + try expect(refs.count) == 1 + try expect(variableGroup.children.filter { $0 == refs.first?.reference }.count) == 1 + } } } } diff --git a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift index 6655c1ed..6fb551a8 100644 --- a/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/ProjectGeneratorTests.swift @@ -62,6 +62,15 @@ func projectGeneratorTests() { try expect(configs).contains(name: "config1") try expect(configs).contains(name: "config2") } + + $0.it("clears config settings when missing type") { + let spec = ProjectSpec(name: "test", configs: [Config(name: "config")]) + let project = try getProject(spec) + guard let config = project.pbxproj.buildConfigurations.first else { + throw failure("configuration not found") + } + try expect(config.buildSettings.isEmpty).to.beTrue() + } $0.it("merges settings") { let spec = try ProjectSpec(path: fixturePath + "settings_test.yml") diff --git a/Tests/XcodeGenKitTests/ProjectSpecTests.swift b/Tests/XcodeGenKitTests/ProjectSpecTests.swift new file mode 100644 index 00000000..ae5ec8ad --- /dev/null +++ b/Tests/XcodeGenKitTests/ProjectSpecTests.swift @@ -0,0 +1,28 @@ +import Spectre +import XcodeGenKit +import xcproj +import ProjectSpec + +func projectSpecTests() { + + describe("ProjectSpec") { + + let framework = Target(name: "MyFramework", type: .framework, platform: .iOS, + settings: Settings(buildSettings: ["SETTING_2": "VALUE"])) + let staticLibrary = Target(name: "MyStaticLibrary", type: .staticLibrary, platform: .iOS, + settings: Settings(buildSettings: ["SETTING_2": "VALUE"])) + let dynamicLibrary = Target(name: "MyDynamicLibrary", type: .dynamicLibrary, platform: .iOS, + settings: Settings(buildSettings: ["SETTING_2": "VALUE"])) + + $0.describe("Types") { + $0.it("is a framework when it has the right extension") { + try expect(framework.type.isFramework).to.beTrue() + } + + $0.it("is a library when it has the right type") { + try expect(staticLibrary.type.isLibrary).to.beTrue() + try expect(dynamicLibrary.type.isLibrary).to.beTrue() + } + } + } +} diff --git a/Tests/XcodeGenKitTests/XCTest.swift b/Tests/XcodeGenKitTests/XCTest.swift index cb066129..e989d4bc 100644 --- a/Tests/XcodeGenKitTests/XCTest.swift +++ b/Tests/XcodeGenKitTests/XCTest.swift @@ -7,5 +7,6 @@ class XCodeGenKitTests: XCTestCase { projectGeneratorTests() specLoadingTests() fixtureTests() + projectSpecTests() } }