diff --git a/.travis.yml b/.travis.yml index 61a9eecb5d..e15a321e46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ os: - osx env: - - V=0.5.3 + - V=0.12.0 before_install: - | @@ -33,14 +33,19 @@ before_install: script: - | + excludes=() + if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then + # https://github.com/bazelbuild/rules_closure/issues/247 + excludes+=('//closure/testing/test:noto_fonts_render_as_expected') + fi + + targets=$(bazel query "... except set($excludes)") bazel \ --output_base=$HOME/.cache/bazel \ --batch \ --host_jvm_args=-Xmx500m \ --host_jvm_args=-Xms500m \ test \ - closure/... \ - javatests/... \ --worker_verbose \ --verbose_failures \ --test_output=errors \ @@ -48,7 +53,8 @@ script: --genrule_strategy=sandboxed \ --local_resources=400,2,1.0 \ --strategy=Javac=worker \ - --strategy=Closure=worker + --strategy=Closure=worker \ + $targets notifications: email: false diff --git a/README.md b/README.md index dc943033da..5dacc63566 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ JavaScript | Templating | Stylesheets | Miscellaneous --- | --- | --- | --- [closure_js_library] | [closure_js_template_library] | [closure_css_library] | [closure_js_proto_library] [closure_js_binary] | [closure_java_template_library] | [closure_css_binary] | [phantomjs_test] -[closure_js_deps] | [closure_py_template_library] | | +[closure_js_deps] | [closure_py_template_library] | | [closure_proto_library] \(Experimental\) [closure_js_test] | | | ## Overview @@ -950,6 +950,30 @@ This rule can be referenced as though it were the following: - `IMPORT_ES6` // import { member } from '' +## closure\_proto\_library + +```python +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_proto_library") +closure_proto_library(name, deps) +``` + +`closure_proto_library` generates JS code from `.proto` files. + +`deps` must point to [proto_library] rules. + +#### Rule Polymorphism + +This rule can be referenced as though it were the following: + +- [closure_js_library]: `srcs` will be the generated JS files, + and `deps` will contain necessary libraries. + +- **name:** ([Name]; required) A unique name for this rule. Convention states + that such rules be named `*_closure_proto`. + +- **deps:** (List of [labels]; required) The list of [proto_library] rules + to generate JS code for. + [Bazel]: http://bazel.io/ [Closure Compiler]: https://developers.google.com/closure/compiler/ [Closure Library]: https://developers.google.com/closure/library/ @@ -980,6 +1004,7 @@ This rule can be referenced as though it were the following: [closure_js_proto_library]: #closure_js_proto_library [closure_js_template_library]: #closure_js_template_library [closure_js_test]: #closure_js_test +[closure_proto_library]: #closure_proto_library [closure_py_template_library]: #closure_py_template_library [coffeescript]: http://coffeescript.org/ [compiler-issue]: https://github.com/google/closure-compiler/issues/new @@ -995,6 +1020,7 @@ This rule can be referenced as though it were the following: [output-wrapper-faq]: https://github.com/google/closure-compiler/wiki/FAQ#when-using-advanced-optimizations-closure-compiler-adds-new-variables-to-the-global-scope-how-do-i-make-sure-my-variables-dont-collide-with-other-scripts-on-the-page [phantomjs-bug]: https://github.com/ariya/phantomjs/issues/14028 [phantomjs_test]: #phantomjs_test +[proto_library]: https://docs.bazel.build/versions/master/be/protocol-buffer.html#proto_library [protobuf-generator]: https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/js/js_generator.h [protobuf-js]: https://github.com/google/protobuf/tree/master/js [repositories.bzl]: https://github.com/bazelbuild/rules_closure/tree/master/closure/repositories.bzl diff --git a/closure/compiler/closure_js_binary.bzl b/closure/compiler/closure_js_binary.bzl index 0521bd7e50..f2f8f61909 100644 --- a/closure/compiler/closure_js_binary.bzl +++ b/closure/compiler/closure_js_binary.bzl @@ -43,7 +43,8 @@ def _impl(ctx): ctx.attr.language, ", ".join(JS_LANGUAGES))) deps = unfurl(ctx.attr.deps, provider="closure_js_library") - js = collect_js(ctx, deps, css=ctx.attr.css) + js = collect_js(deps, ctx.file._closure_library_base, + ctx.file._closure_library_deps, css=ctx.attr.css) if not js.srcs: fail("There are no JS source files in the transitive closure") @@ -100,8 +101,11 @@ def _impl(ctx): # should be cut off. These are basically the same thing as C++ include dirs, # except unlike C++ there's no I/O operation penalty to using them since all # source paths that exist are being passed as flags. - js_module_roots = sort_roots(find_js_module_roots(ctx, [ctx.outputs.bin]) + - js.js_module_roots) + js_module_roots = sort_roots( + find_js_module_roots( + [ctx.outputs.bin], ctx.workspace_name, ctx.label, + getattr(ctx.attr, "includes", [])) + + js.js_module_roots) for root in js_module_roots: args.append("--js_module_root") args.append(root) @@ -199,7 +203,7 @@ def _impl(ctx): # Insert an edge into the build graph that produces the minified version of # all JavaScript sources in the transitive closure, sans dead code. - argfile = create_argfile(ctx, args) + argfile = create_argfile(ctx.actions, ctx.label.name, args) inputs.append(argfile) ctx.action( inputs=inputs, diff --git a/closure/compiler/closure_js_deps.bzl b/closure/compiler/closure_js_deps.bzl index 90c05b8174..2cdc4b6e2a 100644 --- a/closure/compiler/closure_js_deps.bzl +++ b/closure/compiler/closure_js_deps.bzl @@ -23,7 +23,7 @@ load("//closure/private:defs.bzl", def _impl(ctx): deps = unfurl(ctx.attr.deps, provider="closure_js_library") - js = collect_js(ctx, deps) + js = collect_js(deps) closure_root = _dirname(long_path(ctx, ctx.file._closure_library_base)) closure_rel = '/'.join(['..' for _ in range(len(closure_root.split('/')))]) outputs = [ctx.outputs.out] diff --git a/closure/compiler/closure_js_library.bzl b/closure/compiler/closure_js_library.bzl index becc678908..ec16bafa2d 100644 --- a/closure/compiler/closure_js_library.bzl +++ b/closure/compiler/closure_js_library.bzl @@ -33,28 +33,49 @@ load("//closure/private:defs.bzl", load("//closure/compiler:closure_js_aspect.bzl", "closure_js_aspect") -def _closure_js_library(ctx): - if not ctx.files.srcs and not ctx.files.externs and not ctx.attr.exports: - fail("Either 'srcs' or 'exports' must be specified") - if not ctx.files.srcs and ctx.attr.deps: - fail("'srcs' must be set when using 'deps', otherwise consider 'exports'") - if ctx.attr.language: - print("The closure_js_library 'language' attribute is now removed and " + - "is always set to " + JS_LANGUAGE_IN) +def _maybe_declare_file(actions, file, name): + if file: + return file + return actions.declare_file(name) - # Create a list of the sources defined by this specific rule. - srcs = ctx.files.srcs - if ctx.files.externs: - print("closure_js_library 'externs' is deprecated; just use 'srcs'") - srcs = ctx.files.externs + srcs +def closure_js_library_impl( + actions, label, workspace_name, + + srcs, deps, testonly, suppress, + closure_library_base, closure_library_deps, _ClosureWorker, + + includes=[], + exports=depset(), + internal_descriptors=depset(), + convention='CLOSURE', + no_closure_library=False, + internal_expect_failure=False, + + # These file definitions for our outputs are deprecated, + # and will be replaced with |actions.declare_file()| soon. + deprecated_info_file=None, + deprecated_stderr_file=None, + deprecated_ijs_file=None, + deprecated_typecheck_file=None): + # TODO(yannic): Figure out how to modify |find_js_module_roots| + # so that we won't need |workspace_name| anymore. + + # TODO(yannic): Always use |actions.declare_file()|. + info_file = _maybe_declare_file( + actions, deprecated_info_file, '%s.pbtxt' % label.name) + stderr_file = _maybe_declare_file( + actions, deprecated_stderr_file, '%s-stderr.txt' % label.name) + ijs_file = _maybe_declare_file( + actions, deprecated_ijs_file, '%s.i.js' % label.name) # Create a list of direct children of this rule. If any direct dependencies # have the exports attribute, those labels become direct dependencies here. - deps = unfurl(ctx.attr.deps, provider="closure_js_library") + deps = unfurl(deps, provider="closure_js_library") # Collect all the transitive stuff the child rules have propagated. Bazel has # a special nested set data structure that makes this efficient. - js = collect_js(ctx, deps, bool(srcs), ctx.attr.no_closure_library) + js = collect_js(deps, closure_library_base, closure_library_deps, + bool(srcs), no_closure_library) # If closure_js_library depends on closure_css_library, that means # goog.getCssName() is being used in srcs to reference CSS names in the @@ -77,11 +98,11 @@ def _closure_js_library(ctx): # used by the Closure Compiler when producing the final JS binary. args = [ "JsChecker", - "--label", str(ctx.label), - "--output", ctx.outputs.info.path, - "--output_errors", ctx.outputs.stderr.path, - "--output_ijs_file", ctx.outputs.ijs.path, - "--convention", ctx.attr.convention, + "--label", str(label), + "--output", info_file.path, + "--output_errors", stderr_file.path, + "--output_ijs_file", ijs_file.path, + "--convention", convention, ] # Because JsChecker is an edge in the build graph, we need to declare all of @@ -91,19 +112,19 @@ def _closure_js_library(ctx): # We want to test the failure conditions of this rule from within Bazel, # rather than from a meta-system like shell scripts. In order to do that, we # need a way to toggle the return status of the process. - if ctx.attr.internal_expect_failure: + if internal_expect_failure: args.append("--expect_failure") # JsChecker wants to know if this is a testonly rule so it can throw an error # if goog.setTestOnly() is used. - if ctx.attr.testonly: + if testonly: args.append("--testonly") # The suppress attribute is a Closure Rules feature that makes warnings and # errors go away. It's a list of strings containing DiagnosticGroup (coarse # grained) or DiagnosticType (fine grained) codes. These apply not only to # JsChecker, but also propagate up to closure_js_binary. - for s in ctx.attr.suppress: + for s in suppress: args.append("--suppress") args.append(s) @@ -124,7 +145,8 @@ def _closure_js_library(ctx): # module name is the same as the filename relative to the root of the # repository, ignoring the workspace name. The exception is when the includes # attribute is being used, which chops the path down even further. - js_module_roots = sort_roots(find_js_module_roots(ctx, srcs)) + js_module_roots = sort_roots( + find_js_module_roots(srcs, workspace_name, label, includes)) for root in js_module_roots: args.append("--js_module_root") args.append(root) @@ -154,27 +176,29 @@ def _closure_js_library(ctx): # The list of flags could potentially be very long. So we're going to write # them all to a file which gets loaded automatically by our BazelWorker # middleware. - argfile = create_argfile(ctx, args) + argfile = create_argfile(actions, label.name, args) inputs.append(argfile) # Add a JsChecker edge to the build graph. The command itself will only be # executed if something that requires its output is executed. - ctx.action( + actions.run( inputs=inputs, - outputs=[ctx.outputs.info, ctx.outputs.stderr, ctx.outputs.ijs], - executable=ctx.executable._ClosureWorker, + outputs=[info_file, stderr_file, ijs_file], + executable=_ClosureWorker, arguments=["@@" + argfile.path], mnemonic="Closure", execution_requirements={"supports-workers": "1"}, - progress_message=make_jschecker_progress_message(srcs, ctx.label)) + progress_message=make_jschecker_progress_message(srcs, label)) library_level_checks( - ctx=ctx, + actions=actions, + label=label, ijs_deps=js.ijs_files, srcs=srcs, - executable=ctx.executable._ClosureWorker, - output=ctx.outputs.typecheck, - suppress=ctx.attr.suppress, + executable=_ClosureWorker, + output=_maybe_declare_file( + actions, deprecated_typecheck_file, '%s_typecheck' % label.name), + suppress=suppress, ) # We now export providers to any parent Target. This is considered a public @@ -199,7 +223,7 @@ def _closure_js_library(ctx): # the exports attribute does not exist. The exports feature can be abused # by users to circumvent strict deps checking and therefore should be # used with caution. - exports=unfurl(ctx.attr.exports), + exports=unfurl(exports), # All of the subproviders below are considered optional and MUST be # accessed using getattr(x, y, default). See collect_js() in defs.bzl. closure_js_library=struct( @@ -208,13 +232,13 @@ def _closure_js_library(ctx): # as well as information extracted from inside the srcs files, e.g. # goog.provide'd namespaces. It is used for strict dependency # checking, a.k.a. layering checks. - info=ctx.outputs.info, + info=info_file, # NestedSet of all info files in the transitive closure. This # is used by JsCompiler to apply error suppression on a file-by-file # basis. - infos=js.infos + [ctx.outputs.info], - ijs = ctx.outputs.ijs, - ijs_files = js.ijs_files + [ctx.outputs.ijs], + infos=js.infos + [info_file], + ijs = ijs_file, + ijs_files = js.ijs_files + [ijs_file], # NestedSet of all JavaScript source File artifacts in the # transitive closure. These files MUST be JavaScript. srcs=js.srcs + srcs, @@ -236,27 +260,72 @@ def _closure_js_library(ctx): # closure. It is used so Closure Templates can have information about # the structure of protobufs so they can be easily rendered in .soy # files with type safety. See closure_js_template_library.bzl. - descriptors=js.descriptors + ctx.files.internal_descriptors, + descriptors=js.descriptors + internal_descriptors, # NestedSet