Commit e881a857 authored by Drew's avatar Drew

Emit XCTest targets

parent 0e4540f7
Pipeline #1796 failed with stage
......@@ -39,9 +39,10 @@
:ioscheck {
:tool "shell"
:dependencies ["xcodeemit"]
:script "cd tests/fixtures/ios && ../../../.atllbuild/products/xcode-emit ios --platform ios"
:script "cd tests/fixtures/ios && ../../../.atllbuild/products/xcode-emit ios --platform ios --test-task ios-tests && xcodebuild -scheme main -destination 'platform=iOS Simulator,name=iPhone 6' test"
}
:check {
:tool "shell"
:script ".atllbuild/products/xcode-emit xcodeemit"
......
......@@ -18,28 +18,35 @@ enum OutputType {
case Executable
case StaticLibrary
case Application
case TestTarget
}
import atpkg
func emit(task: Task, package: Package) {
func emit(task: Task, testTask: Task?, package: Package) {
precondition(task.tool == "atllbuild", "Unsupported tool \(task.tool)")
//make the xcodeproj directory
guard let taskname = task["name"]?.string else { fatalError("No task name.")}
let xcodeproj = Path(taskname+".xcodeproj")
let _ = try? FS.removeItem(path: xcodeproj, recursive: true)
try! FS.createDirectory(path: xcodeproj)
let str = pbxproj(task: task, package: package)
let project = pbxproj(task: task, testTask: testTask, package: package)
let str = project.serialize()
try! str.write(to: Path("\(xcodeproj)/project.pbxproj"))
try! FS.createDirectory(path: Path("\(xcodeproj)/xcshareddata/xcschemes"), intermediate: true)
let schemes = xcscheme(project: project)
try! schemes.write(to: Path("\(xcodeproj)/xcshareddata/xcschemes/\(taskname).xcscheme"))
}
func process(tasks: [Task], package: Package) -> [PbxprojSerializable] {
func process(tasks: [Task], testTask: Task?, package: Package) -> [PbxprojSerializable] {
let task = tasks[0] //pull off head
//are there dependencies?
var objects: [PbxprojSerializable] = []
if tasks.count > 1 {
let nextTasks = Array(tasks[1..<tasks.count])
objects.append(contentsOf: process(tasks: nextTasks, package: package))
objects.append(contentsOf: process(tasks: nextTasks, testTask: nil, package: package))
}
guard let taskname = task["name"]?.string else { fatalError("No task name.")}
guard let sourceDescriptions = task["sources"]?.vector?.flatMap({$0.string}) else { fatalError("Can't find sources for atllbuild.") }
......@@ -96,9 +103,23 @@ func process(tasks: [Task], package: Package) -> [PbxprojSerializable] {
let target = PbxNativeTarget(productReference: product, outputType: outputType, sourceFiles: sourceRefs, linkFiles: linkWith)
let target = PbxNativeTarget(productReference: product, outputType: outputType, sourceFiles: sourceRefs, linkFiles: linkWith, appTarget: nil)
objects.append(target)
objects.append(product)
if let testTask = testTask {
guard let testSourceDescriptions = testTask["sources"]?.vector?.flatMap({$0.string}) else { fatalError("Can't find sources for atllbuild.") }
let sources = collectSources(sourceDescriptions: testSourceDescriptions, taskForCalculatingPath: testTask).map() {PbxSourceFileReference(path: $0.description)}
let testProduct = PbxProductReference(name: taskname+"Tests", type: .TestTarget)
let testTarget = PbxNativeTarget(productReference: testProduct, outputType: .TestTarget, sourceFiles: sources, linkFiles: [], appTarget: target)
objects.append(testProduct)
objects.append(testTarget)
for o in sources {
objects.append(o)
}
}
for sourceRef in sourceRefs {
objects.append(sourceRef)
}
......@@ -108,9 +129,9 @@ func process(tasks: [Task], package: Package) -> [PbxprojSerializable] {
return objects
}
func pbxproj(task: Task, package: Package) -> String {
func pbxproj(task: Task, testTask: Task?, package: Package) -> Pbxproj {
var targets : [PbxNativeTarget] = []
var objects = process(tasks: package.prunedDependencyGraph(task: task).reversed(), package: package)
var objects = process(tasks: package.prunedDependencyGraph(task: task).reversed(), testTask: testTask, package: package)
for object in objects {
if object.dynamicType == PbxNativeTarget.self {
targets.append(object as! PbxNativeTarget)
......@@ -121,5 +142,5 @@ func pbxproj(task: Task, package: Package) -> String {
let groups = PbxGroups(targets: targets)
objects.append(groups)
let p = Pbxproj(objects: objects, rootObjectGUID: project.guid)
return p.serialize()
return p
}
\ No newline at end of file
......@@ -35,16 +35,28 @@ if Process.arguments[1] == "--help" {
usage()
exit(1)
}
var testTaskName: String? = nil
for (x,arg) in Process.arguments.enumerated() {
if arg == "--platform" && Process.arguments[x+1] == "ios" {
iosPlatform = true
}
if arg == "--test-task" {
testTaskName = Process.arguments[x+1]
}
}
let taskName = Process.arguments[1]
let package = try! Package(filepath:Path("build.atpkg"), overlay: [], focusOnTask:taskName)
guard let task = package.tasks[taskName] else { fatalError("Can't find task named \(taskName)")}
emit(task: task, package: package)
\ No newline at end of file
let testTask: Task?
if let t = testTaskName {
guard let t_ = package.tasks[t] else { fatalError("Can't find task named \(t)")}
testTask = t_
}
else { testTask = nil }
emit(task: task, testTask: testTask, package: package)
\ No newline at end of file
......@@ -49,6 +49,8 @@ struct Pbxproj: PbxprojSerializable {
}
}
struct Pbxproject: PbxprojSerializable {
let guid = xcodeguid()
var targets: [PbxNativeTarget]
......@@ -271,6 +273,12 @@ struct PbxGroups : PbxprojSerializable {
}
}
class TargetWrapper {
var target: PbxNativeTarget
init(target: PbxNativeTarget) { self.target = target }
}
struct PbxNativeTarget: PbxprojSerializable {
let guid = xcodeguid()
let productReference: PbxProductReference
......@@ -280,19 +288,25 @@ struct PbxNativeTarget: PbxprojSerializable {
let linkFiles: [PbxProductReference]
let configurationList: PbxTargetConfigurations
let appTarget: TargetWrapper?
//todo: This should be a more generic type than plists
let otherFiles: [PbxPlistFileReference]
let phases: PbxPhases
init(productReference: PbxProductReference, outputType: OutputType, sourceFiles: [PbxSourceFileReference], linkFiles:[PbxProductReference] ) {
init(productReference: PbxProductReference, outputType: OutputType, sourceFiles: [PbxSourceFileReference], linkFiles:[PbxProductReference], appTarget: PbxNativeTarget?) {
if let a = appTarget {
self.appTarget = TargetWrapper(target: a)
}
else { self.appTarget = nil}
self.productReference = productReference
self.outputType = outputType
self.phases = PbxPhases(sourceFiles: sourceFiles, linkFiles: linkFiles)
self.sourceFiles = sourceFiles
self.linkFiles = linkFiles
if outputType == OutputType.Application {
switch (outputType) {
case .Application:
var s = ""
s += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
s += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
......@@ -323,10 +337,42 @@ struct PbxNativeTarget: PbxprojSerializable {
let plistName = "\(productReference.name)-xcode-emit-Info.plist"
try! s.write(to: Path(plistName))
self.otherFiles = [PbxPlistFileReference(path: plistName)]
self.configurationList = PbxTargetConfigurations(plistPath: plistName)
}
else {
self.configurationList = PbxTargetConfigurations(plistPath: nil)
self.configurationList = PbxTargetConfigurations(plistPath: plistName, testThisApp: nil)
case .TestTarget:
var s = ""
s += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
s += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
s += "<plist version=\"1.0\">\n"
s += "<dict>\n"
s += " <key>CFBundleDevelopmentRegion</key>\n"
s += " <string>en</string>\n"
s += " <key>CFBundleExecutable</key>\n"
s += " <string>$(EXECUTABLE_NAME)</string>\n"
s += " <key>CFBundleIdentifier</key>\n"
s += " <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n"
s += " <key>CFBundleInfoDictionaryVersion</key>\n"
s += " <string>6.0</string>\n"
s += " <key>CFBundleName</key>\n"
s += " <string>$(PRODUCT_NAME)</string>\n"
s += " <key>CFBundlePackageType</key>\n"
s += " <string>BNDL</string>\n"
s += " <key>CFBundleShortVersionString</key>\n"
s += " <string>1.0</string>\n"
s += " <key>CFBundleSignature</key>\n"
s += " <string>????</string>\n"
s += " <key>CFBundleVersion</key>\n"
s += " <string>1</string>\n"
s += " <key>LSRequiresIPhoneOS</key>\n"
s += " <true/>\n"
s += "</dict>\n"
s += "</plist>\n"
let plistName = "\(productReference.name)-xcode-emit-Info.plist"
try! s.write(to: Path(plistName))
self.otherFiles = [PbxPlistFileReference(path: plistName)]
self.configurationList = PbxTargetConfigurations(plistPath: plistName, testThisApp: appTarget!.name)
case .StaticLibrary, .Executable:
self.configurationList = PbxTargetConfigurations(plistPath: nil, testThisApp: nil)
self.otherFiles = []
}
}
......@@ -357,6 +403,8 @@ struct PbxNativeTarget: PbxprojSerializable {
s += " productType = \"com.apple.product-type.library.dynamic\";"
case .Application:
s += " productType = \"com.apple.product-type.application\";\n"
case .TestTarget:
s += " productType = \"com.apple.product-type.bundle.unit-test\";\n"
}
s += "};\n"
s += "/* End PBXNativeTarget section */\n"
......@@ -373,6 +421,9 @@ struct PbxTargetConfigurations: PbxprojSerializable {
let debugGUID = xcodeguid()
let releaseGUID = xcodeguid()
let plistPath: String?
///If non-nil, we will emit a test target to test the given app. Use the name of the executable, a.k.a. the app name without `.app` extension
let testThisApp: String?
func serialize() -> String {
......@@ -387,7 +438,12 @@ struct PbxTargetConfigurations: PbxprojSerializable {
if let plistPath = self.plistPath {
sx += " INFOPLIST_FILE = \(plistPath);\n"
}
sx += " PRODUCT_BUNDLE_IDENTIFIER = org.anarchytools.XcodeEmittedApp;\n"
sx += " PRODUCT_BUNDLE_IDENTIFIER = org.anarchytools.XcodeEmittedApp-\(guid);\n"
if let t = testThisApp {
sx += " BUNDLE_LOADER = \"$(TEST_HOST)\";\n"
sx += " LD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n"
sx += " TEST_HOST = \"$(BUILT_PRODUCTS_DIR)/\(t).app/\(t)\";\n"
}
var s = ""
s += "\(guid) /* Build configuration list for PBXNativeTarget */ = {\n"
......@@ -469,6 +525,7 @@ struct PbxProductReference: PbxprojSerializable {
enum ReferenceType: String {
case Executable = "compiled.mach-o.executable"
case Application = "wrapper.application"
case TestTarget = "wrapper.cfbundle"
}
let name: String
......@@ -482,6 +539,8 @@ struct PbxProductReference: PbxprojSerializable {
return name
case .Application:
return "\(name).app"
case .TestTarget:
return "\(name).xctest"
}
}
......
// Copyright (c) 2016 Anarchy Tools Contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
func xcscheme(project: Pbxproj) -> String {
var s = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
for obj in project.objects {
if let obj = obj as? PbxNativeTarget {
if let appTarget = obj.appTarget?.target {
s += "<Scheme\n"
s += " LastUpgradeVersion = \"0730\"\n"
s += " version = \"1.3\">\n"
s += " <BuildAction\n"
s += " parallelizeBuildables = \"YES\"\n"
s += " buildImplicitDependencies = \"YES\">\n"
s += " <BuildActionEntries>\n"
s += " <BuildActionEntry\n"
s += " buildForTesting = \"YES\"\n"
s += " buildForRunning = \"YES\"\n"
s += " buildForProfiling = \"YES\"\n"
s += " buildForArchiving = \"YES\"\n"
s += " buildForAnalyzing = \"YES\">\n"
s += " <BuildableReference\n"
s += " BuildableIdentifier = \"primary\"\n"
s += " BlueprintIdentifier = \"\(appTarget.guid)\"\n"
s += " BuildableName = \"\(appTarget.name).app\"\n"
s += " BlueprintName = \"\(appTarget.name)\"\n"
s += " ReferencedContainer = \"container:\(appTarget.name).xcodeproj\">\n"
s += " </BuildableReference>\n"
s += " </BuildActionEntry>\n"
s += " </BuildActionEntries>\n"
s += " </BuildAction>\n"
s += " <TestAction\n"
s += " buildConfiguration = \"Debug\"\n"
s += " selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n"
s += " selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n"
s += " shouldUseLaunchSchemeArgsEnv = \"YES\">\n"
s += " <Testables>\n"
s += " <TestableReference\n"
s += " skipped = \"NO\">\n"
s += " <BuildableReference\n"
s += " BuildableIdentifier = \"primary\"\n"
s += " BlueprintIdentifier = \"\(obj.guid)\"\n"
s += " BuildableName = \"\(obj.name).xctest\"\n"
s += " BlueprintName = \"\(obj.name)\"\n"
s += " ReferencedContainer = \"container:\(appTarget.name).xcodeproj\">\n"
s += " </BuildableReference>\n"
s += " </TestableReference>\n"
s += " </Testables>\n"
s += " <MacroExpansion>\n"
s += " <BuildableReference\n"
s += " BuildableIdentifier = \"primary\"\n"
s += " BlueprintIdentifier = \"\(appTarget.guid)\"\n"
s += " BuildableName = \"\(appTarget.name).app\"\n"
s += " BlueprintName = \"\(appTarget.name)\"\n"
s += " ReferencedContainer = \"container:\(appTarget.name).xcodeproj\">\n"
s += " </BuildableReference>\n"
s += " </MacroExpansion>\n"
s += " <AdditionalOptions>\n"
s += " </AdditionalOptions>\n"
s += " </TestAction>\n"
s += " <LaunchAction\n"
s += " buildConfiguration = \"Debug\"\n"
s += " selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n"
s += " selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n"
s += " launchStyle = \"0\"\n"
s += " useCustomWorkingDirectory = \"NO\"\n"
s += " ignoresPersistentStateOnLaunch = \"NO\"\n"
s += " debugDocumentVersioning = \"YES\"\n"
s += " debugServiceExtension = \"internal\"\n"
s += " allowLocationSimulation = \"YES\">\n"
s += " <BuildableProductRunnable\n"
s += " runnableDebuggingMode = \"0\">\n"
s += " <BuildableReference\n"
s += " BuildableIdentifier = \"primary\"\n"
s += " BlueprintIdentifier = \"\(appTarget.guid)\"\n"
s += " BuildableName = \"\(appTarget.name).app\"\n"
s += " BlueprintName = \"\(appTarget.name)\"\n"
s += " ReferencedContainer = \"container:\(appTarget.name).xcodeproj\">\n"
s += " </BuildableReference>\n"
s += " </BuildableProductRunnable>\n"
s += " <AdditionalOptions>\n"
s += " </AdditionalOptions>\n"
s += " </LaunchAction>\n"
s += " <ProfileAction\n"
s += " buildConfiguration = \"Release\"\n"
s += " shouldUseLaunchSchemeArgsEnv = \"YES\"\n"
s += " savedToolIdentifier = \"\"\n"
s += " useCustomWorkingDirectory = \"NO\"\n"
s += " debugDocumentVersioning = \"YES\">\n"
s += " <BuildableProductRunnable\n"
s += " runnableDebuggingMode = \"0\">\n"
s += " <BuildableReference\n"
s += " BuildableIdentifier = \"primary\"\n"
s += " BlueprintIdentifier = \"\(appTarget.guid)\"\n"
s += " BuildableName = \"\(appTarget.name).app\"\n"
s += " BlueprintName = \"\(appTarget.name)\"\n"
s += " ReferencedContainer = \"container:\(appTarget.name).xcodeproj\">\n"
s += " </BuildableReference>\n"
s += " </BuildableProductRunnable>\n"
s += " </ProfileAction>\n"
s += " <AnalyzeAction\n"
s += " buildConfiguration = \"Debug\">\n"
s += " </AnalyzeAction>\n"
s += " <ArchiveAction\n"
s += " buildConfiguration = \"Release\"\n"
s += " revealArchiveInOrganizer = \"YES\">\n"
s += " </ArchiveAction>\n"
s += "</Scheme>\n"
}
}
}
return s
}
\ No newline at end of file
......@@ -24,5 +24,12 @@
:name "main"
:output-type "executable"
}
:ios-tests {
:tool "atllbuild"
:sources ["tests/**.swift"]
:name "main"
:output-type "executable"
}
}
)
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
}
\ No newline at end of file
print("hello world!")
\ No newline at end of file
import XCTest
class F: XCTestCase { }
\ No newline at end of file
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