Initial checkin of sass_binary support for bazel

RELNOTES[NEW]: Support for build with libsass.

--
Change-Id: I2a24212d9466e2e2a8b653027f1cc9579b4d4221
Reviewed-on: https://bazel-review.googlesource.com/#/c/1990/
MOS_MIGRATED_REVID=103740130
diff --git a/WORKSPACE b/WORKSPACE
index 897060c..6f2df12 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -102,3 +102,17 @@
     tag = "v0.8.0",
     build_file = "tools/build_defs/jsonnet/jsonnet.BUILD",
 )
+
+new_http_archive(
+    name = "libsass",
+    url = "https://github.com/sass/libsass/archive/3.3.0-beta1.tar.gz",
+    sha256 = "6a4da39cc0b585f7a6ee660dc522916f0f417c890c3c0ac7ebbf6a85a16d220f",
+    build_file = "tools/build_defs/sass/libsass.BUILD",
+)
+
+new_http_archive(
+    name = "sassc",
+    url = "https://github.com/sass/sassc/archive/3.3.0-beta1.tar.gz",
+    sha256 = "87494218eea2441a7a24b40f227330877dbba75c5fa9014ac6188711baed53f6",
+    build_file = "tools/build_defs/sass/sassc.BUILD",
+)
diff --git a/examples/sass/hello_world/BUILD b/examples/sass/hello_world/BUILD
new file mode 100644
index 0000000..7109872
--- /dev/null
+++ b/examples/sass/hello_world/BUILD
@@ -0,0 +1,13 @@
+package(default_visibility = ["//visibility:public"])
+
+load("/tools/build_defs/sass/sass", "sass_binary")
+
+# Import our shared colors and fonts so we can generate a CSS file.
+sass_binary(
+    name = "hello_world",
+    src = "main.scss",
+    deps = [
+        "//examples/sass/shared:colors",
+        "//examples/sass/shared:fonts",
+    ],
+)
diff --git a/examples/sass/hello_world/main.scss b/examples/sass/hello_world/main.scss
new file mode 100644
index 0000000..3b560b7
--- /dev/null
+++ b/examples/sass/hello_world/main.scss
@@ -0,0 +1,13 @@
+@import 'examples/sass/shared/fonts';
+@import 'examples/sass/shared/colors';
+
+html {
+  body {
+    font-family: $default-font-stack;
+
+    h1 {
+      color: $example-red;
+      font-family: $modern-font-stack;
+    }
+  }
+}
diff --git a/examples/sass/shared/BUILD b/examples/sass/shared/BUILD
new file mode 100644
index 0000000..c9bdc2f
--- /dev/null
+++ b/examples/sass/shared/BUILD
@@ -0,0 +1,15 @@
+package(default_visibility = ["//visibility:public"])
+
+load("/tools/build_defs/sass/sass", "sass_library")
+
+# make a :colors target that any sass_binary rules can depend on.
+sass_library(
+    name = "colors",
+    srcs = ["_colors.scss"],
+)
+
+# make a :fonts target that any sass_binary rules can depend on.
+sass_library(
+    name = "fonts",
+    srcs = ["_fonts.scss"],
+)
diff --git a/examples/sass/shared/_colors.scss b/examples/sass/shared/_colors.scss
new file mode 100644
index 0000000..cd1430b
--- /dev/null
+++ b/examples/sass/shared/_colors.scss
@@ -0,0 +1,5 @@
+// Colors that all Sass code can share.
+
+$example-blue: #00f;
+$example-red: #f00;
+$example-green: #008000;
diff --git a/examples/sass/shared/_fonts.scss b/examples/sass/shared/_fonts.scss
new file mode 100644
index 0000000..6fea9ae
--- /dev/null
+++ b/examples/sass/shared/_fonts.scss
@@ -0,0 +1,5 @@
+// Fonts that all Sass code can share.
+
+$default-font-stack: Cambria, "Hoefler Text", Utopia, "Liberation Serif", "Nimbus Roman No9 L Regular", Times, "Times New Roman", serif;
+
+$modern-font-stack: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
diff --git a/tools/BUILD b/tools/BUILD
index f370eb3..62faa74 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -25,6 +25,7 @@
         "//tools/build_defs/d:srcs",
         "//tools/build_defs/jsonnet:srcs",
         "//tools/build_defs/docker:srcs",
+        "//tools/build_defs/sass:srcs",
         "//tools/build_rules/appengine:srcs",
         "//tools/build_rules/closure:srcs",
         "//tools/build_rules/rust:srcs",
diff --git a/tools/build_defs/sass/BUILD b/tools/build_defs/sass/BUILD
new file mode 100644
index 0000000..062ab56
--- /dev/null
+++ b/tools/build_defs/sass/BUILD
@@ -0,0 +1,12 @@
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["//tools:__pkg__"],
+)
+
+filegroup(
+    name = "sassc",
+    srcs = ["@sassc//:sassc"],
+)
diff --git a/tools/build_defs/sass/README.md b/tools/build_defs/sass/README.md
new file mode 100644
index 0000000..42b39a4
--- /dev/null
+++ b/tools/build_defs/sass/README.md
@@ -0,0 +1,208 @@
+# Sass Rules for Bazel
+
+## Overview
+These build rules are used for building [Sass][sass] projects with Bazel.
+
+* [Setup](#setup)
+* [Basic Example](#basic-example)
+* [Reference](#reference)
+  * [`sass_binary`](#reference-sass_binary)
+  * [`sass_library`](#reference-sass_library)
+
+[sass]: http://www.sass-lang.com
+
+<a name="setup"></a>
+## Setup
+To  use the Sass rules, simply copy the contents of `sass.WORKSPACE` to your own top level `WORKSPACE` file.
+
+<a name="basic-example"></a>
+## Basic Example
+Suppose you have the following directory structure for a simple Sass project:
+```
+[workspace]/
+    WORKSPACE
+    hello_world/
+        BUILD
+        main.scss
+    shared/
+        BUILD
+        _fonts.scss
+        _colors.scss
+```
+`shared/_fonts.scss`
+```scss
+$default-font-stack: Cambria, "Hoefler Text", Utopia, "Liberation Serif", "Nimbus Roman No9 L Regular", Times, "Times New Roman", ser
+if;
+$modern-font-stack: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liber
+ation Serif", Georgia, serif;
+```
+`shared/_colors.scss`
+```scss
+$example-blue: #0000ff;
+$example-red: #ff0000;
+```
+
+`shared/BUILD`
+```python
+package(default_visibility = ["//visibility:public"])
+
+load("/tools/build_defs/sass/sass", "sass_library")
+
+sass_library(
+    name = "colors",
+    srcs = ["_colors.scss"],
+)
+
+sass_library(
+    name = "fonts",
+    srcs = ["_fonts.scss"],
+)
+```
+`hello_world/main.scss`:
+```scss
+@import "examples/sass/shared/fonts";
+@import "examples/sass/shared/colors";
+
+html {
+  body {
+    font-family: $default-font-stack;
+    h1 {
+      font-family: $modern-font-stack;
+      colors: $example-red;
+    }
+  }
+}
+```
+`hello_world/BUILD:`
+```python
+package(default_visibility = ["//visibility:public"])
+
+load("/tools/build_defs/sass/sass", "sass_binary")
+
+sass_binary(
+    name = "hello_world",
+    src = "main.scss",
+    deps = [
+         "//shared:colors",
+         "//shared:fonts",
+    ],
+)
+```
+Build the binary:
+```
+$ bazel build //hello_world
+INFO: Found 1 target...
+Target //hello_world:hello_world up-to-date:
+  bazel-bin/hello_world/hello_world.css
+  bazel-bin/hello_world/hello_world.css.map
+INFO: Elapsed time: 1.911s, Critical Path: 0.01s
+```
+
+<a name="reference"></a>
+## Build Rule Reference
+
+<a name="reference-sass_binary"></a>
+### `sass_binary`
+`sass_binary(name, src, deps=[], output_style="compressed")`
+Used to generate a CSS artifact from a given `src` sass file.
+
+#### Implicit output targets
+  - **name**.css: The generated CSS artifact containing all the styles.
+  - **name**.css.map: a [source map](http://thesassway.com/intermediate/using-source-maps-with-sass) that can be used to optionally debug the generated CSS in a browser.
+
+<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 also be used as the name of the generated CSS and source map file of
+          this rule.
+        </p>
+      </td>
+    </tr>
+    <tr>
+      <td><code>src</code></td>
+      <td>
+        <code>Main source file, required</code>
+        <p>The primary Sass source file that will be compiled to CSS.</p>
+        <p>
+        <code>sass_binary</code> assumes a 1:1 mapping of src to output CSS file (and source map).
+        </p>
+      </td>
+    </tr>
+    <tr>
+      <td><code>deps</code></td>
+      <td>
+        <code>list of labels, optional</code>
+        <p></p>
+        <p>
+        Each target should be defined using a <code>filegroup</code> rule and should only include "_" prefixed files that are referenced via <code>@import</code> in the target's source file.
+        </p>
+      </td>
+    </tr>
+    <tr>
+      <td><code>output_style</code></td>
+      <td>
+        <code>string; optional</code>
+        <p>Defaults to <code>compressed</code>.</p>
+        <p>
+        Can be set to <a href="http://sass-lang.com/documentation/file.SASS_REFERENCE.html#output_style">one of the following</a> output styles defined by <code>sassc</code>.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
+
+<a name="reference-sass_library"></a>
+### `sass_library`
+`sass_library(name, src, deps=[])`
+Used to reference sass a collection of sass files that a [`sass_binary`](#reference-sass_binary) may depend on (via <code>@import</code> statements), but should not result in any output targets.
+
+<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>
+        </p>
+      </td>
+    </tr>
+    <tr>
+      <td><code>srcs</code></td>
+      <td>
+        <code>a list of labels, required</code>
+        <p></p>
+        <p>
+        <code>sass_library</code> all files should start with an underscore, eg: _colors.scss.
+        </p>
+      </td>
+    </tr>
+    <tr>
+      <td><code>deps</code></td>
+      <td>
+        <code>list of labels, optional</code>
+        <p></p>
+        <p>
+          This could be any other <code>sass_library</code> targets that this target may include.
+        </p>
+      </td>
+    </tr>
+  </tbody>
+</table>
diff --git a/tools/build_defs/sass/libsass.BUILD b/tools/build_defs/sass/libsass.BUILD
new file mode 100644
index 0000000..d2bd5f6
--- /dev/null
+++ b/tools/build_defs/sass/libsass.BUILD
@@ -0,0 +1,16 @@
+package(default_visibility = ["@sassc//:__pkg__"])
+
+BASE_DIR = "libsass-3.3.0-beta1/"
+
+filegroup(
+    name = "srcs",
+    srcs = glob([
+         BASE_DIR + "src/**/*.h*",
+         BASE_DIR + "src/**/*.c*",
+    ]),
+)
+
+cc_library(
+    name = "headers",
+    includes = [BASE_DIR + "include"],
+)
\ No newline at end of file
diff --git a/tools/build_defs/sass/sass.WORKSPACE b/tools/build_defs/sass/sass.WORKSPACE
new file mode 100644
index 0000000..95cf332
--- /dev/null
+++ b/tools/build_defs/sass/sass.WORKSPACE
@@ -0,0 +1,13 @@
+new_http_archive(
+    name = "libsass",
+    url = "https://github.com/sass/libsass/archive/3.3.0-beta1.tar.gz",
+    sha256 = "6a4da39cc0b585f7a6ee660dc522916f0f417c890c3c0ac7ebbf6a85a16d220f",
+    build_file = "tools/build_defs/sass/libsass.BUILD",
+)
+
+new_http_archive(
+    name = "sassc",
+    url = "https://github.com/sass/sassc/archive/3.3.0-beta1.tar.gz",
+    sha256 = "87494218eea2441a7a24b40f227330877dbba75c5fa9014ac6188711baed53f6",
+    build_file = "tools/build_defs/sass/sassc.BUILD",
+)
diff --git a/tools/build_defs/sass/sass.bzl b/tools/build_defs/sass/sass.bzl
new file mode 100644
index 0000000..366a71e
--- /dev/null
+++ b/tools/build_defs/sass/sass.bzl
@@ -0,0 +1,89 @@
+# 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.
+
+SASS_FILETYPES = FileType([".sass", ".scss"])
+
+def collect_transitive_sources(ctx):
+    source_files = set(order="compile")
+    for dep in ctx.attr.deps:
+        source_files += dep.transitive_sass_files
+    return source_files
+
+def _sass_library_impl(ctx):
+    transitive_sources = collect_transitive_sources(ctx)
+    transitive_sources += SASS_FILETYPES.filter(ctx.files.srcs)
+    return struct(
+        files = set(),
+        transitive_sass_files = transitive_sources)
+
+def _sass_binary_impl(ctx):
+    # Reference the sass compiler and define the default options
+    # that sass_binary uses.
+    sassc = ctx.file._sassc
+    options = [
+        "--style={0}".format(ctx.attr.output_style),
+        "--sourcemap",
+    ]
+
+    # Load up all the transitive sources as dependent includes.
+    transitive_sources = collect_transitive_sources(ctx)
+    for src in transitive_sources:
+        options += ["-I={0}".format(src)]
+
+    ctx.action(
+        inputs = [sassc, ctx.file.src] + list(transitive_sources),
+        executable = sassc,
+        arguments = options + [ctx.file.src.path, ctx.outputs.css_file.path],
+        mnemonic = "SassCompiler",
+        outputs = [ctx.outputs.css_file, ctx.outputs.css_map_file],
+    )
+
+sass_deps_attr = attr.label_list(
+    providers = ["transitive_sass_files"],
+    allow_files = False,
+)
+
+sass_library = rule(
+    implementation = _sass_library_impl,
+    attrs = {
+        "srcs": attr.label_list(
+            allow_files = SASS_FILETYPES,
+            non_empty = True,
+            mandatory = True,
+        ),
+        "deps": sass_deps_attr,
+    },
+)
+
+sass_binary = rule(
+    implementation = _sass_binary_impl,
+    attrs = {
+        "src": attr.label(
+            allow_files = SASS_FILETYPES,
+            mandatory = True,
+            single_file = True,
+        ),
+        "output_style": attr.string(default = "compressed"),
+        "deps": sass_deps_attr,
+        "_sassc": attr.label(
+            default = Label("//tools/build_defs/sass:sassc"),
+            executable = True,
+            single_file = True,
+        ),
+    },
+    outputs = {
+        "css_file": "%{name}.css",
+        "css_map_file": "%{name}.css.map",
+    },
+)
diff --git a/tools/build_defs/sass/sassc.BUILD b/tools/build_defs/sass/sassc.BUILD
new file mode 100644
index 0000000..6ec0432d
--- /dev/null
+++ b/tools/build_defs/sass/sassc.BUILD
@@ -0,0 +1,13 @@
+package(default_visibility = ["//tools/build_defs/sass:__pkg__"])
+
+BASE_DIR = "sassc-3.3.0-beta1/"
+
+cc_binary(
+    name = "sassc",
+    srcs = [
+        "@libsass//:srcs",
+        BASE_DIR + "sassc.c",
+    ],
+    linkopts = ["-ldl", "-lm"],
+    deps = ["@libsass//:headers"],
+)
diff --git a/tools/build_defs/sass/test/BUILD b/tools/build_defs/sass/test/BUILD
new file mode 100644
index 0000000..061761f
--- /dev/null
+++ b/tools/build_defs/sass/test/BUILD
@@ -0,0 +1,3 @@
+load("sass_rule_test", "sass_rule_test")
+
+sass_rule_test("//examples/sass")
diff --git a/tools/build_defs/sass/test/sass_rule_test.bzl b/tools/build_defs/sass/test/sass_rule_test.bzl
new file mode 100644
index 0000000..d06394d
--- /dev/null
+++ b/tools/build_defs/sass/test/sass_rule_test.bzl
@@ -0,0 +1,45 @@
+# 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.
+
+load(
+    "/tools/build_defs/sass/sass",
+    "sass_binary",
+)
+
+load(
+    "/tools/build_rules/test_rules",
+    "success_target",
+    "successful_test",
+    "failure_target",
+    "failed_test",
+    "assert_",
+    "strip_prefix",
+    "expectation_description",
+    "check_results",
+    "load_results",
+    "analysis_results",
+    "rule_test",
+    "file_test",
+)
+
+def _sass_binary_test(package):
+    rule_test(
+        name = "hello_world_rule_test",
+        generates = ["hello_world.css", "hello_world.css.map"],
+        rule = package + "/hello_world:hello_world",
+    )
+
+def sass_rule_test(package):
+    """Issue simple tests on sass rules."""
+    _sass_binary_test(package)