Skip to content
Merged
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
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM swift:4.2.1
COPY XcodeProject.xcodeproj ./XcodeProject.xcodeproj
COPY Package.swift ./Package.swift
COPY Sources ./Sources
COPY Tests ./Tests
RUN swift test --configuration debug
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ test:

xcode:
swift package generate-xcodeproj --xcconfig-overrides=overrides.xcconfig

linux:
swift test --generate-linuxmain $(SWIFTC_FLAGS)

clean:
swift build --clean
swift build --clean
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:4.0
// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down
36 changes: 28 additions & 8 deletions Sources/XcodeProject/Objects/PBXFileType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
//

import Foundation
#if canImport(CoreServices)
import CoreServices
#endif

public struct PBXFileType {
static var fileTypes: [PBXFileType] = [
Expand Down Expand Up @@ -39,25 +41,43 @@ public struct PBXFileType {
var isSource: Bool {
return UTTypeConformsTo(uniformType as CFString, kUTTypeSourceCode)
}

static func fileType(filePath: String) -> PBXFileType? {
let fileExtension = (filePath as NSString).pathExtension
return fileType(fileExtension: fileExtension)
}

static func fileType(reference: PBXFileReference) -> PBXFileType? {
return fileType(filePath: reference.fileType.type)
}
}

#if !canImport(CoreServices)
typealias CFString = String
let kUTTypeCHeader: CFString = "public.c-header"
let kUTTypeCPlusPlusHeader: CFString = "public.c-plus-plus-header"
let kUTTypeSourceCode: CFString = "public.source-code"
func UTTypeConformsTo(_ inUTI: CFString, _ inConformsToUTI: CFString) -> Bool {
return true
}
#endif

#if canImport(CoreServices)
public extension PBXFileType {
static func fileType(fileExtension: String) -> PBXFileType? {
guard let uniformType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension as CFString, nil)?.takeUnretainedValue() as String? else { return nil }
return fileTypes.first {
$0.uniformType == uniformType
}
}

static func fileType(filePath: String) -> PBXFileType? {
let fileExtension = (filePath as NSString).pathExtension
return fileType(fileExtension: fileExtension)
}

static func fileType(reference: PBXFileReference) -> PBXFileType? {
return fileType(filePath: reference.fileType.type)
}
#else
public extension PBXFileType {
static func fileType(fileExtension: String) -> PBXFileType? {
return nil
}
}
#endif

extension PBXFileType {
var buildPhaseType: PBXBuildPhase.Type? {
Expand Down
8 changes: 2 additions & 6 deletions Sources/XcodeProject/Objects/PBXGlobalID+Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,13 @@ extension PBXGlobalID {
private var lastTime: UInt32 = 0
private var firstSequence: UInt16 = 0

init(userName: String = NSUserName(), processId: pid_t = getpid(), random: inout RandomNumberGenerator, referenceDateGenerator: @escaping () -> Date = Generator.referenceDate) {
init(userName: String = NSUserName(), processId: pid_t = getpid(), hostId: UInt32, random: UInt16, referenceDateGenerator: @escaping () -> Date = Generator.referenceDate) {
self.referenceDateGenerator = referenceDateGenerator

var hostId: UInt32 = UInt32(gethostid())
if hostId == 0 {
hostId = random.next()
}
self.gid = GlobalIdentifier(
userHash: Generator.userHash(userName: userName),
pidByte: UInt8(truncatingIfNeeded: processId),
random: random.next(),
random: random,
time: 0,
hostShift: UInt8(truncatingIfNeeded: (hostId >> 0x10) & 0xff),
hostHigh: UInt8(truncatingIfNeeded: (hostId & 0xff00) >> 0x8),
Expand Down
14 changes: 10 additions & 4 deletions Sources/XcodeProject/Objects/PBXGlobalID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ public struct PBXGlobalID: RawRepresentable {
return strings?.compactMap { return PBXGlobalID(rawValue: $0) }
}

private static var randomNumberGenerator: RandomNumberGenerator = SystemRandomNumberGenerator()
private static var generator: PBXGlobalID.Generator = PBXGlobalID.Generator(random: &randomNumberGenerator)
private static var generator: PBXGlobalID.Generator = {
var randomNumberGenerator: RandomNumberGenerator = SystemRandomNumberGenerator()
var hostId: UInt32 = UInt32(gethostid())
if hostId == 0 {
hostId = randomNumberGenerator.next()
}
return PBXGlobalID.Generator(hostId: hostId, random: randomNumberGenerator.next())
}()
}

extension PBXGlobalID: Hashable {
public var hashValue: Int {
return rawValue.hashValue
public func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}

public static func ==(lhs: PBXGlobalID, rhs: PBXGlobalID) -> Bool {
Expand Down
4 changes: 2 additions & 2 deletions Sources/XcodeProject/Objects/PBXObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public class PBXObject {
}

extension PBXObject: Hashable {
public var hashValue: Int {
return globalID.hashValue
public func hash(into hasher: inout Hasher) {
hasher.combine(globalID)
}

public static func ==(lhs: PBXObject, rhs: PBXObject) -> Bool {
Expand Down
24 changes: 20 additions & 4 deletions Sources/XcodeProject/Objects/References/PBXReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,30 @@ public class PBXReference: PBXObject {
var indentWidth: Int?

var buildFiles: [PBXBuildFile] {
return _buildFiles.allObjects
return _buildFiles.compactMap { $0.item }
}
private var _buildFiles = NSHashTable<PBXBuildFile>.weakObjects()

private struct WeakBuildFile: Hashable {
weak var item: PBXBuildFile?

static func ==(lhs: WeakBuildFile, rhs: WeakBuildFile) -> Bool {
return lhs.item == rhs.item
}

func hash(into hasher: inout Hasher) {
hasher.combine(item)
}
}

private var _buildFiles: Array<WeakBuildFile> = []

func register(buildFile: PBXBuildFile) {
_buildFiles.add(buildFile)
_buildFiles.removeAll { $0.item == nil }
_buildFiles.append(WeakBuildFile(item: buildFile))
}

func unregister(buildFile: PBXBuildFile) {
_buildFiles.remove(buildFile)
_buildFiles.removeAll { $0.item == nil || $0.item == buildFile }
}

public var displayName: String {
Expand Down
45 changes: 4 additions & 41 deletions Sources/XcodeProject/ProjectFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,15 @@ public final class ProjectFile {
public private(set) var project: PBXProject

let url: URL
let fileWrapper: FileWrapper

/// Initializes a new project file from the given URL
///
/// - Parameter url: Path to an xcodeproj file to be opened
/// - Returns: A fully parsed project from the provided source or `nil` if an error happened
public init(url: URL) throws {
self.url = url
self.fileWrapper = try FileWrapper(url: url, options: [])
guard fileWrapper.isDirectory else {
throw CocoaError.error(.fileReadUnknown)
}

guard let pbxproj = fileWrapper.fileWrappers?["project.pbxproj"], pbxproj.isRegularFile else {
throw CocoaError.error(.fileReadUnknown)
}

guard let data = pbxproj.regularFileContents else {
throw Error.invalid
}
let pbxproj = URL(fileURLWithPath: "project.pbxproj", relativeTo: url)
let data = try Data(contentsOf: pbxproj)

guard let plist = try PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any] else {
throw Error.invalidPlist
Expand All @@ -85,23 +74,6 @@ public final class ProjectFile {
project.path = url.path
self.project = project
}

public func currentFileWrapper() throws -> FileWrapper {
let currentFileWrapper = fileWrapper

let oldPbxproj = currentFileWrapper.fileWrappers!["project.pbxproj"]!

let dataStream = DataStreamWriter()
let archiver = PBXPListArchiver(projectFile: self)
try archiver.write(stream: dataStream)
let newPbxproj = FileWrapper(regularFileWithContents: dataStream.data)
newPbxproj.preferredFilename = "project.pbxproj"

currentFileWrapper.removeFileWrapper(oldPbxproj)
currentFileWrapper.addFileWrapper(newPbxproj)

return currentFileWrapper
}
}

extension ProjectFile {
Expand All @@ -112,19 +84,10 @@ extension ProjectFile {
public func save(to destination: URL? = nil) throws {
let destination = destination ?? url

guard let oldPbxproj = fileWrapper.fileWrappers?["project.pbxproj"], oldPbxproj.isRegularFile else {
throw CocoaError.error(.fileReadUnknown)
}

let dataStream = DataStreamWriter()
let archiver = PBXPListArchiver(projectFile: self)
try archiver.write(stream: dataStream)
let newPbxproj = FileWrapper(regularFileWithContents: dataStream.data)
newPbxproj.preferredFilename = "project.pbxproj"

fileWrapper.removeFileWrapper(oldPbxproj)
fileWrapper.addFileWrapper(newPbxproj)

try fileWrapper.write(to: destination, options: [.atomic], originalContentsURL: nil)
let pbxprojURL = URL(fileURLWithPath: "project.pbxproj", relativeTo: destination)
try dataStream.data.write(to: pbxprojURL, options: [.atomic])
}
}
12 changes: 2 additions & 10 deletions Sources/XcodeProject/Workspace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public final class Workspace: WorkspaceReference {
}
let url: URL
public var referenceURL: URL? { return url.deletingLastPathComponent() }
public let fileWrapper: FileWrapper
public var references: [WorkspaceItem] = []

public class FileReference: WorkspaceItem {
Expand All @@ -88,15 +87,8 @@ public final class Workspace: WorkspaceReference {

public init(url: URL) throws {
self.url = url
self.fileWrapper = try FileWrapper(url: url, options: [])
if fileWrapper.isDirectory == false {
//throw
}

guard let workspaceData = fileWrapper.fileWrappers?["contents.xcworkspacedata"], workspaceData.isRegularFile,
let data = workspaceData.regularFileContents else {
throw Error.invalid
}
let workspaceFileURL = URL(fileURLWithPath: "contents.xcworkspacedata", relativeTo: url)
let data = try Data(contentsOf: workspaceFileURL)

let document = try XMLDocument(data: data, options: [])
guard let children = document.rootElement()?.children as? [XMLElement] else { throw Error.invalid }
Expand Down
8 changes: 8 additions & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import XCTest

import XcodeProjectTests

var tests = [XCTestCaseEntry]()
tests += XcodeProjectTests.__allTests()

XCTMain(tests)
12 changes: 1 addition & 11 deletions Tests/XcodeProjectTests/PBXGlobalIDTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,14 @@ import XCTest
@testable import XcodeProject

class PBXGlobalIDTests: XCTestCase {
struct FakeRandomGenerator: RandomNumberGenerator {
private var value: UInt64 = 1234567890
mutating func next() -> UInt64 {
defer { value += 1 }
return value
}
}

func mockDateGenerator() -> Date {
let components = DateComponents(timeZone: TimeZone(secondsFromGMT: 0), year: 2014, month: 07, day: 29, hour: 17, minute: 04, second: 20)
let date = Calendar(identifier: .gregorian).date(from: components)
return date!
}

func testIDGeneration() {
let processId: pid_t = 50_000
var randomNumberGenerator: RandomNumberGenerator = FakeRandomGenerator()
var generator = PBXGlobalID.Generator(userName: "XcodeProject", processId: processId, random: &randomNumberGenerator, referenceDateGenerator: mockDateGenerator)
var generator = PBXGlobalID.Generator(userName: "XcodeProject", processId: 50_000, hostId: 1234567890, random: 723, referenceDateGenerator: mockDateGenerator)
XCTAssertEqual("8C5002D419880B94009602D2", generator.next())
XCTAssertEqual("8C5002D519880B94009602D2", generator.next())
XCTAssertEqual("8C5002D619880B94009602D2", generator.next())
Expand Down
25 changes: 25 additions & 0 deletions Tests/XcodeProjectTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import XCTest

extension PBXGlobalIDTests {
static let __allTests = [
("testIDGeneration", testIDGeneration),
]
}

extension XcodeProjectTests {
static let __allTests = [
("testAggregateTarget", testAggregateTarget),
("testPrivateProjectFolder", testPrivateProjectFolder),
("testSelfArchive", testSelfArchive),
("testSelfSave", testSelfSave),
]
}

#if !os(macOS)
public func __allTests() -> [XCTestCaseEntry] {
return [
testCase(PBXGlobalIDTests.__allTests),
testCase(XcodeProjectTests.__allTests),
]
}
#endif
Loading