Commit c009b545 authored by Johannes Schriewer's avatar Johannes Schriewer

Merge branch 'master' into atpm

# Conflicts:
#	bootstrap/bootstrap-linux-atpkg.swift-build
#	bootstrap/bootstrap-macosx-atpkg.swift-build
parents 2a8ec34f ac237cb1
Pipeline #1738 failed with stage
stages:
- build
before_script:
- git submodule update --init --recursive
linux:
stage: build
script:
- docker build -t atbuild .
- id=$(docker create atbuild)
- mkdir bin
- docker cp $id:atbuild/bin/atbuild bin/atbuild
- docker rm -v $id
- docker rmi atbuild
- apt-get update
- apt-get install --no-install-recommends xz-utils curl git ca-certificates -y
- curl -L https://github.com/AnarchyTools/atbuild/releases/download/0.9.0/atbuild-0.9.0-linux.tar.xz | tar xJ -C /usr/local
- git submodule update --init --recursive
- bootstrap/build.sh linux
- tests/test.sh
- mkdir atbuild-${CI_BUILD_REF}
- cp bin/atbuild atbuild-${CI_BUILD_REF}
- tar cJf atbuild-${CI_BUILD_REF}-linux.tar.xz atbuild-${CI_BUILD_REF}
tags:
- docker
- autoscale-linux
artifacts:
paths:
- bin
- atbuild-${CI_BUILD_REF}-linux.tar.xz
image: drewcrawford/swift:latest
osx:
stage: build
script:
- git submodule update --init --recursive
- export PATH=/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin:"${PATH}"
- ./bootstrap/build.sh
- bin/atbuild check
- mkdir atbuild-${CI_BUILD_REF}
- cp bin/atbuild atbuild-${CI_BUILD_REF}
- tar cJf atbuild-${CI_BUILD_REF}-osx.tar.xz atbuild-${CI_BUILD_REF}
tags:
- openswift
artifacts:
paths:
- bin/atbuild
- atbuild-${CI_BUILD_REF}-osx.tar.xz
......@@ -69,7 +69,18 @@ How do we build a Swift project? There's a built-in tool called `atllbuild`, wh
)
```
That's all you need to get started! `atbuild` supports many more usecases than can fit in a README. For more information, browse our [documentation](/docs).
That's all you need to get started! `atbuild` supports many more usecases than can fit in a README. For more information, browse our [documentation](http://anarchytools.org).
# Options
`atbuild` supports several command-line options:
* `--use-overlay [overlay]`, which you can read more about in our [overlays](http://anarchytools.org/docs/overlays.html) documentation.
* `-f [atpkg-file]` which builds a package file other than `build.atpkg`
* `--help`, which displays a usage message
* `--clean`, which forces a clean build
* `--toolchain` which specifies a nonstandard toolchain (swift installation). By default we try to guess, but you can override our guess here. The special string `xcode` uses "xcode swift" for building. (Swift 2.2 does not contain all the tools we use, so you need to have a 3.x snapshot installed as well. However, this is a "mostly" xcode-flavored buildchain.)
* `--platform` which specifies the target platform. By default, this is the current platform. Pass a different value (`osx`, `linux`, `ios-x86_64`, `ios-i386`, `ios-arm64`, or `ios-armv7`) to cross-compile, only if your compiler supports it.
# Building
......@@ -99,3 +110,7 @@ To declare your project to be compatible with Anarchy Tools, simply
```markdown
[![Anarchy Tools compatible](https://img.shields.io/badge/Anarchy%20Tools-compatible-4BC51D.svg?style=flat)](http://anarchytools.org)
```
*Maintainer note: if you edit this file, edit the one in [this repo](https://github.com/AnarchyTools/anarchytools.github.io) as well.*
{
"folders":
[
{
"path": ".",
"folder_exclude_patterns": [ ".atllbuild", "bin", "user" ],
"file_exclude_patterns": [ ".gitignore", ".gitlab-ci.yml", "Dockerfile", "LICENSE" ]
},
]
}
// 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.
......@@ -12,25 +12,62 @@
// See the License for the specific language governing permissions and
// limitations under the License.
let version = "0.8.1"
#if os(Linux)
import Glibc
#else
import Darwin
#endif
import Foundation
let version = "0.10.0"
import atfoundation
import atpkg
import attools
// This is a workaround for jumbled up output from print statements
setbuf(stdout, nil)
enum Options: String {
case Overlay = "--use-overlay"
case CustomFile = "-f"
case Help = "--help"
case Clean = "--clean"
case Toolchain = "--toolchain"
case Platform = "--platform"
static var allOptions : [Options] { return [Overlay, CustomFile] }
static var allOptions : [Options] { return [
Overlay,
CustomFile,
Help,
Clean,
Toolchain,
Platform
]
}
}
let defaultPackageFile = "build.atpkg"
let defaultPackageFile = Path("build.atpkg")
var focusOnTask : String? = nil
var packageFile = defaultPackageFile
var toolchain = Platform.buildPlatform.defaultToolchainPath
for (i, x) in Process.arguments.enumerated() {
if x == Options.CustomFile.rawValue {
packageFile = Path(Process.arguments[i+1])
}
if x == Options.Toolchain.rawValue {
toolchain = Process.arguments[i+1]
if toolchain == "xcode" {
toolchain = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain"
}
}
if x == Options.Platform.rawValue {
let platformString = Process.arguments[i+1]
Platform.targetPlatform = Platform(string: platformString)
}
}
//build overlays
var overlays : [String] = []
for (i, x) in Process.arguments.enumerated() {
......@@ -39,37 +76,54 @@ for (i, x) in Process.arguments.enumerated() {
overlays.append(overlay)
}
}
var packageFile = defaultPackageFile
for (i, x) in Process.arguments.enumerated() {
if x == Options.CustomFile.rawValue {
packageFile = Process.arguments[i+1]
}
}
let package = try! Package(filepath: packageFile, overlay: overlays, focusOnTask: focusOnTask)
overlays.append(contentsOf: Platform.targetPlatform.overlays)
//usage message
if Process.arguments.contains("--help") {
print("enabling overlays \(overlays)")
var package: Package! = nil
func usage() {
print("atbuild - Anarchy Tools Build Tool \(version)")
print("https://github.com/AnarchyTools")
print("© 2016 Anarchy Tools Contributors.")
print("")
print("Usage:")
print("atbuild [-f packagefile] [task] [--clean]")
print("tasks:")
for (key, task) in package.tasks {
print(" \(key)")
}
print("atbuild [--toolchain (/toolchain/path | xcode)] [-f packagefile] [task] [--clean]")
if let p = package {
print("tasks:")
for (key, _) in p.tasks {
print(" \(key)")
}
}
else {
print("No tasks are available; run --help in a directory with a build.atpkg for project-specific help")
}
exit(1)
}
do {
package = try Package(filepath: packageFile, overlay: overlays, focusOnTask: focusOnTask)
} catch {
print("Could not load package file: \(error)")
usage()
}
//usage message
if Process.arguments.contains("--help") {
usage()
}
func runTask(taskName: String, package: Package) {
guard let task = package.tasks[taskName] else { fatalError("No \(taskName) task in build configuration.") }
for task in package.prunedDependencyGraph(task) {
TaskRunner.runTask(task, package: package)
for task in package.prunedDependencyGraph(task: task) {
TaskRunner.runTask(task: task, package: package, toolchain: toolchain)
}
}
//choose which task to run
if Process.arguments.count > 1 {
var i = 1
......@@ -91,7 +145,7 @@ if focusOnTask == nil {
print("Building package \(package.name)...")
runTask(focusOnTask!, package: package)
runTask(taskName: focusOnTask!, package: package)
//success message
print("Built package \(package.name).")
\ No newline at end of file
Subproject commit 578fa7c77a52316110be27b4b1ff1843d908a49b
Subproject commit c42d4e01d7bd3cc93bdb0b122ae11bd8f67017f7
#if os(Linux)
import Glibc
#else
import Darwin
#endif
import atpkg
///Create a tool out of another program someone has lying around on their system
///Important: this is for programs that are written for AT. For other programs, use shell instead.
final class CustomTool: Tool {
static func isCustomTool(name: String) -> Bool {
return name.hasSuffix(".attool")
}
let name: String
init(name: String) {
self.name = String(name.characters[name.characters.startIndex..<name.characters.startIndex.advanced(by: name.characters.count - 7)])
}
func run(task: Task, toolchain: String) {
var cmd = "\(self.name) "
for key in task.allKeys.sorted() {
if Task.Option.allOptions.map({$0.rawValue}).contains(key) { continue }
guard let value = task[key]?.string else {
fatalError("\(task.qualifiedName).\(key) is not string")
}
cmd += "--\(key) \"\(evaluateSubstitutions(input: value, package: task.package))\" "
}
setenv("ATBUILD_USER_PATH", userPath().description, 1)
anarchySystem(cmd)
}
}
\ No newline at end of file
// 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.
import Foundation
//SR-138
extension String {
var toNSString: NSString {
#if os(Linux)
return self.bridge()
#elseif os(OSX)
return (self as NSString)
#endif
}
}
extension NSString {
var toString: String {
#if os(Linux)
return self.bridge()
#elseif os(OSX)
return (self as String)
#endif
}
}
// MARK: NSFileManager.copyItemAtPath
// https://github.com/apple/swift-corelibs-foundation/pull/248
enum CopyError: ErrorProtocol {
case CantOpenSourceFile(Int32)
case CantOpenDestFile(Int32)
case CantReadSourceFile(Int32)
case CantWriteDestFile(Int32)
}
extension NSFileManager {
func copyItemAtPath_SWIFTBUG(srcPath: String, toPath dstPath: String) throws {
let fd_from = open(srcPath, O_RDONLY)
if fd_from < 0 {
throw CopyError.CantOpenSourceFile(errno)
}
defer { precondition(close(fd_from) >= 0) }
let permission_ = (try! attributesOfItem(atPath: srcPath)[NSFilePosixPermissions] as! NSNumber)
#if os(OSX) || os(iOS)
let permission = permission_.unsignedShortValue
#elseif os(Linux)
let permission = permission_.unsignedIntValue
#endif
let fd_to = open(dstPath, O_WRONLY | O_CREAT | O_EXCL, permission)
if fd_to < 0 {
throw CopyError.CantOpenDestFile(errno)
}
defer { precondition(close(fd_to) >= 0) }
var buf = [UInt8](repeating: 0, count: 4096)
while true {
let nread = read(fd_from, &buf, buf.count)
if nread < 0 { throw CopyError.CantReadSourceFile(errno) }
if nread == 0 { break }
var writeSlice = buf[0..<nread]
while true {
var nwritten: Int! = nil
writeSlice.withUnsafeBufferPointer({ (ptr) -> () in
nwritten = write(fd_to, ptr.baseAddress, ptr.count)
})
if nwritten < 0 {
throw CopyError.CantWriteDestFile(errno)
}
writeSlice = writeSlice[writeSlice.startIndex.advanced(by: nwritten)..<writeSlice.endIndex]
if writeSlice.count == 0 { break }
}
}
}
}
//These parts of Swift 3 Renaming are not yet implemented on Linux
#if os(Linux)
extension NSFileManager {
func enumerator(atPath path: String) -> NSDirectoryEnumerator? {
return self.enumeratorAtPath(path)
}
func createDirectory(atPath path: String, withIntermediateDirectories createIntermediates: Bool, attributes: [String : AnyObject]? = [:]) throws {
return try self.createDirectoryAtPath(path, withIntermediateDirectories: createIntermediates, attributes: attributes)
}
func attributesOfItem(atPath path: String) throws -> [String : Any] {
return try self.attributesOfItemAtPath(path)
}
func removeItem(atPath path: String) throws {
return try self.removeItemAtPath(path)
}
func fileExists(atPath path: String) -> Bool {
return self.fileExistsAtPath(path)
}
}
extension String {
func componentsSeparated(by separator: String) -> [String] {
return self.componentsSeparatedByString(separator)
}
func write(toFile path: String, atomically useAuxiliaryFile:Bool, encoding enc: NSStringEncoding) throws {
return try self.writeToFile(path, atomically: useAuxiliaryFile, encoding: enc)
}
}
#endif
\ No newline at end of file
......@@ -19,5 +19,5 @@ import atpkg
* dependencies together.
*/
final class Nop: Tool {
func run(task: Task) {}
func run(task: Task, toolchain: String) {}
}
// 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.
#if os(Linux)
import Glibc
#else
import Darwin
#endif
import atfoundation
import atpkg
private enum ModuleMapType: String {
case Synthesized = "synthesized"
}
private enum Options: String {
case Resources = "resources"
case Headers = "headers"
case ModuleMapType = "module-map-type"
case Name = "name"
}
class PackageFramework: Tool {
func compiler_crash() {
#if !os(OSX)
fatalError("packageframework is unsupported on this platform")
#endif
}
func run(task: Task, toolchain: String) {
compiler_crash() //work around a compiler crash
guard let moduleMapType = task[Options.ModuleMapType.rawValue]?.string else {
fatalError("Specify a \(Options.ModuleMapType.rawValue)")
}
guard let name = task[Options.Name.rawValue]?.string else {
fatalError("Specify a \(Options.Name.rawValue)")
}
precondition(moduleMapType == ModuleMapType.Synthesized.rawValue, "Unknown \(Options.ModuleMapType.rawValue) \(moduleMapType)")
guard let resourcesV = task[Options.Resources.rawValue]?.vector else {
fatalError("Specify \(Options.Resources.rawValue). This should contain at least an info plist.")
}
if resourcesV.count < 0 {
fatalError("\(Options.Resources.rawValue) should contain an info plist.")
}
var resources: [String] = []
for resource in resourcesV {
guard let s = resource.string else { fatalError("Non-string resource \(resource)")}
resources.append(s)
}
//rm framework if it exists
let frameworkPath = Path("bin/\(name).framework")
let _ = try? FS.removeItem(path: frameworkPath, recursive: true)
let _ = try? FS.createDirectory(path: frameworkPath)
//'a' version
let relativeAVersionPath = Path("Versions/A")
let AVersionPath = frameworkPath + relativeAVersionPath
try! FS.createDirectory(path: AVersionPath, intermediate: true)
//'current' (produces code signing failures if absent)
try! FS.symlinkItem(from: Path("A"), to: frameworkPath + "Versions/Current")
//copy payload
let payloadPath = task.importedPath.appending("bin").appending(name + Platform.targetPlatform.dynamicLibraryExtension)
print(payloadPath)
try! FS.copyItem(from: payloadPath, to: AVersionPath.appending(name))
try! FS.symlinkItem(from: relativeAVersionPath.appending(name), to: frameworkPath.appending(name))
//copy modules
let modulePath = AVersionPath.appending("Modules").appending(name + ".swiftmodule")
try! FS.createDirectory(path: modulePath, intermediate: true)
try! FS.copyItem(from: Path("bin/\(name).swiftmodule"), to: modulePath.appending(Platform.targetPlatform.architecture.description + ".swiftmodule"))
try! FS.copyItem(from: Path("bin/\(name).swiftdoc"), to: modulePath.appending(Platform.targetPlatform.architecture.description + ".swiftdoc"))
try! FS.symlinkItem(from: relativeAVersionPath.appending("Modules"), to: frameworkPath.appending("Modules"))
//copy resources
let resourcesPath = AVersionPath.appending("Resources")
try! FS.createDirectory(path: resourcesPath, intermediate: true)
for resource in resources {
try! FS.copyItem(from: task.importedPath + resource, to: resourcesPath + resource)
}
try! FS.symlinkItem(from: relativeAVersionPath + "Resources", to: frameworkPath + "Resources")
//codesign
let cmd = "codesign --force --deep --sign - --timestamp=none '\(AVersionPath)'"
print(cmd)
if system(cmd) != 0 {
fatalError("Codesign failed.")
}
}
}
\ No newline at end of file
......@@ -12,14 +12,172 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
//todo, support multiple toolchains
#if os(OSX)
let SDKPath = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk"
let SwiftCPath = "/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin/swiftc"
let SwiftBuildToolpath = "/Library/Developer/Toolchains/swift-latest.xctoolchain/usr/bin/swift-build-tool"
#elseif os(Linux)
let SwiftCPath = "/usr/local/bin/swiftc"
let SwiftBuildToolpath = "/usr/local/bin/swift-build-tool"
#endif
\ No newline at end of file
import atfoundation
public enum Architecture {
case x86_64
case i386
case armv7
case arm64
}
extension Architecture: CustomStringConvertible {
public var description: String {
switch(self) {
case .x86_64:
return "x86_64"
case .i386:
return "i386"
case .armv7:
return "armv7"
case .arm64:
return "arm64"
}
}
}
func ==(a: Platform, b: Platform) -> Bool {
switch(a, b) {
case (.OSX, .OSX): return true
case (.Linux, .Linux): return true
case (.iOS(let a), .iOS(let b)) where a == b: return true
default: return false
}
}
public enum Platform {
case OSX
case Linux
case iOS(Architecture)
public init(string: String) {
switch(string) {
case "osx", "mac":
self = Platform.OSX
case "linux":
self = Platform.Linux
case "ios-x86_64":
self = Platform.iOS(Architecture.x86_64)
case "ios-i386":
self = Platform.iOS(Architecture.i386)
case "ios-armv7":
self = Platform.iOS(Architecture.armv7)
case "ios-arm64":
self = Platform.iOS(Architecture.arm64)
default:
fatalError("Unknown platform \(string)")
}
}
///The overlays that should be enabled when building for this platform
public var overlays: [String] {
switch(self) {
case .OSX:
return ["atbuild.platform.osx", "atbuild.platform.mac"]
case .Linux:
return ["atbuild.platform.linux"]
case .iOS:
return ["atbuild.platform.ios"]
}
}
///The typical path to a toolchain binary of the platform
var defaultToolchainBinaryPath: String {
switch(self) {
case .OSX, .iOS:
return "\(defaultToolchainPath)/usr/bin/"
case .Linux:
return "\(defaultToolchainPath)/usr/local/bin/"
}
}
public var defaultToolchainPath: String {
switch(self) {
case .OSX, .iOS:
return "/Library/Developer/Toolchains/swift-latest.xctoolchain"
case .Linux:
return "/"
}
}
var sdkPath: String? {
switch(self) {
case .OSX:
return "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk"
case .Linux:
return nil
case .iOS(.x86_64), .iOS(.i386):
return "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator9.3.sdk"
case .iOS(.armv7), .iOS(.arm64):
return "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk"
}
}
var architecture: Architecture {
switch(self) {
case .OSX, .Linux:
return Architecture.x86_64
case .iOS(let arch):
return arch
}
}
var dynamicLibraryExtension: String {
switch(self) {
case .OSX, .iOS:
return ".dylib"
case .Linux:
return ".so"
}
}
///The platform on which atbuild is currently running
public static var hostPlatform: Platform {
#if os(OSX)
return Platform.OSX
#elseif os(Linux)
return Platform.Linux
#endif
}
///The platform for which atbuild is currently building
///By default, we build for the hostPlatform
public static var targetPlatform: Platform = Platform.hostPlatform
///The platform on which the build will take place (e.g. swift-build-tool will run).
///Ordinarily we build on the hostPlatform, but in the case of bootstrapping,
///we may be only emitting a yaml, which the actual build occuring
/// on some other platform than either the host or the target.
public static var buildPlatform: Platform = Platform.hostPlatform
}
func findToolPath(toolName: String, toolchain: String) -> Path {
if Platform.buildPlatform == Platform.hostPlatform {
//poke around on the filesystem
//look in /usr/bin
let usrBin = Path("\(toolchain)/usr/bin/\(toolName)")
if FS.fileExists(path: usrBin) { return usrBin }
//look in /usr/local/bin
let usrLocalBin = Path("\(toolchain)/usr/local/bin/\(toolName)")
if FS.fileExists(path: usrLocalBin) { return usrLocalBin }
//swift-build-tool isn't available in 2.2.
//If we're looking for SBT, try in the default location
if toolName == "swift-build-tool" {
let sbtPath = Path("\(Platform.hostPlatform.defaultToolchainPath)/usr/bin/\(toolName)")
if FS.fileExists(path: sbtPath) { return sbtPath }
}
}
else {
//file system isn't live; hope the path is in a typical place
return Path("\(Platform.buildPlatform.defaultToolchainBinaryPath)\(toolName)")
}
fatalError("Can't find a path for \(toolName)")
}
\ No newline at end of file
......@@ -12,8 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.