Skip to content

Commit

Permalink
Optionally make intermediate filler groups
Browse files Browse the repository at this point in the history
This commit adds a new option `createIntermediateGroups` that defaults
to false. When it is false, the behavior of XcodeGen is the same as
before. When it is true, we make intermediate groups recursively until
we reach the basePath. In practice that means if you've chosen
`Platform/PINFoundation/Sources` as one of your sourcePaths, you get a
top-level group of `Platform` and under that `PINFoundation` and under
that `Sources`. This is instead of the default behavior of just making
`Sources` a top-level group (which is confusing when your directory is
called `Sources` for example).
  • Loading branch information
Brandon Kase committed Nov 2, 2017
1 parent b274ae6 commit e48045d
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 9 deletions.
3 changes: 3 additions & 0 deletions Fixtures/TestProject/NestedFiles/Foo/Nested.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
func nested() -> String {
return "Nested"
}
12 changes: 12 additions & 0 deletions Fixtures/TestProject/Project.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@
FR7078510801 /* FrameworkFile.swift */,
FR1345298503 /* Info.plist */,
FR7740960501 /* MyFramework.h */,
FR7078510801 /* FrameworkFile.swift */,

This comment has been minimized.

Copy link
@yonaskolb

yonaskolb Nov 9, 2017

Owner

@bkase, I just noticed some issues here with children being duplicated if multiple targets have the same source

FR1345298503 /* Info.plist */,
FR7740960501 /* MyFramework.h */,
FR7078510801 /* FrameworkFile.swift */,
FR1345298503 /* Info.plist */,
FR7740960501 /* MyFramework.h */,
FR7078510801 /* FrameworkFile.swift */,
FR1345298503 /* Info.plist */,
FR7740960501 /* MyFramework.h */,
);
name = Framework;
path = Framework;
Expand Down Expand Up @@ -258,6 +267,9 @@
G82523211001 /* App_iOS */,
G78312289901 /* App_iOS_Tests */,
G46615002701 /* Framework */,
G46615002701 /* Framework */,
G46615002701 /* Framework */,
G46615002701 /* Framework */,
G86202385201 /* Products */,
G19527407101 /* Frameworks */,
);
Expand Down
3 changes: 3 additions & 0 deletions Sources/ProjectSpec/ProjectSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public struct ProjectSpec {

public struct Options {
public var carthageBuildPath: String?
public var createIntermediateGroups: Bool
public var bundleIdPrefix: String?
public var settingPresets: SettingPresets = .all

Expand All @@ -54,6 +55,7 @@ public struct ProjectSpec {
}

public init() {
createIntermediateGroups = false
}
}

Expand Down Expand Up @@ -160,5 +162,6 @@ extension ProjectSpec.Options: JSONObjectConvertible {
carthageBuildPath = jsonDictionary.json(atKeyPath: "carthageBuildPath")
bundleIdPrefix = jsonDictionary.json(atKeyPath: "bundleIdPrefix")
settingPresets = jsonDictionary.json(atKeyPath: "settingPresets") ?? .all
createIntermediateGroups = jsonDictionary.json(atKeyPath: "createIntermediateGroups") ?? false
}
}
60 changes: 51 additions & 9 deletions Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,47 @@ public class PBXProjGenerator {
return (fromFiles + fromDirs).flatMap { $0.sourceFiles }
}

func getSingleGroup(path: Path, mergingChildren children: [String], depth: Int = 0) -> PBXGroup {
let group: PBXGroup
if let cachedGroup = groupsByPath[path] {
cachedGroup.children += children
group = cachedGroup
} else {
group = PBXGroup(
reference: generateUUID(PBXGroup.self, path.lastComponent),
children: children,
sourceTree: .group,
name: path.lastComponent,
path: depth == 0 && !spec.options.createIntermediateGroups ?
path.byRemovingBase(path: spec.basePath).string :
path.lastComponent
)
addObject(group)
groupsByPath[path] = group
}
return group
}

// Add groups for all parents recursively
// ex: path/foo/bar/baz/Hello.swift -> path:[foo:[bar:[baz:[Hello.swift]]]]
func getIntermediateGroups(path: Path, group: PBXGroup) -> PBXGroup {
// verify path is a subpath of spec.basePath
guard Path(components: zip(path.components, spec.basePath.components).map{ $0.0 }) == spec.basePath else {
return group
}

// base case
if path == spec.basePath {
return group
}

// recursive case
return getIntermediateGroups(
path: path.parent(),
group: getSingleGroup(path: path, mergingChildren: [group.reference])
)
}

func getSources(path: Path, children: [Path]? = nil, depth: Int = 0) throws -> (sourceFiles: [SourceFile], groups: [PBXGroup]) {
let children = try children ?? (try path.children())
let excludedFiles: [String] = [".DS_Store"]
Expand Down Expand Up @@ -605,17 +646,18 @@ public class PBXProjGenerator {
}
}

let groupPath: String = depth == 0 ? path.byRemovingBase(path: spec.basePath).string : path.lastComponent
let group: PBXGroup
if let cachedGroup = groupsByPath[path] {
group = cachedGroup
if spec.options.createIntermediateGroups {
group = getIntermediateGroups(
path: path.parent(),
group: getSingleGroup(path: path, mergingChildren: groupChildren, depth: depth)
)
} else {
group = PBXGroup(reference: generateUUID(PBXGroup.self, path.lastComponent), children: groupChildren, sourceTree: .group, name: path.lastComponent, path: groupPath)
addObject(group)
if depth == 0 {
topLevelGroups.append(group)
}
groupsByPath[path] = group
group = getSingleGroup(path: path, mergingChildren: groupChildren, depth: depth)
}

if depth == 0 {
topLevelGroups.append(group)
}
groups.insert(group, at: 0)
return (allSourceFiles, groups)
Expand Down
1 change: 1 addition & 0 deletions docs/ProjectSpec.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Note that target names can also be changed by adding a `name` property to a targ

### Options
- ⚪️ **carthageBuildPath**: `String` - The path to the carthage build directory. Defaults to `Carthage/Build`. This is used when specifying target carthage dependencies
- ⚪️ **createIntermediateGroups**: `String` - If this is specified and set to `true`, then intermediate groups will be created for every path component between the folder containing the source and the base path. For example, when enabled if a source path is specified as `Vendor/Foo/Hello.swift`, the group `Vendor` will created as a parent of the `Foo` group.
- ⚪️ **bundleIdPrefix**: `String` - If this is specified then any target that doesn't have an `PRODUCT_BUNDLE_IDENTIFIER` (via all levels of build settings) will get an autogenerated one by combining `bundleIdPrefix` and the target name: `bundleIdPrefix.name`. The target name will be stripped of all characters that aren't alphanumerics, hyphens, or periods. Underscores will be replace with hyphens.
- ⚪️ **settingPresets**: `String` - This controls the settings that are automatically applied to the project and its targets. These are the same build settings that Xcode would add when creating a new project. Project settings are applied by config type. Target settings are applied by the product type and platform. By default this is set to `all`
- `all`: project and target settings
Expand Down

0 comments on commit e48045d

Please sign in to comment.