diff options
-rw-r--r-- | WORKSPACE | 14 | ||||
-rw-r--r-- | examples/d/hello_lib/BUILD | 40 | ||||
-rw-r--r-- | examples/d/hello_lib/greeter.d | 51 | ||||
-rw-r--r-- | examples/d/hello_lib/greeter_test.d | 22 | ||||
-rw-r--r-- | examples/d/hello_lib/native-greeter.c | 49 | ||||
-rw-r--r-- | examples/d/hello_lib/native-greeter.h | 28 | ||||
-rw-r--r-- | examples/d/hello_lib/native_greeter.d | 43 | ||||
-rw-r--r-- | examples/d/hello_world/BUILD | 12 | ||||
-rw-r--r-- | examples/d/hello_world/hello_world.d | 26 | ||||
-rw-r--r-- | site/docs/supported-rules.md | 1 | ||||
-rw-r--r-- | tools/build_defs/d/BUILD | 43 | ||||
-rw-r--r-- | tools/build_defs/d/README.md | 651 | ||||
-rw-r--r-- | tools/build_defs/d/d.WORKSPACE | 13 | ||||
-rw-r--r-- | tools/build_defs/d/d.bzl | 450 | ||||
-rw-r--r-- | tools/build_defs/d/dmd.BUILD | 43 |
15 files changed, 1486 insertions, 0 deletions
@@ -81,3 +81,17 @@ bind( name = "android_ndk_for_testing", actual = "//:dummy", ) + +new_http_archive( + name = "dmd-linux-x86_64", + url = "http://downloads.dlang.org/releases/2.x/2.067.1/dmd.2.067.1.linux.zip", + sha256 = "a5014886773853b4a42df19ee9591774cf281d33fbc511b265df30ba832926cd", + build_file = "tools/build_defs/d/dmd.BUILD", +) + +new_http_archive( + name = "dmd-darwin-x86_64", + url = "http://downloads.dlang.org/releases/2.x/2.067.1/dmd.2.067.1.osx.zip", + sha256 = "aa76bb83c38b3f7495516eb08977fc9700c664d7a945ba3ac3c0004a6a8509f2", + build_file = "tools/build_defs/d/dmd.BUILD", +) diff --git a/examples/d/hello_lib/BUILD b/examples/d/hello_lib/BUILD new file mode 100644 index 0000000000..7ed1624a41 --- /dev/null +++ b/examples/d/hello_lib/BUILD @@ -0,0 +1,40 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "/tools/build_defs/d/d", + "d_docs", + "d_library", + "d_source_library", + "d_test", +) + +d_library( + name = "greeter", + srcs = ["greeter.d"], +) + +d_test( + name = "greeter_test", + srcs = ["greeter_test.d"], + deps = [":greeter"], +) + +cc_library( + name = "native_greeter_lib", + srcs = ["native-greeter.c"], + hdrs = ["native-greeter.h"], +) + +d_source_library( + name = "native_greeter", + srcs = ["native_greeter.d"], + deps = [":native_greeter_lib"], +) + +d_docs( + name = "greeter_docs", + srcs = glob( + ["*.d"], + exclude = ["*_test.d"], + ), +) diff --git a/examples/d/hello_lib/greeter.d b/examples/d/hello_lib/greeter.d new file mode 100644 index 0000000000..ec916ffbe5 --- /dev/null +++ b/examples/d/hello_lib/greeter.d @@ -0,0 +1,51 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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. + +module greeter; + +import std.stdio; +import std.string; + +/// Displays a greeting. +class Greeter { + private string greeting; + + public: + /// Creates a new greeter. + /// + /// Params: + /// greeting = The greeting to use. + this(in string greeting) { + this.greeting = greeting.dup; + } + + /// Returns the greeting as a string. + /// + /// Params: + /// thing = The thing to greet + /// + /// Returns: + /// A greeting as a string. + string makeGreeting(in immutable string thing) { + return format("%s %s!", this.greeting, thing); + } + + /// Prints a greeting. + /// + /// Params: + /// thing = The thing to greet. + void greet(in immutable string thing) { + writeln(makeGreeting(thing)); + } +} diff --git a/examples/d/hello_lib/greeter_test.d b/examples/d/hello_lib/greeter_test.d new file mode 100644 index 0000000000..6ebd7f9cb1 --- /dev/null +++ b/examples/d/hello_lib/greeter_test.d @@ -0,0 +1,22 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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 examples.d.hello_lib.greeter; + +unittest { + auto greeter = new Greeter("Hello"); + assert(greeter.makeGreeting("world") == "Hello world!"); +} + +void main() {} diff --git a/examples/d/hello_lib/native-greeter.c b/examples/d/hello_lib/native-greeter.c new file mode 100644 index 0000000000..cdbb388e32 --- /dev/null +++ b/examples/d/hello_lib/native-greeter.c @@ -0,0 +1,49 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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. + +#include "examples/d/hello_lib/native-greeter.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +NativeGreeter* native_greeter_new(const char* greeting) { + if (greeting == NULL) { + return NULL; + } + NativeGreeter* greeter = NULL; + greeter = (NativeGreeter*)malloc(sizeof(*greeter)); + if (greeter == NULL) { + return NULL; + } + greeter->greeting = strdup(greeting); + return greeter; +} + +void native_greeter_greet(const NativeGreeter* greeter, const char* thing) { + if (greeter == NULL || thing == NULL) { + return; + } + printf("%s %s!\n", greeter->greeting, thing); +} + +void native_greeter_free(NativeGreeter* greeter) { + if (greeter == NULL) { + return; + } + if (greeter->greeting != NULL) { + free(greeter->greeting); + } + free(greeter); +} diff --git a/examples/d/hello_lib/native-greeter.h b/examples/d/hello_lib/native-greeter.h new file mode 100644 index 0000000000..d01d8cae8b --- /dev/null +++ b/examples/d/hello_lib/native-greeter.h @@ -0,0 +1,28 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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. + +#ifndef EXAMPLES_D_HELLO_LIB_NATIVE_GREETER_H_ +#define EXAMPLES_D_HELLO_LIB_NATIVE_GREETER_H_ + +typedef struct NativeGreeter { + char* greeting; +} NativeGreeter; + +NativeGreeter* native_greeter_new(const char* greeting); + +void native_greeter_greet(const NativeGreeter* greeter, const char* thing); + +void native_greeter_free(NativeGreeter* greeter); + +#endif // EXAMPLES_D_HELLO_LIB_NATIVE_GREETER_H_ diff --git a/examples/d/hello_lib/native_greeter.d b/examples/d/hello_lib/native_greeter.d new file mode 100644 index 0000000000..417492aaf6 --- /dev/null +++ b/examples/d/hello_lib/native_greeter.d @@ -0,0 +1,43 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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. + +module native_greeter; + +extern (C): + +struct NativeGreeter { + char* greeting; +}; + +/// Creates a new NativeGreeter. +/// +/// Params: +/// greeting = The greeting to use. +/// +/// Returns: +/// A pointer to a new NativeGreeting struct. +NativeGreeter* native_greeter_new(const(char)* greeting); + +/// Prints a greeting to stdout. +/// +/// Params: +/// greeter = The pointer to the NativeGreeter object to use. +/// thing = The thing to greet. +void native_greeter_greet(const(NativeGreeter)* greeter, const(char)* thing); + +/// Frees the NativeGreeter. +/// +/// Params: +/// greeter = The pointer to the NativeGreeter object to use. +void native_greeter_free(NativeGreeter* greeter); diff --git a/examples/d/hello_world/BUILD b/examples/d/hello_world/BUILD new file mode 100644 index 0000000000..47c7342060 --- /dev/null +++ b/examples/d/hello_world/BUILD @@ -0,0 +1,12 @@ +package(default_visibility = ["//visibility:public"]) + +load("/tools/build_defs/d/d", "d_binary") + +d_binary( + name = "hello_world", + srcs = ["hello_world.d"], + deps = [ + "//examples/d/hello_lib:greeter", + "//examples/d/hello_lib:native_greeter", + ], +) diff --git a/examples/d/hello_world/hello_world.d b/examples/d/hello_world/hello_world.d new file mode 100644 index 0000000000..3c60f4a24e --- /dev/null +++ b/examples/d/hello_world/hello_world.d @@ -0,0 +1,26 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// 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 std.stdio; +import examples.d.hello_lib.greeter; +import examples.d.hello_lib.native_greeter; + +void main() { + Greeter greeter = new Greeter("Hello"); + greeter.greet("World"); + + NativeGreeter* nativeGreeter = native_greeter_new("Hello"); + native_greeter_greet(nativeGreeter, "World"); + native_greeter_free(nativeGreeter); +} diff --git a/site/docs/supported-rules.md b/site/docs/supported-rules.md index 7010dcfe5f..d76395254c 100644 --- a/site/docs/supported-rules.md +++ b/site/docs/supported-rules.md @@ -12,5 +12,6 @@ to build the following: * [Docker images](https://github.com/bazelbuild/bazel/tree/master/tools/build_defs/docker) * [Groovy projects](https://github.com/bazelbuild/bazel/tree/master/tools/build_defs/groovy) * [Java App Engine applications](https://github.com/bazelbuild/bazel/tree/master/tools/build_rules/appengine) +* [D projects](https://github.com/bazelbuild/bazel/tree/master/tools/build_defs/d) * [Rust projects](https://github.com/bazelbuild/bazel/tree/master/tools/build_rules/rust) * [Scala projects](https://github.com/bazelbuild/bazel/tree/master/tools/build_defs/scala) - experimental diff --git a/tools/build_defs/d/BUILD b/tools/build_defs/d/BUILD new file mode 100644 index 0000000000..7d5b723037 --- /dev/null +++ b/tools/build_defs/d/BUILD @@ -0,0 +1,43 @@ +package(default_visibility = ["//visibility:public"]) + +config_setting( + name = "darwin", + values = {"host_cpu": "darwin"}, +) + +config_setting( + name = "k8", + values = {"host_cpu": "k8"}, +) + +filegroup( + name = "dmd", + srcs = select({ + ":darwin": ["@dmd-darwin-x86_64//:dmd"], + ":k8": ["@dmd-linux-x86_64//:dmd"], + }), +) + +filegroup( + name = "libphobos2", + srcs = select({ + ":darwin": ["@dmd-darwin-x86_64//:libphobos2"], + ":k8": ["@dmd-linux-x86_64//:libphobos2"], + }), +) + +filegroup( + name = "phobos-src", + srcs = select({ + ":darwin": ["@dmd-darwin-x86_64//:phobos-src"], + ":k8": ["@dmd-linux-x86_64//:phobos-src"], + }), +) + +filegroup( + name = "druntime-import-src", + srcs = select({ + ":darwin": ["@dmd-darwin-x86_64//:druntime-import-src"], + ":k8": ["@dmd-linux-x86_64//:druntime-import-src"], + }), +) diff --git a/tools/build_defs/d/README.md b/tools/build_defs/d/README.md new file mode 100644 index 0000000000..89707a83e5 --- /dev/null +++ b/tools/build_defs/d/README.md @@ -0,0 +1,651 @@ +# D rules + +## Rules + +* [`d_library`](#d_library) +* [`d_source_library`](#d_source_library) +* [`d_binary`](#d_binary) +* [`d_test`](#d_test) +* [`d_docs`](#d_docs) + +## Setup + +To use the D rules, simply copy the contents of `d.WORKSPACE` into your +`WORKSPACE` file. + +## Roadmap + +* Generate documentation using [`ddox`](https://github.com/rejectedsoftware/ddox) + for `d_docs` rule. +* Support for other options as defined in the [Dub package + format](http://code.dlang.org/package-format?lang=json) +* Support for specifying different configurations of a library, closer to + [Dub's model for configurations](http://code.dlang.org/package-format?lang=json#configurations) +* Workspace rule for retrieving dependencies from [Dub](http://code.dlang.org/) + +<a name="d_library"></a> +## d_library + +```python +d_library(name, srcs, deps, includes, linkopts, versions) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + <p> + This name will be used as the name of the library built by this rule. + </p> + </td> + </tr> + <tr> + <td><code>srcs</code></td> + <td> + <code>List of labels, required</code> + <p>List of D <code>.d</code> source files used to build the library.</p> + </td> + </tr> + <tr> + <td><code>deps</code></td> + <td> + <code>List of labels, optional</code> + <p>List of libraries to be linked to this library target.</p> + <p> + These can either be other <code>d_library</code> targets, + source-only <code>d_source_library</code> targets, or + <code>cc_library</code> targets if linking a native library. + </p> + </td> + </tr> + <tr> + <td><code>imports</code></td> + <td> + <code>List of strings, optional</code> + <p>List of import dirs to add to the compile line.</p> + <p> + These will be passed to the D compiler via <code>-I</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>linkopts</code></td> + <td> + <code>List of strings, optional</code> + <p>List of flags that are added to the D linker command.</p> + <p> + These will be passed to the D compiler via <code>-L</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>versions</code></td> + <td> + <code>List of strings, optional</code> + <p>List of versions to be defined during compilation.</p> + <p> + Versions are used for conditional compilation and are enabled in the + code using <code>version</code> condition blocks. These versions + listed here will be passed to the D compiler using + <code>-version</code> flags. + </p> + </td> + </tr> + </tbody> +</table> + +### Example + +Suppose you have the following directory structure for a D project: + +``` +[workspace]/ + WORKSPACE + foo/ + BUILD + foo.d + bar.d + baz.d +``` + +The library `foo` is built using a `d_library` target: + +`foo/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_library") + +d_binary( + name = "foo", + srcs = [ + "foo.d", + "bar.d", + "baz.d", + ], +) +``` + +<a name="d_source_library"></a> +## d_source_library + +```python +d_source_library(name, srcs, deps, includes, linkopts, versions) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + </td> + </tr> + <tr> + <td><code>srcs</code></td> + <td> + <code>List of labels, required</code> + <p> + List of D <code>.d</code> source files that comprises this source + library target. + </p> + </td> + </tr> + <tr> + <td><code>deps</code></td> + <td> + <code>List of labels, optional</code> + <p>List of library targets depended on by this target.</p> + <p> + These can either be other <code>d_source_library</code> targets or + <code>cc_library</code> targets, such as when this source library + target implements the D interface for a native library. Any native + libraries will be linked by <code>d_library</code> targets that + depend on this target. + </p> + </td> + </tr> + <tr> + <td><code>imports</code></td> + <td> + <code>List of strings, optional</code> + <p>List of import dirs to add to the compile line.</p> + <p> + These will be passed to the D compiler via <code>-I</code> flags for + any <code>d_library</code> targets that depend on this target. + </p> + </td> + </tr> + <tr> + <td><code>linkopts</code></td> + <td> + <code>List of strings, optional</code> + <p>List of flags that are added to the D linker command.</p> + <p> + These will be passed to the D compiler via <code>-L</code> flags for + any <code>d_library</code> targets that depend on this target. + </p> + </td> + </tr> + <tr> + <td><code>versions</code></td> + <td> + <code>List of strings, optional</code> + <p>List of version flags to be defined during compilation.</p> + <p> + Versions are used for conditional compilation and are enabled in the + code using <code>version</code> condition blocks. These versions + listed here will be passed to the D compiler using + <code>-version</code> flags for any <code>d_library</code> targets + that depend on this target. + </p> + </td> + </tr> + </tbody> +</table> + +### Example + +Suppose you have the following directory structure for a project building a +C library and a [D interface](http://dlang.org/interfaceToC.html) for the C +library: + +``` +[workspace]/ + WORKSPACE + greeter/ + BUILD + native_greeter.c + native_greeter.h + native_greeter.d + hello_world + BUILD + hello_world.d +``` + +Build the C library using the `cc_library` rule and then use the +`d_source_library` to define the target for the D interface for the C +`native_greeter` library: + +`greeter/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_source_library") + +cc_library( + name = "native_greeter_lib", + srcs = ["native_greeter.c"], + hdrs = ["native_greeter.h"], +) + +d_source_library( + name = "native_greeter", + srcs = ["native_greeter.d"], + deps = [":native_greeter_lib"], +) +``` + +Other targets can directly depend on the `d_source_library` target to link +the C library: + +`hello_world/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_source_library") + +d_binary( + name = "hello_world", + srcs = ["hello_world.d"], + deps = ["//greeter:native_greeter"], +) +``` + +<a name="d_binary"></a> +## d_binary + +```python +d_binary(name, srcs, deps, includes, linkopts, versions) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + <p> + This name will be used as the name of the binary built by this rule. + </p> + </td> + </tr> + <tr> + <td><code>srcs</code></td> + <td> + <code>List of labels, required</code> + <p>List of D <code>.d</code> source files used to build the binary.</p> + </td> + </tr> + <tr> + <td><code>deps</code></td> + <td> + <code>List of labels, optional</code> + <p>List of libraries to be linked to this binary target.</p> + <p> + These can either be other <code>d_library</code> targets, + source-only <code>d_source_library</code> targets, or + <code>cc_library</code> targets if linking a native library. + </p> + </td> + </tr> + <tr> + <td><code>imports</code></td> + <td> + <code>List of strings, optional</code> + <p>List of import dirs to add to the compile line.</p> + <p> + These will be passed to the D compiler via <code>-I</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>linkopts</code></td> + <td> + <code>List of strings, optional</code> + <p>List of flags that are added to the D linker command.</p> + <p> + These will be passed to the D compiler via <code>-L</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>versions</code></td> + <td> + <code>List of strings, optional</code> + <p>List of versions to be defined during compilation.</p> + <p> + Versions are used for conditional compilation and are enabled in the + code using <code>version</code> condition blocks. These versions + listed here will be passed to the D compiler using + <code>-version</code> flags. + </p> + </td> + </tr> + </tbody> +</table> + +Suppose you have the following directory structure for a D project: + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + greeter.d + hello_world + BUILD + hello_world.d +``` + +The source file `hello_lib/greeter.d` defines a module `greeter`: + +```d +module greeter; +... +``` + +The `hello_lib` library is built using a `d_library` target: + +`hello_lib/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_library") + +d_library( + name = "hello_lib", + srcs = ["greeter.d"], +) +``` + +By default, import paths are from the root of the workspace. Thus, the source +for the `hello_world` binary, `hello_world.d`, would import the `greeter` +module as follows: + +```d +import hello_lib.greeter; +``` + +However, this can be changed via the `imports` attribute on the `d_library` +rule. + +The `hello_world` binary is built using a `d_binary` target: + +`hello_world/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_library") + +d_binary( + name = "hello_world", + srcs = ["hello_world.d"], + deps = ["//hello_lib"], +) +``` + +<a name="d_test"></a> +## d_test + +```python +d_test(name, srcs, deps, includes, linkopts, versions) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + <p> + This name will be used as the name of the test built by this rule. + </p> + </td> + </tr> + <tr> + <td><code>srcs</code></td> + <td> + <code>List of labels, required</code> + <p>List of D <code>.d</code> source files used to build the test.</p> + </td> + </tr> + <tr> + <td><code>deps</code></td> + <td> + <code>List of labels, optional</code> + <p>List of libraries to be linked to this test target.</p> + <p> + These can either be other <code>d_library</code> targets, + source-only <code>d_source_library</code> targets, or + <code>cc_library</code> targets if linking a native library. + </p> + </td> + </tr> + <tr> + <td><code>imports</code></td> + <td> + <code>List of strings, optional</code> + <p>List of import dirs to add to the compile line.</p> + <p> + These will be passed to the D compiler via <code>-I</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>linkopts</code></td> + <td> + <code>List of strings, optional</code> + <p>List of flags that are added to the D linker command.</p> + <p> + These will be passed to the D compiler via <code>-L</code> flags. + </p> + </td> + </tr> + <tr> + <td><code>versions</code></td> + <td> + <code>List of strings, optional</code> + <p>List of versions to be defined during compilation.</p> + <p> + Versions are used for conditional compilation and are enabled in the + code using <code>version</code> condition blocks. These versions + listed here will be passed to the D compiler using + <code>-version</code> flags. + </p> + </td> + </tr> + </tbody> +</table> + +### Example + +Suppose you have the following directory structure for a D project: + +``` +[workspace]/ + WORKSPACE + hello_lib/ + BUILD + greeter.d + greeter_test.d +``` + +`hello_lib/greeter.d`: + +```d +module greeter; + +import std.stdio; +import std.string; + +class Greeter { + private string greeting; + + public: + this(in string greeting) { + this.greeting = greeting.dup; + } + + string makeGreeting(in immutable string thing) { + return format("%s %s!", this.greeting, thing); + } + + void greet(in immutable string thing) { + writeln(makeGreeting(thing)); + } +} +``` + +`hello_lib/greeter_test.d`: + +```d +import hello_lib.greeter; + +unittest { + auto greeter = new Greeter("Hello"); + assert(greeter.makeGreeting("world") == "Hello world!"); +} + +void main() {} +``` + +To build the library and unit test: + +`hello_lib/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_library", "d_test") + +d_library( + name = "greeter", + srcs = ["greeter.d"], +) + +d_test( + name = "greeter_test", + srcs = ["greeter_test.d"], + deps = [":greeter"], +) +``` + +The unit test can then be run using: + +```sh +bazel test //hello_lib:greeter_test +``` + +<a name="d_docs"></a> +## d_docs + +```python +d_docs(name, srcs, imports) +``` + +<table> + <thead> + <tr> + <th>Attribute</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>name</code></td> + <td> + <code>Name, required</code> + <p>A unique name for this rule.</p> + </td> + </tr> + <tr> + <td><code>srcs</code></td> + <td> + <code>List of labels, required</code> + <p> + List of D <code>.d</code> source files to generate documentation for. + </p> + </td> + </tr> + <tr> + <td><code>imports</code></td> + <td> + <code>List of strings, optional</code> + <p>List of import dirs to add to the compile line.</p> + <p> + These will be passed to the D compiler via <code>-I</code> flags. + </p> + </td> + </tr> + </tbody> +</table> + +### Example + +Suppose you have the following directory structure for a D project: + +``` +[workspace]/ + WORKSPACE + foo/ + BUILD + foo.d + bar.d + baz.d +``` + +To generate HTML documentation for the `foo` package, define a `d_docs` target: + +`foo/BUILD`: + +```python +load("/tools/build_defs/d/d", "d_docs") + +d_docs( + name = "foo_docs", + srcs = [ + "foo.d", + "bar.d", + "baz.d", + ], +) +``` + +Running `bazel build //foo:foo_docs` will generate a zip file containing the +HTML documentation generated from the source files. See the official D language +documentation on the [Documentation Generator](http://dlang.org/ddoc.html) for +more information on the conventions for source documentation. diff --git a/tools/build_defs/d/d.WORKSPACE b/tools/build_defs/d/d.WORKSPACE new file mode 100644 index 0000000000..004276473e --- /dev/null +++ b/tools/build_defs/d/d.WORKSPACE @@ -0,0 +1,13 @@ +new_http_archive( + name = "dmd-linux-x86_64", + url = "http://downloads.dlang.org/releases/2.x/2.067.1/dmd.2.067.1.linux.zip", + sha256 = "a5014886773853b4a42df19ee9591774cf281d33fbc511b265df30ba832926cd", + build_file = "tools/build_defs/d/dmd.BUILD", +) + +new_http_archive( + name = "dmd-darwin-x86_64", + url = "http://downloads.dlang.org/releases/2.x/2.067.1/dmd.2.067.1.linux.zip", + sha256 = "aa76bb83c38b3f7495516eb08977fc9700c664d7a945ba3ac3c0004a6a8509f2", + build_file = "tools/build_defs/d/dmd.BUILD", +) diff --git a/tools/build_defs/d/d.bzl b/tools/build_defs/d/d.bzl new file mode 100644 index 0000000000..aaa924149d --- /dev/null +++ b/tools/build_defs/d/d.bzl @@ -0,0 +1,450 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# 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. + +"""D rules for Bazel.""" + +A_FILETYPE = FileType([".a"]) +D_FILETYPE = FileType([".d", ".di"]) + +ZIP_PATH = "/usr/bin/zip" + +def _relative(src_path, dest_path): + """Returns the relative path from src_path to dest_path.""" + src_parts = src_path.split("/") + dest_parts = dest_path.split("/") + n = 0 + for src_part, dest_part in zip(src_parts, dest_parts): + if src_part != dest_part: + break + n += 1 + + relative_path = "" + for _ in range(n, len(src_parts)): + relative_path += "../" + relative_path += "/".join(dest_parts[n:]) + + return relative_path + +def _create_setup_cmd(lib, deps_dir): + """Constructs a command for symlinking a library into the deps directory.""" + return ( + "ln -sf " + _relative(deps_dir, lib.path) + " " + + deps_dir + "/" + lib.basename + "\n" + ) + +def _d_toolchain(ctx): + """Returns a struct containing info about the D toolchain. + + Args: + ctx: The ctx object. + + Return: + Struct containing the following fields: + d_compiler_path: The path to the D compiler. + link_flags: Linker (-L) flags for adding the standard library to the + library search paths. + import_flags: import (-L) flags for adding the standard library sources + to the import paths. + """ + + d_compiler_path = ctx.file._d_compiler.path + return struct( + d_compiler_path = d_compiler_path, + link_flags = ["-L-L" + ctx.files._d_stdlib[0].dirname], + import_flags = [ + "-I" + ctx.files._d_stdlib_src[0].dirname, + "-I" + ctx.files._d_runtime_import_src[0].dirname]) + +def _format_version(name): + """Formats the string name to be used in a --version flag.""" + return name.replace("-", "_") + +def _build_compile_command(ctx, srcs, out, depinfo, extra_flags=[]): + """Returns a string containing the D compile command.""" + toolchain = _d_toolchain(ctx) + cmd = ( + ["set -e;"] + + depinfo.setup_cmd + + [toolchain.d_compiler_path] + + extra_flags + [ + "-of" + out.path, + "-I.", + "-debug", + "-w", + "-g", + ] + + ["-I%s/%s" % (ctx.label.package, im) for im in ctx.attr.imports] + + ["-I%s" % im for im in depinfo.imports] + + toolchain.import_flags + + ["-version=Have_%s" % _format_version(ctx.label.name)] + + ["-version=%s" % v for v in ctx.attr.versions] + + ["-version=%s" % v for v in depinfo.versions] + + srcs) + return " ".join(cmd) + +def _build_link_command(ctx, objs, out, depinfo): + """Returns a string containing the D link command.""" + toolchain = _d_toolchain(ctx) + cmd = ( + ["set -e;"] + + depinfo.setup_cmd + + [toolchain.d_compiler_path] + + ["-of" + out.path] + + toolchain.link_flags + + depinfo.lib_flags + + depinfo.link_flags + + objs) + return " ".join(cmd) + +def _setup_deps(deps, name, working_dir): + """Sets up dependencies. + + Walks through dependencies and constructs the commands and flags needed + for linking the necessary dependencies. + + Args: + deps: List of deps labels from ctx.attr.deps. + name: Name of the current target. + working_dir: The output directory of the current target's output. + + Returns: + Returns a struct containing the following fields: + libs: List of Files containing the target's direct library dependencies. + transitive_d_libs: List of Files containing all of the target's + transitive libraries. + d_srcs: List of Files representing D source files of dependencies that + will be used as inputs for this target. + versions: List of D versions to be used for compiling the target. + setup_cmd: String containing the symlink commands to be used to set + up the dependencies. + imports: List of Strings containing input paths that will be passed + to the D compiler via -I flags. + link_flags: List of linker flags. + lib_flags: List of library search flags. + """ + deps_dir = working_dir + "/" + name + ".deps" + setup_cmd = ["rm -rf " + deps_dir + ";" + "mkdir -p " + deps_dir + ";"] + + libs = set() + transitive_d_libs = set() + d_srcs = set() + symlinked_libs = set() + versions = set() + imports = set() + link_flags = set() + for dep in deps: + if hasattr(dep, "d_lib"): + # The dependency is a d_library. + libs += [dep.d_lib] + d_srcs += dep.d_srcs + transitive_d_libs += [dep.d_lib] + dep.transitive_d_libs + symlinked_libs += [dep.d_lib] + dep.transitive_d_libs + versions += dep.versions + versions += ["Have_%s" % _format_version(dep.label.name)] + link_flags += ["-L-l%s" % dep.label.name] + dep.link_flags + imports += ["%s/%s" % (dep.label.package, im) for im in dep.imports] + + elif hasattr(dep, "d_srcs"): + # The dependency is a d_source_library. + d_srcs += dep.d_srcs + transitive_d_libs += dep.transitive_d_libs + symlinked_libs += dep.transitive_d_libs + link_flags += ["-L%s" % linkopt for linkopt in dep.linkopts] + imports += ["%s/%s" % (dep.label.package, im) for im in dep.imports] + versions += dep.versions + + elif hasattr(dep, "cc"): + # The dependency is a cc_library + native_libs = A_FILETYPE.filter(dep.cc.libs) + libs += native_libs + transitive_d_libs += native_libs + symlinked_libs += native_libs + link_flags += ["-L-l%s" % dep.label.name] + + else: + fail("D targets can only depend on d_library, d_source_library, or " + + "cc_library targets.", "deps") + + for symlinked_libs in symlinked_libs: + setup_cmd += [_create_setup_cmd(symlinked_libs, deps_dir)] + + return struct( + libs = list(libs), + transitive_d_libs = list(transitive_d_libs), + d_srcs = list(d_srcs), + versions = versions, + setup_cmd = setup_cmd, + imports = list(imports), + link_flags = list(link_flags), + lib_flags = ["-L-L%s" % deps_dir]) + +def _d_library_impl(ctx): + """Implementation of the d_library rule.""" + d_lib = ctx.outputs.d_lib + + # Dependencies + depinfo = _setup_deps(ctx.attr.deps, ctx.label.name, d_lib.dirname) + + # Build compile command. + cmd = _build_compile_command( + ctx = ctx, + srcs = [src.path for src in ctx.files.srcs], + out = d_lib, + depinfo = depinfo, + extra_flags = ["-lib"]) + + compile_inputs = ( + ctx.files.srcs + + depinfo.d_srcs + + depinfo.libs + + depinfo.transitive_d_libs + + [ctx.file._d_compiler] + + ctx.files._d_stdlib + + ctx.files._d_stdlib_src + + ctx.files._d_runtime_import_src) + + ctx.action(inputs = compile_inputs, + outputs = [d_lib], + mnemonic = "Dcompile", + command = cmd, + use_default_shell_env = True, + progress_message = "Compiling D library " + ctx.label.name) + + return struct(files = set([d_lib]), + d_srcs = ctx.files.srcs + depinfo.d_srcs, + transitive_d_libs = depinfo.transitive_d_libs, + link_flags = depinfo.link_flags, + versions = ctx.attr.versions, + imports = ctx.attr.imports, + d_lib = d_lib) + +def _d_binary_impl_common(ctx, extra_flags=[]): + """Common implementation for rules that build a D binary.""" + d_bin = ctx.outputs.executable + d_obj = ctx.new_file(ctx.configuration.bin_dir, + d_bin.basename + ".o") + depinfo = _setup_deps(ctx.attr.deps, ctx.label.name, d_bin.dirname) + + # Build compile command + compile_cmd = _build_compile_command( + ctx = ctx, + srcs = [src.path for src in ctx.files.srcs], + depinfo = depinfo, + out = d_obj, + extra_flags = ["-c"] + extra_flags) + + toolchain_files = ( + [ctx.file._d_compiler] + + ctx.files._d_stdlib + + ctx.files._d_stdlib_src + + ctx.files._d_runtime_import_src) + + compile_inputs = ctx.files.srcs + depinfo.d_srcs + toolchain_files + ctx.action(inputs = compile_inputs, + outputs = [d_obj], + mnemonic = "Dcompile", + command = compile_cmd, + use_default_shell_env = True, + progress_message = "Compiling D binary " + ctx.label.name) + + # Build link command + link_cmd = _build_link_command( + ctx = ctx, + objs = [d_obj.path], + depinfo = depinfo, + out = d_bin) + + link_inputs = ( + [d_obj] + + depinfo.libs + + depinfo.transitive_d_libs + + toolchain_files) + + ctx.action(inputs = link_inputs, + outputs = [d_bin], + mnemonic = "Dlink", + command = link_cmd, + use_default_shell_env = True, + progress_message = "Linking D binary " + ctx.label.name) + +def _d_binary_impl(ctx): + """Implementation of the d_binary rule.""" + return _d_binary_impl_common(ctx) + +def _d_test_impl(ctx): + """Implementation of the d_test rule.""" + return _d_binary_impl_common(ctx, extra_flags=["-unittest"]) + +def _d_source_library_impl(ctx): + """Implementation of the d_source_library rule.""" + transitive_d_srcs = set(order="compile") + transitive_d_libs = set() + transitive_imports = set() + transitive_linkopts = set() + transitive_versions = set() + for dep in ctx.attr.deps: + if hasattr(dep, "d_srcs"): + # Dependency is another d_source_library target. + transitive_d_srcs += dep.d_srcs + transitive_imports += dep.imports + transitive_linkopts += dep.linkopts + transitive_versions += dep.versions + + elif hasattr(dep, "cc"): + # Dependency is a cc_library target. + native_libs = A_FILETYPE.filter(dep.cc.libs) + transitive_d_libs += native_libs + transitive_linkopts += ["-l%s" % dep.label.name] + + else: + fail("d_source_library can only depend on other " + + "d_source_library or cc_library targets.", "deps") + + return struct( + d_srcs = ctx.files.srcs + list(transitive_d_srcs), + transitive_d_libs = transitive_d_libs, + imports = ctx.attr.imports + list(transitive_imports), + linkopts = ctx.attr.linkopts + list(transitive_linkopts), + versions = ctx.attr.versions + list(transitive_versions)) + +# TODO(dzc): Use ddox for generating HTML documentation. +def _d_docs_impl(ctx): + """Implementation for the d_docs rule + + This rule runs the following steps to generate an archive containing + HTML documentation generated from doc comments in D source code: + 1. Run the D compiler with the -D flags to generate HTML code + documentation. + 2. Create a ZIP archive containing the HTML documentation. + """ + d_docs_zip = ctx.outputs.d_docs + docs_dir = d_docs_zip.dirname + "/_d_docs" + objs_dir = d_docs_zip.dirname + "/_d_objs" + + # Build D docs command + toolchain = _d_toolchain(ctx) + doc_cmd = ( + [ + "set -e;", + "rm -rf %s; mkdir %s;" % (docs_dir, docs_dir), + "rm -rf %s; mkdir %s;" % (objs_dir, objs_dir), + toolchain.d_compiler_path, + "-c", + "-D", + "-Dd%s" % docs_dir, + "-od%s" % objs_dir, + "-I.", + ] + + ["-I%s/%s" % (ctx.label.package, im) for im in ctx.attr.imports] + + toolchain.import_flags + + [src.path for src in ctx.files.srcs]) + + # HTML documentation generated by the D compiler. + d_docs = [ + ctx.new_file(ctx.configuration.bin_dir, + "_d_docs/%s" % src.basename.replace(".d", ".html")) + for src in ctx.files.srcs] + + # Object files created by the D compiler during documentation generation. + d_objs = [ + ctx.new_file(ctx.configuration.bin_dir, + "_d_objs/%s" % src.basename.replace(".d", ".o")) + for src in ctx.files.srcs] + + toolchain_files = ( + [ctx.file._d_compiler] + + ctx.files._d_stdlib + + ctx.files._d_stdlib_src + + ctx.files._d_runtime_import_src) + ddoc_inputs = ctx.files.srcs + toolchain_files + ctx.action(inputs = ddoc_inputs, + outputs = d_docs + d_objs, + mnemonic = "Ddoc", + command = " ".join(doc_cmd), + use_default_shell_env = True, + progress_message = "Generating D docs for " + ctx.label.name) + + # Build zip command. + zip_cmd = [ + ZIP_PATH, + "-qj", + d_docs_zip.path, + ] + [doc.path for doc in d_docs] + + ctx.action(inputs = d_docs, + outputs = [d_docs_zip], + mnemonic = "Ddoczip", + command = " ".join(zip_cmd), + use_default_shell_env = True, + progress_message = "Creating D doc archive for " + ctx.label.name) + +_d_common_attrs = { + "srcs": attr.label_list(allow_files = D_FILETYPE), + "deps": attr.label_list(), + "imports": attr.string_list(), + "linkopts": attr.string_list(), + "versions": attr.string_list(), +} + +_d_compile_attrs = { + "_d_compiler": attr.label( + default = Label("//tools/build_defs/d:dmd"), + executable = True, + single_file = True), + "_d_stdlib": attr.label( + default = Label("//tools/build_defs/d:libphobos2")), + "_d_stdlib_src": attr.label( + default = Label("//tools/build_defs/d:phobos-src")), + "_d_runtime_import_src": attr.label( + default = Label("//tools/build_defs/d:druntime-import-src")), +} + +d_library = rule( + _d_library_impl, + attrs = _d_common_attrs + _d_compile_attrs, + outputs = { + "d_lib": "lib%{name}.a", + }, +) + +d_source_library = rule( + _d_source_library_impl, + attrs = _d_common_attrs, +) + +d_binary = rule( + _d_binary_impl, + executable = True, + attrs = _d_common_attrs + _d_compile_attrs, +) + +d_test = rule( + _d_test_impl, + executable = True, + attrs = _d_common_attrs + _d_compile_attrs, + test = True, +) + +_d_docs_attrs = { + "srcs": attr.label_list(allow_files = D_FILETYPE), + "imports": attr.string_list(), +} + +d_docs = rule( + _d_docs_impl, + attrs = _d_docs_attrs + _d_compile_attrs, + outputs = { + "d_docs": "%{name}-docs.zip", + }, +) diff --git a/tools/build_defs/d/dmd.BUILD b/tools/build_defs/d/dmd.BUILD new file mode 100644 index 0000000000..fc5df609b3 --- /dev/null +++ b/tools/build_defs/d/dmd.BUILD @@ -0,0 +1,43 @@ +package(default_visibility = ["//visibility:public"]) + +config_setting( + name = "darwin", + values = {"host_cpu": "darwin"}, +) + +config_setting( + name = "k8", + values = {"host_cpu": "k8"}, +) + +filegroup( + name = "dmd", + srcs = select({ + ":darwin": ["dmd2/osx/bin/dmd"], + ":k8": ["dmd2/linux/bin64/dmd"], + }), +) + +filegroup( + name = "libphobos2", + srcs = select({ + ":darwin": ["dmd2/osx/lib/libphobos2.a"], + ":k8": [ + "dmd2/linux/lib64/libphobos2.a", + "dmd2/linux/lib64/libphobos2.so", + ], + }), +) + +filegroup( + name = "phobos-src", + srcs = glob(["dmd2/src/phobos/**/*.*"]), +) + +filegroup( + name = "druntime-import-src", + srcs = glob([ + "dmd2/src/druntime/import/*.*", + "dmd2/src/druntime/import/**/*.*", + ]), +) |