Tools.swift 4.21 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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.

15 16 17 18 19 20 21
#if os(Linux)
import Glibc
#else
import Darwin
#endif

import atfoundation
22 23 24 25 26 27 28
import atpkg

/** The builtin tools. */
let tools: [String:Tool] = [
    "shell": Shell(),
    "atllbuild": ATllbuild(),
    "nop": Nop(),
29
    "xctestrun":XCTestRun(),
Drew's avatar
Drew committed
30 31
    "packageframework":PackageFramework(),
    "packageatbin":PackageAtbin()
Drew's avatar
Drew committed
32
]
33 34 35 36 37 38 39

/**
 * A tool is a function that performs some operation, like building, or
 * running a shell command. We provide several builtin tools, but users
 * can build new ones out of the existing ones.
 */
public protocol Tool {
Drew's avatar
Drew committed
40
    func run(task: Task)
41 42 43 44 45 46
}

/**
 * Look up a tool by name.
 */
func toolByName(name: String) -> Tool {
Drew's avatar
Drew committed
47 48
    if CustomTool.isCustomTool(name: name) {
        return CustomTool(name: name)
Drew's avatar
Drew committed
49
    }
50 51 52
    guard let tool = tools[name] else { fatalError("Unknown build tool \(name)") }
    return tool
}
Drew's avatar
Drew committed
53 54 55 56 57

private var userPathCreated = false
/**Returns the "user" path.  This is a path that the user may use to store artifacts or for any other purposes.  This path is shared for all tasks built as part of the same `atbuild` invocation.
- postcondition: The path exists at this absolute locaton on disk.
- warning: This path is cleared between atbuild invocations. */
58
func userPath() -> Path {
59 60 61
    do {
        let userPath = try FS.getWorkingDirectory().appending("user")
        if !userPathCreated {
62 63 64
            if FS.isDirectory(path: userPath) {
                try FS.removeItem(path: userPath, recursive: true)
            }
65 66 67 68 69 70
            try FS.createDirectory(path: userPath)
            userPathCreated = true
        }
        return userPath
    } catch {
        fatalError("Could not create user dir: \(error)")
Drew's avatar
Drew committed
71 72
    }
}
73

Drew's avatar
Drew committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
private func _WSTATUS(_ status: CInt) -> CInt {
    return status & 0x7f
}

private func WIFEXITED(_ status: CInt) -> Bool {
    return _WSTATUS(status) == 0
}

private func WEXITSTATUS(_ status: CInt) -> CInt {
    return (status >> 8) & 0xff
}

/// convenience wrapper for waitpid
func waitpid(_ pid: pid_t) -> Int32 {
    while true {
        var exitStatus: Int32 = 0
        let rv = waitpid(pid, &exitStatus, 0)

        if rv != -1 {
            if WIFEXITED(exitStatus) {
                return WEXITSTATUS(exitStatus)
            } else {
                fatalError("Exit signal")
            }
        } else if errno == EINTR {
            continue  // see: man waitpid
        } else {
            fatalError("waitpid: \(errno)")
        }
    }
}

106 107 108 109 110
///A wrapper for POSIX "system" call.
///If return value is non-zero, we exit (not fatalError)
///See #72 for details.
///- note: This function call is appropriate for commands that are user-perceivable (such as compilation)
///Rather than calls that aren't
Drew's avatar
Drew committed
111 112
func anarchySystem(_ cmd: String, environment: [String: String]) {
    var pid : pid_t = 0
Drew's avatar
Drew committed
113
    //copy a few well-known values
Drew's avatar
Drew committed
114
    var environment = environment
Drew's avatar
Drew committed
115 116 117 118 119 120
    for arg in ["PATH","HOME"] {
        if let path = getenv(arg) {
            environment[arg] = String(validatingUTF8: path)!
        }
    }
    
Drew's avatar
Drew committed
121 122 123
    let args: [String] =  ["sh","-c",cmd]
    let argv = args.map{ $0.withCString(strdup) }
    let env: [UnsafeMutablePointer<CChar>?] = environment.map{ "\($0.0)=\($0.1)".withCString(strdup) }
Drew's avatar
Drew committed
124 125 126 127 128 129
    
    let directory = try! FS.getWorkingDirectory()
    defer {try! FS.changeWorkingDirectory(path: directory)}
    if let e = environment["PWD"] {
        try! FS.changeWorkingDirectory(path: Path(environment["PWD"]!))
    }
Drew's avatar
Drew committed
130 131
    let status = posix_spawn(&pid, "/bin/sh",nil,nil,argv + [nil],env + [nil])

Drew's avatar
Drew committed
132

Drew's avatar
Drew committed
133 134 135 136 137
    if status != 0 {
        fatalError("spawn error \(status)")
    }

    let returnCode = try! waitpid(pid)
138
    if returnCode != 0 {
Drew's avatar
Drew committed
139
        fatalError("process \(cmd) error \(returnCode)")
140 141
    }
}