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

Update fork #1516

Closed
wants to merge 3 commits into from
Closed
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 Sources/ProjectSpec/AggregateTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct AggregateTarget: ProjectTarget {
public var configFiles: [String: String]
public var scheme: TargetScheme?
public var attributes: [String: Any]
public var nameDividerChar: String = Target.defaultNameDividerChar

public init(
name: String,
Expand Down
1 change: 1 addition & 0 deletions Sources/ProjectSpec/ProjectTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public protocol ProjectTarget: BuildSettingsContainer {
var buildToolPlugins: [BuildToolPlugin] { get }
var scheme: TargetScheme? { get }
var attributes: [String: Any] { get }
var nameDividerChar: String? { get }
}

extension Target {
Expand Down
8 changes: 8 additions & 0 deletions Sources/ProjectSpec/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ extension LegacyTarget: PathContainer {
}

public struct Target: ProjectTarget {

public static var defaultNameDividerChar = "_"

public var name: String
public var nameDividerChar: String
public var type: PBXProductType
public var platform: Platform
public var supportedDestinations: [SupportedDestination]?
Expand Down Expand Up @@ -77,6 +81,7 @@ public struct Target: ProjectTarget {

public init(
name: String,
nameDividerChar: String = Target.defaultNameDividerChar,
type: PBXProductType,
platform: Platform,
supportedDestinations: [SupportedDestination]? = nil,
Expand All @@ -103,6 +108,7 @@ public struct Target: ProjectTarget {
putResourcesBeforeSourcesBuildPhase: Bool = false
) {
self.name = name
self.nameDividerChar = nameDividerChar
self.type = type
self.platform = platform
self.supportedDestinations = supportedDestinations
Expand Down Expand Up @@ -272,6 +278,8 @@ extension Target: NamedJSONDictionaryConvertible {
public init(name: String, jsonDictionary: JSONDictionary) throws {
let resolvedName: String = jsonDictionary.json(atKeyPath: "name") ?? name
self.name = resolvedName
self.nameDividerChar = jsonDictionary.json(atKeyPath: "nameDividerChar")
?? Target.defaultNameDividerChar
productName = jsonDictionary.json(atKeyPath: "productName") ?? resolvedName

let typeString: String = jsonDictionary.json(atKeyPath: "type") ?? ""
Expand Down
7 changes: 7 additions & 0 deletions Sources/ProjectSpec/TargetSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public struct TargetSource: Equatable {
public var group: String?
public var compilerFlags: [String]
public var excludes: [String]
public var excludePatterns: [NSRegularExpression]
public var includes: [String]
public var type: SourceType?
public var optional: Bool
Expand Down Expand Up @@ -46,6 +47,7 @@ public struct TargetSource: Equatable {
group: String? = nil,
compilerFlags: [String] = [],
excludes: [String] = [],
excludePatterns: [NSRegularExpression] = [],
includes: [String] = [],
type: SourceType? = nil,
optional: Bool = optionalDefault,
Expand All @@ -62,6 +64,7 @@ public struct TargetSource: Equatable {
self.group = group
self.compilerFlags = compilerFlags
self.excludes = excludes
self.excludePatterns = excludePatterns
self.includes = includes
self.type = type
self.optional = optional
Expand Down Expand Up @@ -105,6 +108,10 @@ extension TargetSource: JSONObjectConvertible {

headerVisibility = jsonDictionary.json(atKeyPath: "headerVisibility")
excludes = jsonDictionary.json(atKeyPath: "excludes") ?? []
let regexPatterns: [String] = jsonDictionary.json(atKeyPath: "excludePatterns") ?? []
excludePatterns = try regexPatterns.map({
try NSRegularExpression(pattern: $0)
})
includes = jsonDictionary.json(atKeyPath: "includes") ?? []
type = jsonDictionary.json(atKeyPath: "type")
optional = jsonDictionary.json(atKeyPath: "optional") ?? TargetSource.optionalDefault
Expand Down
2 changes: 1 addition & 1 deletion Sources/XcodeGenKit/PBXProjGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ 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)
let sourceFiles = try sourceGenerator.getAllSourceFiles(targetType: target.type, sources: target.sources, platform: target.platform, buildPhases: sourceFileBuildPhaseOverrides)
.sorted { $0.path.lastComponent < $1.path.lastComponent }

var anyDependencyRequiresObjCLinking = false
Expand Down
3 changes: 2 additions & 1 deletion Sources/XcodeGenKit/SchemeGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public class SchemeGenerator {
} else {
for configVariant in targetScheme.configVariants {

let schemeName = "\(target.name) \(configVariant)"
let divider = target.nameDividerChar ?? Target.defaultNameDividerChar
let schemeName = "\(target.name)\(divider)\(configVariant)"

let debugConfig = project.configs
.first(including: configVariant, for: .debug)!
Expand Down
43 changes: 40 additions & 3 deletions Sources/XcodeGenKit/SourceGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class SourceGenerator {
private let project: Project
let pbxProj: PBXProj

var excludePatterns: [NSRegularExpression] = []
private var defaultExcludedFiles = [
".DS_Store",
]
Expand Down Expand Up @@ -86,8 +87,16 @@ 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(targetType: PBXProductType, sources: [TargetSource], platform: Platform, buildPhases: [Path : BuildPhaseSpec]) throws -> [SourceFile] {
try sources
.flatMap {
try getSourceFiles(
targetType: targetType,
targetSource: $0,
platform: platform,
buildPhases: buildPhases
)
}
}

// get groups without build files. Use for Project.fileGroups
Expand Down Expand Up @@ -393,12 +402,25 @@ class SourceGenerator {
.reduce([], +)
)
}

func isExcludedPattern(_ path: Path) -> Bool {
return excludePatterns.reduce(false) {
(result: Bool, expression: NSRegularExpression) -> Bool in

let string: String = path.string
let range = NSRange(location: 0, length: string.count)
let matches = expression.matches(in: string, range: range)

return result || (matches.count > 0)
}
}

/// Checks whether the path is not in any default or TargetSource excludes
func isIncludedPath(_ path: Path, excludePaths: Set<Path>, includePaths: SortedArray<Path>?) -> Bool {
return !defaultExcludedFiles.contains(where: { path.lastComponent == $0 })
&& !(path.extension.map(defaultExcludedExtensions.contains) ?? false)
&& !excludePaths.contains(path)
&& !isExcludedPattern(path)
// If includes is empty, it's included. If it's not empty, the path either needs to match exactly, or it needs to be a direct parent of an included path.
&& (includePaths.flatMap { _isIncludedPathSorted(path, sortedPaths: $0) } ?? true)
}
Expand Down Expand Up @@ -599,13 +621,28 @@ class SourceGenerator {
groups.insert(group, at: 0)
return (allSourceFiles, groups)
}

private func excludePatternsForPlatform(_ platform: Platform) throws
-> NSRegularExpression {

let pattern = "\\/\(platform.rawValue)\\/"
return try NSRegularExpression(pattern: pattern)
}

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

// generate excluded paths
let path = project.basePath + targetSource.path
let excludePaths = getSourceMatches(targetSource: targetSource, patterns: targetSource.excludes)
excludePatterns = targetSource.excludePatterns
if let platform = platform {
var platforms = Set(Platform.allCases)
platforms.remove(platform)
excludePatterns += try platforms.map({
try excludePatternsForPlatform($0)
})
}
// generate included paths. Excluded paths will override this.
let includePaths = targetSource.includes.isEmpty ? nil : getSourceMatches(targetSource: targetSource, patterns: targetSource.includes)

Expand Down
2 changes: 1 addition & 1 deletion Tests/Fixtures/TestProject/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ XCODE_XCCONFIG_FILE="$PWD/carthage_static.xcconfig" \

echo "
⚙️ Building iOS app"
xcodebuild -quiet -workspace Workspace.xcworkspace -scheme "App_iOS Test" -configuration "Test Debug" -xcconfig fixtures.xcconfig
xcodebuild -quiet -workspace Workspace.xcworkspace -scheme "App_iOS_Test" -configuration "Test Debug" -xcconfig fixtures.xcconfig
echo "✅ Successfully built iOS app"

echo "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64=arm64 arm64e armv7 armv7s armv6 armv8
EXCLUDED_ARCHS_1200=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT))
EXCLUDED_ARCHS_1300=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT))
<<<<<<<< HEAD:Tests/Fixtures/TestProject/xcode12_13_and_14_workaround.xcconfig
EXCLUDED_ARCHS_1400=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT))
========
>>>>>>>> 122aa7fc (IOS-5280 Align with main repo (#6)):Tests/Fixtures/TestProject/xcode12_and_13_workaround.xcconfig
EXCLUDED_ARCHS=$(inherited) $(EXCLUDED_ARCHS_$(XCODE_VERSION_MAJOR))
14 changes: 14 additions & 0 deletions Tests/Fixtures/TestProject/xcode12_and_13_workaround.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// See https://github.com/Carthage/Carthage/issues/3019
//
// Skips building ARM slices for simulators until Carthage can support it
//

EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64=arm64 arm64e armv7 armv7s armv6 armv8
EXCLUDED_ARCHS_1200=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT))
EXCLUDED_ARCHS_1300=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT))
<<<<<<<< HEAD:Tests/Fixtures/TestProject/xcode12_13_and_14_workaround.xcconfig
EXCLUDED_ARCHS_1400=$(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT))
========
>>>>>>>> 122aa7fc (IOS-5280 Align with main repo (#6)):Tests/Fixtures/TestProject/xcode12_and_13_workaround.xcconfig
EXCLUDED_ARCHS=$(inherited) $(EXCLUDED_ARCHS_$(XCODE_VERSION_MAJOR))
153 changes: 153 additions & 0 deletions Tests/XcodeGenKitTests/PBXProjGeneratorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -504,5 +504,158 @@ class PBXProjGeneratorTests: XCTestCase {
}
}
}

func testDefaultLastUpgradeCheckWhenUserDidSpecifyInvalidValue() throws {
let lastUpgradeKey = "LastUpgradeCheck"
let attributes: [String: Any] = [lastUpgradeKey: 1234]
let project = Project(name: "Test", attributes: attributes)
let projGenerator = PBXProjGenerator(project: project)

let pbxProj = try projGenerator.generate()

for pbxProject in pbxProj.projects {
XCTAssertEqual(pbxProject.attributes[lastUpgradeKey] as? String, project.xcodeVersion)
}
}

func testOverrideLastUpgradeCheckWhenUserDidSpecifyValue() throws {
let lastUpgradeKey = "LastUpgradeCheck"
let lastUpgradeValue = "1234"
let attributes: [String: Any] = [lastUpgradeKey: lastUpgradeValue]
let project = Project(name: "Test", attributes: attributes)
let projGenerator = PBXProjGenerator(project: project)

let pbxProj = try projGenerator.generate()

for pbxProject in pbxProj.projects {
XCTAssertEqual(pbxProject.attributes[lastUpgradeKey] as? String, lastUpgradeValue)
}
}

func testDefaultLastUpgradeCheckWhenUserDidNotSpecifyValue() throws {
let lastUpgradeKey = "LastUpgradeCheck"
let project = Project(name: "Test")
let projGenerator = PBXProjGenerator(project: project)

let pbxProj = try projGenerator.generate()

for pbxProject in pbxProj.projects {
XCTAssertEqual(pbxProject.attributes[lastUpgradeKey] as? String, project.xcodeVersion)
}
}

func testPlatformDependencies() {
describe {
let directoryPath = Path("TestDirectory")

func createDirectories(_ directories: String) throws {
let yaml = try Yams.load(yaml: directories)!

func getFiles(_ file: Any, path: Path) -> [Path] {
if let array = file as? [Any] {
return array.flatMap { getFiles($0, path: path) }
} else if let string = file as? String {
return [path + string]
} else if let dictionary = file as? [String: Any] {
var array: [Path] = []
for (key, value) in dictionary {
array += getFiles(value, path: path + key)
}
return array
} else {
return []
}
}

let files = getFiles(yaml, path: directoryPath).filter { $0.extension != nil }
for file in files {
try file.parent().mkpath()
try file.write("")
}
}

func removeDirectories() {
try? directoryPath.delete()
}

$0.before {
removeDirectories()
}

$0.after {
removeDirectories()
}

$0.it("setups target with different dependencies") {
let directories = """
Sources:
- MainScreen:
- Entities:
- file.swift
"""
try createDirectories(directories)
let target1 = Target(name: "TestAll", type: .application, platform: .iOS, sources: ["Sources"])
let target2 = Target(name: "TestiOS", type: .application, platform: .iOS, sources: ["Sources"])
let target3 = Target(name: "TestmacOS", type: .application, platform: .iOS, sources: ["Sources"])
let dependency1 = Dependency(type: .target, reference: "TestAll", platformFilter: .all)
let dependency2 = Dependency(type: .target, reference: "TestiOS", platformFilter: .iOS)
let dependency3 = Dependency(type: .target, reference: "TestmacOS", platformFilter: .macOS)
let dependency4 = Dependency(type: .package(product: "Swinject"), reference: "Swinject", platformFilter: .iOS)
let target = Target(name: "Test", type: .application, platform: .iOS, sources: ["Sources"], dependencies: [dependency1, dependency2, dependency3, dependency4])
let swinjectPackage = SwiftPackage.remote(url: "https://github.com/Swinject/Swinject", versionRequirement: .exact("2.8.0"))
let project = Project(basePath: directoryPath, name: "Test", targets: [target, target1, target2, target3], packages: ["Swinject": swinjectPackage])

let pbxProj = try project.generatePbxProj()

let targets = pbxProj.projects.first?.targets
let testTarget = pbxProj.projects.first?.targets.first(where: { $0.name == "Test" })
let testTargetDependencies = testTarget?.dependencies
try expect(targets?.count) == 4
try expect(testTargetDependencies?.count) == 3
try expect(testTargetDependencies?[0].platformFilter).beNil()
try expect(testTargetDependencies?[1].platformFilter) == "ios"
try expect(testTargetDependencies?[2].platformFilter) == "maccatalyst"
try expect(testTarget?.frameworksBuildPhase()?.files?.count) == 1
try expect(testTarget?.frameworksBuildPhase()?.files?[0].platformFilter) == "ios"
}

$0.it("places resources before sources buildPhase") {
let directories = """
Sources:
- MainScreen:
- Entities:
- file.swift
- image.jpg
"""
try createDirectories(directories)
let target1 = Target(
name: "TestAll",
type: .application,
platform: .iOS,
sources: ["Sources"],
putResourcesBeforeSourcesBuildPhase: true
)
let target2 = Target(
name: "TestiOS",
type: .application,
platform: .iOS,
sources: ["Sources"],
putResourcesBeforeSourcesBuildPhase: false
)

let project = Project(basePath: directoryPath, name: "Test", targets: [target1, target2])

let pbxProj = try project.generatePbxProj()

let targets = pbxProj.projects.first?.targets
try expect(targets?.count) == 2
try expect(targets?.first?.buildPhases.first).to.beOfType(PBXResourcesBuildPhase.self)
try expect(targets?.first?.buildPhases.last).to.beOfType(PBXSourcesBuildPhase.self)

try expect(targets?.last?.buildPhases.first).to.beOfType(PBXSourcesBuildPhase.self)
try expect(targets?.last?.buildPhases.last).to.beOfType(PBXResourcesBuildPhase.self)
}
}
}

}
Loading
Loading