Skip to content

Commit

Permalink
Merge branch 'deploy/0.6.0' into productive
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeehut committed Apr 19, 2019
2 parents a7d44d1 + b10b9c8 commit cf48b7b
Show file tree
Hide file tree
Showing 20 changed files with 253 additions and 91 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
### Security
- None.

## [0.6.0] - 2019-04-19
### Added
- Correctly recognizes App Extensions and doesn't add build phases for them. Fixes [#25](https://github.com/JamitLabs/Accio/issues/25).
- Points to detailed information about conflicting name issues with SwiftPM. Fixes [#26](https://github.com/JamitLabs/Accio/issues/26).
- 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).
### Changed
- Improves reading of supported deployment targets.
- Improves init command by treating empty manifest files like non-existing ones. Fixes [#24](https://github.com/JamitLabs/Accio/issues/24).
### Deprecated
- None.
### Removed
- None.
### Fixed
- Fixes an issue where Accio commands where failing when Git resets failed.
- Fixes an issue where Accio didn't reset changed files untracked by Git.
### Security
- None.

## [0.5.6] - 2019-04-09
### Added
- Adds support for automatically finding schemes named like 'MBProgressHUD Framework tvOS'.
Expand Down
2 changes: 1 addition & 1 deletion Formula/accio.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Accio < Formula
desc "Dependency manager driven by SwiftPM for iOS/macOS/tvOS/watchOS"
homepage "https://github.com/JamitLabs/Accio"
url "https://github.com/JamitLabs/Accio.git", :tag => "0.5.5", :revision => "3b8a910fc4e627c81ffa2e4eafeff450136bb0e4"
url "https://github.com/JamitLabs/Accio.git", :tag => "0.5.6", :revision => "a7d44d187b2f86c129e757fd92f61173494efb55"
head "https://github.com/JamitLabs/Accio.git"

depends_on :xcode => ["10.2", :build]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
alt="Codebeat Badge">
</a>
<a href="https://github.com/JamitLabs/Accio/releases">
<img src="https://img.shields.io/badge/Version-0.5.6-blue.svg"
alt="Version: 0.5.6">
<img src="https://img.shields.io/badge/Version-0.6.0-blue.svg"
alt="Version: 0.6.0">
</a>
<img src="https://img.shields.io/badge/Swift-5.0-FFAC45.svg"
alt="Swift: 5.0">
Expand Down
2 changes: 1 addition & 1 deletion Sources/Accio/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
import SwiftCLI

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

cli.commands = [InitCommand(), InstallCommand(), UpdateCommand(), CleanCommand(), ClearCacheCommand(), SetSharedCacheCommand()]
cli.globalOptions.append(contentsOf: GlobalOptions.all)
Expand Down
10 changes: 6 additions & 4 deletions Sources/AccioKit/Commands/Protocols/DependencyInstaller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ extension DependencyInstaller {
let frameworkCheckoutPath: String = checkoutsDirUrl.appendingPathComponent(fileName).path

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

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

let parsingResults: [ParsingResult] = try manifest.appTargets.compactMap { appTarget in
let appTargets: [AppTarget] = try manifest.appTargets()
let parsingResults: [ParsingResult] = try appTargets.compactMap { appTarget in
guard !appTarget.dependentLibraryNames.isEmpty else {
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)
return nil
Expand All @@ -74,7 +76,7 @@ extension DependencyInstaller {
try XcodeProjectIntegrationService.shared.updateDependencies(of: parsingResult.target, for: parsingResult.platform, with: parsingResult.frameworkProducts)
}

try XcodeProjectIntegrationService.shared.handleRemovedTargets(keepingTargets: manifest.appTargets)
try XcodeProjectIntegrationService.shared.handleRemovedTargets(keepingTargets: appTargets)
try bash("rm -rf '\(Constants.temporaryFrameworksUrl.path)'")
}
}
28 changes: 26 additions & 2 deletions Sources/AccioKit/Models/AppTarget.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
import Foundation

struct AppTarget {
enum TargetType: String {
case regular
enum TargetType: String, CaseIterable {
case app
case test
case appExtension

var packageSpecifier: String {
switch self {
case .app, .appExtension:
return "target"

case .test:
return "testTarget"
}
}

var wrapperExtension: String {
switch self {
case .app:
return "app"

case .appExtension:
return "appex"

case .test:
return "xctest"
}
}
}

let projectName: String
Expand Down
22 changes: 18 additions & 4 deletions Sources/AccioKit/Models/Manifest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,24 @@ class Manifest: Decodable {
}

extension Manifest {
var appTargets: [AppTarget] {
return targets.compactMap {
guard let targetType = AppTarget.TargetType(rawValue: $0.type) else { return nil }
return AppTarget(projectName: name, targetName: $0.name, dependentLibraryNames: $0.dependencies.flatMap { $0.byName }, targetType: targetType)
func appTargets(workingDirectory: String = GlobalOptions.workingDirectory.value ?? FileManager.default.currentDirectoryPath) throws -> [AppTarget] {
return try targets.compactMap {
var targetType: AppTarget.TargetType?

switch $0.type {
case AppTarget.TargetType.test.rawValue:
targetType = AppTarget.TargetType.test

default:
let targetTypeDetectorService = TargetTypeDetectorService(workingDirectory: workingDirectory)
targetType = try targetTypeDetectorService.detectTargetType(ofTarget: $0.name, in: name)

if targetType! == .test {
print("Unexpectedly found '\(targetType!.wrapperExtension)' wrapper extension product for non-test target '\($0.name)'.", level: .warning)
}
}

return AppTarget(projectName: name, targetName: $0.name, dependentLibraryNames: $0.dependencies.flatMap { $0.byName }, targetType: targetType!)
}
}

Expand Down
5 changes: 3 additions & 2 deletions Sources/AccioKit/Services/CarthageBuilderService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ final class CarthageBuilderService {

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

guard FileManager.default.fileExists(atPath: frameworkProduct.frameworkDirPath) && FileManager.default.fileExists(atPath: frameworkProduct.symbolsFilePath) else {
print("Failed to build products to \(platformBuildDir)/\(framework.libraryName).framework(.dSYM).", level: .error)
Expand Down
2 changes: 2 additions & 0 deletions Sources/AccioKit/Services/DependencyResolverService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ final class DependencyResolverService {
""",
level: .warning
)
} else if output.stderror.contains("multiple targets named") {
print("This is a known issue. For more details, please see here: https://github.com/JamitLabs/Accio/issues/26", level: .warning)
}

throw DependencyResolverError.dependencyGraphGenerationFailed
Expand Down
20 changes: 13 additions & 7 deletions Sources/AccioKit/Services/ManifestHandlerService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ final class ManifestHandlerService {
func createManifestFromDefaultTemplateIfNeeded(projectName: String, targetNames: [String]) throws {
let packageManifestPath = URL(fileURLWithPath: workingDirectory).appendingPathComponent("Package.swift").path

guard !FileManager.default.fileExists(atPath: packageManifestPath) else {
print("Package.swift file already exists, skipping template based creation.", level: .warning)
return
if FileManager.default.fileExists(atPath: packageManifestPath) {
guard let manifestContents = try? String(contentsOfFile: packageManifestPath), manifestContents.isBlank else {
print("A non-empty Package.swift file already exists, skipping template based creation.", level: .warning)
return
}

try FileManager.default.removeItem(atPath: packageManifestPath)
}

let targetsContents = self.targetsContents(targetNames: targetNames)
let targetsContents = try self.targetsContents(workingDirectory: workingDirectory, projectName: projectName, targetNames: targetNames)
let manifestTemplate = self.manifestTemplate(projectName: projectName, targetsContents: targetsContents)

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

private func targetsContents(targetNames: [String]) -> String {
return targetNames.reduce("") { result, targetName in
private func targetsContents(workingDirectory: String, projectName: String, targetNames: [String]) throws -> String {
return try targetNames.reduce("") { result, targetName in
let targetTypeDetectorService = TargetTypeDetectorService(workingDirectory: workingDirectory)
let targetType: AppTarget.TargetType = try targetTypeDetectorService.detectTargetType(ofTarget: targetName, in: projectName)
return result + """
.target(
.\(targetType.packageSpecifier)(
name: \"\(targetName)\",
dependencies: [
// add your dependencies scheme names here, for example:
Expand Down
31 changes: 31 additions & 0 deletions Sources/AccioKit/Services/TargetTypeDetectorService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Foundation
import xcodeproj
import PathKit

enum TargetTypeDetectorError: Error {
case targetNotFound
case platformNotSpecified
}

final class TargetTypeDetectorService {
static let shared = TargetTypeDetectorService(workingDirectory: GlobalOptions.workingDirectory.value ?? FileManager.default.currentDirectoryPath)

private let workingDirectory: String

init(workingDirectory: String) {
self.workingDirectory = workingDirectory
}

func detectTargetType(ofTarget targetName: String, in projectName: String) throws -> AppTarget.TargetType {
let xcodeProjectPath = "\(workingDirectory)/\(projectName).xcodeproj"
let projectFile = try XcodeProj(path: Path(xcodeProjectPath))

for targetType in [AppTarget.TargetType.app, AppTarget.TargetType.appExtension] {
if projectFile.pbxproj.fileReferences.contains(where: { $0.path == "\(targetName).\(targetType.wrapperExtension)" }) {
return targetType
}
}

return targetName.contains("Tests") ? .test : .app // fall back to target name based logic
}
}
25 changes: 7 additions & 18 deletions Sources/AccioKit/Services/XcodeProjectGeneratorService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,33 +74,22 @@ final class XcodeProjectGeneratorService {
}
}

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

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

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

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

for platformSpecifier in platformSpecifierComponents {
guard let match = platformVersionRegex.firstMatch(in: platformSpecifier) else {
print("Could not read platform specifier '\(platformSpecifier)' – expected format: .<platform>(\"<version>\")", level: .warning)
continue
}

guard let platform = Platform(rawValue: match.captures[0]!) else {
print("Did not recognize platform with name '\(match.captures[0]!)' in '\(platformSpecifier)' – expected one of \(Platform.allCases.map { $0.rawValue })", level: .warning)
continue
}
let majorVersionString = match.captures[1]!
let minorVersionString = match.captures[2] ?? "0"

platformToVersion[platform] = match.captures[1]!
}
platformToVersion[platform] = "\(majorVersionString).\(minorVersionString)"
}

return platformToVersion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ final class XcodeProjectIntegrationService {
targetGroup.children.sort { $0.name! < $1.name! }

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

case .appExtension:
break
}

try projectFile.write(path: Path(xcodeProjectPath), override: true)
Expand Down
2 changes: 1 addition & 1 deletion Tests/AccioKitTests/Models/AppTargetTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class AppTargetTests: XCTestCase {
resourcesLoaded([manifestResource, xcodeProjectResource, exampleSwiftFile]) {
let manifest = try! ManifestHandlerService(workingDirectory: testResourcesDir.path).loadManifest(isDependency: false)
let dependencyGraph = try! DependencyResolverService(workingDirectory: testResourcesDir.path).dependencyGraph()
let appTarget = manifest.appTargets.first!
let appTarget = try! manifest.appTargets(workingDirectory: testResourcesDir.path).first!

let frameworks: [Framework] = try! appTarget.frameworkDependencies(manifest: manifest, dependencyGraph: dependencyGraph).flattenedDeepFirstOrder()
XCTAssertEqual(frameworks.map { $0.libraryName }, ["HandySwift", "HandyUIKit", "Imperio", "MungoHealer", "Alamofire", "Result", "Moya"])
Expand Down
2 changes: 1 addition & 1 deletion Tests/AccioKitTests/Models/FrameworkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class FrameworkTests: XCTestCase {
resourcesLoaded([manifestResource, xcodeProjectResource, exampleSwiftFile]) {
let manifest = try! ManifestHandlerService(workingDirectory: testResourcesDir.path).loadManifest(isDependency: false)
let dependencyGraph = try! DependencyResolverService(workingDirectory: testResourcesDir.path).dependencyGraph()
let appTarget = manifest.appTargets.first!
let appTarget = try! manifest.appTargets(workingDirectory: testResourcesDir.path).first!

let frameworks: [Framework] = try! appTarget.frameworkDependencies(manifest: manifest, dependencyGraph: dependencyGraph).flattenedDeepFirstOrder()
let rxFramework: Framework = frameworks.last!
Expand Down
2 changes: 1 addition & 1 deletion Tests/AccioKitTests/Models/ManifestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class ManifestTests: XCTestCase {
resourcesLoaded([manifestResource, xcodeProjectResource, exampleSwiftFile, exampleSwiftTestFile]) {
let manifest = try! ManifestHandlerService(workingDirectory: testResourcesDir.path).loadManifest(isDependency: false)

let appTargets = manifest.appTargets
let appTargets = try! manifest.appTargets(workingDirectory: testResourcesDir.path)
XCTAssertEqual(appTargets.count, 2)

XCTAssertEqual(appTargets[0].projectName, "TestProject")
Expand Down
Loading

0 comments on commit cf48b7b

Please sign in to comment.