diff --git a/closure/private/closure_common.bzl b/closure/private/closure_common.bzl new file mode 100644 index 0000000000..1e7a8c0047 --- /dev/null +++ b/closure/private/closure_common.bzl @@ -0,0 +1,19 @@ +# Copyright 2019 The Closure Rules Authors. 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("//closure/private/utils:soy.bzl", "soy") + +closure_common = struct( + soy = soy, +) diff --git a/closure/private/providers.bzl b/closure/private/providers.bzl new file mode 100644 index 0000000000..0d2c8cca4f --- /dev/null +++ b/closure/private/providers.bzl @@ -0,0 +1,30 @@ +# Copyright 2019 The Closure Rules Authors. 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. + +SoyInfo = provider( + doc = "Encapsulates information provided by soy_library.", + fields = { + "direct_headers": "A set of soy header files of the direct sources. " + + "If the library is a proxy library that has no " + + "sources, it contains the direct_headers from this " + + "library's direct deps.", + "direct_sources": "A set of soy sources from the 'srcs' attribute.", + "proto_descriptor_sets": "A set of FileDescriptorSet files of all " + + "dependent proto_library rules.", + "indirect_headers": "A set of soy header files from all dependent " + + "soy_library rules, but excluding direct headers.", + "indirect_sources": "A set of soy sources of all dependent" + + "soy_library rules, but excluding direct sources.", + }, +) diff --git a/closure/private/rules/BUILD b/closure/private/rules/BUILD new file mode 100644 index 0000000000..f6a6c08b82 --- /dev/null +++ b/closure/private/rules/BUILD @@ -0,0 +1,13 @@ +# Copyright 2019 The Closure Rules Authors. 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. diff --git a/closure/private/rules/soy_library.bzl b/closure/private/rules/soy_library.bzl new file mode 100644 index 0000000000..72818f873b --- /dev/null +++ b/closure/private/rules/soy_library.bzl @@ -0,0 +1,66 @@ +# Copyright 2019 The Closure Rules Authors. 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("//closure/private:closure_common.bzl", "closure_common") +load("//closure/private:providers.bzl", "SoyInfo") + +def _soy_library_impl(ctx): + soyh = ctx.actions.declare_file("{}.soyh.bin".format(ctx.label.name)) + return [ + DefaultInfo( + files = depset([soyh]), + ), + closure_common.soy.compile( + actions = ctx.actions, + closure_toolchain = struct( + soy = struct( + header_compiler = ctx.attr._compiler.files_to_run, + ), + ), + srcs = ctx.files.srcs, + output = soyh, + deps = [dep[SoyInfo] for dep in ctx.attr.deps], + proto_descriptor_sets = depset( + direct = [], + transitive = [dep[ProtoInfo].transitive_descriptor_sets for dep in ctx.attr.proto_deps], + ), + ), + ] + +soy_library = rule( + implementation = _soy_library_impl, + attrs = { + "srcs": attr.label_list( + mandatory = True, + allow_files = [".soy"], + ), + "deps": attr.label_list( + mandatory = False, + providers = [SoyInfo], + ), + "proto_deps": attr.label_list( + mandatory = False, + providers = [ProtoInfo], + ), + # TODO(yannic): Add this to closure_toolchain. + "_compiler": attr.label( + default = "@com_google_template_soy//:SoyHeaderCompiler", + executable = True, + cfg = "host", + ), + }, + provides = [ + SoyInfo, + ], +) diff --git a/closure/private/utils/BUILD b/closure/private/utils/BUILD new file mode 100644 index 0000000000..f6a6c08b82 --- /dev/null +++ b/closure/private/utils/BUILD @@ -0,0 +1,13 @@ +# Copyright 2019 The Closure Rules Authors. 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. diff --git a/closure/private/utils/soy.bzl b/closure/private/utils/soy.bzl new file mode 100644 index 0000000000..0bd45f10b9 --- /dev/null +++ b/closure/private/utils/soy.bzl @@ -0,0 +1,19 @@ +# Copyright 2019 The Closure Rules Authors. 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("//closure/private/utils/soy:compile.bzl", "compile") + +soy = struct( + compile = compile, +) diff --git a/closure/private/utils/soy/BUILD b/closure/private/utils/soy/BUILD new file mode 100644 index 0000000000..f6a6c08b82 --- /dev/null +++ b/closure/private/utils/soy/BUILD @@ -0,0 +1,13 @@ +# Copyright 2019 The Closure Rules Authors. 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. diff --git a/closure/private/utils/soy/compile.bzl b/closure/private/utils/soy/compile.bzl new file mode 100644 index 0000000000..0926af7047 --- /dev/null +++ b/closure/private/utils/soy/compile.bzl @@ -0,0 +1,95 @@ +# Copyright 2019 The Closure Rules Authors. 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("//closure/private:providers.bzl", "SoyInfo") + +def compile( + actions, + closure_toolchain, + srcs, + output, + deps = [], + proto_descriptor_sets = depset()): + """Compiles Soy source files into an intermediate build artifact and returns + a provider that represents the results of the compilation and can be added + to the set of providers emitted by this rule. + + Compiling the source files that represent a single library into an + intermediate build artifact that later compilations can use in preference to + parsing dependencies improves overall compiler performance by making builds + more cacheable. + + Args: + actions: Required. Instance of `ctx.actions`. + closure_toolchain: Required. The toolchain used to find the + SoyHeaderCompiler binary. + srcs: Required. A list of the Soy source files to be compiled. + output: Required. The output intermediate build artifact. + deps: Optional. A list of `ClosureTemplateInfo` of (direct) dependencies. + proto_descriptor_sets: + Returns: + An instance of `SoyInfo`. + """ + + # TODO(yannic): Add support for dependency pruning. + + # TODO(yannic): Add support for `--cssMetadataOutput`. + + args = actions.args() + + args.add("--output", output) + args.add_all(srcs, before_each = "--srcs") + + direct_deps = depset( + direct = [], + transitive = [dep.direct_headers for dep in deps], + ) + args.add_all(direct_deps, before_each = "--depHeaders") + + indirect_deps = depset( + direct = [], + transitive = [dep.indirect_headers for dep in deps], + ) + args.add_all(indirect_deps, before_each = "--indirectDepHeaders") + + proto_descriptors = depset( + direct = [], + transitive = [proto_descriptor_sets] + [dep.proto_descriptor_sets for dep in deps], + ) + args.add_all(proto_descriptors, before_each = "--protoFileDescriptors") + + actions.run( + executable = closure_toolchain.soy.header_compiler, + inputs = depset( + direct = srcs, + transitive = [direct_deps, indirect_deps, proto_descriptors], + ), + outputs = [output], + arguments = [args], + ) + + return SoyInfo( + direct_headers = depset([output]), + direct_sources = depset(srcs), + indirect_headers = depset( + direct = [], + transitive = [direct_deps, indirect_deps], + ), + indirect_sources = depset( + direct = [], + transitive = [dep.indirect_sources for dep in deps] + + [dep.direct_sources for dep in deps], + ), + proto_descriptor_sets = proto_descriptor_sets, + ) diff --git a/closure/repositories.bzl b/closure/repositories.bzl index 565e8675b6..ab58d72fb2 100644 --- a/closure/repositories.bzl +++ b/closure/repositories.bzl @@ -798,6 +798,7 @@ def com_google_template_soy(): ")\n") % (name, name) for name in ( "SoyParseInfoGenerator", + "SoyHeaderCompiler", "SoyToJbcSrcCompiler", "SoyToJsSrcCompiler", "SoyToPySrcCompiler", diff --git a/tests/soy/BUILD b/tests/soy/BUILD new file mode 100644 index 0000000000..ed121881ba --- /dev/null +++ b/tests/soy/BUILD @@ -0,0 +1,29 @@ +# Copyright 2019 The Closure Rules Authors. 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("//closure/private/rules:soy_library.bzl", "soy_library") + +soy_library( + name = "bold_soy", + srcs = [ + "bold.soy", + ], +) + +soy_library( + name = "greet_soy", + srcs = [ + "greet.soy", + ], +) diff --git a/tests/soy/bold.soy b/tests/soy/bold.soy new file mode 100644 index 0000000000..5a46a453f3 --- /dev/null +++ b/tests/soy/bold.soy @@ -0,0 +1,26 @@ +// Copyright 2019 The Closure Rules Authors. 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. + +{namespace soy.io.bazel.rules.closure.tests} + +/** + * Prints |text| in bold. + */ +{template .bold} + {@param text : html} + + + {$text} + +{/template} diff --git a/tests/soy/greet.soy b/tests/soy/greet.soy new file mode 100644 index 0000000000..a315107d39 --- /dev/null +++ b/tests/soy/greet.soy @@ -0,0 +1,28 @@ +// Copyright 2019 The Closure Rules Authors. 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. + +{namespace soy.io.bazel.rules.closure.tests} + +/** + * Prints |text| in bold. + */ +{template .greet} + {@param person : string} + + {call .strong} + {param text kind="css"} + {$person} + {/param} + {/call} +{/template}