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

Allow specifying run script and copy files phase ordering #402

Merged
merged 8 commits into from
Nov 8, 2018
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#### Fixed
- Fixed XPC Service package type [#435](https://github.com/yonaskolb/XcodeGen/pull/435) @alvarhansen
- Fixed phase ordering for modulemap and static libary header Copy File phases. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones

#### Changed
- Changed spelling of build phases to **preBuildPhase** and **postBuildPhase**. [402](https://github.com/yonaskolb/XcodeGen/pull/402) @brentleyjones

## 2.0.0

Expand Down
22 changes: 16 additions & 6 deletions Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,9 @@ Settings are merged in the following order: groups, base, configs.
- [ ] **transitivelyLinkDependencies**: **Bool** - If this is not specified the value from the project set in [Options](#options)`.transitivelyLinkDependencies` will be used.
- [ ] **directlyEmbedCarthageDependencies**: **Bool** - If this is `true` Carthage dependencies will be embedded using an `Embed Frameworks` build phase instead of the `copy-frameworks` script. Defaults to `true` for all targets except iOS/tvOS/watchOS Applications.
- [ ] **requiresObjCLinking**: **Bool** - If this is `true` any targets that link to this target will have `-ObjC` added to their `OTHER_LDFLAGS`. This is required if a static library has any catagories or extensions on Objective-C code. See [this guide](https://pewpewthespells.com/blog/objc_linker_flags.html#objc) for more details. Defaults to `true` if `type` is `library.static`. If you are 100% sure you don't have catagories or extensions on Objective-C code (pure Swift with no use of Foundation/UIKit) you can set this to `false`, otherwise it's best to leave it alone.
- [ ] **prebuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *before* any other build phases
- [ ] **postbuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *after* any other build phases
- [ ] **preBuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *before* any other build phases
- [ ] **postCompileScripts**: **[[Build Script](#build-script)]** - Build scripts that run after the Compile Sources phase
- [ ] **postBuildScripts**: **[[Build Script](#build-script)]** - Build scripts that run *after* any other build phases
- [ ] **buildRules**: **[[Build Rule](#build-rule)]** - Custom build rules
- [ ] **scheme**: **[Target Scheme](#target-scheme)** - Generated scheme with tests or config variants
- [ ] **legacy**: **[Legacy Target](#legacy-target)** - When present, opt-in to make an Xcode "External Build System" legacy target instead.
Expand Down Expand Up @@ -404,7 +405,13 @@ targets:

### Build Script

Run script build phases added via **prebuildScripts** or **postBuildScripts**. They run before or after any other build phases respectively and in the order defined. Each script can contain:
Run script build phases can be added at 3 different points in the build:
brentleyjones marked this conversation as resolved.
Show resolved Hide resolved

- **preBuildScripts**: Before any other build phases
- **postCompileScripts**: After the compile sources build phase
- **postBuildScripts**: After any other build phases

Each script can contain:

- [x] **path**: **String** - a relative or absolute path to a shell script
- [x] **script**: **String** - an inline shell script
Expand All @@ -422,7 +429,7 @@ A multiline script can be written using the various YAML multiline methods, for
```yaml
targets:
MyTarget:
prebuildScripts:
preBuildScripts:
- path: myscripts/my_script.sh
name: My Script
inputFiles:
Expand All @@ -431,12 +438,15 @@ targets:
outputFiles:
- $(DERIVED_FILE_DIR)/file1
- $(DERIVED_FILE_DIR)/file2
postbuildScripts:
postCompileScripts:
- script: swiftlint
name: Swiftlint
- script: |
command do
othercommand
postBuildScripts:
- path: myscripts/my_final_script.sh
name: My Final Script
```

### Build Rule
Expand Down Expand Up @@ -513,7 +523,7 @@ This is used to override settings or run build scripts in specific targets

- [x] **targets**: **[String]** - The list of target names to include as target dependencies
- [ ] **configFiles**: **[Config Files](#config-files)** - `.xcconfig` files per config
- [ ] **settings**: **[Settings](#settings)** - Target specific build settings.
- [ ] **settings**: **[Settings](#settings)** - Target specific build settings.
- [ ] **buildScripts**: **[[Build Script](#build-script)]** - Build scripts to run
- [ ] **scheme**: **[Target Scheme](#target-scheme)** - Generated scheme
- [ ] **attributes**: **[String: Any]** - This sets values in the project `TargetAttributes`. It is merged with `attributes` from the project and anything automatically added by XcodeGen, with any duplicate values being override by values specified here
Expand Down
2 changes: 1 addition & 1 deletion Sources/ProjectSpec/ProjectTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public protocol ProjectTarget: BuildSettingsContainer {
extension Target {

public var buildScripts: [BuildScript] {
return prebuildScripts + postbuildScripts
return preBuildScripts + postCompileScripts + postBuildScripts
}
}

Expand Down
25 changes: 15 additions & 10 deletions Sources/ProjectSpec/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ public struct Target: ProjectTarget {
public var transitivelyLinkDependencies: Bool?
public var directlyEmbedCarthageDependencies: Bool?
public var requiresObjCLinking: Bool?
public var prebuildScripts: [BuildScript]
public var postbuildScripts: [BuildScript]
public var preBuildScripts: [BuildScript]
public var postCompileScripts: [BuildScript]
brentleyjones marked this conversation as resolved.
Show resolved Hide resolved
public var postBuildScripts: [BuildScript]
public var buildRules: [BuildRule]
public var configFiles: [String: String]
public var scheme: TargetScheme?
Expand Down Expand Up @@ -70,8 +71,9 @@ public struct Target: ProjectTarget {
transitivelyLinkDependencies: Bool? = nil,
directlyEmbedCarthageDependencies: Bool? = nil,
requiresObjCLinking: Bool? = nil,
prebuildScripts: [BuildScript] = [],
postbuildScripts: [BuildScript] = [],
preBuildScripts: [BuildScript] = [],
postCompileScripts: [BuildScript] = [],
postBuildScripts: [BuildScript] = [],
buildRules: [BuildRule] = [],
scheme: TargetScheme? = nil,
legacy: LegacyTarget? = nil,
Expand All @@ -91,8 +93,9 @@ public struct Target: ProjectTarget {
self.transitivelyLinkDependencies = transitivelyLinkDependencies
self.directlyEmbedCarthageDependencies = directlyEmbedCarthageDependencies
self.requiresObjCLinking = requiresObjCLinking
self.prebuildScripts = prebuildScripts
self.postbuildScripts = postbuildScripts
self.preBuildScripts = preBuildScripts
self.postCompileScripts = postCompileScripts
self.postBuildScripts = postBuildScripts
self.buildRules = buildRules
self.scheme = scheme
self.legacy = legacy
Expand Down Expand Up @@ -218,8 +221,9 @@ extension Target: Equatable {
lhs.info == rhs.info &&
lhs.entitlements == rhs.entitlements &&
lhs.dependencies == rhs.dependencies &&
lhs.prebuildScripts == rhs.prebuildScripts &&
lhs.postbuildScripts == rhs.postbuildScripts &&
lhs.preBuildScripts == rhs.preBuildScripts &&
lhs.postCompileScripts == rhs.postCompileScripts &&
lhs.postBuildScripts == rhs.postBuildScripts &&
lhs.buildRules == rhs.buildRules &&
lhs.scheme == rhs.scheme &&
lhs.legacy == rhs.legacy &&
Expand Down Expand Up @@ -298,8 +302,9 @@ extension Target: NamedJSONDictionaryConvertible {
directlyEmbedCarthageDependencies = jsonDictionary.json(atKeyPath: "directlyEmbedCarthageDependencies")
requiresObjCLinking = jsonDictionary.json(atKeyPath: "requiresObjCLinking")

prebuildScripts = jsonDictionary.json(atKeyPath: "prebuildScripts") ?? []
postbuildScripts = jsonDictionary.json(atKeyPath: "postbuildScripts") ?? []
preBuildScripts = jsonDictionary.json(atKeyPath: "preBuildScripts") ?? jsonDictionary.json(atKeyPath: "prebuildScripts") ?? []
Copy link
Owner

Choose a reason for hiding this comment

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

Makes me think the lint command in #214 could also be used to highlight deprecations

postCompileScripts = jsonDictionary.json(atKeyPath: "postCompileScripts") ?? []
postBuildScripts = jsonDictionary.json(atKeyPath: "postBuildScripts") ?? jsonDictionary.json(atKeyPath: "postbuildScripts") ?? []
buildRules = jsonDictionary.json(atKeyPath: "buildRules") ?? []
scheme = jsonDictionary.json(atKeyPath: "scheme")
legacy = jsonDictionary.json(atKeyPath: "legacy")
Expand Down
19 changes: 17 additions & 2 deletions Sources/ProjectSpec/TargetSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public struct TargetSource: Equatable {
public struct CopyFilesSettings: Equatable, Hashable {
public static let xpcServices = CopyFilesSettings(
destination: .productsDirectory,
subpath: "$(CONTENTS_FOLDER_PATH)/XPCServices"
subpath: "$(CONTENTS_FOLDER_PATH)/XPCServices",
phaseOrder: .postCompile
)

public enum Destination: String {
Expand Down Expand Up @@ -72,13 +73,26 @@ public struct TargetSource: Equatable {
}
}
}

public enum PhaseOrder: String {
/// Run before the Compile Sources phase
case preCompile
/// Run after the Compile Sources and post-compile Run Script phases
case postCompile
}

public var destination: Destination
public var subpath: String
public var phaseOrder: PhaseOrder

public init(destination: Destination, subpath: String) {
public init(
destination: Destination,
subpath: String,
phaseOrder: PhaseOrder
) {
self.destination = destination
self.subpath = subpath
self.phaseOrder = phaseOrder
}
}

Expand Down Expand Up @@ -190,5 +204,6 @@ extension TargetSource.BuildPhase.CopyFilesSettings: JSONObjectConvertible {
public init(jsonDictionary: JSONDictionary) throws {
destination = try jsonDictionary.json(atKeyPath: "destination")
subpath = jsonDictionary.json(atKeyPath: "subpath") ?? ""
phaseOrder = .postCompile
}
}
72 changes: 39 additions & 33 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,15 @@ public class PBXProjGenerator {
)
return addObject(shellScriptPhase)
}

func generateCopyFiles(targetName: String, copyFiles: TargetSource.BuildPhase.CopyFilesSettings, buildPhaseFiles: [PBXBuildFile]) -> PBXCopyFilesBuildPhase {
let copyFilesBuildPhase = PBXCopyFilesBuildPhase(
dstPath: copyFiles.subpath,
dstSubfolderSpec: copyFiles.destination.destination,
files: buildPhaseFiles
)
return addObject(copyFilesBuildPhase)
}

func generateTargetAttributes() -> [PBXTarget: [String: Any]] {

Expand Down Expand Up @@ -634,7 +643,13 @@ public class PBXProjGenerator {
return sourceFilesByCopyFiles.mapValues { getBuildFilesForSourceFiles($0) }
}

buildPhases += try target.prebuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
copyFilesBuildPhasesFiles.merge(getBuildFilesForCopyFilesPhases()) { $0 + $1 }

buildPhases += try target.preBuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }

buildPhases += copyFilesBuildPhasesFiles
.filter { $0.key.phaseOrder == .preCompile }
.map { generateCopyFiles(targetName: target.name, copyFiles: $0, buildPhaseFiles: $1) }

let headersBuildPhaseFiles = getBuildFilesForPhase(.headers)
if !headersBuildPhaseFiles.isEmpty {
Expand All @@ -650,6 +665,8 @@ public class PBXProjGenerator {
let sourcesBuildPhase = addObject(PBXSourcesBuildPhase(files: sourcesBuildPhaseFiles))
buildPhases.append(sourcesBuildPhase)

buildPhases += try target.postCompileScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }

let resourcesBuildPhaseFiles = getBuildFilesForPhase(.resources) + copyResourcesReferences
if !resourcesBuildPhaseFiles.isEmpty {
let resourcesBuildPhase = addObject(PBXResourcesBuildPhase(files: resourcesBuildPhaseFiles))
Expand All @@ -676,19 +693,27 @@ public class PBXProjGenerator {
buildPhases.append(script)
}

copyFilesBuildPhasesFiles.merge(getBuildFilesForCopyFilesPhases()) { $0 + $1 }
if !copyFilesBuildPhasesFiles.isEmpty {
for (copyFiles, buildPhaseFiles) in copyFilesBuildPhasesFiles {
let copyFilesBuildPhase = addObject(
PBXCopyFilesBuildPhase(
dstPath: copyFiles.subpath,
dstSubfolderSpec: copyFiles.destination.destination,
files: buildPhaseFiles
)
)
buildPhases += copyFilesBuildPhasesFiles
.filter { $0.key.phaseOrder == .postCompile }
.map { generateCopyFiles(targetName: target.name, copyFiles: $0, buildPhaseFiles: $1) }

if !carthageFrameworksToEmbed.isEmpty {

buildPhases.append(copyFilesBuildPhase)
}
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 carthageExecutable = project.options.carthageExecutablePath ?? "carthage"
let carthageScript = addObject(
PBXShellScriptBuildPhase(
name: "Carthage",
inputPaths: inputPaths,
outputPaths: outputPaths,
shellPath: "/bin/sh",
shellScript: "\(carthageExecutable) copy-frameworks\n"
)
)
buildPhases.append(carthageScript)
}

if !targetFrameworkBuildFiles.isEmpty {
Expand Down Expand Up @@ -742,25 +767,6 @@ public class PBXProjGenerator {
buildPhases.append(copyFilesPhase)
}

if !carthageFrameworksToEmbed.isEmpty {

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 carthageExecutable = project.options.carthageExecutablePath ?? "carthage"
let carthageScript = addObject(
PBXShellScriptBuildPhase(
name: "Carthage",
inputPaths: inputPaths,
outputPaths: outputPaths,
shellPath: "/bin/sh",
shellScript: "\(carthageExecutable) copy-frameworks\n"
)
)
buildPhases.append(carthageScript)
}

let buildRules = target.buildRules.map { buildRule in
addObject(
PBXBuildRule(
Expand All @@ -776,7 +782,7 @@ public class PBXProjGenerator {
)
}

buildPhases += try target.postbuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }
buildPhases += try target.postBuildScripts.map { try generateBuildScript(targetName: target.name, buildScript: $0) }

let configs: [XCBuildConfiguration] = project.configs.map { config in
var buildSettings = project.getTargetBuildSettings(target: target, config: config)
Expand Down
6 changes: 4 additions & 2 deletions Sources/XcodeGenKit/SourceGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class SourceGenerator {
if headerVisibility == .public {
chosenBuildPhase = .copyFiles(TargetSource.BuildPhase.CopyFilesSettings(
destination: .productsDirectory,
subpath: "include/$(PRODUCT_NAME)"
subpath: "include/$(PRODUCT_NAME)",
phaseOrder: .preCompile
))
} else {
chosenBuildPhase = nil
Expand Down Expand Up @@ -188,7 +189,8 @@ class SourceGenerator {
guard targetType == .staticLibrary else { return nil }
return .copyFiles(TargetSource.BuildPhase.CopyFilesSettings(
destination: .productsDirectory,
subpath: "include/$(PRODUCT_NAME)"
subpath: "include/$(PRODUCT_NAME)",
phaseOrder: .preCompile
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This being .preCompile is a prime example of why these changes are needed.

))
case "framework": return .frameworks
case "xpc": return .copyFiles(.xpcServices)
Expand Down
12 changes: 6 additions & 6 deletions Tests/Fixtures/TestProject/Project.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -967,8 +967,8 @@
isa = PBXNativeTarget;
buildConfigurationList = CL_3BB54CFB2F2EFE7302C0B0709BD0AE04 /* Build configuration list for PBXNativeTarget "StaticLibrary_ObjC_macOS" */;
buildPhases = (
SBP_52AE5DCFB4487297D6889C7F014AD281 /* Sources */,
CFBP_712E86A86BBF89DB5B5F3874B24B8996 /* CopyFiles */,
SBP_52AE5DCFB4487297D6889C7F014AD281 /* Sources */,
);
buildRules = (
);
Expand Down Expand Up @@ -1031,8 +1031,8 @@
isa = PBXNativeTarget;
buildConfigurationList = CL_2E30D27084B352548CCAA3500158519B /* Build configuration list for PBXNativeTarget "StaticLibrary_ObjC_watchOS" */;
buildPhases = (
SBP_924258C7A9F27B13508604CAF625E0D3 /* Sources */,
CFBP_4DFB7882C9F8BAB250BB0A6B9457B4CC /* CopyFiles */,
SBP_924258C7A9F27B13508604CAF625E0D3 /* Sources */,
);
buildRules = (
);
Expand All @@ -1049,8 +1049,8 @@
buildPhases = (
SBP_ED63FCA00BD617EFF125ECDF9D24FAD8 /* Sources */,
RBP_5623703FDC4F6DF4679D8BAF27518F2F /* Resources */,
CFBP_469B033759EACBB99ECBF1008677C590 /* Embed App Extensions */,
SSBP_7F147E7ED45BAAE2186975B7FF9EB08A /* Carthage */,
CFBP_469B033759EACBB99ECBF1008677C590 /* Embed App Extensions */,
);
buildRules = (
);
Expand Down Expand Up @@ -1133,8 +1133,8 @@
isa = PBXNativeTarget;
buildConfigurationList = CL_F6ECF2D45799DBBB48DE9AE80AC280AA /* Build configuration list for PBXNativeTarget "StaticLibrary_ObjC_iOS" */;
buildPhases = (
SBP_D6209A702C851D0CFD1CC2B225986D45 /* Sources */,
CFBP_0B6C520DAC94DDCE678BE15C53528F25 /* CopyFiles */,
SBP_D6209A702C851D0CFD1CC2B225986D45 /* Sources */,
);
buildRules = (
);
Expand Down Expand Up @@ -1278,10 +1278,10 @@
SBP_77EF8BC7FC3D693C9C0C1CB51984F3E2 /* Sources */,
RBP_85DF5DDC76E0E2A78CAFC9A3EC508232 /* Resources */,
CFBP_D7E07645BC437C4DFBB212DFC4F3B09E /* CopyFiles */,
SSBP_7F8DED07519BA4B70C40A9A755844874 /* Carthage */,
FBP_18D4325BBE506E88E1C742F57AECB0CD /* Frameworks */,
CFBP_1C40B0777F31334440F91C2DB34EF404 /* Embed Frameworks */,
CFBP_961F46C30E720E886AEFD11D45DDA199 /* Embed Watch Content */,
SSBP_7F8DED07519BA4B70C40A9A755844874 /* Carthage */,
SSBP_062CBBF024005F57EECA660F9C7B0C7D /* Strip Unused Architectures from Frameworks */,
SSBP_376C0662E4E8416C049A50660864798B /* MyScript */,
);
Expand Down Expand Up @@ -1320,8 +1320,8 @@
isa = PBXNativeTarget;
buildConfigurationList = CL_91682ADED17A2C81A700A67F5D70BA1F /* Build configuration list for PBXNativeTarget "StaticLibrary_ObjC_tvOS" */;
buildPhases = (
SBP_645418A0FA6E3F5727685DE191C6B793 /* Sources */,
CFBP_CC1EF1963551F7E08925519982C248B9 /* CopyFiles */,
SBP_645418A0FA6E3F5727685DE191C6B793 /* Sources */,
);
buildRules = (
);
Expand Down
Loading