Skip to content

Commit cf48b7b

Browse files
committed
Merge branch 'deploy/0.6.0' into productive
2 parents a7d44d1 + b10b9c8 commit cf48b7b

20 files changed

+253
-91
lines changed

CHANGELOG.md

+18
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1717
### Security
1818
- None.
1919

20+
## [0.6.0] - 2019-04-19
21+
### Added
22+
- Correctly recognizes App Extensions and doesn't add build phases for them. Fixes [#25](https://github.com/JamitLabs/Accio/issues/25).
23+
- Points to detailed information about conflicting name issues with SwiftPM. Fixes [#26](https://github.com/JamitLabs/Accio/issues/26).
24+
- The `init` command now properly detects test targets and lists them as such in the created manifest file. Fixes [#23](https://github.com/JamitLabs/Accio/issues/23).
25+
### Changed
26+
- Improves reading of supported deployment targets.
27+
- Improves init command by treating empty manifest files like non-existing ones. Fixes [#24](https://github.com/JamitLabs/Accio/issues/24).
28+
### Deprecated
29+
- None.
30+
### Removed
31+
- None.
32+
### Fixed
33+
- Fixes an issue where Accio commands where failing when Git resets failed.
34+
- Fixes an issue where Accio didn't reset changed files untracked by Git.
35+
### Security
36+
- None.
37+
2038
## [0.5.6] - 2019-04-09
2139
### Added
2240
- Adds support for automatically finding schemes named like 'MBProgressHUD Framework tvOS'.

Formula/accio.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class Accio < Formula
22
desc "Dependency manager driven by SwiftPM for iOS/macOS/tvOS/watchOS"
33
homepage "https://github.com/JamitLabs/Accio"
4-
url "https://github.com/JamitLabs/Accio.git", :tag => "0.5.5", :revision => "3b8a910fc4e627c81ffa2e4eafeff450136bb0e4"
4+
url "https://github.com/JamitLabs/Accio.git", :tag => "0.5.6", :revision => "a7d44d187b2f86c129e757fd92f61173494efb55"
55
head "https://github.com/JamitLabs/Accio.git"
66

77
depends_on :xcode => ["10.2", :build]

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
alt="Codebeat Badge">
1414
</a>
1515
<a href="https://github.com/JamitLabs/Accio/releases">
16-
<img src="https://img.shields.io/badge/Version-0.5.6-blue.svg"
17-
alt="Version: 0.5.6">
16+
<img src="https://img.shields.io/badge/Version-0.6.0-blue.svg"
17+
alt="Version: 0.6.0">
1818
</a>
1919
<img src="https://img.shields.io/badge/Swift-5.0-FFAC45.svg"
2020
alt="Swift: 5.0">

Sources/Accio/main.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33
import SwiftCLI
44

55
// MARK: - CLI
6-
let cli = CLI(name: "accio", version: "0.5.6", description: "A dependency manager driven by SwiftPM that works for iOS/tvOS/watchOS/macOS projects.")
6+
let cli = CLI(name: "accio", version: "0.6.0", description: "A dependency manager driven by SwiftPM that works for iOS/tvOS/watchOS/macOS projects.")
77

88
cli.commands = [InitCommand(), InstallCommand(), UpdateCommand(), CleanCommand(), ClearCacheCommand(), SetSharedCacheCommand()]
99
cli.globalOptions.append(contentsOf: GlobalOptions.all)

Sources/AccioKit/Commands/Protocols/DependencyInstaller.swift

+6-4
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ extension DependencyInstaller {
3434
let frameworkCheckoutPath: String = checkoutsDirUrl.appendingPathComponent(fileName).path
3535

3636
if try FileManager.default.isDirectory(atPath: frameworkCheckoutPath) {
37-
try bash("git -C '\(frameworkCheckoutPath)' reset HEAD --hard --quiet")
38-
try bash("git -C '\(frameworkCheckoutPath)' clean -fd --quiet")
37+
try bash("git -C '\(frameworkCheckoutPath)' reset HEAD --hard --quiet 2> /dev/null")
38+
try bash("git -C '\(frameworkCheckoutPath)' clean -fd --quiet 2> /dev/null")
39+
try bash("git -C '\(frameworkCheckoutPath)' clean -fdX --quiet 2> /dev/null")
3940
}
4041
}
4142
}
@@ -55,7 +56,8 @@ extension DependencyInstaller {
5556

5657
typealias ParsingResult = (target: AppTarget, platform: Platform, frameworkProducts: [FrameworkProduct])
5758

58-
let parsingResults: [ParsingResult] = try manifest.appTargets.compactMap { appTarget in
59+
let appTargets: [AppTarget] = try manifest.appTargets()
60+
let parsingResults: [ParsingResult] = try appTargets.compactMap { appTarget in
5961
guard !appTarget.dependentLibraryNames.isEmpty else {
6062
print("No dependencies specified for target '\(appTarget.targetName)'. Please add at least one dependency scheme to the 'dependencies' array of the target in Package.swift.", level: .warning)
6163
return nil
@@ -74,7 +76,7 @@ extension DependencyInstaller {
7476
try XcodeProjectIntegrationService.shared.updateDependencies(of: parsingResult.target, for: parsingResult.platform, with: parsingResult.frameworkProducts)
7577
}
7678

77-
try XcodeProjectIntegrationService.shared.handleRemovedTargets(keepingTargets: manifest.appTargets)
79+
try XcodeProjectIntegrationService.shared.handleRemovedTargets(keepingTargets: appTargets)
7880
try bash("rm -rf '\(Constants.temporaryFrameworksUrl.path)'")
7981
}
8082
}

Sources/AccioKit/Models/AppTarget.swift

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,33 @@
11
import Foundation
22

33
struct AppTarget {
4-
enum TargetType: String {
5-
case regular
4+
enum TargetType: String, CaseIterable {
5+
case app
66
case test
7+
case appExtension
8+
9+
var packageSpecifier: String {
10+
switch self {
11+
case .app, .appExtension:
12+
return "target"
13+
14+
case .test:
15+
return "testTarget"
16+
}
17+
}
18+
19+
var wrapperExtension: String {
20+
switch self {
21+
case .app:
22+
return "app"
23+
24+
case .appExtension:
25+
return "appex"
26+
27+
case .test:
28+
return "xctest"
29+
}
30+
}
731
}
832

933
let projectName: String

Sources/AccioKit/Models/Manifest.swift

+18-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,24 @@ class Manifest: Decodable {
3333
}
3434

3535
extension Manifest {
36-
var appTargets: [AppTarget] {
37-
return targets.compactMap {
38-
guard let targetType = AppTarget.TargetType(rawValue: $0.type) else { return nil }
39-
return AppTarget(projectName: name, targetName: $0.name, dependentLibraryNames: $0.dependencies.flatMap { $0.byName }, targetType: targetType)
36+
func appTargets(workingDirectory: String = GlobalOptions.workingDirectory.value ?? FileManager.default.currentDirectoryPath) throws -> [AppTarget] {
37+
return try targets.compactMap {
38+
var targetType: AppTarget.TargetType?
39+
40+
switch $0.type {
41+
case AppTarget.TargetType.test.rawValue:
42+
targetType = AppTarget.TargetType.test
43+
44+
default:
45+
let targetTypeDetectorService = TargetTypeDetectorService(workingDirectory: workingDirectory)
46+
targetType = try targetTypeDetectorService.detectTargetType(ofTarget: $0.name, in: name)
47+
48+
if targetType! == .test {
49+
print("Unexpectedly found '\(targetType!.wrapperExtension)' wrapper extension product for non-test target '\($0.name)'.", level: .warning)
50+
}
51+
}
52+
53+
return AppTarget(projectName: name, targetName: $0.name, dependentLibraryNames: $0.dependencies.flatMap { $0.byName }, targetType: targetType!)
4054
}
4155
}
4256

Sources/AccioKit/Services/CarthageBuilderService.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ final class CarthageBuilderService {
4646

4747
// revert any changes to prevent issues when removing checked out dependency
4848
try bash("rm -rf '\(framework.projectDirectory)/Carthage/Build'")
49-
try bash("git -C '\(framework.projectDirectory)' reset HEAD --hard --quiet")
50-
try bash("git -C '\(framework.projectDirectory)' clean -fd --quiet")
49+
try bash("git -C '\(framework.projectDirectory)' reset HEAD --hard --quiet 2> /dev/null")
50+
try bash("git -C '\(framework.projectDirectory)' clean -fd --quiet 2> /dev/null")
51+
try bash("git -C '\(framework.projectDirectory)' clean -fdX --quiet 2> /dev/null")
5152

5253
guard FileManager.default.fileExists(atPath: frameworkProduct.frameworkDirPath) && FileManager.default.fileExists(atPath: frameworkProduct.symbolsFilePath) else {
5354
print("Failed to build products to \(platformBuildDir)/\(framework.libraryName).framework(.dSYM).", level: .error)

Sources/AccioKit/Services/DependencyResolverService.swift

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ final class DependencyResolverService {
3939
""",
4040
level: .warning
4141
)
42+
} else if output.stderror.contains("multiple targets named") {
43+
print("This is a known issue. For more details, please see here: https://github.com/JamitLabs/Accio/issues/26", level: .warning)
4244
}
4345

4446
throw DependencyResolverError.dependencyGraphGenerationFailed

Sources/AccioKit/Services/ManifestHandlerService.swift

+13-7
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ final class ManifestHandlerService {
2020
func createManifestFromDefaultTemplateIfNeeded(projectName: String, targetNames: [String]) throws {
2121
let packageManifestPath = URL(fileURLWithPath: workingDirectory).appendingPathComponent("Package.swift").path
2222

23-
guard !FileManager.default.fileExists(atPath: packageManifestPath) else {
24-
print("Package.swift file already exists, skipping template based creation.", level: .warning)
25-
return
23+
if FileManager.default.fileExists(atPath: packageManifestPath) {
24+
guard let manifestContents = try? String(contentsOfFile: packageManifestPath), manifestContents.isBlank else {
25+
print("A non-empty Package.swift file already exists, skipping template based creation.", level: .warning)
26+
return
27+
}
28+
29+
try FileManager.default.removeItem(atPath: packageManifestPath)
2630
}
2731

28-
let targetsContents = self.targetsContents(targetNames: targetNames)
32+
let targetsContents = try self.targetsContents(workingDirectory: workingDirectory, projectName: projectName, targetNames: targetNames)
2933
let manifestTemplate = self.manifestTemplate(projectName: projectName, targetsContents: targetsContents)
3034

3135
FileManager.default.createFile(atPath: packageManifestPath, contents: manifestTemplate.data(using: .utf8), attributes: nil)
@@ -51,10 +55,12 @@ final class ManifestHandlerService {
5155
"""
5256
}
5357

54-
private func targetsContents(targetNames: [String]) -> String {
55-
return targetNames.reduce("") { result, targetName in
58+
private func targetsContents(workingDirectory: String, projectName: String, targetNames: [String]) throws -> String {
59+
return try targetNames.reduce("") { result, targetName in
60+
let targetTypeDetectorService = TargetTypeDetectorService(workingDirectory: workingDirectory)
61+
let targetType: AppTarget.TargetType = try targetTypeDetectorService.detectTargetType(ofTarget: targetName, in: projectName)
5662
return result + """
57-
.target(
63+
.\(targetType.packageSpecifier)(
5864
name: \"\(targetName)\",
5965
dependencies: [
6066
// add your dependencies scheme names here, for example:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
import xcodeproj
3+
import PathKit
4+
5+
enum TargetTypeDetectorError: Error {
6+
case targetNotFound
7+
case platformNotSpecified
8+
}
9+
10+
final class TargetTypeDetectorService {
11+
static let shared = TargetTypeDetectorService(workingDirectory: GlobalOptions.workingDirectory.value ?? FileManager.default.currentDirectoryPath)
12+
13+
private let workingDirectory: String
14+
15+
init(workingDirectory: String) {
16+
self.workingDirectory = workingDirectory
17+
}
18+
19+
func detectTargetType(ofTarget targetName: String, in projectName: String) throws -> AppTarget.TargetType {
20+
let xcodeProjectPath = "\(workingDirectory)/\(projectName).xcodeproj"
21+
let projectFile = try XcodeProj(path: Path(xcodeProjectPath))
22+
23+
for targetType in [AppTarget.TargetType.app, AppTarget.TargetType.appExtension] {
24+
if projectFile.pbxproj.fileReferences.contains(where: { $0.path == "\(targetName).\(targetType.wrapperExtension)" }) {
25+
return targetType
26+
}
27+
}
28+
29+
return targetName.contains("Tests") ? .test : .app // fall back to target name based logic
30+
}
31+
}

Sources/AccioKit/Services/XcodeProjectGeneratorService.swift

+7-18
Original file line numberDiff line numberDiff line change
@@ -74,33 +74,22 @@ final class XcodeProjectGeneratorService {
7474
}
7575
}
7676

77-
/// Swift 4.2 doesn't support the `platform` parameter in the Package manifest, thus read it from a comment with this method.
77+
/// Swift 4.2 doesn't support the `platform` parameter in the Package manifest, thus read it from a comment with this method. Also ensure Swift 5 support.
7878
func platformToVersion(framework: Framework) throws -> [Platform: String] {
79-
let commentedPlatformsRegex = Regex("// *platforms: \\[([^\n]+)\\]")
79+
let platformRegex = Regex(#"\.(iOS|macOS|tvOS|watchOS)\((?:"|.v)(\d+)[\._]?(\d+)?"?\)"#)
8080

8181
let manifestPath: String = URL(fileURLWithPath: framework.projectDirectory).appendingPathComponent("Package.swift").path
8282
let manifestContents: String = try String(contentsOfFile: manifestPath)
8383

8484
var platformToVersion: [Platform: String] = [.iOS: "8.0", .macOS: "10.10", .tvOS: "9.0", .watchOS: "2.0"]
8585

86-
if let match = commentedPlatformsRegex.firstMatch(in: manifestContents) {
87-
let capture = match.captures[0]!
88-
let platformSpecifierComponents: [String] = capture.components(separatedBy: ",").map { $0.stripped() }
89-
let platformVersionRegex = Regex("\\.(\\w+)\\(\"(\\S+)\"\\)")
86+
for match in platformRegex.matches(in: manifestContents) {
87+
guard let platform = Platform(rawValue: match.captures[0]!) else { fatalError("Matched unknown platform rawValue.") }
9088

91-
for platformSpecifier in platformSpecifierComponents {
92-
guard let match = platformVersionRegex.firstMatch(in: platformSpecifier) else {
93-
print("Could not read platform specifier '\(platformSpecifier)' – expected format: .<platform>(\"<version>\")", level: .warning)
94-
continue
95-
}
96-
97-
guard let platform = Platform(rawValue: match.captures[0]!) else {
98-
print("Did not recognize platform with name '\(match.captures[0]!)' in '\(platformSpecifier)' – expected one of \(Platform.allCases.map { $0.rawValue })", level: .warning)
99-
continue
100-
}
89+
let majorVersionString = match.captures[1]!
90+
let minorVersionString = match.captures[2] ?? "0"
10191

102-
platformToVersion[platform] = match.captures[1]!
103-
}
92+
platformToVersion[platform] = "\(majorVersionString).\(minorVersionString)"
10493
}
10594

10695
return platformToVersion

Sources/AccioKit/Services/XcodeProjectIntegrationService.swift

+4-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ final class XcodeProjectIntegrationService {
185185
targetGroup.children.sort { $0.name! < $1.name! }
186186

187187
switch appTarget.targetType {
188-
case .regular:
188+
case .app:
189189
// manage copy build script for regular targets
190190
var copyBuildScript: PBXShellScriptBuildPhase! = targetObject.buildPhases.first { $0.type() == .runScript && ($0 as! PBXShellScriptBuildPhase).name == Constants.copyBuildScript } as? PBXShellScriptBuildPhase
191191
if copyBuildScript == nil {
@@ -216,6 +216,9 @@ final class XcodeProjectIntegrationService {
216216
print("Updating frameworks in copy frameworks phase '\(Constants.copyFilesPhase)' for target '\(appTarget.targetName)' ...", level: .info)
217217
try targetGroup.children.forEach { _ = try copyFrameworksPhase.add(file: $0) }
218218
copyFrameworksPhase.files?.forEach { $0.settings = ["ATTRIBUTES": ["CodeSignOnCopy"]] }
219+
220+
case .appExtension:
221+
break
219222
}
220223

221224
try projectFile.write(path: Path(xcodeProjectPath), override: true)

Tests/AccioKitTests/Models/AppTargetTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ class AppTargetTests: XCTestCase {
6565
resourcesLoaded([manifestResource, xcodeProjectResource, exampleSwiftFile]) {
6666
let manifest = try! ManifestHandlerService(workingDirectory: testResourcesDir.path).loadManifest(isDependency: false)
6767
let dependencyGraph = try! DependencyResolverService(workingDirectory: testResourcesDir.path).dependencyGraph()
68-
let appTarget = manifest.appTargets.first!
68+
let appTarget = try! manifest.appTargets(workingDirectory: testResourcesDir.path).first!
6969

7070
let frameworks: [Framework] = try! appTarget.frameworkDependencies(manifest: manifest, dependencyGraph: dependencyGraph).flattenedDeepFirstOrder()
7171
XCTAssertEqual(frameworks.map { $0.libraryName }, ["HandySwift", "HandyUIKit", "Imperio", "MungoHealer", "Alamofire", "Result", "Moya"])

Tests/AccioKitTests/Models/FrameworkTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class FrameworkTests: XCTestCase {
6161
resourcesLoaded([manifestResource, xcodeProjectResource, exampleSwiftFile]) {
6262
let manifest = try! ManifestHandlerService(workingDirectory: testResourcesDir.path).loadManifest(isDependency: false)
6363
let dependencyGraph = try! DependencyResolverService(workingDirectory: testResourcesDir.path).dependencyGraph()
64-
let appTarget = manifest.appTargets.first!
64+
let appTarget = try! manifest.appTargets(workingDirectory: testResourcesDir.path).first!
6565

6666
let frameworks: [Framework] = try! appTarget.frameworkDependencies(manifest: manifest, dependencyGraph: dependencyGraph).flattenedDeepFirstOrder()
6767
let rxFramework: Framework = frameworks.last!

Tests/AccioKitTests/Models/ManifestTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class ManifestTests: XCTestCase {
8282
resourcesLoaded([manifestResource, xcodeProjectResource, exampleSwiftFile, exampleSwiftTestFile]) {
8383
let manifest = try! ManifestHandlerService(workingDirectory: testResourcesDir.path).loadManifest(isDependency: false)
8484

85-
let appTargets = manifest.appTargets
85+
let appTargets = try! manifest.appTargets(workingDirectory: testResourcesDir.path)
8686
XCTAssertEqual(appTargets.count, 2)
8787

8888
XCTAssertEqual(appTargets[0].projectName, "TestProject")

0 commit comments

Comments
 (0)