Commit 6b53edcb authored by Drew's avatar Drew

Merge remote-tracking branch 'origin/master' into non_vector_import

parents deef8263 8e86cdec
Pipeline #1015 failed with stage
[submodule "atpkg"]
path = atpkg
url = https://github.com/AnarchyTools/atpkg
[submodule "docs"]
path = docs
url = https://github.com/AnarchyTools/AnarchyTools.git
......@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
let version = "0.6.0-dev"
let version = "0.7.0-dev"
import Foundation
import atpkg
import attools
enum Options: String {
case Overlay = "--overlay"
case Overlay = "--use-overlay"
case CustomFile = "-f"
static var allOptions : [Options] { return [Overlay, CustomFile] }
......
Subproject commit 8431c56cb88e81bd6c22132c4e05f010ddeca7a6
Subproject commit b558e76ca1674b2c7278aeebf16e3df26731e678
......@@ -16,7 +16,7 @@ import Foundation
class XCTestRun : Tool {
func run(task: Task) {
guard let testExecutable = task["testExecutable"]?.string else {
guard let testExecutable = task["test-executable"]?.string else {
fatalError("No testExecutable for XCTestRun")
}
#if os(OSX)
......
......@@ -183,27 +183,49 @@ final class ATllbuild : Tool {
}
}
private enum Options: String {
case Tool = "tool"
case Name = "name"
case Dependencies = "dependencies"
case OutputType = "output-type"
case Source = "sources"
case BootstrapOnly = "bootstrap-only"
case llBuildYaml = "llbuildyaml"
case CompileOptions = "compile-options"
case LinkOptions = "link-options"
case LinkSDK = "link-sdk"
case LinkWithProduct = "link-with"
case SwiftCPath = "swiftc-path"
case XCTestify = "xctestify"
case XCTestStrict = "xctest-strict"
case PublishProduct = "publish-product"
static var allOptions : [Options] {
return [
Tool,
Name,
Dependencies,
OutputType,
Source,
BootstrapOnly,
llBuildYaml,
CompileOptions,
LinkOptions,
LinkSDK,
LinkWithProduct,
SwiftCPath,
XCTestify,
XCTestStrict,
PublishProduct
]
}
}
func run(task: Task) {
//warn if we don't understand an option
let knownOptions = ["tool",
"name",
"dependencies",
"outputType",
"source",
"bootstrapOnly",
"llbuildyaml",
"compileOptions",
"linkOptions",
"linkSDK",
"linkWithProduct",
"swiftCPath",
"xctestify",
"xctestStrict",
"publishProduct"]
for key in task.allKeys {
if !knownOptions.contains(key) {
if !Options.allOptions.map({$0.rawValue}).contains(key) {
print("Warning: unknown option \(key) for task \(task.qualifiedName)")
}
}
......@@ -224,17 +246,17 @@ final class ATllbuild : Tool {
//parse arguments
var linkWithProduct: [String] = []
if let arr = task["linkWithProduct"]?.vector {
if let arr = task[Options.LinkWithProduct.rawValue]?.vector {
for product in arr {
guard let p = product.string else { fatalError("non-string product \(product)") }
linkWithProduct.append(p)
}
}
let outputType: OutputType
if task["outputType"]?.string == "static-library" {
if task[Options.OutputType.rawValue]?.string == "static-library" {
outputType = .StaticLibrary
}
else if task["outputType"]?.string == "executable" {
else if task[Options.OutputType.rawValue]?.string == "executable" {
outputType = .Executable
}
else {
......@@ -242,25 +264,25 @@ final class ATllbuild : Tool {
}
var compileOptions: [String] = []
if let opts = task["compileOptions"]?.vector {
if let opts = task[Options.CompileOptions.rawValue]?.vector {
for o in opts {
guard let os = o.string else { fatalError("Compile option \(o) is not a string") }
compileOptions.append(os)
}
}
var linkOptions: [String] = []
if let opts = task["linkOptions"]?.vector {
if let opts = task[Options.LinkOptions.rawValue]?.vector {
for o in opts {
guard let os = o.string else { fatalError("Link option \(o) is not a string") }
linkOptions.append(os)
}
}
guard let sourceDescriptions = task["source"]?.vector?.flatMap({$0.string}) else { fatalError("Can't find sources for atllbuild.") }
guard let sourceDescriptions = task[Options.Source.rawValue]?.vector?.flatMap({$0.string}) else { fatalError("Can't find sources for atllbuild.") }
var sources = collectSources(sourceDescriptions, taskForCalculatingPath: task)
//xctestify
if task["xctestify"]?.bool == true {
if task[Options.XCTestify.rawValue]?.bool == true {
precondition(outputType == .Executable, "You must use outputType: executable with xctestify.")
//inject platform-specific flags
#if os(OSX)
......@@ -268,7 +290,7 @@ final class ATllbuild : Tool {
linkOptions.appendContentsOf(["-F", "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/", "-target", "x86_64-apple-macosx10.11", "-Xlinker", "-rpath", "-Xlinker", "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/", "-Xlinker", "-bundle"])
#endif
}
if task["xctestStrict"]?.bool == true {
if task[Options.XCTestStrict.rawValue]?.bool == true {
#if os(OSX)
//inject XCTestCaseProvider.swift
var xcTestCaseProviderPath = "/tmp/XXXXXXX"
......@@ -280,11 +302,11 @@ final class ATllbuild : Tool {
#endif
}
guard let name = task["name"]?.string else { fatalError("No name for atllbuild task") }
guard let name = task[Options.Name.rawValue]?.string else { fatalError("No name for atllbuild task") }
let bootstrapOnly: Bool
if task["bootstrapOnly"]?.bool == true {
if task[Options.BootstrapOnly.rawValue]?.bool == true {
bootstrapOnly = true
}
else {
......@@ -292,14 +314,14 @@ final class ATllbuild : Tool {
}
let sdk: Bool
if task["linkSDK"]?.bool == false {
if task[Options.LinkSDK.rawValue]?.bool == false {
sdk = false
}
else { sdk = true }
let llbuildyamlpath : String
if let value = task["llbuildyaml"]?.string {
if let value = task[Options.llBuildYaml.rawValue]?.string {
llbuildyamlpath = value
}
else {
......@@ -307,7 +329,7 @@ final class ATllbuild : Tool {
}
let swiftCPath: String
if let c = task["swiftCPath"]?.string {
if let c = task[Options.SwiftCPath.rawValue]?.string {
swiftCPath = c
}
else {
......@@ -323,7 +345,7 @@ final class ATllbuild : Tool {
if system(cmd) != 0 {
fatalError(cmd)
}
if task["publishProduct"]?.bool == true {
if task[Options.PublishProduct.rawValue]?.bool == true {
if !manager.fileExistsAtPath("bin") {
try! manager.createDirectoryAtPath("bin", withIntermediateDirectories: false, attributes: nil)
}
......
......@@ -16,57 +16,57 @@
(package
:name "atbuild"
:import ["atpkg/build.atpkg"]
:import-packages ["atpkg/build.atpkg"]
:tasks {
:atbuild {
:tool "atllbuild"
:source ["atbuild/src/**.swift"]
:name "atbuild"
:outputType "executable"
:linkWithProduct ["attools.a" "atpkg.a"]
:dependencies ["attools" "atpkg.atpkg"]
:atbuild {
:tool "atllbuild"
:sources ["atbuild/src/**.swift"]
:name "atbuild"
:output-type "executable"
:link-with ["attools.a" "atpkg.a"]
:dependencies ["attools" "atpkg.atpkg"]
:overlays {
:bootstrap-osx {
:bootstrapOnly true
:llbuildyaml "bootstrap/bootstrap-macosx.swift-build"
}
:bootstrap-linux {
:bootstrapOnly true
:swiftCPath "/usr/local/bin/swiftc"
:linkSDK false
:llbuildyaml "bootstrap/bootstrap-linux.swift-build"
}
}
}
:attools {
:tool "atllbuild"
:source ["attools/src/**.swift"]
:name "attools"
:outputType "static-library"
:dependencies ["atpkg.atpkg"]
:linkWithProduct ["atpkg.a"]
:overlays {
:bootstrap-osx {
:bootstrapOnly true
:llbuildyaml "bootstrap/bootstrap-macosx-attools.swift-build"
}
:bootstrap-linux {
:bootstrapOnly true
:swiftCPath "/usr/local/bin/swiftc"
:linkSDK false
:llbuildyaml "bootstrap/bootstrap-linux-attools.swift-build"
}
}
}
:overlays {
:bootstrap-osx {
:bootstrap-only true
:llbuildyaml "bootstrap/bootstrap-macosx.swift-build"
}
:bootstrap-linux {
:bootstrap-only true
:swiftc-path "/usr/local/bin/swiftc"
:link-sdk false
:llbuildyaml "bootstrap/bootstrap-linux.swift-build"
}
}
}
:attools {
:tool "atllbuild"
:sources ["attools/src/**.swift"]
:name "attools"
:output-type "static-library"
:dependencies ["atpkg.atpkg"]
:link-with ["atpkg.a"]
:overlays {
:bootstrap-osx {
:bootstrap-only true
:llbuildyaml "bootstrap/bootstrap-macosx-attools.swift-build"
}
:bootstrap-linux {
:bootstrap-only true
:swiftc-path "/usr/local/bin/swiftc"
:link-sdk false
:llbuildyaml "bootstrap/bootstrap-linux-attools.swift-build"
}
}
}
:check {
:tool "shell"
:script "./tests/test.sh"
:dependencies ["atbuild"]
}
:check {
:tool "shell"
:script "./tests/test.sh"
:dependencies ["atbuild"]
}
}
)
Subproject commit 3a80085d1e23e240d0d3eaff9f9a8defb79a90d6
# atllbuild
The `atllbuild` tool uses the [`swift-llbuild`](https://github.com/apple/swift-llbuild) project to compile a swift module.
## API
```clojure
:tasks {
:build {
:tool "atllbuild"
;;Name of the module to build
:name "build"
;;Type of build. "library" and "executable" are supported.
:outputType "library"
;;walk the src directory and recursively find all swift files
:source ["src/**.swift"]
;;if true, we publish the product to the bin/ directory.
:publishProduct false
;;If true, we don't build, we only output llbuild.yaml False is the default value.
:bootstrapOnly: false
;;Path to emit llbuild.yaml
:llbuildyaml "llbuild.yaml"
;;Provide an array of compile options. NOTHING IS IMPOSSIBLE
:compileOptions []
:linkOptions [] ;;link options too!
:linkSDK true #Whether to link the platform SDK. True is the default value.
;;A product from another atllbuild task to link with.
;;You should supply a filename here, like "yaml.a".
;;Note that this is for linking dependencies built by atllbuild;
;;or other libraries, you should use UNSUPPORTED https://github.com/AnarchyTools/atbuild/issues/13
:linkWithProduct ["attools.a" "atpkg.a"]
;;The path to swiftC. By default, we guess based on your platform
swiftCPath:"/usr/local/bin/swiftc"
;; Inject platform-specific options related to
;; XCTest targets
xctestify: false
;; Inject platform-specific behavior related to
;; checking for API differences across XCTest
;; platforms
xctestStrict: false
}
}
```
## Conditional compilation
If you're packaging a project for atbuild and wish to conditionally compile your sources, you can use
```swift
#if ATBUILD
//use this code only when packaged with atbuild
#endif
```
This is useful when packaging Xcode projects that resolve their imports through bridging headers, for example.
## Implementation
The `atllbuild` tool emits an `llbuild.yaml` file. This is undocumented, but we [reverse-engineered the format from SwiftPM](https://github.com/apple/swift-package-manager). You can see an example in our [repository](/llbuild.yaml). Essentially, this file contains the compile/link commands for building the swift module.
Finally, we call `swift-build-tool -f /path/to/llbuild.yaml`.
## Boostrapping
In addition to building, we can choose to (only) emit the llbuild.yaml file, and save it for compiling later.
This is how we bootstrap atbuild, by creating an llbuild.yaml on a working machine and then using swift-build-tool on a new machine. For this reason, the `llbuild.yaml` file in this repository must be kept up to date.
\ No newline at end of file
You can import tasks in remote files.
This is useful to depend on tasks specified in another package.
```clojure
(package
:name "atbuild"
;; import all the tasks in `atpkg/build.atpkg`.
;; These will be imported as `packagename.taskname`.
;; Since you cannot declare packages with periods manually, this
;; cannot conflict with any current tasks
:import ["atpkg/build.atpkg"]
;; We can then depend on a target from the remote package in our current one
:tasks {
:mytask {
:tool "atllbuild"
:source ["src/**.swift]
:name "mytask"
:outputType "executable"
:dependencies ["atpkg.atpkg"]
}
}
)
```
You can also reference remote tasks on the command line by providing their fully-qualified name:
```bash
$ atbuild atpkg.atpkg
$ atbuild mytask
$ atbuild atbuild.mytask #equivalent to previous line
```
Packages are imported in a flat topology; if `a` imports `b` and `b` imports `c`, use `c.taskname` to refer to the task, not `b.c.taskname` or `a.b.c.taskname`.
# Implementation note
Remote packages may have paths specified in a key, like "source". *Quite possibly, these keys should be interpreted relative to the path the task was declared in, NOT the working directory.*
To support this, `Task` has a property `importedPath` that tools may want to use.
\ No newline at end of file
# nop
The nop tool has no effect. You can use this tool to create tasks that group dependencies.
\ No newline at end of file
# Overlays
You can activate settings, called "overlays", by passing them on the command line.
```bash
$ atbuild --overlay bar
```
This activates the settings for the overlay across all targets:
```clojure
(package
:name "Foo"
:overlays {
:bar {
:compileOptions ["-D" "FOO"]
}
}
)
```
You can add as many overlays as you like: `atbuild --overlay bar --overlay baz`. They are processed in order.
# Task-scoped overlays
You can also only apply an overlay to a particular task. To do this:
```clojure
(package
:name "Foo"
:tasks {
:foo {
:overlays {
:bar {
:compileOptions ["-D" "BAZ"]
}
}
}
}
)
```
Now when we build with `--overlay bar`, this will add `-D BAZ` to compile options for the `foo` task, but not for other tasks in our file.
# 'Always' overlay
We can use an overlay even when it was not specified on the CLI. This way we can share common configuration options between several tasks.
```clojure
(package
:name "Foo"
:overlays {
:optimized {
:compileOptions ["-Owholemodule"] ;;whole module optimization
}
}
:tasks {
:foo {
:overlay ["optimized"] ;;apply to this task
}
:baz {
:overlay ["optimized"] ;;apply to this task too
}
}
)
```
# Imported overlays
Overlays can be [imported](import.md). This allows libraries to export required compile flags to clients.
```clojure
(package
:name "Library"
:overlays {
:compile-linux {
:compileOptions ["-Xcc" "-fblocks"] ;; work around https://bugs.swift.org/browse/SR-397
}
}
)
```
```clojure
(package
:name "Executable"
:import ["Library.atpkg"]
:tasks {
:foo {
:overlay ["Library.compile-linux"] ;;apply to this task
}
)
```
# shell
The shell tool allows you to call a shell command.
# API
```clojure
:taskname {
:tool "shell"
;;run the following script in /bin/sh.
;;A non-zero return code indicates that build should halt.
:script "echo hello world"
}
```
# Tasks
All tasks have the following options:
```clojure
taskname: {
;;the name of a tool. Valid tools are shell, atllbuild, nop
:tool "tool"
;;What other tasks should run before this one.
:dependency []
;;see docs/overlays.md for more information about overlays
:overlay [] ;;what overlays to apply to this task
:overlays { } ;;what overlays can be applied to this task
}
```
# XCTest
atbuild is unopinionated about how you test your code; you can launch any executable as an atbuild [task](tasks.md), and use that executable to run tests.
However, if you want to use XCTest, it is tricky to configure, particularly as the implementation is quite different between platforms. To smooth this problem out, `atbuild` includes some additonal options.
```clojure
(package
:name "xctestexample"
:tasks {
;; Define a task to build our library
:build-lib {
:tool "atllbuild"
:source ["src/**.swift"]
:outputType "static-library"
:name "Foo"
;; Make sure we enable testing so that
;; we can import with @testable
:compileOptions ["-enable-testing"]
}
;; Define a task to build the tests.
;; This is sometimes called a "test target" in Xcode.
;; This is primarily an executable that links our library.
:build-tests {
:tool "atllbuild"
:source ["test/**.swift"]
:outputType "executable"
:name "footests"
:dependencies ["build-lib"]
:linkWithProduct["Foo.a"]
;; atbuild will inject platform-specific
;; compile options to make XCTest work
:xctestify true
;; XCTest on Linux is a bit stricter
;; than on OSX. Tell atbuild
;; we want stricter checks for API compliance
:xctestStrict true
}
;; A task to run our tests
:run-tests {
:dependencies ["build-tests"]
;; The xctestrun tool is a cross-platform XCTest runner
:tool "xctestrun"
;; Tell xctestrun where we built our tests
:testExecutable ".atllbuild/products/footests"
}
}
)
```
Now you can run your tests on any platform with just `atbuild run-tests`.
For more information, see a [complete example](tests/fixtures/xcs)
\ No newline at end of file
(package
:name "NaOH"
:import ["remote.atpkg"]
:import-packages ["remote.atpkg"]
:tasks {
:default {
......
......@@ -17,18 +17,18 @@
:overlays {
:got-overlay {
:compileOptions ["-D" "GOT_OVERLAY"]
:compile-options ["-D" "GOT_OVERLAY"]
}
}
:tasks {
:default {
:tool "atllbuild"
:source ["src/**.swift"]
:sources ["src/**.swift"]
:name "overlay"
:outputType "static-library"
:output-type "static-library"
:germany "awesome"
:compileOptions []
:compile-options []
}
}
)
\ No newline at end of file
......@@ -4,17 +4,17 @@
:tasks {
:executable {
:tool "atllbuild"
:outputType "executable"
:output-type "executable"
:name "executable"
:source ["main.swift"]
:publishProduct true
:sources ["main.swift"]
:publish-product true
}
:library {
:tool "atllbuild"
:outputType "static-library"
:output-type "static-library"
:name "library"
:source ["lib.swift"]
:publishProduct true
:sources ["lib.swift"]
:publish-product true
}
:default {
:tool "nop"
......
......@@ -5,10 +5,10 @@
:tasks {
:default {
:tool "atllbuild"
:outputType "static-library"
:output-type "static-library"
:name "test"
:germany "invalid"
:source ["Foo.swift"]
:sources ["Foo.swift"]
}
}
)
\ No newline at end of file
......@@ -4,24 +4,24 @@
:tasks {
:build-lib {
:tool "atllbuild"
:source ["src/**.swift"]
:outputType "static-library"
:sources ["src/**.swift"]
:output-type "static-library"
:name "Foo"
:compileOptions ["-enable-testing"]
:compile-options ["-enable-testing"]
}
:build-tests {
:tool "atllbuild"
:source ["test/**.swift"]
:outputType "executable"
:sources ["test/**.swift"]
:output-type "executable"
:name "footests"
:dependencies ["build-lib"]
:xctestify true
:xctestStrict true
:linkWithProduct["Foo.a"]
:xctest-strict true
:link-with ["Foo.a"]
}
:run-tests {
:tool "xctestrun"
:testExecutable ".atllbuild/products/footests"
:test-executable ".atllbuild/products/footests"
:dependencies ["build-tests"]
}
}
......
......@@ -5,23 +5,23 @@
:build-lib {
:tool "atllbuild"
:source ["src/**.swift"]
:outputType "static-library"
:output-type "static-library"
:name "Foo"
:compileOptions ["-enable-testing"]