Generate projects in a DerivedProjects directory (#146)
* Add option to generate the projects and workspaces in the ~/.tuist/DerivedProjects directory * Remove unnecessary import * Test ProjectDirectoryHelper * Update CHANGELOG * Remove unnecessary line
This commit is contained in:
parent
41dff04ae9
commit
8fcbe8bc3f
|
@ -6,6 +6,12 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
|
|||
|
||||
### Added
|
||||
|
||||
- Geration of projects and workspaces in the `~/.tuist/DerivedProjects` directory https://github.com/tuist/tuist/pull/146 by pepibumur.
|
||||
|
||||
## 0.7.0
|
||||
|
||||
### Added
|
||||
|
||||
- Support for actions https://github.com/tuist/tuist/pull/136 by @pepibumur.
|
||||
|
||||
## 0.6.0
|
||||
|
|
|
@ -21,7 +21,11 @@ let package = Package(
|
|||
targets: [
|
||||
.target(
|
||||
name: "TuistShared",
|
||||
dependencies: []
|
||||
dependencies: ["Utility", "TuistCore"]
|
||||
),
|
||||
.target(
|
||||
name: "TuistSharedTesting",
|
||||
dependencies: ["TuistShared"]
|
||||
),
|
||||
.target(
|
||||
name: "TuistKit",
|
||||
|
@ -29,7 +33,7 @@ let package = Package(
|
|||
),
|
||||
.testTarget(
|
||||
name: "TuistKitTests",
|
||||
dependencies: ["TuistKit", "TuistCoreTesting"]
|
||||
dependencies: ["TuistKit", "TuistCoreTesting", "TuistSharedTesting"]
|
||||
),
|
||||
.target(
|
||||
name: "tuist",
|
||||
|
@ -37,7 +41,7 @@ let package = Package(
|
|||
),
|
||||
.target(
|
||||
name: "TuistEnvKit",
|
||||
dependencies: ["Utility", "TuistCore", "TuistShared"]
|
||||
dependencies: ["Utility", "TuistCore", "TuistShared", "TuistSharedTesting"]
|
||||
),
|
||||
.testTarget(
|
||||
name: "TuistEnvKitTests",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Foundation
|
||||
import TuistCore
|
||||
import TuistShared
|
||||
import Utility
|
||||
|
||||
final class UninstallCommand: Command {
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
|
||||
/// Protocol that defines the interface of a local environment controller.
|
||||
/// It manages the local directory where tuistenv stores the tuist versions and user settings.
|
||||
protocol EnvironmentControlling: AnyObject {
|
||||
/// Returns the versions directory.
|
||||
var versionsDirectory: AbsolutePath { get }
|
||||
|
||||
/// Returns the path to the settings.
|
||||
var settingsPath: AbsolutePath { get }
|
||||
}
|
||||
|
||||
/// Local environment controller.
|
||||
class EnvironmentController: EnvironmentControlling {
|
||||
/// Returns the default local directory.
|
||||
static let defaultDirectory: AbsolutePath = AbsolutePath(URL(fileURLWithPath: NSHomeDirectory()).path).appending(component: ".tuist")
|
||||
|
||||
// MARK: - Attributes
|
||||
|
||||
/// Directory.
|
||||
private let directory: AbsolutePath
|
||||
|
||||
/// Filemanager.
|
||||
|
||||
private let fileManager: FileManager = .default
|
||||
|
||||
init(directory: AbsolutePath = EnvironmentController.defaultDirectory) {
|
||||
self.directory = directory
|
||||
setup()
|
||||
}
|
||||
|
||||
// MARK: - EnvironmentControlling
|
||||
|
||||
/// Sets up the local environment.
|
||||
///
|
||||
/// - Throws: an error if something the directories creation fails.
|
||||
private func setup() {
|
||||
// Note: It should be safe to use try! here
|
||||
if !fileManager.fileExists(atPath: directory.asString) {
|
||||
try! fileManager.createDirectory(atPath: directory.asString, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
if !fileManager.fileExists(atPath: versionsDirectory.asString) {
|
||||
try! fileManager.createDirectory(atPath: versionsDirectory.asString, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the directory where all the versions are.
|
||||
var versionsDirectory: AbsolutePath {
|
||||
return directory.appending(component: "Versions")
|
||||
}
|
||||
|
||||
/// Settings path.
|
||||
var settingsPath: AbsolutePath {
|
||||
return directory.appending(component: "settings.json")
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import Foundation
|
||||
import TuistCore
|
||||
import TuistShared
|
||||
|
||||
/// The class that conforms this protocol exposes an interface for interacting with the user settings.
|
||||
protocol SettingsControlling: AnyObject {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import TuistCore
|
||||
import TuistShared
|
||||
import Utility
|
||||
|
||||
protocol VersionsControlling: AnyObject {
|
||||
|
|
|
@ -60,9 +60,7 @@ class FocusCommand: NSObject, Command {
|
|||
let workspacePath = try workspaceGenerator.generate(path: path,
|
||||
graph: graph,
|
||||
options: GenerationOptions(buildConfiguration: config),
|
||||
system: system,
|
||||
printer: printer,
|
||||
resourceLocator: resourceLocator)
|
||||
directory: .manifest)
|
||||
|
||||
try opener.open(path: workspacePath)
|
||||
}
|
||||
|
|
|
@ -60,9 +60,7 @@ class GenerateCommand: NSObject, Command {
|
|||
try workspaceGenerator.generate(path: path,
|
||||
graph: graph,
|
||||
options: GenerationOptions(buildConfiguration: config),
|
||||
system: system,
|
||||
printer: printer,
|
||||
resourceLocator: resourceLocator)
|
||||
directory: .manifest)
|
||||
|
||||
printer.print(success: "Project generated.")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import Foundation
|
||||
|
||||
/// Enum whose cases represent different destinations where projects can be generated.
|
||||
///
|
||||
/// - manifest: Generates the workspace and the project in the same folder where the project manifest is.
|
||||
/// - derivedProjects: Generates the workspace and the project in the ~/.tuist/DerivedProjects directory.
|
||||
enum GenerationDirectory {
|
||||
case manifest
|
||||
case derivedProjects
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import TuistCore
|
||||
import TuistShared
|
||||
|
||||
/// Protocol that defines the interface of a class that can be used to set up the directories where projects are generated.
|
||||
protocol ProjectDirectoryHelping: AnyObject {
|
||||
/// Sets up the directory where the project will be generated. For commands like build or test where
|
||||
/// the Xcode project is necessary to build the project, the project is generated in the ~/.tuist/DerivedProjects directory.
|
||||
/// Optionally, it can be deleted afterwards.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - project: Project that will be generated and whose directory need to be set up.
|
||||
/// - directory: Directory where the
|
||||
/// - Returns: The path to the directory where the project should be created.
|
||||
/// - Throws: An error if the project directory cannot be set up.
|
||||
func setupProjectDirectory(project: Project, directory: GenerationDirectory) throws -> AbsolutePath
|
||||
|
||||
/// Sets up the directory where the element with the given name and path will be generated.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - name: Name of the element whose directory will be set up (workspace or project name).
|
||||
/// - path: Path where the project or workspace is defined. The directory that contains the manifest.
|
||||
/// - directory: Directory where the project will be generated.
|
||||
/// - Returns: The path to the directory where the element (workspace or project) should be created.
|
||||
/// - Throws: An error if the directory cannot be set up.
|
||||
func setupDirectory(name: String, path: AbsolutePath, directory: GenerationDirectory) throws -> AbsolutePath
|
||||
}
|
||||
|
||||
/// Default implementation of the ProjectDirectoryHelping protocol.
|
||||
class ProjectDirectoryHelper: ProjectDirectoryHelping {
|
||||
/// Environment controller instance. It's necessary to obtain the directory where the derived projects are generated into.
|
||||
let environmentController: EnvironmentControlling
|
||||
|
||||
/// File handler instance for file operations.
|
||||
let fileHandler: FileHandling
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
/// Deafult constructor.
|
||||
///
|
||||
/// - Parameter environmentController: Environment controller instance. It's necessary to obtain the directory where the derived projects are generated into.
|
||||
/// - Parameter fileHandler: File handler instance for file operations.
|
||||
init(environmentController: EnvironmentControlling = EnvironmentController(),
|
||||
fileHandler: FileHandling = FileHandler()) {
|
||||
self.environmentController = environmentController
|
||||
self.fileHandler = fileHandler
|
||||
}
|
||||
|
||||
/// Sets up the directory where the project will be generated. For commands like build or test where
|
||||
/// the Xcode project is necessary to build the project, the project is generated in the ~/.tuist/DerivedProjects directory.
|
||||
/// Optionally, it can be deleted afterwards.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - project: Project that will be generated and whose directory need to be set up.
|
||||
/// - directory: Directory where the project will be generated.
|
||||
/// - Returns: The path to the directory where the project should be created.
|
||||
/// - Throws: An error if the directory cannot be set up.
|
||||
func setupProjectDirectory(project: Project, directory: GenerationDirectory) throws -> AbsolutePath {
|
||||
return try setupDirectory(name: project.name,
|
||||
path: project.path,
|
||||
directory: directory)
|
||||
}
|
||||
|
||||
/// Sets up the directory where the element with the given name and path will be generated.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - name: Name of the element whose directory will be set up (workspace or project name).
|
||||
/// - path: Path where the project or workspace is defined. The directory that contains the manifest.
|
||||
/// - directory: Directory where the project will be generated.
|
||||
/// - Returns: The path to the directory where the element (workspace or project) should be created.
|
||||
/// - Throws: An error if the directory cannot be set up.
|
||||
func setupDirectory(name: String, path: AbsolutePath, directory: GenerationDirectory) throws -> AbsolutePath {
|
||||
switch directory {
|
||||
case .derivedProjects:
|
||||
let md5 = path.asString.md5
|
||||
let path = environmentController.derivedProjectsDirectory.appending(component: "\(name)-\(md5)")
|
||||
if !fileHandler.exists(path) {
|
||||
try fileHandler.createFolder(path)
|
||||
}
|
||||
return path
|
||||
case .manifest:
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,23 +5,30 @@ import xcodeproj
|
|||
|
||||
protocol WorkspaceGenerating: AnyObject {
|
||||
@discardableResult
|
||||
func generate(path: AbsolutePath,
|
||||
graph: Graphing,
|
||||
options: GenerationOptions,
|
||||
system: Systeming,
|
||||
printer: Printing,
|
||||
resourceLocator: ResourceLocating) throws -> AbsolutePath
|
||||
func generate(path: AbsolutePath, graph: Graphing, options: GenerationOptions, directory: GenerationDirectory) throws -> AbsolutePath
|
||||
}
|
||||
|
||||
final class WorkspaceGenerator: WorkspaceGenerating {
|
||||
// MARK: - Attributes
|
||||
|
||||
let projectGenerator: ProjectGenerating
|
||||
let system: Systeming
|
||||
let printer: Printing
|
||||
let resourceLocator: ResourceLocating
|
||||
let projectDirectoryHelper: ProjectDirectoryHelping
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init(projectGenerator: ProjectGenerating = ProjectGenerator()) {
|
||||
init(projectGenerator: ProjectGenerating = ProjectGenerator(),
|
||||
system: Systeming = System(),
|
||||
printer: Printing = Printer(),
|
||||
resourceLocator: ResourceLocating = ResourceLocator(),
|
||||
projectDirectoryHelper: ProjectDirectoryHelping = ProjectDirectoryHelper()) {
|
||||
self.projectGenerator = projectGenerator
|
||||
self.system = system
|
||||
self.printer = printer
|
||||
self.resourceLocator = resourceLocator
|
||||
self.projectDirectoryHelper = projectDirectoryHelper
|
||||
}
|
||||
|
||||
// MARK: - WorkspaceGenerating
|
||||
|
@ -30,19 +37,23 @@ final class WorkspaceGenerator: WorkspaceGenerating {
|
|||
func generate(path: AbsolutePath,
|
||||
graph: Graphing,
|
||||
options: GenerationOptions,
|
||||
system: Systeming = System(),
|
||||
printer: Printing = Printer(),
|
||||
resourceLocator: ResourceLocating = ResourceLocator()) throws -> AbsolutePath {
|
||||
directory: GenerationDirectory = .manifest) throws -> AbsolutePath {
|
||||
let workspaceRootPath = try projectDirectoryHelper.setupDirectory(name: graph.name,
|
||||
path: graph.entryPath,
|
||||
directory: directory)
|
||||
let workspaceName = "\(graph.name).xcworkspace"
|
||||
printer.print(section: "Generating workspace \(workspaceName)")
|
||||
let workspacePath = path.appending(component: workspaceName)
|
||||
let workspacePath = workspaceRootPath.appending(component: workspaceName)
|
||||
let workspaceData = XCWorkspaceData(children: [])
|
||||
let workspace = XCWorkspace(data: workspaceData)
|
||||
|
||||
try graph.projects.forEach { project in
|
||||
let sourceRootPath = try projectDirectoryHelper.setupProjectDirectory(project: project,
|
||||
directory: directory)
|
||||
let xcodeprojPath = try projectGenerator.generate(project: project,
|
||||
options: options,
|
||||
graph: graph,
|
||||
sourceRootPath: nil,
|
||||
sourceRootPath: sourceRootPath,
|
||||
system: system,
|
||||
printer: printer,
|
||||
resourceLocator: resourceLocator)
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
import TuistCore
|
||||
|
||||
/// Protocol that defines the interface of a local environment controller.
|
||||
/// It manages the local directory where tuistenv stores the tuist versions and user settings.
|
||||
public protocol EnvironmentControlling: AnyObject {
|
||||
/// Returns the versions directory.
|
||||
var versionsDirectory: AbsolutePath { get }
|
||||
|
||||
/// Returns the path to the settings.
|
||||
var settingsPath: AbsolutePath { get }
|
||||
|
||||
/// Returns the directory where all the derived projects are generated.
|
||||
var derivedProjectsDirectory: AbsolutePath { get }
|
||||
}
|
||||
|
||||
/// Local environment controller.
|
||||
public class EnvironmentController: EnvironmentControlling {
|
||||
/// Returns the default local directory.
|
||||
static let defaultDirectory: AbsolutePath = AbsolutePath(URL(fileURLWithPath: NSHomeDirectory()).path).appending(component: ".tuist")
|
||||
|
||||
// MARK: - Attributes
|
||||
|
||||
/// Directory.
|
||||
private let directory: AbsolutePath
|
||||
|
||||
/// File handler instance.
|
||||
private let fileHandler: FileHandling
|
||||
|
||||
/// Default public constructor.
|
||||
public convenience init() {
|
||||
self.init(directory: EnvironmentController.defaultDirectory,
|
||||
fileHandler: FileHandler())
|
||||
}
|
||||
|
||||
/// Default environment constroller constructor.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - directory: Directory where the Tuist environment files will be stored.
|
||||
/// - fileHandler: File handler instance to perform file operations.
|
||||
init(directory: AbsolutePath, fileHandler: FileHandling) {
|
||||
self.directory = directory
|
||||
self.fileHandler = fileHandler
|
||||
setup()
|
||||
}
|
||||
|
||||
// MARK: - EnvironmentControlling
|
||||
|
||||
/// Sets up the local environment.
|
||||
private func setup() {
|
||||
[directory, versionsDirectory, derivedProjectsDirectory].forEach {
|
||||
if !fileHandler.exists($0) {
|
||||
// Note: It should be safe to use try! here
|
||||
try! fileHandler.createFolder($0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the directory where all the versions are.
|
||||
public var versionsDirectory: AbsolutePath {
|
||||
return directory.appending(component: "Versions")
|
||||
}
|
||||
|
||||
/// Returns the directory where all the derived projects are generated.
|
||||
public var derivedProjectsDirectory: AbsolutePath {
|
||||
return directory.appending(component: "DerivedProjects")
|
||||
}
|
||||
|
||||
/// Settings path.
|
||||
public var settingsPath: AbsolutePath {
|
||||
return directory.appending(component: "settings.json")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
// swiftlint:disable all
|
||||
//
|
||||
// String+MD5.swift
|
||||
// Kingfisher
|
||||
//
|
||||
// To date, adding CommonCrypto to a Swift framework is problematic. See:
|
||||
// http://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework
|
||||
// We're using a subset and modified version of CryptoSwift as an alternative.
|
||||
// The following is an altered source version that only includes MD5. The original software can be found at:
|
||||
// https://github.com/krzyzanowskim/CryptoSwift
|
||||
// This is the original copyright notice:
|
||||
|
||||
/*
|
||||
Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
- The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
|
||||
- Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
- This notice may not be removed or altered from any source or binary distribution.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension String {
|
||||
public var md5: String {
|
||||
if let data = data(using: .utf8, allowLossyConversion: true) {
|
||||
let message = data.withUnsafeBytes { bytes -> [UInt8] in
|
||||
return Array(UnsafeBufferPointer(start: bytes, count: data.count))
|
||||
}
|
||||
|
||||
let MD5Calculator = MD5(message)
|
||||
let MD5Data = MD5Calculator.calculate()
|
||||
|
||||
var MD5String = String()
|
||||
for c in MD5Data {
|
||||
MD5String += String(format: "%02x", c)
|
||||
}
|
||||
return MD5String
|
||||
|
||||
} else {
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** array of bytes, little-endian representation */
|
||||
func arrayOfBytes<T>(_ value: T, length: Int? = nil) -> [UInt8] {
|
||||
let totalBytes = length ?? (MemoryLayout<T>.size * 8)
|
||||
|
||||
let valuePointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
|
||||
valuePointer.pointee = value
|
||||
|
||||
let bytes = valuePointer.withMemoryRebound(to: UInt8.self, capacity: totalBytes) { (bytesPointer) -> [UInt8] in
|
||||
var bytes = [UInt8](repeating: 0, count: totalBytes)
|
||||
for j in 0 ..< min(MemoryLayout<T>.size, totalBytes) {
|
||||
bytes[totalBytes - 1 - j] = (bytesPointer + j).pointee
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
#if swift(>=4.1)
|
||||
valuePointer.deinitialize(count: 1)
|
||||
valuePointer.deallocate()
|
||||
#else
|
||||
valuePointer.deinitialize()
|
||||
valuePointer.deallocate(capacity: 1)
|
||||
#endif
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
extension Int {
|
||||
/** Array of bytes with optional padding (little-endian) */
|
||||
func bytes(_ totalBytes: Int = MemoryLayout<Int>.size) -> [UInt8] {
|
||||
return arrayOfBytes(self, length: totalBytes)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSMutableData {
|
||||
/** Convenient way to append bytes */
|
||||
func appendBytes(_ arrayOfBytes: [UInt8]) {
|
||||
append(arrayOfBytes, length: arrayOfBytes.count)
|
||||
}
|
||||
}
|
||||
|
||||
protocol HashProtocol {
|
||||
var message: Array<UInt8> { get }
|
||||
|
||||
/** Common part for hash calculation. Prepare header data. */
|
||||
func prepare(_ len: Int) -> Array<UInt8>
|
||||
}
|
||||
|
||||
extension HashProtocol {
|
||||
func prepare(_ len: Int) -> Array<UInt8> {
|
||||
var tmpMessage = message
|
||||
|
||||
// Step 1. Append Padding Bits
|
||||
tmpMessage.append(0x80) // append one bit (UInt8 with one bit) to message
|
||||
|
||||
// append "0" bit until message length in bits ≡ 448 (mod 512)
|
||||
var msgLength = tmpMessage.count
|
||||
var counter = 0
|
||||
|
||||
while msgLength % len != (len - 8) {
|
||||
counter += 1
|
||||
msgLength += 1
|
||||
}
|
||||
|
||||
tmpMessage += Array<UInt8>(repeating: 0, count: counter)
|
||||
return tmpMessage
|
||||
}
|
||||
}
|
||||
|
||||
func toUInt32Array(_ slice: ArraySlice<UInt8>) -> Array<UInt32> {
|
||||
var result = Array<UInt32>()
|
||||
result.reserveCapacity(16)
|
||||
|
||||
for idx in stride(from: slice.startIndex, to: slice.endIndex, by: MemoryLayout<UInt32>.size) {
|
||||
let d0 = UInt32(slice[idx.advanced(by: 3)]) << 24
|
||||
let d1 = UInt32(slice[idx.advanced(by: 2)]) << 16
|
||||
let d2 = UInt32(slice[idx.advanced(by: 1)]) << 8
|
||||
let d3 = UInt32(slice[idx])
|
||||
let val: UInt32 = d0 | d1 | d2 | d3
|
||||
|
||||
result.append(val)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
struct BytesIterator: IteratorProtocol {
|
||||
let chunkSize: Int
|
||||
let data: [UInt8]
|
||||
|
||||
init(chunkSize: Int, data: [UInt8]) {
|
||||
self.chunkSize = chunkSize
|
||||
self.data = data
|
||||
}
|
||||
|
||||
var offset = 0
|
||||
|
||||
mutating func next() -> ArraySlice<UInt8>? {
|
||||
let end = min(chunkSize, data.count - offset)
|
||||
let result = data[offset ..< offset + end]
|
||||
offset += result.count
|
||||
return result.count > 0 ? result : nil
|
||||
}
|
||||
}
|
||||
|
||||
struct BytesSequence: Sequence {
|
||||
let chunkSize: Int
|
||||
let data: [UInt8]
|
||||
|
||||
func makeIterator() -> BytesIterator {
|
||||
return BytesIterator(chunkSize: chunkSize, data: data)
|
||||
}
|
||||
}
|
||||
|
||||
func rotateLeft(_ value: UInt32, bits: UInt32) -> UInt32 {
|
||||
return ((value << bits) & 0xFFFF_FFFF) | (value >> (32 - bits))
|
||||
}
|
||||
|
||||
class MD5: HashProtocol {
|
||||
static let size = 16 // 128 / 8
|
||||
let message: [UInt8]
|
||||
|
||||
init(_ message: [UInt8]) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
/** specifies the per-round shift amounts */
|
||||
private let shifts: [UInt32] = [
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
|
||||
]
|
||||
|
||||
/** binary integer part of the sines of integers (Radians) */
|
||||
private let sines: [UInt32] = [
|
||||
0xD76A_A478, 0xE8C7_B756, 0x2420_70DB, 0xC1BD_CEEE,
|
||||
0xF57C_0FAF, 0x4787_C62A, 0xA830_4613, 0xFD46_9501,
|
||||
0x6980_98D8, 0x8B44_F7AF, 0xFFFF_5BB1, 0x895C_D7BE,
|
||||
0x6B90_1122, 0xFD98_7193, 0xA679_438E, 0x49B4_0821,
|
||||
0xF61E_2562, 0xC040_B340, 0x265E_5A51, 0xE9B6_C7AA,
|
||||
0xD62F_105D, 0x0244_1453, 0xD8A1_E681, 0xE7D3_FBC8,
|
||||
0x21E1_CDE6, 0xC337_07D6, 0xF4D5_0D87, 0x455A_14ED,
|
||||
0xA9E3_E905, 0xFCEF_A3F8, 0x676F_02D9, 0x8D2A_4C8A,
|
||||
0xFFFA_3942, 0x8771_F681, 0x6D9D_6122, 0xFDE5_380C,
|
||||
0xA4BE_EA44, 0x4BDE_CFA9, 0xF6BB_4B60, 0xBEBF_BC70,
|
||||
0x289B_7EC6, 0xEAA1_27FA, 0xD4EF_3085, 0x4881D05,
|
||||
0xD9D4_D039, 0xE6DB_99E5, 0x1FA2_7CF8, 0xC4AC_5665,
|
||||
0xF429_2244, 0x432A_FF97, 0xAB94_23A7, 0xFC93_A039,
|
||||
0x655B_59C3, 0x8F0C_CC92, 0xFFEF_F47D, 0x8584_5DD1,
|
||||
0x6FA8_7E4F, 0xFE2C_E6E0, 0xA301_4314, 0x4E08_11A1,
|
||||
0xF753_7E82, 0xBD3A_F235, 0x2AD7_D2BB, 0xEB86_D391,
|
||||
]
|
||||
|
||||
private let hashes: [UInt32] = [0x6745_2301, 0xEFCD_AB89, 0x98BA_DCFE, 0x1032_5476]
|
||||
|
||||
func calculate() -> [UInt8] {
|
||||
var tmpMessage = prepare(64)
|
||||
tmpMessage.reserveCapacity(tmpMessage.count + 4)
|
||||
|
||||
// hash values
|
||||
var hh = hashes
|
||||
|
||||
// Step 2. Append Length a 64-bit representation of lengthInBits
|
||||
let lengthInBits = (message.count * 8)
|
||||
let lengthBytes = lengthInBits.bytes(64 / 8)
|
||||
tmpMessage += lengthBytes.reversed()
|
||||
|
||||
// Process the message in successive 512-bit chunks:
|
||||
let chunkSizeBytes = 512 / 8 // 64
|
||||
|
||||
for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) {
|
||||
// break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
|
||||
var M = toUInt32Array(chunk)
|
||||
assert(M.count == 16, "Invalid array")
|
||||
|
||||
// Initialize hash value for this chunk:
|
||||
var A: UInt32 = hh[0]
|
||||
var B: UInt32 = hh[1]
|
||||
var C: UInt32 = hh[2]
|
||||
var D: UInt32 = hh[3]
|
||||
|
||||
var dTemp: UInt32 = 0
|
||||
|
||||
// Main loop
|
||||
for j in 0 ..< sines.count {
|
||||
var g = 0
|
||||
var F: UInt32 = 0
|
||||
|
||||
switch j {
|
||||
case 0 ... 15:
|
||||
F = (B & C) | ((~B) & D)
|
||||
g = j
|
||||
break
|
||||
case 16 ... 31:
|
||||
F = (D & B) | (~D & C)
|
||||
g = (5 * j + 1) % 16
|
||||
break
|
||||
case 32 ... 47:
|
||||
F = B ^ C ^ D
|
||||
g = (3 * j + 5) % 16
|
||||
break
|
||||
case 48 ... 63:
|
||||
F = C ^ (B | (~D))
|
||||
g = (7 * j) % 16
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
dTemp = D
|
||||
D = C
|
||||
C = B
|
||||
B = B &+ rotateLeft((A &+ F &+ sines[j] &+ M[g]), bits: shifts[j])
|
||||
A = dTemp
|
||||
}
|
||||
|
||||
hh[0] = hh[0] &+ A
|
||||
hh[1] = hh[1] &+ B
|
||||
hh[2] = hh[2] &+ C
|
||||
hh[3] = hh[3] &+ D
|
||||
}
|
||||
|
||||
var result = [UInt8]()
|
||||
result.reserveCapacity(hh.count / 4)
|
||||
|
||||
hh.forEach {
|
||||
let itemLE = $0.littleEndian
|
||||
let r1 = UInt8(itemLE & 0xFF)
|
||||
let r2 = UInt8((itemLE >> 8) & 0xFF)
|
||||
let r3 = UInt8((itemLE >> 16) & 0xFF)
|
||||
let r4 = UInt8((itemLE >> 24) & 0xFF)
|
||||
result += [r1, r2, r3, r4]
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:enable all
|
|
@ -1,8 +1,8 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
@testable import TuistEnvKit
|
||||
import TuistShared
|
||||
|
||||
class MockEnvironmentController: EnvironmentControlling {
|
||||
public class MockEnvironmentController: EnvironmentControlling {
|
||||
let directory: TemporaryDirectory
|
||||
var setupCallCount: UInt = 0
|
||||
var setupErrorStub: Error?
|
||||
|
@ -14,11 +14,15 @@ class MockEnvironmentController: EnvironmentControlling {
|
|||
attributes: nil)
|
||||
}
|
||||
|
||||
var versionsDirectory: AbsolutePath {
|
||||
public var versionsDirectory: AbsolutePath {
|
||||
return directory.path.appending(component: "Versions")
|
||||
}
|
||||
|
||||
var settingsPath: AbsolutePath {
|
||||
public var derivedProjectsDirectory: AbsolutePath {
|
||||
return directory.path.appending(component: "DerivedProjects")
|
||||
}
|
||||
|
||||
public var settingsPath: AbsolutePath {
|
||||
return directory.path.appending(component: "settings.json")
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import Basic
|
||||
import Foundation
|
||||
@testable import TuistEnvKit
|
||||
@testable import TuistSharedTesting
|
||||
import XCTest
|
||||
|
||||
final class SettingsControllerTests: XCTestCase {
|
||||
|
|
|
@ -2,6 +2,7 @@ import Basic
|
|||
import Foundation
|
||||
@testable import TuistCoreTesting
|
||||
@testable import TuistEnvKit
|
||||
@testable import TuistSharedTesting
|
||||
import Utility
|
||||
import XCTest
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ final class FocusCommandTests: XCTestCase {
|
|||
let result = try parser.parse([FocusCommand.command, "-c", "Debug"])
|
||||
var configuration: BuildConfiguration?
|
||||
let error = NSError.test()
|
||||
workspaceGenerator.generateStub = { _, _, options, _, _, _ in
|
||||
workspaceGenerator.generateStub = { _, _, options, _ in
|
||||
configuration = options.buildConfiguration
|
||||
throw error
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ final class FocusCommandTests: XCTestCase {
|
|||
func test_run() throws {
|
||||
let result = try parser.parse([FocusCommand.command, "-c", "Debug"])
|
||||
let workspacePath = AbsolutePath("/test.xcworkspace")
|
||||
workspaceGenerator.generateStub = { _, _, _, _, _, _ in
|
||||
workspaceGenerator.generateStub = { _, _, _, _ in
|
||||
workspacePath
|
||||
}
|
||||
try subject.run(with: result)
|
||||
|
|
|
@ -44,7 +44,7 @@ final class GenerateCommandTests: XCTestCase {
|
|||
let result = try parser.parse([GenerateCommand.command, "-c", "Debug"])
|
||||
var configuration: BuildConfiguration?
|
||||
let error = NSError.test()
|
||||
workspaceGenerator.generateStub = { _, _, options, _, _, _ in
|
||||
workspaceGenerator.generateStub = { _, _, options, _ in
|
||||
configuration = options.buildConfiguration
|
||||
throw error
|
||||
}
|
||||
|
|
|
@ -4,14 +4,9 @@ import TuistCore
|
|||
@testable import TuistKit
|
||||
|
||||
final class MockWorkspaceGenerator: WorkspaceGenerating {
|
||||
var generateStub: ((AbsolutePath, Graphing, GenerationOptions, Systeming, Printing, ResourceLocating) throws -> AbsolutePath)?
|
||||
var generateStub: ((AbsolutePath, Graphing, GenerationOptions, GenerationDirectory) throws -> AbsolutePath)?
|
||||
|
||||
func generate(path: AbsolutePath,
|
||||
graph: Graphing,
|
||||
options: GenerationOptions,
|
||||
system: Systeming,
|
||||
printer: Printing,
|
||||
resourceLocator: ResourceLocating) throws -> AbsolutePath {
|
||||
return (try generateStub?(path, graph, options, system, printer, resourceLocator)) ?? AbsolutePath("/test")
|
||||
func generate(path: AbsolutePath, graph: Graphing, options: GenerationOptions, directory: GenerationDirectory) throws -> AbsolutePath {
|
||||
return (try generateStub?(path, graph, options, directory)) ?? AbsolutePath("/test")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import Basic
|
||||
@testable import TuistCoreTesting
|
||||
@testable import TuistKit
|
||||
@testable import TuistSharedTesting
|
||||
import XCTest
|
||||
|
||||
final class ProjectDirectoryHelperTests: XCTestCase {
|
||||
var subject: ProjectDirectoryHelper!
|
||||
var fileHandler: MockFileHandler!
|
||||
var environmentController: MockEnvironmentController!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
fileHandler = try! MockFileHandler()
|
||||
environmentController = try! MockEnvironmentController()
|
||||
subject = ProjectDirectoryHelper(environmentController: environmentController,
|
||||
fileHandler: fileHandler)
|
||||
}
|
||||
|
||||
func test_setupDirectory_when_derivedProjects() throws {
|
||||
let path = fileHandler.currentPath
|
||||
let got = try subject.setupDirectory(name: "MyApp",
|
||||
path: path,
|
||||
directory: .derivedProjects)
|
||||
let expected = environmentController.derivedProjectsDirectory.appending(component: "MyApp-\(path.asString.md5)")
|
||||
|
||||
XCTAssertEqual(got, expected)
|
||||
XCTAssertTrue(fileHandler.exists(expected))
|
||||
}
|
||||
|
||||
func test_setupDirectory_when_manifest() throws {
|
||||
let path = fileHandler.currentPath
|
||||
let got = try subject.setupDirectory(name: "name",
|
||||
path: path,
|
||||
directory: .manifest)
|
||||
XCTAssertEqual(got, path)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue