Commit 487f4835 authored by Drew's avatar Drew

Support for Xcode integration

parent 6121089d
......@@ -32,19 +32,20 @@ import Glibc
#endif
enum FileUtilsError: ErrorProtocol {
case OpenError(Int)
case OpenError(Int, String)
case ReadError(Int)
case CloseError(Int)
case WriteError(Int)
case UTF8Error
}
extension String {
init(fromFile fileName: String) throws {
init(file fileName: String) throws {
let file = open(fileName, O_RDONLY)
var completeFile: [CChar] = []
//CANNOT defer cleaning up this resource
//PAY ATTENTION
if file == -1 { throw FileUtilsError.OpenError(Int(errno)) }
if file == -1 { throw FileUtilsError.OpenError(Int(errno), fileName) }
readLoop: while true {
var buf = [CChar](repeating: 0, count: 4096)
let bytesRead = buf.withUnsafeMutableBufferPointer {
......@@ -75,4 +76,41 @@ extension String {
throw FileUtilsError.CloseError(Int(errno))
}
}
func write(file fileName: String) throws {
#if os(OSX)
let os_write = Darwin.write
#elseif os(Linux)
let os_write = Glibc.write
#endif
let file = open(fileName,O_WRONLY | O_CREAT, 0o600)
//CANNOT defer cleaning up this resource
//PAY ATTENTION
if file == -1 { throw FileUtilsError.OpenError(Int(errno), fileName) }
let length = self.utf8.count
let bytesWritten = self.withCString() {(ptr) -> Int in
return os_write(file, ptr, length)
}
if bytesWritten != self.utf8.count {
close(file) //CLEAN UP open resource
throw FileUtilsError.WriteError(Int(errno))
}
if close(file) != 0 {
throw FileUtilsError.CloseError(Int(errno))
}
}
}
extension String {
///The location of the directory for the item.
var directoryPath: String {
var position = self.characters.endIndex
for (x,c) in self.characters.reversed().enumerated() {
if c == "/" {
return String(self.characters[self.characters.startIndex..<position])
}
position = position.predecessor()
}
return self
}
}
\ No newline at end of file
let MITLicense: String = {
var s = ""
s += "/*\n"
s += "Copyright (C) 2016 Drew Crawford\n"
s += "All rights reserved.\n"
s += "\n"
s += "Redistribution and use in source and binary forms, with or without\n"
s += "modification, are permitted provided that the following conditions\n"
s += "are met:\n"
s += "1. Redistributions of source code must retain the above copyright\n"
s += " notice, this list of conditions and the following disclaimer.\n"
s += "2. Redistributions in binary form must reproduce the above copyright\n"
s += " notice, this list of conditions and the following disclaimer in the\n"
s += " documentation and/or other materials provided with the distribution.\n"
s += "3. Neither the names of the copyright holders nor the names of any\n"
s += " contributors may be used to endorse or promote products derived from this\n"
s += " software without specific prior written permission.\n"
s += "\n"
s += "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
s += "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
s += "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
s += "ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
s += "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
s += "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
s += "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
s += "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
s += "CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
s += "ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
s += "POSSIBILITY OF SUCH DAMAGE.\n"
s += "*/\n"
return s
}()
\ No newline at end of file
/*
Copyright (C) 2016 Drew Crawford
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the names of the copyright holders nor the names of any
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
func findTests(sourceFiles: [String]) -> [String] {
var tests: [String] = []
for file in sourceFiles {
let sourceText = try! String(file: file)
for match in try! Regex(pattern: "class[[:space:]]+([[:alnum:]]+)[[:space:]]*:[[:space:]]*CarolineTest[[:space:]]*\\{").findAll(inString: sourceText) {
let className = match.groups[0]!
tests.append(className.description)
}
}
return tests
}
func allTestsDeclaration(tests: [String], indentation: Int) -> String {
var s = ""
for _ in 0..<indentation {s += " "}
s += "let allTests: [CarolineTest] = [\n"
var i = 0
for test in tests {
if i != 0 { s += ",\n" }
i += 1
for _ in 0..<indentation+4 {s += " "}
s += "\(test)()"
}
s += "\n"
for _ in 0..<indentation {s += " "}
s += "]"
return s
}
\ No newline at end of file
/*
Copyright (C) 2016 Drew Crawford
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the names of the copyright holders nor the names of any
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#if os(Linux)
import Glibc
#else
import Darwin
#endif
func env_(variable: String) -> String? {
let env = getenv(variable)
if env == nil { return nil }
return String(validatingUTF8: env)
}
///Performs XCTest integration
///- returns: True if XCTest integration was performed.
///This means we can avoid doing atbuild-like activity.
func performXcodeIntegration() -> Bool {
if env_("WRAPPER_EXTENSION") != "xctest" { return false }
print("performing XCTest integration")
let xcodeProject = env_("PROJECT_FILE_PATH")!
let target = env_("TARGETNAME")!
let paths = parseXcodeProj(xcodeProject, targetName: target)
print(paths)
let tests = findTests(paths)
var xcTestFile = ""
xcTestFile += MITLicense
xcTestFile += "import XCTest\n"
xcTestFile += "import CarolineCore\n"
xcTestFile += "@testable import MyMacFramework\n"
xcTestFile += "\n"
xcTestFile += "class CarolineEngineTests: XCTestCase {\n"
xcTestFile += " func testAllCarolineTests() {\n"
xcTestFile += allTestsDeclaration(tests, indentation: 8) + "\n"
xcTestFile += " let engine = CarolineCoreEngine()\n"
xcTestFile += " engine.testAll(allTests)\n"
xcTestFile += " }\n"
xcTestFile += "}\n"
let generatedFilePath = "\(xcodeProject.directoryPath)/\(target)/CarolineXCTest.swift"
try! xcTestFile.write(file: generatedFilePath)
if !paths.contains(generatedFilePath) {
print("You need to add \(generatedFilePath) to your Xcode Project.")
exit(1)
}
return true
}
func parseXcodeProj(fileName: String, targetName: String) -> [String] {
let pathToXcodeProject = fileName.directoryPath
let file = try! String(file: fileName + "/project.pbxproj")
let plistCharacters = "[[:space:][:alnum:]/\\*={};_\"\\(\\),\\.\\-]"
let allTargetsRegex = try! Regex(pattern: "/\\* Begin PBXNativeTarget section \\*/\(plistCharacters)*/\\* End PBXNativeTarget section \\*/")
guard let allTargets = try! allTargetsRegex.findFirst(inString: file)?.entireMatch.description else {
fatalError("Can't find any targets for file \(fileName)")
}
let searchTargetRegex = try! Regex(pattern: "/\\* \(targetName) \\*/ = {\(plistCharacters)*};")
guard let searchTarget = try! searchTargetRegex.findFirst(inString: allTargets)?.entireMatch.description else {
fatalError("Can't find so-called target \(targetName)")
}
let sourcePhaseRegex = try! Regex(pattern: "([[:upper:][:digit:]]{24}) /\\* Sources \\*/")
guard let sourcePhase = try! sourcePhaseRegex.findFirst(inString: searchTarget)?.groups[0]?.description else {
fatalError("Can't find a source phase for \(searchTarget)")
}
let sourcePhaseDefinitionRegex = try! Regex(pattern: "\(sourcePhase) /\\* Sources \\*/ = {[^\\}]+};")
guard let sourcePhaseDefinition = try! sourcePhaseDefinitionRegex.findFirst(inString: file)?.entireMatch.description else {
fatalError("Can't find source phase definition for source phase \(sourcePhase)")
}
let filesListingRegex = try! Regex(pattern: "files = \\([^\\)]+);")
guard let filesListing = try! filesListingRegex.findFirst(inString: sourcePhaseDefinition)?.entireMatch.description else {
fatalError("Can't find filesListing for source phase.")
}
var paths: [String] = []
let fileIDRegex = try! Regex(pattern: "([[:upper:][:digit:]]{24}) /\\*")
for fileID_ in fileIDRegex.findAll(inString: filesListing) {
guard let fileID = fileID_.groups[0] else {
fatalError("No group for fileID \(fileID_)")
}
let fileRefRegex = try! Regex(pattern: "\(fileID) [^\n]* \\{[^\n]*fileRef = ([[:upper:][:digit:]]{24})")
guard let fileRef = try! fileRefRegex.findFirst(inString: file)?.groups[0]?.description else {
fatalError("Can't find fileRef for file \(fileID)")
}
let pathRegex = try! Regex(pattern: "\(fileRef) [^\n]*isa = PBXFileReference;[^\n]* path = [\"]?([^;]+);")
guard var path = try! pathRegex.findFirst(inString: file)?.groups[0]?.description else {
fatalError("Can't find path for file \(fileRef)")
}
if path.hasSuffix("\"") { path.remove(at: path.characters.endIndex.predecessor())}
//this is cheating; the targetName may not be the same as the group, but fuck it
paths.append("\(pathToXcodeProject)/\(targetName)/\(path)")
}
return paths
}
\ No newline at end of file
......@@ -33,6 +33,8 @@ import Darwin
import Glibc
#endif
if performXcodeIntegration() { exit(0) }
func usage() {
print("caroline-static-tool 0.1-dev")
print("Usage: caroline-static-tool --core file1.swift file2.swift file3.swift > /path/to/main.swift")
......@@ -45,56 +47,16 @@ if Process.arguments.count < 3 {
precondition(Process.arguments[1] == "--core", "Non-core is unsupported")
let fileNames = Process.arguments[2..<Process.arguments.count]
var s = ""
s += "/*\n"
s += "Copyright (C) 2016 Drew Crawford\n"
s += "All rights reserved.\n"
s += "\n"
s += "Redistribution and use in source and binary forms, with or without\n"
s += "modification, are permitted provided that the following conditions\n"
s += "are met:\n"
s += "1. Redistributions of source code must retain the above copyright\n"
s += " notice, this list of conditions and the following disclaimer.\n"
s += "2. Redistributions in binary form must reproduce the above copyright\n"
s += " notice, this list of conditions and the following disclaimer in the\n"
s += " documentation and/or other materials provided with the distribution.\n"
s += "3. Neither the names of the copyright holders nor the names of any\n"
s += " contributors may be used to endorse or promote products derived from this\n"
s += " software without specific prior written permission.\n"
s += "\n"
s += "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
s += "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
s += "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
s += "ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
s += "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
s += "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
s += "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
s += "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
s += "CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
s += "ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
s += "POSSIBILITY OF SUCH DAMAGE.\n"
s += "*/\n"
let fileNames = Array(Process.arguments[2..<Process.arguments.count])
print(s)
print(MITLicense)
print()
print("// This file is automatically generated by Caroline and should not be edited by hand.")
print ("import CarolineCore")
print("let allTests: [CarolineTest] = [")
print(allTestsDeclaration(findTests(fileNames), indentation:0))
var i = 0
for file in fileNames {
let sourceText = try! String(fromFile: file)
for match in try! Regex(pattern: "class[[:space:]]+([[:alnum:]]+)[[:space:]]*:[[:space:]]*CarolineTest[[:space:]]*\\{").findAll(inString: sourceText) {
if i != 0 { print(",")}
i += 1
let className = match.groups[0]!
print(" \(className)()", terminator:"")
}
}
print ("\n]")
print("let engine = CarolineCoreEngine()")
print("engine.testAll(allTests)")
\ No newline at end of file
......@@ -6,4 +6,5 @@
"folder_exclude_patterns": [".atllbuild"]
}
],
"executable":"bin/example"
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment