Skip to content
Open
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
5 changes: 5 additions & 0 deletions Docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ Note that target names can also be changed by adding a `name` property to a targ
- [ ] **postGenCommand**: **String** - A bash command to run after the project has been generated. If the project isn't generated due to no changes when using the cache then this won't run. This is useful for running things like `pod install` only if the project is actually regenerated.
- [ ] **useBaseInternationalization**: **Bool** If this is `false` and your project does not include resources located in a **Base.lproj** directory then `Base` will not be included in the projects 'known regions'. The default value is `true`.
- [ ] **schemePathPrefix**: **String** - A path prefix for relative paths in schemes, such as StoreKitConfiguration. The default is `"../../"`, which is suitable for non-workspace projects. For use in workspaces, use `"../"`.
- [ ] **defaultSourceDirectoryType**: **String** - When a [Target source](#target-source) doesn't specify a type and is a directory, this is the type that will be used. If nothing is specified for either then `group` will be used.
- `group` (default)
- `folder`
- `syncedFolder`

```yaml
options:
Expand Down Expand Up @@ -542,6 +546,7 @@ A source can be provided via a string (the path) or an object of the form:
- `file`: a file reference with a parent group will be created (Default for files or directories with extensions)
- `group`: a group with all it's containing files. (Default for directories without extensions)
- `folder`: a folder reference.
- `syncedFolder`: Xcode 16's synchronized folders, also knows as buildable folders
- [ ] **headerVisibility**: **String** - The visibility of any headers. This defaults to `public`, but can be either:
- `public`
- `private`
Expand Down
8 changes: 4 additions & 4 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
.package(url: "https://github.com/yonaskolb/JSONUtilities.git", from: "4.2.0"),
.package(url: "https://github.com/kylef/Spectre.git", from: "0.9.2"),
.package(url: "https://github.com/onevcat/Rainbow.git", from: "4.0.0"),
.package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.24.3"),
.package(url: "https://github.com/tuist/XcodeProj.git", exact: "8.27.7"),
.package(url: "https://github.com/jakeheis/SwiftCLI.git", from: "6.0.3"),
.package(url: "https://github.com/mxcl/Version", from: "2.0.0"),
.package(url: "https://github.com/freddi-kit/ArtifactBundleGen", exact: "0.0.6")
Expand Down
2 changes: 1 addition & 1 deletion Sources/ProjectSpec/CacheFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class CacheFile {

guard #available(OSX 10.13, *) else { return nil }

let files = Set(project.allFiles)
let files = Set(project.allTrackedFiles)
.map { ((try? $0.relativePath(from: project.basePath)) ?? $0).string }
.sorted { $0.localizedStandardCompare($1) == .orderedAscending }
.joined(separator: "\n")
Expand Down
22 changes: 19 additions & 3 deletions Sources/ProjectSpec/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ extension Project: PathContainer {

extension Project {

public var allFiles: [Path] {
public var allTrackedFiles: [Path] {
var files: [Path] = []
files.append(contentsOf: configFilePaths)
for fileGroup in fileGroups {
Expand All @@ -270,15 +270,31 @@ extension Project {
files.append(contentsOf: target.configFilePaths)
for source in target.sources {
let sourcePath = basePath + source.path
let sourceChildren = (try? sourcePath.recursiveChildren()) ?? []
files.append(contentsOf: sourceChildren)

let type = source.type ?? options.defaultSourceDirectoryType ?? .group
if type.projectTracksChildren {
let sourceChildren = (try? sourcePath.recursiveChildren()) ?? []
files.append(contentsOf: sourceChildren)
}
files.append(sourcePath)
}
}
return files
}
}

extension SourceType {

var projectTracksChildren: Bool {
switch self {
case .file: false
case .folder: false
case .group: true
case .syncedFolder: false
}
}
}

extension BuildSettingsContainer {

fileprivate var configFilePaths: [Path] {
Expand Down
1 change: 1 addition & 0 deletions Sources/ProjectSpec/SourceType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ public enum SourceType: String {
case group
case file
case folder
case syncedFolder
}
6 changes: 5 additions & 1 deletion Sources/ProjectSpec/SpecOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public struct SpecOptions: Equatable {
public var postGenCommand: String?
public var useBaseInternationalization: Bool
public var schemePathPrefix: String
public var defaultSourceDirectoryType: SourceType?

public enum ValidationType: String {
case missingConfigs
Expand Down Expand Up @@ -100,7 +101,8 @@ public struct SpecOptions: Equatable {
preGenCommand: String? = nil,
postGenCommand: String? = nil,
useBaseInternationalization: Bool = useBaseInternationalizationDefault,
schemePathPrefix: String = schemePathPrefixDefault
schemePathPrefix: String = schemePathPrefixDefault,
defaultSourceDirectoryType: SourceType? = nil
) {
self.minimumXcodeGenVersion = minimumXcodeGenVersion
self.carthageBuildPath = carthageBuildPath
Expand All @@ -127,6 +129,7 @@ public struct SpecOptions: Equatable {
self.postGenCommand = postGenCommand
self.useBaseInternationalization = useBaseInternationalization
self.schemePathPrefix = schemePathPrefix
self.defaultSourceDirectoryType = defaultSourceDirectoryType
}
}

Expand Down Expand Up @@ -160,6 +163,7 @@ extension SpecOptions: JSONObjectConvertible {
postGenCommand = jsonDictionary.json(atKeyPath: "postGenCommand")
useBaseInternationalization = jsonDictionary.json(atKeyPath: "useBaseInternationalization") ?? SpecOptions.useBaseInternationalizationDefault
schemePathPrefix = jsonDictionary.json(atKeyPath: "schemePathPrefix") ?? SpecOptions.schemePathPrefixDefault
defaultSourceDirectoryType = jsonDictionary.json(atKeyPath: "defaultSourceDirectoryType")
if jsonDictionary["fileTypes"] != nil {
fileTypes = try jsonDictionary.json(atKeyPath: "fileTypes")
} else {
Expand Down
18 changes: 14 additions & 4 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -686,8 +686,14 @@ public class PBXProjGenerator {
let infoPlistFiles: [Config: String] = getInfoPlists(for: target)
let sourceFileBuildPhaseOverrideSequence: [(Path, BuildPhaseSpec)] = Set(infoPlistFiles.values).map({ (project.basePath + $0, .none) })
let sourceFileBuildPhaseOverrides = Dictionary(uniqueKeysWithValues: sourceFileBuildPhaseOverrideSequence)
let sourceFiles = try sourceGenerator.getAllSourceFiles(targetType: target.type, sources: target.sources, buildPhases: sourceFileBuildPhaseOverrides)
.sorted { $0.path.lastComponent < $1.path.lastComponent }
let targetObject = targetObjects[target.name]!
let sourceFiles = try sourceGenerator.getAllSourceFiles(
target: targetObject,
targetType: target.type,
sources: target.sources,
buildPhases: sourceFileBuildPhaseOverrides
)
.sorted { $0.path.lastComponent < $1.path.lastComponent }

var anyDependencyRequiresObjCLinking = false

Expand Down Expand Up @@ -1439,8 +1445,6 @@ public class PBXProjGenerator {
defaultConfigurationName: defaultConfigurationName
))

let targetObject = targetObjects[target.name]!

let targetFileReference = targetFileReferences[target.name]

targetObject.name = target.name
Expand All @@ -1454,6 +1458,12 @@ public class PBXProjGenerator {
if !target.isLegacy {
targetObject.productType = target.type
}

// add fileSystemSynchronizedGroups
let synchronizedRootGroups = sourceFiles.compactMap { $0.fileReference as? PBXFileSystemSynchronizedRootGroup }
if !synchronizedRootGroups.isEmpty {
targetObject.fileSystemSynchronizedGroups = synchronizedRootGroups
}
}

private func makePlatformFilter(for filter: Dependency.PlatformFilter) -> String? {
Expand Down
56 changes: 51 additions & 5 deletions Sources/XcodeGenKit/SourceGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ class SourceGenerator {
/// - targetType: The type of target that the source files should belong to.
/// - sources: The array of sources defined as part of the targets spec.
/// - buildPhases: A dictionary containing any build phases that should be applied to source files at specific paths in the event that the associated `TargetSource` didn't already define a `buildPhase`. Values from this dictionary are used in cases where the project generator knows more about a file than the spec/filesystem does (i.e if the file should be treated as the targets Info.plist and so on).
func getAllSourceFiles(targetType: PBXProductType, sources: [TargetSource], buildPhases: [Path : BuildPhaseSpec]) throws -> [SourceFile] {
try sources.flatMap { try getSourceFiles(targetType: targetType, targetSource: $0, buildPhases: buildPhases) }
func getAllSourceFiles(target: PBXTarget, targetType: PBXProductType, sources: [TargetSource], buildPhases: [Path : BuildPhaseSpec]) throws -> [SourceFile] {
try sources.flatMap { try getSourceFiles(target: target, targetType: targetType, targetSource: $0, buildPhases: buildPhases) }
}

// get groups without build files. Use for Project.fileGroups
func getFileGroups(path: String) throws {
_ = try getSourceFiles(targetType: .none, targetSource: TargetSource(path: path), buildPhases: [:])
_ = try getSourceFiles(target: nil, targetType: .none, targetSource: TargetSource(path: path), buildPhases: [:])
}

func getFileType(path: Path) -> FileType? {
Expand Down Expand Up @@ -601,7 +601,7 @@ class SourceGenerator {
}

/// creates source files
private func getSourceFiles(targetType: PBXProductType, targetSource: TargetSource, buildPhases: [Path: BuildPhaseSpec]) throws -> [SourceFile] {
private func getSourceFiles(target: PBXTarget?, targetType: PBXProductType, targetSource: TargetSource, buildPhases: [Path: BuildPhaseSpec]) throws -> [SourceFile] {

// generate excluded paths
let path = project.basePath + targetSource.path
Expand Down Expand Up @@ -687,6 +687,42 @@ class SourceGenerator {

sourceFiles += groupSourceFiles
sourceReference = group
case .syncedFolder:

let relativePath = (try? path.relativePath(from: project.basePath)) ?? path

var exceptions: [PBXFileSystemSynchronizedExceptionSet] = []
let ignoredFiles = Array(buildPhases.filter { $0.value == .none }.keys)
if let target, !ignoredFiles.isEmpty {
let memberShipExceptions = ignoredFiles
.map { (try? $0.relativePath(from: project.basePath)) ?? $0 }
.map(\.string)
let exception = PBXFileSystemSynchronizedBuildFileExceptionSet(target: target, membershipExceptions: memberShipExceptions, publicHeaders: nil, privateHeaders: nil, additionalCompilerFlagsByRelativePath: nil, attributesByRelativePath: nil)
addObject(exception)
exceptions.append(exception)
}
let syncedRootGroup = PBXFileSystemSynchronizedRootGroup(
sourceTree: .group,
path: relativePath.string,
name: targetSource.name,
explicitFileTypes: [:],
exceptions: exceptions,
explicitFolders: []
)
addObject(syncedRootGroup)
sourceReference = syncedRootGroup

// TODO: adjust if hasCustomParent == true
rootGroups.insert(syncedRootGroup)

let sourceFile = generateSourceFile(
targetType: targetType,
targetSource: targetSource,
path: path,
fileReference: syncedRootGroup,
buildPhases: buildPhases
)
sourceFiles.append(sourceFile)
}

if hasCustomParent {
Expand All @@ -703,7 +739,17 @@ class SourceGenerator {
///
/// While `TargetSource` declares `type`, its optional and in the event that the value is not defined then we must resolve a sensible default based on the path of the source.
private func resolvedTargetSourceType(for targetSource: TargetSource, at path: Path) -> SourceType {
return targetSource.type ?? (path.isFile || path.extension != nil ? .file : .group)
if let chosenType = targetSource.type {
return chosenType
} else {
if path.isFile || path.extension != nil {
return .file
} else if let sourceType = project.options.defaultSourceDirectoryType {
return sourceType
} else {
return .group
}
}
}

private func createParentGroups(_ parentGroups: [String], for fileElement: PBXFileElement) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/XcodeGenKit/Version.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extension Project {
}

var objectVersion: UInt {
54
70
}

var minimizedProjectReferenceProxies: Int {
Expand Down
2 changes: 2 additions & 0 deletions Sources/XcodeGenKit/XCProjExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ extension PBXProj {
string += "\n 🌎 " + variantGroup.nameOrPath
} else if let versionGroup = child as? XCVersionGroup {
string += "\n 🔢 " + versionGroup.nameOrPath
} else if let syncedFolder = child as? PBXFileSystemSynchronizedRootGroup {
string += "\n 📁 " + syncedFolder.nameOrPath
}
}
return string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 70;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -322,8 +322,6 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
TargetAttributes = {
};
};
buildConfigurationList = D91E14E36EC0B415578456F2 /* Build configuration list for PBXProject "Project" */;
compatibilityVersion = "Xcode 14.0";
Expand All @@ -335,7 +333,7 @@
);
mainGroup = 293D0FF827366B513839236A;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 54;
preferredProjectObjectVersion = 70;
projectDirPath = "";
projectRoot = "";
targets = (
Expand Down
6 changes: 2 additions & 4 deletions Tests/Fixtures/SPM/SPM.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 70;
objects = {

/* Begin PBXAggregateTarget section */
Expand Down Expand Up @@ -239,8 +239,6 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
TargetAttributes = {
};
};
buildConfigurationList = 425866ADA259DB93FC4AF1E3 /* Build configuration list for PBXProject "SPM" */;
compatibilityVersion = "Xcode 14.0";
Expand All @@ -259,7 +257,7 @@
630A8CE9F2BE39704ED9D461 /* XCLocalSwiftPackageReference "FooFeature" */,
C6539B364583AE96C18CE377 /* XCLocalSwiftPackageReference "../../.." */,
);
preferredProjectObjectVersion = 54;
preferredProjectObjectVersion = 70;
projectDirPath = "";
projectRoot = "";
targets = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
skipped = "NO"
parallelizable = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XcodeGenKitTests"
Expand All @@ -50,7 +51,8 @@
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
skipped = "NO"
parallelizable = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "339863E54E2D955C00B56802"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 70;
objects = {

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -120,8 +120,6 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
TargetAttributes = {
};
};
buildConfigurationList = 3DFC1105373EDB6483D4BC5D /* Build configuration list for PBXProject "AnotherProject" */;
compatibilityVersion = "Xcode 14.0";
Expand All @@ -132,7 +130,7 @@
);
mainGroup = 4E8CFA4275C972686621210C;
minimizedProjectReferenceProxies = 1;
preferredProjectObjectVersion = 54;
preferredProjectObjectVersion = 70;
projectDirPath = "";
projectRoot = "";
targets = (
Expand Down
Loading
Loading