Skip to content

Refactor describe(), add llbuild tool types #175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 9, 2016
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
65 changes: 22 additions & 43 deletions Sources/Build/YAML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,35 @@
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import struct Utility.Path
import struct libc.FILE
import func libc.fclose
import POSIX

class YAML {
let path: String
private let fp: UnsafeMutablePointer<FILE>

init(path: String...) throws {
self.path = Path.join(path)
fp = try fopen(self.path, mode: .Write)
}
protocol YAMLRepresentable {
var YAML: String { get }
}

func close() {
fclose(fp)
extension String: YAMLRepresentable {
var YAML: String {
if self == "" { return "\"\"" }
return self
}
}

func write(anys: Any...) throws {
var anys = anys
try fputs(anys.removeFirst() as! String, fp)
if !anys.isEmpty {
try fputs(anys.map(toYAML).joinWithSeparator(""), fp)
}
try fputs("\n", fp)

extension Bool: YAMLRepresentable {
var YAML: String {
if self { return "true" }
return "false"
}
}

private func toYAML(any: Any) -> String {

func quote(input: String) -> String {
for c in input.characters {
if c == "@" || c == " " || c == "-" {
return "\"\(input)\""
extension Array where Element: YAMLRepresentable {
var YAML: String {
func quote(input: String) -> String {
for c in input.characters {
if c == "@" || c == " " || c == "-" {
return "\"\(input)\""
}
}
return input
}
return input
}

switch any {
case let string as String where string == "":
return "\"\""
case let string as String:
return string
case let array as [String]:
return "[" + array.map(quote).joinWithSeparator(", ") + "]"
case let bool as Bool:
return bool ? "true" : "false"
default:
fatalError("Unimplemented YAML type")
let stringArray = self.flatMap { String($0) }
return "[" + stringArray.map(quote).joinWithSeparator(", ") + "]"
}
}
105 changes: 59 additions & 46 deletions Sources/Build/describe().swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import func POSIX.getenv
import func POSIX.mkdir
import PackageType
import Utility
import func POSIX.fopen
import func libc.fclose

/**
- Returns: path to generated YAML for consumption by the llbuild based swift-build-tool
Expand All @@ -25,25 +27,20 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
let Xcc = Xcc.flatMap{ ["-Xcc", $0] }
let Xld = Xld.flatMap{ ["-Xlinker", $0] }
let prefix = try mkdir(prefix, conf.dirname)
let yaml = try YAML(path: "\(prefix).yaml")
let write = yaml.write

let (buildableTests, buildableNonTests) = (modules.map{$0 as Buildable} + products.map{$0 as Buildable}).partition{$0.isTest}
let (tests, nontests) = (buildableTests.map{$0.targetName}, buildableNonTests.map{$0.targetName})

defer { yaml.close() }

try write("client:")
try write(" name: swift-build")
try write("tools: {}")
try write("targets:")
try write(" default: ", nontests)
try write(" test: ", tests)
try write("commands: ")
var nonTests = [Command]()
var tests = [Command]()

/// Appends the command to appropriate array
func append(command: Command, buildable: Buildable) {
if buildable.isTest {
tests.append(command)
} else {
nonTests.append(command)
}
}

var mkdirs = Set<String>()


let swiftcArgs = Xcc + Xswiftc

for case let module as SwiftModule in modules {
Expand All @@ -65,23 +62,22 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
#endif

let node = IncrementalNode(module: module, prefix: prefix)

try write(" ", module.targetName, ":")
try write(" tool: swift-compiler")
try write(" executable: ", Resources.path.swiftc)
try write(" module-name: ", module.c99name)
try write(" module-output-path: ", node.moduleOutputPath)
try write(" inputs: ", node.inputs)
try write(" outputs: ", node.outputs)
try write(" import-paths: ", prefix)
try write(" temps-path: ", node.tempsPath)
try write(" objects: ", node.objectPaths)
try write(" other-args: ", args + otherArgs)
try write(" sources: ", module.sources.paths)

// this must be set or swiftc compiles single source file
// modules with a main() for some reason
try write(" is-library: ", module.type == .Library)
let swiftc = SwiftcTool(
inputs: node.inputs,
outputs: node.outputs,
executable: Resources.path.swiftc,
moduleName: module.c99name,
moduleOutputPath: node.moduleOutputPath,
importPaths: prefix,
tempsPath: node.tempsPath,
objects: node.objectPaths,
otherArgs: args + otherArgs,
sources: module.sources.paths,
isLibrary: module.type == .Library) /// this must be set or swiftc compiles single source
/// file modules with a main() for some reason

let command = Command(name: module.targetName, tool: swiftc)
append(command, buildable: module)

for o in node.objectPaths {
mkdirs.insert(o.parentDirectory)
Expand All @@ -96,12 +92,14 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],
args += ["-parse-as-library"]
}

try write(" ", module.targetName, ":")
try write(" tool: shell")
try write(" description: Compiling \(module.name)")
try write(" inputs: ", inputs)
try write(" outputs: ", [productPath, module.targetName])
try write(" args: ", [Resources.path.swiftc, "-o", productPath] + args + module.sources.paths + otherArgs)
let shell = ShellTool(
description: "Compiling \(module.name)",
inputs: inputs,
outputs: [productPath, module.targetName],
args: [Resources.path.swiftc, "-o", productPath] + args + module.sources.paths + otherArgs)

let command = Command(name: module.targetName, tool: shell)
append(command, buildable: module)
}
}

Expand Down Expand Up @@ -178,15 +176,30 @@ public func describe(prefix: String, _ conf: Configuration, _ modules: [Module],

let inputs = product.modules.flatMap{ [$0.targetName] + IncrementalNode(module: $0, prefix: prefix).inputs }

try write(" \(product.targetName):")
try write(" tool: shell")
try write(" description: Linking \(product)")
try write(" inputs: ", inputs)
try write(" outputs: ", [product.targetName, outpath])
try write(" args: ", args)
let shell = ShellTool(
description: "Linking \(product)",
inputs: inputs,
outputs: [product.targetName, outpath],
args: args)

let command = Command(name: product.targetName, tool: shell)
append(command, buildable: product)
}

return yaml.path
//Create Targets
let nontestTarget = Target(name: "default", commands: nonTests)
let testTarget = Target(name: "test", commands: tests)

//Generate YAML String for the targets
let yamlString = llbuildYAML(targets: [nontestTarget, testTarget])

//Write YAML to file
let yamlPath = "\(prefix).yaml"
let fp = try fopen(yamlPath, mode: .Write)
defer { fclose(fp) }
try fputs(yamlString, fp)

return yamlPath
}


Expand Down
126 changes: 126 additions & 0 deletions Sources/Build/llbuild.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
This source file is part of the Swift.org open source project

Copyright 2015 - 2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

protocol ToolType {
var name: String { get }
var inputs: [String] { get }
var outputs: [String] { get }
///YAML representation of the tool
var llbuildYAML: String { get }
}

protocol ShellToolType: ToolType {
var description: String { get }
var args: [String] { get }
}

extension ShellToolType {

var name: String {
return "shell"
}

var llbuildYAML: String {
var yaml = ""
yaml += " tool: " + name.YAML + "\n"
yaml += " description: " + description.YAML + "\n"
yaml += " inputs: " + inputs.YAML + "\n"
yaml += " outputs: " + outputs.YAML + "\n"
yaml += " args: " + args.YAML + "\n"
return yaml
}
}

struct ShellTool: ShellToolType {
let description: String
let inputs: [String]
let outputs: [String]
let args: [String]
}

protocol SwiftcToolType: ToolType {
var executable: String { get }
var moduleName: String { get }
var moduleOutputPath: String { get }
var importPaths: String { get }
var tempsPath: String { get }
var objects: [String] { get }
var otherArgs: [String] { get }
var sources: [String] { get }
var isLibrary: Bool { get }
}

extension SwiftcToolType {

var name: String {
return "swift-compiler"
}

var llbuildYAML: String {
var yaml = ""
yaml += " tool: " + name.YAML + "\n"
yaml += " executable: " + executable.YAML + "\n"
yaml += " module-name: " + moduleName.YAML + "\n"
yaml += " module-output-path: " + moduleOutputPath.YAML + "\n"
yaml += " inputs: " + inputs.YAML + "\n"
yaml += " outputs: " + outputs.YAML + "\n"
yaml += " import-paths: " + importPaths.YAML + "\n"
yaml += " temps-path: " + tempsPath.YAML + "\n"
yaml += " objects: " + objects.YAML + "\n"
yaml += " other-args: " + otherArgs.YAML + "\n"
yaml += " sources: " + sources.YAML + "\n"
yaml += " is-library: " + isLibrary.YAML + "\n"
return yaml
}
}

struct SwiftcTool: SwiftcToolType {
let inputs: [String]
let outputs: [String]
let executable: String
let moduleName: String
let moduleOutputPath: String
let importPaths: String
let tempsPath: String
let objects: [String]
let otherArgs: [String]
let sources: [String]
let isLibrary: Bool
}

typealias Command = (name: String, tool: ToolType)

struct Target {
let name: String
let commands: [Command]
}

func llbuildYAML(targets targets: [Target]) -> String {

var yaml = ""
yaml += "client:" + "\n"
yaml += " name: swift-build" + "\n"
yaml += "tools: {}" + "\n"

yaml += "targets:" + "\n"
for target in targets {
yaml += " \(target.name): " + target.commands.map{$0.name}.YAML + "\n"
}

yaml += "commands: " + "\n"

let commands = targets.reduce([Command]()) { $0 + $1.commands }
for command in commands {
yaml += " " + command.name + ":" + "\n"
yaml += command.tool.llbuildYAML
}

return yaml
}