summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:18:29 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:18:29 +0000
commitabbb9641304c2c6a282681626d586dea05573ce5 (patch)
tree3f1f16ec6e8cf77a12f3fec5a2f5812422d63fce
parent0414882bff672c8dbd39f9517fd5a144d654f6a2 (diff)
parent4969b3aeeda40ddb1c4ab4909d4bc5b9fe4a0f16 (diff)
downloadwayland-protocols-android14-mainline-uwb-release.tar.gz
Change-Id: I6f6a6a333829633c231ba38b8fce076d9e5a3928
-rw-r--r--Android.bp49
-rw-r--r--bazel/BUILD.bazel20
-rw-r--r--bazel/gensrcs.bzl192
-rw-r--r--bazel/gensrcs_test.bzl244
-rw-r--r--chromium.org/unstable/aura-shell/aura-shell.xml663
-rw-r--r--go.mod17
-rw-r--r--locations.go112
-rw-r--r--wayland_protocol_codegen.go1144
-rw-r--r--wayland_protocol_codegen_test.go488
9 files changed, 2671 insertions, 258 deletions
diff --git a/Android.bp b/Android.bp
index ea50526..4a84d24 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,8 +54,10 @@ bootstrap_go_package {
"soong-genrule",
],
srcs: [
+ "locations.go",
"wayland_protocol_codegen.go",
],
+ testSrcs: ["wayland_protocol_codegen_test.go"],
pluginFor: ["soong_build"],
}
@@ -120,31 +122,60 @@ filegroup {
],
}
+// Common settings for processing these protocols
+wayland_protocol_codegen_defaults {
+ name: "wayland_extension_protocol_defaults",
+
+ // All the protocol files to generate code for.
+ srcs: [":wayland_extension_protocols"],
+
+ // Use "wayland_scanner" out of external/wayland.
+ tools: ["wayland_scanner"],
+}
+
// Generate protocol source files used by both client and server
wayland_protocol_codegen {
name: "wayland_extension_protocol_sources",
+ defaults: ["wayland_extension_protocol_defaults"],
+
+ // Specifies the command to run to generate each output file for each input file.
cmd: "$(location wayland_scanner) private-code < $(in) > $(out)",
- suffix: ".c",
- srcs: [":wayland_extension_protocols"],
- tools: ["wayland_scanner"],
+
+ // There is a 1:1 correspondence between each generated output file and each source file.
+ // The output filename should use the base filename of the protocol file (no extension), and
+ // add a ".c" suffix. For example, "freedesktop.org/stable/xdg-shell/xdg-shell.xml" generates
+ // "xdg-shell.c".
+ output: "$(in).c",
}
// Generate protocol header files used by the client
wayland_protocol_codegen {
name: "wayland_extension_client_protocol_headers",
+ defaults: ["wayland_extension_protocol_defaults"],
+
+ // Specifies the command to run to generate each output file for each input file.
cmd: "$(location wayland_scanner) client-header < $(in) > $(out)",
- suffix: "-client-protocol.h",
- srcs: [":wayland_extension_protocols"],
- tools: ["wayland_scanner"],
+
+ // There is a 1:1 correspondence between each generated output file and each source file.
+ // The output filename should use the base filename of the protocol file (no extension), and
+ // add a "-client-protocol.h" suffix. For example,
+ // "freedesktop.org/stable/xdg-shell/xdg-shell.xml" generates "xdg-shell-client-protocol.h".
+ output: "$(in)-client-protocol.h",
}
// Generate protocol header files used by the server
wayland_protocol_codegen {
name: "wayland_extension_server_protocol_headers",
+ defaults: ["wayland_extension_protocol_defaults"],
+
+ // Specifies the command to run to generate each output file for each input file.
cmd: "$(location wayland_scanner) server-header < $(in) > $(out)",
- suffix: "-server-protocol.h",
- srcs: [":wayland_extension_protocols"],
- tools: ["wayland_scanner"],
+
+ // There is a 1:1 correspondence between each generated output file and each source file.
+ // The output filename should use the base filename of the protocol file (no extension), and
+ // add a "-server-protocol.h" suffix. For example,
+ // "freedesktop.org/stable/xdg-shell/xdg-shell.xml" generates "xdg-shell-server-protocol.h".
+ output: "$(in)-server-protocol.h",
}
// Generate a library with the protocol files, configured to export the client
diff --git a/bazel/BUILD.bazel b/bazel/BUILD.bazel
new file mode 100644
index 0000000..1b7969d
--- /dev/null
+++ b/bazel/BUILD.bazel
@@ -0,0 +1,20 @@
+"""
+Copyright 2023 The Android Open Source Project
+
+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(":gensrcs.bzl", "gensrcs")
+load(":gensrcs_test.bzl", "gensrcs_test_suite")
+
+gensrcs_test_suite(name = "gensrcs_tests")
diff --git a/bazel/gensrcs.bzl b/bazel/gensrcs.bzl
new file mode 100644
index 0000000..730922e
--- /dev/null
+++ b/bazel/gensrcs.bzl
@@ -0,0 +1,192 @@
+"""
+Copyright 2023 The Android Open Source Project
+
+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("@bazel_skylib//lib:paths.bzl", "paths")
+
+def _remove_extension(p):
+ """Removes the extension from the path `p`.
+
+ Leading periods on the basename are ignored, so
+ `_strip_extension(".bashrc")` returns `".bashrc"`.
+
+ Args:
+ p: The path to modify.
+
+ Returns:
+ The path with the extension removed.
+ """
+
+ # paths.split_extension() does all of the work.
+ return paths.split_extension(p)[0]
+
+# Expands an output path template for the given context and input file.
+def _expand_out_path_template(ctx, src_file):
+ # Each src_file has a short_path that looks like:
+ #
+ # <source-package-path>/<source-package-rel-path>/<base.ext>
+ #
+ # For some expansions, we want to strip of the source package path, and
+ # only use the rest for output file path in the expansion.
+ #
+ # There is also an option during expansion to just use the <base> or
+ # <base.ext> portion of the input path.
+ #
+ # This means there can be collisions if input files are taken from
+ # `filegroups` defined in different packages, if they happen to use
+ # the same relative path for that package.
+ #
+ # These conflcits are left to the user of this `gensrcs` rule to resolve for
+ # their use case, as at least Bazel will raise an error when they occur.
+
+ # Try to obtain the path to the package that defines `src_file`. It may or
+ # may not be defined by the same package this `gensrcs` rule is in.
+ # The `owner` label `package` attribute value is the closest we can get
+ # to that path, but it may not be correct in all cases, such as if the
+ # source path is itself for a generated file, where the generated file is
+ # under a build artifact path, and not in the source tree.
+ pkg_dirname = paths.dirname(src_file.short_path)
+ rel_dirname = pkg_dirname
+ if (src_file.is_source and src_file.owner and
+ src_file.short_path.startswith(src_file.owner.package + "/")):
+ rel_dirname = paths.dirname(paths.relativize(
+ src_file.short_path,
+ src_file.owner.package,
+ ))
+
+ base_inc_ext = src_file.basename
+ base_exc_ext = _remove_extension(base_inc_ext)
+ rel_path_base_inc_ext = paths.join(rel_dirname, base_inc_ext)
+ rel_path_base_exc_ext = paths.join(rel_dirname, base_exc_ext)
+ pkg_path_base_inc_ext = paths.join(pkg_dirname, base_inc_ext)
+ pkg_path_base_exc_ext = paths.join(pkg_dirname, base_exc_ext)
+
+ # Expand the output template
+ return ctx.attr.output \
+ .replace("$(SRC:PKG/PATH/BASE.EXT)", pkg_path_base_inc_ext) \
+ .replace("$(SRC:PKG/PATH/BASE)", pkg_path_base_exc_ext) \
+ .replace("$(SRC:PATH/BASE.EXT)", rel_path_base_inc_ext) \
+ .replace("$(SRC:PATH/BASE)", rel_path_base_exc_ext) \
+ .replace("$(SRC:BASE.EXT)", base_inc_ext) \
+ .replace("$(SRC:BASE)", base_exc_ext) \
+ .replace("$(SRC)", rel_path_base_inc_ext)
+
+# A rule to generate files based on provided srcs and tools.
+def _gensrcs_impl(ctx):
+ # The next two assignments can be created by using ctx.resolve_command.
+ # TODO: Switch to using ctx.resolve_command when it is out of
+ # experimental.
+ command = ctx.expand_location(ctx.attr.cmd)
+ tools = [
+ tool[DefaultInfo].files_to_run
+ for tool in ctx.attr.tools
+ ]
+
+ # Expand the shell command by substituting $(RULEDIR), which will be
+ # the same for any source file.
+ command = command.replace(
+ "$(RULEDIR)",
+ paths.join(
+ ctx.var["GENDIR"],
+ ctx.label.package,
+ ),
+ )
+
+ src_files = ctx.files.srcs
+ out_files = []
+ for src_file in src_files:
+ # Expand the output path template for this source file.
+ out_file_path = _expand_out_path_template(ctx, src_file)
+
+ # out_file is at output_file_path that is relative to
+ # <GENDIR>/<gensrc-package-dir>, hence, the fullpath to out_file is
+ # <GENDIR>/<gensrc-package-dir>/<out_file_path>
+ out_file = ctx.actions.declare_file(out_file_path)
+
+ # Expand the command template for this source file by performing
+ # substitution for $(SRC) and $(OUT).
+ shell_command = command \
+ .replace("$(SRC)", src_file.path) \
+ .replace("$(OUT)", out_file.path)
+
+ # Run the shell comand to generate the output from the input.
+ ctx.actions.run_shell(
+ tools = tools,
+ outputs = [out_file],
+ inputs = [src_file],
+ command = shell_command,
+ progress_message = "Generating %s from %s" % (
+ out_file.path,
+ src_file.path,
+ ),
+ )
+ out_files.append(out_file)
+
+ return [DefaultInfo(
+ files = depset(out_files),
+ )]
+
+gensrcs = rule(
+ implementation = _gensrcs_impl,
+ doc = "This rule generates files, where each of the `srcs` files is " +
+ "passed to `cmd` to generate an `output`.",
+ attrs = {
+ "srcs": attr.label_list(
+ # We allow srcs to directly reference files, instead of only
+ # allowing references to other rules such as filegroups.
+ allow_files = True,
+ # An empty srcs is likely an mistake.
+ allow_empty = False,
+ # srcs must be explicitly specified.
+ mandatory = True,
+ doc = "A list of source files to process",
+ ),
+ "output": attr.string(
+ # By default we generate an output filename based on the input
+ # filename (no extension).
+ default = "$(SRC)",
+ doc = "An output path template which is expanded to generate " +
+ "the output path given an source file. Portions " +
+ "of the source filename can be included in the expansion " +
+ "with one of: $(SRC:BASE), $(SRC:BASE.EXT), " +
+ "$(SRC:PATH/BASE), $(SRC:PATH/BASE), " +
+ "$(SRC:PKG/PATH/BASE), or $(SRC:PKG/PATH/BASE.ext). For " +
+ "example, specifying `output = " +
+ "\"includes/lib/$(SRC:BASE).h\"` would mean the input " +
+ "file `some_path/to/a.txt` generates `includes/lib/a.h`, " +
+ "while instead specifying `output = " +
+ "\"includes/lib/$(SRC:PATH/BASE.EXT).h\"` would expand " +
+ "to `includes/lib/some_path/to/a.txt.h`.",
+ ),
+ "cmd": attr.string(
+ # cmd must be explicitly specified.
+ mandatory = True,
+ doc = "The command to run. Subject to $(location) expansion. " +
+ "$(SRC) represents each input file provided in `srcs` " +
+ "while $(OUT) reprensents corresponding output file " +
+ "generated by the rule. $(RULEDIR) is intepreted the same " +
+ "as it is in genrule.",
+ ),
+ "tools": attr.label_list(
+ # We allow tools to directly reference files, as there could be a local script
+ # used as a tool.
+ allow_files = True,
+ doc = "A list of tool dependencies for this rule. " +
+ "The path of an individual `tools` target //x:y can be " +
+ "obtained using `$(location //x:y)`",
+ cfg = "exec",
+ ),
+ },
+)
diff --git a/bazel/gensrcs_test.bzl b/bazel/gensrcs_test.bzl
new file mode 100644
index 0000000..7c85c61
--- /dev/null
+++ b/bazel/gensrcs_test.bzl
@@ -0,0 +1,244 @@
+"""
+Copyright 2023 The Android Open Source Project
+
+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("@bazel_skylib//lib:new_sets.bzl", "sets")
+load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load(":gensrcs.bzl", "gensrcs")
+
+SRCS = [
+ "texts/src1.txt",
+ "texts/src2.txt",
+ "src3.txt",
+]
+
+# ==== Check the output paths created by gensrcs ====
+
+def _test_output_path_expansion_impl(ctx):
+ env = analysistest.begin(ctx)
+ target = analysistest.target_under_test(env)
+ actions = analysistest.target_actions(env)
+
+ # Expect an action for each input/output file pair.
+ asserts.equals(
+ env,
+ expected = len(ctx.attr.expected_outputs),
+ actual = len(actions),
+ )
+
+ # Expect the correct set of output files.
+ asserts.set_equals(
+ env,
+ expected = sets.make([
+ paths.join(
+ ctx.genfiles_dir.path,
+ paths.dirname(ctx.build_file_path),
+ out,
+ )
+ for out in ctx.attr.expected_outputs
+ ]),
+ actual = sets.make(
+ [file.path for file in target.files.to_list()],
+ ),
+ )
+
+ return analysistest.end(env)
+
+output_path_expansion_test = analysistest.make(
+ _test_output_path_expansion_impl,
+ attrs = {
+ "expected_outputs": attr.string_list(
+ doc = "The expected list of output files",
+ ),
+ },
+)
+
+def _test_output_expansion_base():
+ name = "gensrcs_output_expansion_base"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ output = "prefix_$(SRC:BASE)_suffix",
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "prefix_src1_suffix",
+ "prefix_src2_suffix",
+ "prefix_src3_suffix",
+ ],
+ )
+ return test_name
+
+def _test_output_expansion_base_ext():
+ name = "gensrcs_output_expansion_base_ext"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ output = "prefix_$(SRC:BASE.EXT)_suffix",
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "prefix_src1.txt_suffix",
+ "prefix_src2.txt_suffix",
+ "prefix_src3.txt_suffix",
+ ],
+ )
+ return test_name
+
+def _test_output_expansion_path_base():
+ name = "gensrcs_output_expansion_path_base"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ output = "prefix_$(SRC:PATH/BASE)_suffix",
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "prefix_texts/src1_suffix",
+ "prefix_texts/src2_suffix",
+ "prefix_src3_suffix",
+ ],
+ )
+ return test_name
+
+def _test_output_expansion_path_base_ext():
+ name = "gensrcs_output_expansion_path_base_ext"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ output = "prefix_$(SRC:PATH/BASE.EXT)_suffix",
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "prefix_texts/src1.txt_suffix",
+ "prefix_texts/src2.txt_suffix",
+ "prefix_src3.txt_suffix",
+ ],
+ )
+ return test_name
+
+def _test_output_expansion_pkg_path_base():
+ name = "gensrcs_output_expansion_pkg_path_base"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ output = "prefix_$(SRC:PKG/PATH/BASE)_suffix",
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "prefix_external/wayland-protocols/bazel/texts/src1_suffix",
+ "prefix_external/wayland-protocols/bazel/texts/src2_suffix",
+ "prefix_external/wayland-protocols/bazel/src3_suffix",
+ ],
+ )
+ return test_name
+
+def _test_output_expansion_pkg_path_base_ext():
+ name = "gensrcs_output_expansion_pkg_path_base_ext"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ output = "prefix_$(SRC:PKG/PATH/BASE.EXT)_suffix",
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "prefix_external/wayland-protocols/bazel/texts/src1.txt_suffix",
+ "prefix_external/wayland-protocols/bazel/texts/src2.txt_suffix",
+ "prefix_external/wayland-protocols/bazel/src3.txt_suffix",
+ ],
+ )
+ return test_name
+
+def _test_output_expansion_default():
+ name = "gensrcs_output_expansion_default"
+ test_name = name + "_test"
+
+ gensrcs(
+ name = name,
+ cmd = "cat $(SRC) > $(OUT)",
+ srcs = SRCS,
+ tags = ["manual"], # make sure it's not built using `:all`
+ )
+
+ output_path_expansion_test(
+ name = test_name,
+ target_under_test = name,
+ expected_outputs = [
+ "texts/src1.txt",
+ "texts/src2.txt",
+ "src3.txt",
+ ],
+ )
+ return test_name
+
+# ==== test suite ====
+
+def gensrcs_test_suite(name):
+ """Creates test targets for gensrcs.bzl"""
+ native.test_suite(
+ name = name,
+ tests = [
+ _test_output_expansion_base(),
+ _test_output_expansion_base_ext(),
+ _test_output_expansion_path_base(),
+ _test_output_expansion_path_base_ext(),
+ _test_output_expansion_pkg_path_base(),
+ _test_output_expansion_pkg_path_base_ext(),
+ _test_output_expansion_default(),
+ ],
+ )
diff --git a/chromium.org/unstable/aura-shell/aura-shell.xml b/chromium.org/unstable/aura-shell/aura-shell.xml
index 51d652b..f889bf0 100644
--- a/chromium.org/unstable/aura-shell/aura-shell.xml
+++ b/chromium.org/unstable/aura-shell/aura-shell.xml
@@ -2,7 +2,7 @@
<protocol name="aura_shell">
<copyright>
- Copyright 2017 The Chromium Authors.
+ Copyright 2017 The Chromium Authors
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
@@ -24,7 +24,7 @@
DEALINGS IN THE SOFTWARE.
</copyright>
- <interface name="zaura_shell" version="24">
+ <interface name="zaura_shell" version="51">
<description summary="aura_shell">
The global interface exposing aura shell capabilities is used to
instantiate an interface extension for a wl_surface object.
@@ -108,6 +108,7 @@
</description>
<arg name="active_desk_index" type="int" summary="index of the active desk"/>
</event>
+
<!-- Version 24 additions -->
<event name="activated" since="24">
<description summary="activated surface changed">
@@ -116,9 +117,50 @@
<arg name="gained_active" type="object" interface="wl_surface" allow-null="true"/>
<arg name="lost_active" type="object" interface="wl_surface" allow-null="true"/>
</event>
+
+ <!-- Version 26 additions -->
+ <request name="surface_submission_in_pixel_coordinates" since="26">
+ <description summary="surfaces will be submitted in pixel coordinates">
+ [Deprecated] Informs the server that when submitting surfaces, this
+ client will not use wl_surface_set_buffer_scale to report the scales,
+ nor will it apply scale via vp_viewporter. Instead the server should
+ apply an appropriate scale transform to have the submitted buffers
+ composited correctly.
+ </description>
+ </request>
+
+ <request name="get_aura_toplevel_for_xdg_toplevel" since="27">
+ <description summary="get aura toplevel">
+ Retrieve the aura toplevel interface for a given xdg toplevel interface
+ </description>
+ <arg name="id" type="new_id" interface="zaura_toplevel"
+ summary="the new aura toplevel interface id"/>
+ <arg name="toplevel" type="object" interface="xdg_toplevel"/>
+ </request>
+
+ <request name="get_aura_popup_for_xdg_popup" since="28">
+ <description summary="get aura popup">
+ Retrieve the aura popup interface for the given xdg popup interface
+ </description>
+ <arg name="id" type="new_id" interface="zaura_popup"
+ summary="the aura popup interface id"/>
+ <arg name="popup" type="object" interface="xdg_popup"/>
+ </request>
+
+ <request name="release" type="destructor" since="38">
+ <description summary="release zaura_shell object">
+ Using this request a client can tell the server that it is not going to
+ use the zaura_shell object anymore. This does not affect any other objects.
+
+ This is named "release" because "destroy" is a special named function used for
+ freeing wl_proxy objects. Naming it "destroy" will cause marshalling errors
+ when running on lower versioned hosts. All "release" requests here should be
+ renamed to "destroy" if we move to aura-shell v2.
+ </description>
+ </request>
</interface>
- <interface name="zaura_surface" version="23">
+ <interface name="zaura_surface" version="51">
<description summary="aura shell interface to a wl_surface">
An additional interface to a wl_surface object, which allows the
client to access aura shell specific functionality for surface.
@@ -135,7 +177,9 @@
<request name="set_frame">
<description summary="request a frame for surface">
- Suggests a surface should use a specific frame.
+ [Deprecated] Suggests a surface should use a specific frame. Deprecated
+ since M105. See the set_decoration method on zaura_toplevel and
+ zaura_popup.
</description>
<arg name="type" type="uint" summary="the new frame type"/>
</request>
@@ -228,12 +272,12 @@
<request name="activate" since="9">
<description summary="Indicate that this window wants to be the active window">
- Make this the active window. This usually implies something like
- restacking this surface to the foreground. The compositor is free to
- ignore this request if it deems the client to be misbehaving. Typically
- this request will only be honoured in response to some user driven
- event, such as executing an application or opening a file in a window
- that already exists.
+ [Deprecated] Make this the active window. This usually implies something
+ like restacking this surface to the foreground. The compositor is free
+ to ignore this request if it deems the client to be misbehaving.
+ Typically this request will only be honoured in response to some user
+ driven event, such as executing an application or opening a file in a
+ window that already exists.
</description>
</request>
@@ -252,7 +296,7 @@
<enum name="fullscreen_mode">
<description
summary="Specifies the behavior of the surface in fullscreen.">
- Possible windowing system behaviors if this surface were to go
+ [Deprecated] Possible windowing system behaviors if this surface were to go
fullscreen.
</description>
<entry
@@ -268,6 +312,7 @@
<request name="set_fullscreen_mode" since="10">
<description summary="Sets the behavior of the surface in fullscreen.">
+ [Deprecated] Use the set_fullscreen_mode in the toplevel interface.
Suggests how the windowing system should behave if this surface were
to go fullscreen. Does not make the surface fullscreen.
@@ -308,6 +353,7 @@
<request name="intent_to_snap" since="16">
<description summary="client intents to snap the surface.">
+ [Deprecated] Use intent_to_snap on zaura_toplevel.
Notify (or inform) the server the client's intent to snap the window.
To inform it's no longer willing to snap, send 'none'.
</description>
@@ -316,18 +362,21 @@
<request name="set_snap_left" since="16">
<description summary="snap the surface to the left.">
+ [Deprecated] Use set_snap_primary on zaura_toplevel.
Request that surface is snapped to the left.
</description>
</request>
<request name="set_snap_right" since="16">
<description summary="snap the surface to the right.">
+ [Deprecated] Use set_snap_secondary on zaura_toplevel.
Request that surface is snapped to the right.
</description>
</request>
<request name="unset_snap" since="16">
<description summary="Unset the surface snap.">
+ [Deprecated] Use unset_snap on zaura_toplevel.
Request that surface resets snapping.
</description>
</request>
@@ -444,9 +493,113 @@
</description>
<arg name="initial_workspace" type="string" summary="intial workspace for restoring or '-1' for visible on all workspaces"/>
</request>
+
+ <!-- Version 25 additions -->
+ <request name="set_pin" since="25">
+ <description summary="pin a window (trusted or not)">
+ Requests that a window is pinned which means that the system does not allow
+ the user to leave the window until an exit criteria is met.
+
+ This is a request to get the window pinned so that the user cannot get to any
+ other window / application. There are two modes:
+ A. trusted is 0 - which is slightly less restrictive and allows the user to
+ get out of the window by a predefined way of authentication.
+ B. trusted is not 0 in which case a trusted application was locking down the
+ system and needs to unlock. This is used for e.g. School tests.
+ </description>
+ <arg name="trusted" type="int" summary="0 for non trusted"/>
+ </request>
+
+ <request name="unset_pin" since="25">
+ <description summary="unpin a window">
+ Requests that the user can leave a previously pinned window.
+
+ This is a request to unpin a previously pinned window. It does not matter if
+ the window was locked with the trusted state or not.
+ </description>
+ </request>
+
+ <event name="start_throttle" since="29">
+ <description summary="start throttling on the surface">
+ Informs the client to start throttling on the surface.
+ </description>
+ </event>
+ <event name="end_throttle" since="29">
+ <description summary="end throttling on the surface">
+ Informs the client to end throttling on the surface.
+ </description>
+ </event>
+
+ <!-- Version 38 additions -->
+ <request name="release" type="destructor" since="38">
+ <description summary="destroy zaura_surface">
+ Destroy the zaura_surface object. A client should destroy this object when the
+ role is unmapped from a wl_surface.
+
+ See zaura_shell.release for destructor naming.
+ </description>
+ </request>
+
+ <!-- Version 47 additions -->
+ <request name="show_tooltip" since="47">
+ <description summary="show tooltip window">
+ Show tooltip on server side.
+ `x` and `y` specifies the location of tooltip in surface local coordinates.
+ `hide_delay` and `show_delay` specify the time to wait until showing/hiding tooltip.
+ The unit is millisecond.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="tooltip_trigger" type="uint" summary="tooltip_trigger enum"/>
+ <arg name="show_delay" type="uint" summary="delay to show tooltip in millisecond"/>
+ <arg name="hide_delay" type="uint" summary="delay to hide tooltip in millisecond"/>
+ </request>
+
+ <enum name="tooltip_trigger">
+ <description summary="type of tooltip trigger">
+ Describes what triggered tooltip
+ </description>
+ <entry name="cursor" value="0" summary="triggered by cursor"/>
+ <entry name="keyboard" value="1" summary="triggered by keyboard"/>
+ </enum>
+
+ <request name="hide_tooltip" since="47">
+ <description summary="hide tooltip window">
+ Hide tooltip created by the same client on server side.
+ This may be called even when there is no tooltip window to hide.
+ </description>
+ </request>
+
+ <event name="tooltip_shown" since="47">
+ <description summary="tooltip is shown by server side">
+ Informs the client that the tooltip is shown with states.
+ `x` and `y` specifies the location of tooltip in surface local coordinates.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </event>
+
+ <event name="tooltip_hidden" since="47">
+ <description summary="tooltip is hidden by server side">
+ Informs the client that the tooltip is hidden.
+ </description>
+ </event>
+
+ <!-- Version 51 additions -->
+ <request name="set_accessibility_id" since="51">
+ <description summary="set accessibility ID to the surface">
+ Set accessibility window ID to the surface. A negative number removes
+ the existing accessibility ID from the surface.
+ </description>
+ <arg name="id" type="int" summary="Accessibility ID. Negative number removes existing accessibility ID from the surface."/>
+ </request>
</interface>
- <interface name="zaura_output" version="6">
+ <interface name="zaura_output" version="45">
<description summary="aura shell interface to a wl_output">
An additional interface to a wl_output object, which allows the
client to access aura shell specific functionality for output.
@@ -548,6 +701,490 @@
</description>
<arg name="scale" type="uint" enum="scale_factor" summary="output device scale factor"/>
</event>
- </interface>
+ <!-- Version 33 additions -->
+
+ <event name="insets" since="33">
+ <description summary="advertise the insets for the output">
+ This event describes the insets for the output in logical screen
+ coordinates, from which the work area can be calculated.
+
+ This event is sent before wl_output.done, after which the client would
+ apply the change.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="left" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="right" type="int"/>
+ </event>
+
+ <!-- Version 34 additions -->
+
+ <event name="logical_transform" since="34">
+ <description summary="advertise the output's logical transform">
+ This event describes the logical transform for the output. Whereas
+ wl_output.geometry's transform corresponds to the display's panel
+ rotation, the logical transform corresponds to the display's logical
+ rotation.
+
+ This event is sent before wl_output.done, after which the client would
+ apply the change.
+ </description>
+ <arg name="transform" type="int" enum="wl_output.transform"/>
+ </event>
+
+ <!-- Version 38 additions -->
+ <request name="release" type="destructor" since="38">
+ <description summary="destroy zaura_output">
+ Destroy this zaura_shell object.
+
+ Destroying a bound zaura_shell object while there are zaura_surfaces
+ still alive created by this zaura_shell object instance is illegal
+ and will result in a protocol error.
+
+ See zaura_shell.release for destructor naming.
+ </description>
+ </request>
+
+ <event name="display_id" since="43">
+ <description summary="advertise the output's display id">
+ This event describes the 64bit display id assigned to each display by ChromeOS.
+ The value is opaque and should not be interpreted.
+ </description>
+ <arg name="display_id_hi" type="uint"/>
+ <arg name="display_id_lo" type="uint"/>
+ </event>
+
+ <!-- Version 45 additions -->
+ <event name="activated" since="45">
+ <description summary="target display for new windows">
+ Notifies that this output is now active output. It is typically used as a
+ target when a new window is created without specific bounds.
+ </description>
+ </event>
+ </interface>
+
+ <interface name="zaura_toplevel" version="50">
+ <description summary="aura shell interface to the toplevel shell">
+ An interface to the toplevel shell, which allows the
+ client to access shell specific functionality.
+ </description>
+
+ <enum name="orientation_lock">
+ <description summary="orientation lock request">
+ Defines orientation request when a surface is in fullscreen.
+ </description>
+ <entry name="none" value="1" summary="no orientation lock"/>
+ <entry name="portrait" value="2" summary="primary or secondary portrait"/>
+ <entry name="landscape" value="3" summary="primary or secondary landscape"/>
+ <entry name="current" value="4" summary="keep current orientation"/>
+ <entry name="portrait_primary" value="5" summary="primary portrait"/>
+ <entry name="landscape_primary" value="6" summary="primary landscape"/>
+ <entry name="portrait_secondary" value="7" summary="secondary portrait"/>
+ <entry name="landscape_secondary" value="8" summary="secondary landscape"/>
+ </enum>
+
+ <request name="set_orientation_lock" since="26">
+ <description summary="set orientation lock for a remote surface">
+ Request a specific orientation behavior when this surface is in fullscreen.
+ </description>
+ <arg name="orientation_lock" type="uint" enum="orientation_lock"/>
+ </request>
+ <request name="surface_submission_in_pixel_coordinates" since="28">
+ <description summary="surface will be submitted in pixel coordinates">
+ Informs the server that when submitting this surface, this client will not
+ use wl_surface_set_buffer_scale to report the scales, nor will it apply
+ scale via vp_viewporter. Instead the server should apply an appropriate
+ scale transform for the submitted buffers to be composited correctly.
+ </description>
+ </request>
+
+ <request name="set_supports_screen_coordinates" since="29">
+ <description summary="enables screen coordinates in window bounds">
+ Requesting this will enable screen coordinates in surfaces
+ associated with aura_toplevel including sub surfaces and popup
+ windows who added this toplevel as a parent. This should be
+ set before first commit.
+ </description>
+ </request>
+
+ <request name="set_window_bounds" since="29">
+ <description summary="set window size and position">
+ Request a new location and bounds of the surface in DP screen
+ coordinates. The size will be applied to visible bounds used
+ in set_geometry. The output is a hint for the compositor to
+ determine which output the window should move to. If the
+ output is null, the compositor should make decision solely by
+ the given bounds. These parameters are just a request and the
+ compositor may ignore, adjust the size and position based on
+ the rule imposed by the window manager, or may preserve it for
+ future operations. For example, the compositor will not allow
+ a position outside of the output, or the compositor may just
+ store it if the toplevel surface is in maximiezd state, and
+ may use it upon unmaximized.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="output" type="object" interface="wl_output" summary="the output" allow-null="true"/>
+ </request>
+
+ <event name="configure" since="29">
+ <description summary="suggest a surface change">
+ A configuration change that also includes the window origin in screen coordinates.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="states" type="array"/>
+ </event>
+
+ <enum name="state">
+ <description summary="supplemental aura states to xdg states">
+ The states that are contained here are supplemental to the states
+ defined in the XDG shell and specific aura windows.
+ </description>
+
+ <!-- Offset by 100 to prevent collision with new XDG states. -->
+ <entry name="immersive" value="100" since="36">
+ <description summary="immersive mode with hidden title bar and shelf">
+ User can access system UIs such as the shelf and window frame
+ by pointing to, or swiping over, the screen edge.
+ </description>
+ </entry>
+ <entry name="minimized" value="101" since="36">
+ <description summary="surface is minimized">
+ The window has been minimized.
+ </description>
+ </entry>
+ <entry name="snapped_primary" value="102" since="38">
+ <description summary="window is snapped in primary position">
+ The window is snapped to the left if the display is in landscape mode
+ and at the top in portrait mode.
+ </description>
+ </entry>
+ <entry name="snapped_secondary" value="103" since="38">
+ <description summary="window is snapped in secondary position">
+ The window is snapped to the right if the display is in landscape mode
+ and at the bottom in portrait mode.
+ </description>
+ </entry>
+ <entry name="floated" value="104" since="38">
+ <description summary="window is floated on top">
+ The window is floated on top of other windows. One floated window is
+ allowed per desk. Floating a window when there is already a floated
+ window on the same desk will unfloat the floated window.
+ </description>
+ </entry>
+ </enum>
+
+ <event name="origin_change" since="29">
+ <description summary="window origin change">
+ A notification sent when the window origin has changed. Unlike a configure,
+ this does not imply the client needs to resize. The values are in screen
+ coordinates.
+ </description>
+ <arg name="x" type="int" />
+ <arg name="y" type="int" />
+ </event>
+
+ <request name="set_restore_info" since="30">
+ <description summary="set session id and restore id">
+ Request session id and restore id of a newly created browser window.
+ Set the information used by compositor to restore the toplevel
+ surface state, such as window position, window state, upon creation.
+ This is not double buffered and must be set before sending first commit.
+ </description>
+ <arg name="restore_session_id" type="int" summary="unique browser session id"/>
+ <arg name="restore_window_id" type="int" summary="restore browser window id"/>
+ </request>
+
+ <!-- Version 31 additions -->
+ <request name="set_system_modal" since="31">
+ <description summary="make window a system modal">
+ Requests that the toplevel surface should become a system modal. The
+ compositor will prevent other windows from receiving events. If there
+ are multiple system modal surfaces, the compositor will decide which
+ one to receive events.
+ </description>
+ </request>
+
+ <request name="unset_system_modal" since="31">
+ <description summary="unset window system modal state">
+ Requests that the system modal state of the toplevel surface will be
+ unset. The compositor will then allow other windows to recieve events.
+ </description>
+ </request>
+
+ <request name="set_restore_info_with_window_id_source" since="32">
+ <description summary="set session id and restore window id source">
+ Request session id and restore id of the window. Set the information used by compositor to restore the toplevel surface state, such as window position, window state, upon creation. This is not double buffered and must be set before sending first commit. This is different from set_restore_info, used for clients that create multiple windows associated with restore_id_source.
+ </description>
+ <arg name="restore_session_id" type="int" summary="unique browser session id"/>
+ <arg name="restore_window_id_source" type="string" summary="restore window id source"/>
+ </request>
+
+ <request name="set_decoration" since="35">
+ <description summary="request a decoration for surface">
+ Clients are allowed to request a particular decoration for a
+ zaura_toplevel. The server is not required to honor this request. See
+ decoration_type for available options. Available since M105.
+ </description>
+ <arg name="type" type="uint" summary="the new frame type"/>
+ </request>
+
+ <enum name="decoration_type">
+ <description summary="different decoration types">
+ Decoration types are used to modify the surface (e.g. drop shadow).
+ </description>
+ <entry name="none" value="0" summary="no frame"/>
+ <entry name="normal" value="1" summary="caption with shadow"/>
+ <entry name="shadow" value="2" summary="shadow only"/>
+ </enum>
+
+ <!-- Version 38 additions -->
+ <request name="release" type="destructor" since="38">
+ <description summary="destroy zaura_toplevel">
+ Destroy this zaura_toplevel object. A client should call destroy when the role
+ is unmapped from a wl_surface.
+
+ See zaura_shell.release for destructor naming.
+ </description>
+ </request>
+
+ <!-- Version 39 additions -->
+ <request name="set_float" since="39">
+ <description summary="float the surface on top">
+ This is a request to place the surface above others.
+ </description>
+ </request>
+ <request name="unset_float" since="39">
+ <description summary="unset the surface float">
+ Request that the surface resets floating.
+ </description>
+ </request>
+
+ <!-- version 40 additions -->
+ <request name="set_z_order" since="40">
+ <description summary="set z order for window">
+ Sets the z order for a toplevel surface. See z_order_level for available options.
+ </description>
+ <arg name="z_order" type="uint" summary="z order value for the window"/>
+ </request>
+
+ <enum name="z_order_level">
+ <description summary="z order levels for windows">
+ Different z order levels that are used to set the z order for a toplevel surface.
+ </description>
+ <entry name="normal" value="0" summary="the default level for windows"/>
+ <entry name="floating_window" value="1" summary="a floating window z-ordered above other normal windows"/>
+ <entry name="floating_ui_element" value="2" summary="used to show non-window style UIs that are shown above floating windows"/>
+ <entry name="security_surface" value="3" summary="cannot be interfered with or covered up"/>
+ </enum>
+
+ <!-- version 41 additions -->
+ <request name="set_origin" since="41">
+ <description summary="set window position in DPs">
+ Request a new location for the surface in device-independent pixels
+ (DPs), relative to the top-left corner of the given output.
+
+ A null output means whichever output currently contains the surface.
+
+ These parameters are just a request and the compositor may ignore
+ them, adjust them due to rules imposed by the window manager, or
+ preserve them for future operations. For example, the compositor will
+ not allow a position outside of the output; or the compositor may just
+ store it if the toplevel surface is in maximized state, and may use it
+ upon unmaximize.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"
+ summary="output to contain the surface"/>
+ </request>
+
+ <request name="activate" since="42">
+ <description summary="activates the window">
+ Activates this window. This is equivalent to bringing the window to the
+ foreground. The compositor is free to ignore this request if it deems
+ the client to be misbehaving.
+ </description>
+ </request>
+
+ <request name="deactivate" since="42">
+ <description summary="deactivates the window">
+ Deactivates this window. This is equivalent to requesting that the
+ window not be the foreground window. The exact behavior is
+ compositor-defined. The compositor is free to ignore this request
+ if it deems the client to be misbehaving.
+ </description>
+ </request>
+
+ <!-- Version 44 additions -->
+ <enum name="fullscreen_mode">
+ <description
+ summary="Specifies the behavior of the surface in fullscreen.">
+ Possible windowing system behaviors if this surface were to go
+ fullscreen.
+ </description>
+ <entry
+ name="plain"
+ value="0"
+ summary="user cannot access system UIs using mouse/touches"/>
+ <entry
+ name="immersive"
+ value="1"
+ summary="user can access system UIs such as the shelf and window frame
+ by pointing to, or swiping over, the screen edge"/>
+ </enum>
+
+ <request name="set_fullscreen_mode" since="44">
+ <description summary="Sets the behavior of the surface in fullscreen.">
+ Suggests how the windowing manager should behave if this surface were
+ to go fullscreen. Does not make the surface fullscreen.
+ </description>
+ <arg name="mode" type="uint" enum="fullscreen_mode"/>
+ </request>
+
+ <!-- Version 46 additions -->
+ <request name="set_scale_factor" since="46">
+ <description summary="Allows the client to set the scale factor for the future buffer commits.">
+ The client has a 32-bit float scale factor that is associated with each
+ zaura toplevel. This scale factor must be propagated exactly to exo. To
+ do so we reinterpret_cast into a 32-bit uint and later cast back into a
+ float. This is because wayland does not support native transport of
+ floats. As different CPU architectures may use different endian
+ representations for IEEE 754 floats, this protocol implicitly assumes
+ that the caller and receiver are the same machine. To avoid redundant
+ messages, this request needs to only be called once when the zaura
+ toplevel scale factor changes. This is double buffered state and will be
+ applied in the next commit.
+ </description>
+ <arg name="scale_factor_as_uint" type="uint"/>
+ </request>
+
+ <!-- Version 48 additions-->
+ <request name="set_snap_primary" since="48">
+ <description summary="snap the surface to the primary snap position.">
+ Request that surface is snapped to the left or top if primary layout,
+ right or bottom otherwise.
+ </description>/>
+ <arg name="snap_ratio_as_uint" type="uint"/>
+ </request>
+
+ <request name="set_snap_secondary" since="48">
+ <description summary="snap the surface to the secondary snap position.">
+ Request that surface is snapped to the right or bottom if primary
+ layout, left or top otherwise.
+ </description>
+ <arg name="snap_ratio_as_uint" type="uint"/>
+ </request>
+
+ <!-- Version 49 additions-->
+ <enum name="snap_direction">
+ <description summary="window snap directions">
+ Window snap directions.
+ </description>
+ <entry name="none" value="0" summary="unsnap the window"/>
+ <entry name="primary" value="1" summary="snap the window to the left or
+ top in primary layout, right or bottom in secondary layout"/>
+ <entry name="secondary" value="2" summary="snap the window to the right
+ or bottom in primary layout, top or left in secondary layout"/>
+ </enum>
+
+ <request name="intent_to_snap" since="49">
+ <description summary="client intents to snap the surface.">
+ Notify (or inform) the server the client's intent to snap the window.
+ To inform it's no longer willing to snap, send 'none'.
+ </description>
+ <arg name="direction" type="uint" enum="snap_direction"/>
+ </request>
+
+ <request name="unset_snap" since="49">
+ <description summary="Unset the window snap.">
+ Request that window unsets snapping.
+ </description>
+ </request>
+
+ <!-- Version 50 additions -->
+ <event name="configure_raster_scale" since="50">
+ <description summary="set the raster scale during a configure">
+ Sets the raster scale of this window. This should be called during a
+ configure event sequence. To do so we reinterpret_cast into a 32-bit
+ uint and later cast back into a float. This is because wayland does not
+ support native transport of floats. As different CPU architectures may
+ use different endian representations for IEEE 754 floats, this protocol
+ implicitly assumes that the caller and receiver are the same machine.
+ </description>
+ <arg name="scale" type="uint" summary="Raster scale, in float format"/>
+ </event>
+ </interface>
+
+ <interface name="zaura_popup" version="46">
+ <description summary="aura shell interface to the popup shell">
+ An interface to the popup shell, which allows the
+ client to access shell specific functionality.
+ </description>
+
+ <request name="surface_submission_in_pixel_coordinates" since="28">
+ <description summary="surface will be submitted in pixel coordinates">
+ Informs the server that when submitting this surface, this client will not
+ use wl_surface_set_buffer_scale to report the scales, nor will it apply
+ scale via vp_viewporter. Instead the server should apply an appropriate
+ scale transform for the submitted buffers to be composited correctly.
+ </description>
+ </request>
+
+ <request name="set_decoration" since="35">
+ <description summary="request a decoration for surface">
+ Clients are allowed to request a particular decoration for a
+ zaura_toplevel. The server is not required to honor this request. See
+ decoration_type for available options. Available since M105.
+ </description>
+ <arg name="type" type="uint" summary="the new frame type"/>
+ </request>
+
+ <enum name="decoration_type">
+ <description summary="different decoration types">
+ Decoration types are used to modify the surface (e.g. drop shadow).
+ </description>
+ <entry name="none" value="0" summary="no frame"/>
+ <entry name="normal" value="1" summary="caption with shadow"/>
+ <entry name="shadow" value="2" summary="shadow only"/>
+ </enum>
+
+ <request name="set_menu" since="37">
+ <description summary="set popup type to menu">
+ Set popup type to menu
+ </description>
+ </request>
+
+ <!-- Version 38 additions -->
+ <request name="release" type="destructor" since="38">
+ <description summary="destroy zaura_popup">
+ This request destroys the zaura_popup. A client should call destroy when the
+ role is unmapped from a wl_surface.
+
+ See zaura_shell.release for destructor naming.
+ </description>
+ </request>
+
+ <!-- Version 46 additions -->
+ <request name="set_scale_factor" since="46">
+ <description summary="Allows the client to set the scale factor for the future buffer commits.">
+ The client has a 32-bit float scale factor that is associated with each
+ zaura popup. This scale factor must be propagated exactly to exo. To
+ do so we reinterpret_cast into a 32-bit uint and later cast back into a
+ float. This is because wayland does not support native transport of
+ floats. As different CPU architectures may use different endian
+ representations for IEEE 754 floats, this protocol implicitly assumes
+ that the caller and receiver are the same machine. To avoid redundant
+ messages, this request needs to only be called once when the zaura
+ popup's scale factor changes.
+ </description>
+ <arg name="scale_factor_as_uint" type="uint"/>
+ </request>
+ </interface>
</protocol>
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..0cb1089
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,17 @@
+// Copyright 2023 The Android Open Source Project
+//
+// 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 android.googlesource.com/platform/external/wayland_protocols
+
+go 1.19
diff --git a/locations.go b/locations.go
new file mode 100644
index 0000000..a2d06d9
--- /dev/null
+++ b/locations.go
@@ -0,0 +1,112 @@
+// Copyright 2023 The Android Open Source Project
+//
+// 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.
+
+package soong_wayland_protocol_codegen
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+// Note: the types defined here are identical to the types defined in the
+// locations.go file included as "android/soong/genrule" package.
+// Unfortunately all the types defined there are not public, which means we
+// can't reference them from that package.
+
+// location is used to service $(location) and $(locations) entries in
+// wayland_protocol_codegen commands.
+type location interface {
+ Paths(cmd *android.RuleBuilderCommand) []string
+ String() string
+}
+
+// inputLocation is a $(location) result for an entry in the srcs property.
+type inputLocation struct {
+ paths android.Paths
+}
+
+func (l inputLocation) String() string {
+ return strings.Join(l.paths.Strings(), " ")
+}
+
+func (l inputLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return cmd.PathsForInputs(l.paths)
+}
+
+var _ location = inputLocation{}
+
+// outputLocation is a $(location) result for an entry in the out property.
+type outputLocation struct {
+ path android.WritablePath
+}
+
+func (l outputLocation) String() string {
+ return l.path.String()
+}
+
+func (l outputLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{cmd.PathForOutput(l.path)}
+}
+
+var _ location = outputLocation{}
+
+// toolLocation is a $(location) result for an entry in the tools or
+// tool_files property.
+type toolLocation struct {
+ paths android.Paths
+}
+
+func (l toolLocation) String() string {
+ return strings.Join(l.paths.Strings(), " ")
+}
+
+func (l toolLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return cmd.PathsForTools(l.paths)
+}
+
+var _ location = toolLocation{}
+
+// packagedToolLocation is a $(location) result for an entry in the tools or
+// tool_files property that has PackagingSpecs.
+type packagedToolLocation struct {
+ spec android.PackagingSpec
+}
+
+func (l packagedToolLocation) String() string {
+ return l.spec.FileName()
+}
+
+func (l packagedToolLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{cmd.PathForPackagedTool(l.spec)}
+}
+
+var _ location = packagedToolLocation{}
+
+// errorLocation is a placeholder for a $(location) result that returns
+// garbage to break the command when error reporting is delayed by
+// ALLOW_MISSING_DEPENDENCIES=true.
+type errorLocation struct {
+ err string
+}
+
+func (l errorLocation) String() string {
+ return l.err
+}
+
+func (l errorLocation) Paths(cmd *android.RuleBuilderCommand) []string {
+ return []string{l.err}
+}
+
+var _ location = errorLocation{}
diff --git a/wayland_protocol_codegen.go b/wayland_protocol_codegen.go
index f9171af..bf7a9af 100644
--- a/wayland_protocol_codegen.go
+++ b/wayland_protocol_codegen.go
@@ -12,354 +12,1026 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// ---------------------------------------------------------------------------
+/*
+Package wayland_protocol defines an plugin module for the Soong build system,
+which makes it easier to generate code from a list of Wayland protocol files.
-// Package wayland_protcool defines extension modules for the Soong build system
-// to make it easier to generate code from a list of Wayland protocol files.
-//
-// The primary extension module is "wayland_protocol_codegen", which applies a
-// code generation tool to a list of source protocol files.
-//
-// Note that the code generation done here is similar to what is done by the
-// base Soong "gensrcs" module, but there are two functional differences:
-//
-// 1) The output filenames are computed from the input filenames, rather
-// than needing to be specified explicitly. An optional prefix as well
-// as a suffix can be added to the protocol filename (without extension).
-//
-// 2) Code generation is done for each file independently by emitting
-// multiple Ninja build commands, rather than one build command which
-// does it all.
-package wayland_protocol
+The primary build module is "wayland_protocol_codegen", which takes a list of
+protocol files, and runs a configurable code-generation tool to generate
+source code for each one. There is also a "wayland_protocol_codegen_defaults"
+for setting common properties.
+
+This package is substantially similar to the base "android/soong/genrule"
+package, which was originally used for inspiration for this one, and has
+been recently restructured so that it can be kept in sync with a tool like
+"vimdiff" to keep things in sync as needed.
+
+However this build system plugin will not be needed in the future, as
+evidenced by some of the other files that have been added as part of the
+last big sync-up. In the future, Android will be built with Bazel, and it
+is much simpler there to define our own local version of "gensrcs" that
+implements the functionality we need See bazel/gensrcs.bzl for it.
+
+Notable differences:
+
+ - This package implements a more powerful template mechanism for specifying
+ what output path/filename should be used for each source filename. The
+ genrule package only allows the extension on each source filename to be
+ replaced.
+
+ - This package drops support for depfiles, after observing comments that
+ they are problematic in the genrule package sources.
+
+ - This package drops "Extra" and "CmdModifier" from the public Module
+ structure, as this module is not expected to be extended.
+
+ - This package drops "rule" from the public Module structure, as it was
+ unused but present in genrule.
+
+# Usage
+
+ wayland_protocol_codegen {
+ // A standard target name.
+ name: "wayland_extension_protocol_sources",
+
+ // A simple template for generating output filenames.
+ output: "$(in).c"
+
+ // The command line template. See "Cmd".
+ cmd: "$(location wayland_scanner) code < $(in) > $(out)",
+
+ // Protocol source files for the expansion.
+ srcs: [":wayland_extension_protocols"],
+
+ // Any buildable binaries to use as tools
+ tools: ["wayland_scanner"],
+
+ // Any source files to be used (scripts, template files)
+ tools_files: [],
+ }
+*/
+package soong_wayland_protocol_codegen
import (
"fmt"
+ "strconv"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/bazel"
+ "android/soong/bazel/cquery"
"android/soong/genrule"
+ "path/filepath"
)
func init() {
- // Register out extension module type name with Soong.
- android.RegisterModuleType(
- "wayland_protocol_codegen", waylandCodegenModuleFactory)
+ registerCodeGenBuildComponents(android.InitRegistrationContext)
+}
+
+func registerCodeGenBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("wayland_protocol_codegen_defaults", defaultsFactory)
+
+ ctx.RegisterModuleType("wayland_protocol_codegen", codegenFactory)
+
+ ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("wayland_protocol_codegen_tool_deps", toolDepsMutator).Parallel()
+ })
}
var (
- // Create a context for build rule output from this package
- pctx = android.NewPackageContext("android/soong/external/wayland-protocol")
+ pctx = android.NewPackageContext("android/soong/external/wayland_protocol_codegen")
+
+ // Used by wayland_protocol_codegen when there is more than 1 shard to merge the outputs
+ // of each shard into a zip file.
+ gensrcsMerge = pctx.AndroidStaticRule("wayland_protocol_codegenMerge", blueprint.RuleParams{
+ Command: "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}",
+ CommandDeps: []string{"${soongZip}", "${zipSync}"},
+ Rspfile: "${tmpZip}.rsp",
+ RspfileContent: "${zipArgs}",
+ }, "tmpZip", "genDir", "zipArgs")
)
+func init() {
+ pctx.Import("android/soong/android")
+
+ pctx.HostBinToolVariable("soongZip", "soong_zip")
+ pctx.HostBinToolVariable("zipSync", "zipsync")
+}
+
type hostToolDependencyTag struct {
blueprint.BaseDependencyTag
+ android.LicenseAnnotationToolchainDependencyTag
+ label string
+}
+
+func (t hostToolDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
+ // Allow depending on a disabled module if it's replaced by a prebuilt
+ // counterpart. We get the prebuilt through android.PrebuiltGetPreferred in
+ // GenerateAndroidBuildActions.
+ return target.IsReplacedByPrebuilt()
}
-var hostToolDepTag hostToolDependencyTag
+var _ android.AllowDisabledModuleDependency = (*hostToolDependencyTag)(nil)
-// waylandCodegenProperties defines the properties that will be read in from the
-// Android.bp file for each instantiation of the module.
-type waylandCodegenProperties struct {
- // This string gives the command line template to run on each protocol file
- // to wayland_protocol_codegen.
+type generatorProperties struct {
+ // The command to run on one or more input files. Cmd supports
+ // substitution of a few variables (the actual substitution is implemented
+ // in GenerateAndroidBuildActions below)
+ //
+ // Available variables for substitution:
//
- // The string can contain one or more "$" prefixed variable names for
- // values that can vary. At a minimum you need to use ${location}, ${out}
- // and ${in}
+ // - $(location)
+ // the path to the first entry in tools or tool_files
+ // - $(location <label>)
+ // the path to the tool, tool_file, input or output with name <label>. Use
+ // $(location) if <label> refers to a rule that outputs exactly one file.
+ // - $(locations <label>)
+ // the paths to the tools, tool_files, inputs or outputs with name
+ // <label>. Use $(locations) if <label> refers to a rule that outputs two
+ // or more files.
+ // - $(in)
+ // one or more input files
+ // - $(out)
+ // a single output file
+ // - $(genDir)
+ // the sandbox directory for this tool; contains $(out)
+ // - $$
+ // a literal '$'
//
- // $(location): the path to the first entry in tools or tool_files
- // $(location <label>): the path to the tool or tool_file with name <label>
- // $(in): A protocol file from srcs
- // $(out): The constructed output filename from the protocol filename.
- // $$: a literal $
+ // All files used must be declared as inputs (to ensure proper up-to-date
+ // checks). Use "$(in)" directly in Cmd to ensure that all inputs used are
+ // declared.
Cmd *string
- // The string to prepend to every protcol filename to generate the
- // corresponding output filename. The empty string by default.
- Prefix *string
+ // name of the modules (if any) that produces the host executable. Leave
+ // empty for prebuilts or scripts that do not need a module to build them.
+ Tools []string
- // The suffix to append to every protocol filename to generate the
- // corresponding output filename. The empty string by default.
- Suffix *string
+ // Local source files that are used as scripts or other input files needed
+ // by a tool.
+ Tool_files []string `android:"path"`
- // The list of protocol files to process.
- Srcs []string `android:"path"`
+ // List of directories to export generated headers from.
+ Export_include_dirs []string
- // The names of any built host executables to use for code generation. Can
- // be left empty if a local script is used instead (specified in
- // tool_files).
- Tools []string
+ // List of input files.
+ Srcs []string `android:"path,arch_variant"`
- // Local files that are used for code generation. Can be scripts to run, but
- // should also include any other files that the code generation step should
- // depend on that might be used by the code gen tool.
- Tool_files []string
+ // Input files to exclude.
+ Exclude_srcs []string `android:"path,arch_variant"`
}
-// waylandGenModule defines the Soong module for each instance.
-type waylandGenModule struct {
+type Module struct {
android.ModuleBase
+ android.DefaultableModuleBase
+ android.BazelModuleBase
+ android.ApexModuleBase
+
+ android.ImageInterface
- // Store a copy of the parsed properties for easy reference.
- properties waylandCodegenProperties
+ properties generatorProperties
- // Each module emits its own blueprint (Ninja) rule. Store a reference
- // to the one created for this instance.
- rule blueprint.Rule
+ taskGenerator taskFunc
+
+ rawCommands []string
- // Each module exports one or more include directories. Store the paths here
- // here for easy retrieval.
exportedIncludeDirs android.Paths
- // Each module has a list of files it outputs, that can be used by other
- // modules. Store the list of paths here for easy reference.
outputFiles android.Paths
+ outputDeps android.Paths
+
+ subName string
+ subDir string
+
+ // Collect the module directory for IDE info in java/jdeps.go.
+ modulePaths []string
}
-// For the uninitiated, this is an idiom to check that a given object implements
-// an interface. In this case we want to be sure that waylandGenModule
-// implements genrule.SourceFileGenerator
-var _ genrule.SourceFileGenerator = (*waylandGenModule)(nil)
+// Ensure Module implements the android.MixedBuildBuildable interface.
+var _ android.MixedBuildBuildable = (*Module)(nil)
+
+type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
-// Check that we implement android.SourceFileProducer
-var _ android.SourceFileProducer = (*waylandGenModule)(nil)
+type generateTask struct {
+ in android.Paths
+ out android.WritablePaths
+ copyTo android.WritablePaths
+ genDir android.WritablePath
+ cmd string
-// GeneratedSourceFiles implements the genrule.SourceFileGenerator
-// GeneratedSourceFiles method to return the list of generated source files.
-func (g *waylandGenModule) GeneratedSourceFiles() android.Paths {
+ shard int
+ shards int
+}
+
+// Part of genrule.SourceFileGenerator.
+// Returns the list of generated source files.
+func (g *Module) GeneratedSourceFiles() android.Paths {
return g.outputFiles
}
-// GeneratedHeaderDirs implements the genrule.SourceFileGenerator
-// GeneratedHeaderDirs method to return the list of exported include
-// directories.
-func (g *waylandGenModule) GeneratedHeaderDirs() android.Paths {
- return g.exportedIncludeDirs
+// Part of genrule.SourceFileGenerator.
+// Returns the list of input source files.
+func (g *Module) Srcs() android.Paths {
+ return append(android.Paths{}, g.outputFiles...)
}
-// GeneratedDeps implements the genrule.SourceFileGenerator GeneratedDeps
-// method to return the list of files to be used as dependencies when using
-// GeneratedHeaderDirs.
-func (g *waylandGenModule) GeneratedDeps() android.Paths {
- return g.outputFiles
+// Part of genrule.SourceFileGenerator.
+// Returns the list of the list of exported include paths.
+func (g *Module) GeneratedHeaderDirs() android.Paths {
+ return g.exportedIncludeDirs
}
-// Srcs implements the android.SourceFileProducer Srcs method to return the list
-// of source files.
-func (g *waylandGenModule) Srcs() android.Paths {
- return g.outputFiles
+// Part of genrule.SourceFileGenerator.
+// Returns the list of files to be used as dependencies when using
+// GeneratedHeaderDirs
+func (g *Module) GeneratedDeps() android.Paths {
+ return g.outputDeps
}
-// DepsMutator implements the android.Module DepsMutator method to apply a
-// mutator context to the build graph.
-func (g *waylandGenModule) DepsMutator(ctx android.BottomUpMutatorContext) {
- // This implementation duplicates the one from genrule.go, where gensrcs is
- // defined.
- if g, ok := ctx.Module().(*waylandGenModule); ok {
- if len(g.properties.Tools) > 0 {
- ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(),
- hostToolDepTag, g.properties.Tools...)
+// Part of android.OutputFileProducer.
+// Returns the list of output files matching a tag, or an error if there is no
+// match.
+func (g *Module) OutputFiles(tag string) (android.Paths, error) {
+ if tag == "" {
+ return append(android.Paths{}, g.outputFiles...), nil
+ }
+ // otherwise, tag should match one of outputs
+ for _, outputFile := range g.outputFiles {
+ if outputFile.Rel() == tag {
+ return android.Paths{outputFile}, nil
}
}
+ return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
-// GenerateAndroidBuildActions implements the android.Module
-// GenerateAndroidBuildActions method, which generates all the rules and builds
-// commands used by this module instance.
-func (g *waylandGenModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
- ctx.ModuleErrorf("at least one `tools` or `tool_files` is required")
- return
- }
+// Ensure Module implements the genrule.SourceFileGenerator interface.
+var _ genrule.SourceFileGenerator = (*Module)(nil)
- // Prepare the list of tools that were defined for codegen purposes.
- tools, implicitDeps := g.prepareTools(ctx)
+// Ensure Module implements the android.SourceFileProducer interface.
+var _ android.SourceFileProducer = (*Module)(nil)
- if ctx.Failed() {
- return
+// Ensure Module implements the android.OutputFileProducer interface.
+var _ android.OutputFileProducer = (*Module)(nil)
+
+func toolDepsMutator(ctx android.BottomUpMutatorContext) {
+ if g, ok := ctx.Module().(*Module); ok {
+ for _, tool := range g.properties.Tools {
+ tag := hostToolDependencyTag{label: tool}
+ if m := android.SrcIsModule(tool); m != "" {
+ tool = m
+ }
+ ctx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), tag, tool)
+ }
}
+}
- // Emit the rule for generating for processing each source file
- g.emitRule(ctx, tools)
+// Part of android.MixedBuildBuildable.
+// Allow this module to be a bridge between Bazel and Soong. Fills in Soong
+// properties for the module from the Bazel cquery, so that other Soong
+// modules can depend on the module when it was actually built by Bazel.
+func (g *Module) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+ g.generateCommonBuildActions(ctx)
- if ctx.Failed() {
+ // Get the list of output files that Bazel generates for the target.
+ label := g.GetBazelLabel(ctx, g)
+ bazelCtx := ctx.Config().BazelContext
+ filePaths, err := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+ if err != nil {
+ ctx.ModuleErrorf(err.Error())
return
}
- generatedFilenamePrefix := proptools.String(g.properties.Prefix)
- generatedFilenameSuffix := proptools.String(g.properties.Suffix)
- for _, src := range android.PathsForModuleSrc(ctx, g.properties.Srcs) {
- out := g.generateOutputPath(ctx, src, generatedFilenamePrefix, generatedFilenameSuffix)
- if out == nil {
- continue
- }
-
- g.emitBuild(ctx, src, out, implicitDeps)
- g.outputFiles = append(g.outputFiles, out)
+ // Convert to android.Paths, and also form the set of include directories
+ // that might be needed for those paths.
+ var bazelOutputFiles android.Paths
+ exportIncludeDirs := map[string]bool{}
+ for _, bazelOutputFile := range filePaths {
+ bazelOutputFiles = append(bazelOutputFiles,
+ android.PathForBazelOutRelative(ctx, ctx.ModuleDir(), bazelOutputFile))
+ exportIncludeDirs[filepath.Dir(bazelOutputFile)] = true
}
- g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx))
-}
-
-// genrateOutputPath takes an source path, a prefix, and a suffix, and use them
-// to generate and return corresponding an output file path.
-func (g *waylandGenModule) generateOutputPath(ctx android.ModuleContext, src android.Path, prefix string, suffix string) android.WritablePath {
- // Construct a new filename by adding the requested prefix and suffix for this
- // code generator instance. If the input file name is "wayland.xml", and the
- // properties specify a prefix of "test-" and a suffix of "-client.cpp", we
- // will end up with a fulename of "test-wayland-client.cpp"
- protocolFilename, protocolExt := splitExt(src.Base())
- if protocolExt != ".xml" {
- ctx.ModuleErrorf("Source file %q does not end with .xml", src)
- return nil
+ // Set the Soong module properties to refer to the Bazel files
+ g.outputFiles = bazelOutputFiles
+ g.outputDeps = bazelOutputFiles
+ for includePath, _ := range exportIncludeDirs {
+ g.exportedIncludeDirs = append(g.exportedIncludeDirs,
+ android.PathForBazelOut(ctx, includePath))
}
- return android.PathForModuleGen(ctx, prefix+protocolFilename+suffix)
}
-// emitRule is an internal function to emit each Ninja rule.
-func (g *waylandGenModule) emitRule(ctx android.ModuleContext, tools map[string]android.Path) {
- // Get the command to run to process each protocol file. Since everything
- // should be templated, we generate a Ninja rule that uses the command,
- // and invoke it from each Ninja build command we emit.
- g.rule = ctx.Rule(pctx, "generator", blueprint.RuleParams{
- Command: g.expandCmd(ctx, tools),
- })
-}
+// Part of android.Module.
+// Generates all the rules and builds commands used by this module instance.
+func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) {
+ g.subName = ctx.ModuleSubDir()
-// emitBuild is an internal function to emit each Build command.
-func (g *waylandGenModule) emitBuild(ctx android.ModuleContext, src android.Path, out android.WritablePath, implicitDeps android.Paths) android.Path {
- ctx.Build(pctx, android.BuildParams{
- Rule: g.rule,
- Description: "generate " + out.Base(),
- Output: out,
- Inputs: android.Paths{src},
- Implicits: implicitDeps,
- })
+ // Collect the module directory for IDE info in java/jdeps.go.
+ g.modulePaths = append(g.modulePaths, ctx.ModuleDir())
- return out
-}
+ if len(g.properties.Export_include_dirs) > 0 {
+ for _, dir := range g.properties.Export_include_dirs {
+ g.exportedIncludeDirs = append(g.exportedIncludeDirs,
+ android.PathForModuleGen(ctx, g.subDir, ctx.ModuleDir(), dir))
+ }
+ } else {
+ g.exportedIncludeDirs = append(g.exportedIncludeDirs, android.PathForModuleGen(ctx, g.subDir))
+ }
-// prepareTools is an internal function to prepare a list of tools.
-func (g *waylandGenModule) prepareTools(ctx android.ModuleContext) (tools map[string]android.Path, implicitDeps android.Paths) {
- tools = map[string]android.Path{}
+ locationLabels := map[string]location{}
+ firstLabel := ""
- // This was extracted and slightly simplifed from equivalent code in
- // genrule.go.
+ addLocationLabel := func(label string, loc location) {
+ if firstLabel == "" {
+ firstLabel = label
+ }
+ if _, exists := locationLabels[label]; !exists {
+ locationLabels[label] = loc
+ } else {
+ ctx.ModuleErrorf("multiple locations for label %q: %q and %q (do you have duplicate srcs entries?)",
+ label, locationLabels[label], loc)
+ }
+ }
- // For each entry in "tool", walk the dependency graph to get more
- // information about it.
+ var tools android.Paths
+ var packagedTools []android.PackagingSpec
if len(g.properties.Tools) > 0 {
+ seenTools := make(map[string]bool)
+
ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
- tag := ctx.OtherModuleDependencyTag(module)
- if android.IsSourceDepTagWithOutputTag(tag, "") {
- // Nothing to do
- return
- }
- switch tag {
- case hostToolDepTag:
+ switch tag := ctx.OtherModuleDependencyTag(module).(type) {
+ case hostToolDependencyTag:
tool := ctx.OtherModuleName(module)
- var path android.OptionalPath
+ if m, ok := module.(android.Module); ok {
+ // Necessary to retrieve any prebuilt replacement for the tool, since
+ // toolDepsMutator runs too late for the prebuilt mutators to have
+ // replaced the dependency.
+ module = android.PrebuiltGetPreferred(ctx, m)
+ }
- if t, ok := module.(genrule.HostToolProvider); ok {
+ switch t := module.(type) {
+ case android.HostToolProvider:
+ // A HostToolProvider provides the path to a tool, which will be copied
+ // into the sandbox.
if !t.(android.Module).Enabled() {
if ctx.Config().AllowMissingDependencies() {
ctx.AddMissingDependencies([]string{tool})
} else {
ctx.ModuleErrorf("depends on disabled module %q", tool)
}
- break
+ return
}
- path = t.HostToolPath()
- } else {
+ path := t.HostToolPath()
+ if !path.Valid() {
+ ctx.ModuleErrorf("host tool %q missing output file", tool)
+ return
+ }
+ if specs := t.TransitivePackagingSpecs(); specs != nil {
+ // If the HostToolProvider has PackgingSpecs, which are definitions of the
+ // required relative locations of the tool and its dependencies, use those
+ // instead. They will be copied to those relative locations in the sbox
+ // sandbox.
+ packagedTools = append(packagedTools, specs...)
+ // Assume that the first PackagingSpec of the module is the tool.
+ addLocationLabel(tag.label, packagedToolLocation{specs[0]})
+ } else {
+ tools = append(tools, path.Path())
+ addLocationLabel(tag.label, toolLocation{android.Paths{path.Path()}})
+ }
+ case bootstrap.GoBinaryTool:
+ // A GoBinaryTool provides the install path to a tool, which will be copied.
+ p := android.PathForGoBinary(ctx, t)
+ tools = append(tools, p)
+ addLocationLabel(tag.label, toolLocation{android.Paths{p}})
+ default:
ctx.ModuleErrorf("%q is not a host tool provider", tool)
- break
+ return
+ }
+
+ seenTools[tag.label] = true
+ }
+ })
+
+ // If AllowMissingDependencies is enabled, the build will not have stopped when
+ // AddFarVariationDependencies was called on a missing tool, which will result in nonsensical
+ // "cmd: unknown location label ..." errors later. Add a placeholder file to the local label.
+ // The command that uses this placeholder file will never be executed because the rule will be
+ // replaced with an android.Error rule reporting the missing dependencies.
+ if ctx.Config().AllowMissingDependencies() {
+ for _, tool := range g.properties.Tools {
+ if !seenTools[tool] {
+ addLocationLabel(tool, errorLocation{"***missing tool " + tool + "***"})
}
+ }
+ }
+ }
+
+ if ctx.Failed() {
+ return
+ }
- if path.Valid() {
- implicitDeps = append(implicitDeps, path.Path())
- if _, exists := tools[tool]; !exists {
- tools[tool] = path.Path()
+ for _, toolFile := range g.properties.Tool_files {
+ paths := android.PathsForModuleSrc(ctx, []string{toolFile})
+ tools = append(tools, paths...)
+ addLocationLabel(toolFile, toolLocation{paths})
+ }
+
+ includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name())
+ var srcFiles android.Paths
+ for _, in := range g.properties.Srcs {
+ paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{
+ Context: ctx, Paths: []string{in}, ExcludePaths: g.properties.Exclude_srcs, IncludeDirs: includeDirInPaths,
+ })
+ if len(missingDeps) > 0 {
+ if !ctx.Config().AllowMissingDependencies() {
+ panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
+ missingDeps))
+ }
+
+ // If AllowMissingDependencies is enabled, the build will not have stopped when
+ // the dependency was added on a missing SourceFileProducer module, which will result in nonsensical
+ // "cmd: label ":..." has no files" errors later. Add a placeholder file to the local label.
+ // The command that uses this placeholder file will never be executed because the rule will be
+ // replaced with an android.Error rule reporting the missing dependencies.
+ ctx.AddMissingDependencies(missingDeps)
+ addLocationLabel(in, errorLocation{"***missing srcs " + in + "***"})
+ } else {
+ srcFiles = append(srcFiles, paths...)
+ addLocationLabel(in, inputLocation{paths})
+ }
+ }
+
+ var copyFrom android.Paths
+ var outputFiles android.WritablePaths
+ var zipArgs strings.Builder
+
+ cmd := proptools.String(g.properties.Cmd)
+
+ tasks := g.taskGenerator(ctx, cmd, srcFiles)
+ if ctx.Failed() {
+ return
+ }
+
+ for _, task := range tasks {
+ if len(task.out) == 0 {
+ ctx.ModuleErrorf("must have at least one output file")
+ return
+ }
+
+ // Pick a unique path outside the task.genDir for the sbox manifest textproto,
+ // a unique rule name, and the user-visible description.
+ manifestName := "wayland_protocol_codegen.sbox.textproto"
+ desc := "generate"
+ name := "generator"
+ if task.shards > 0 {
+ manifestName = "wayland_protocol_codegen_" + strconv.Itoa(task.shard) + ".sbox.textproto"
+ desc += " " + strconv.Itoa(task.shard)
+ name += strconv.Itoa(task.shard)
+ } else if len(task.out) == 1 {
+ desc += " " + task.out[0].Base()
+ }
+
+ manifestPath := android.PathForModuleOut(ctx, manifestName)
+
+ // Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
+ rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
+ cmd := rule.Command()
+
+ for _, out := range task.out {
+ addLocationLabel(out.Rel(), outputLocation{out})
+ }
+
+ rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
+ // Report the error directly without returning an error to android.Expand to catch multiple errors in a
+ // single run
+ reportError := func(fmt string, args ...interface{}) (string, error) {
+ ctx.PropertyErrorf("cmd", fmt, args...)
+ return "SOONG_ERROR", nil
+ }
+
+ // Apply shell escape to each cases to prevent source file paths containing $ from being evaluated in shell
+ switch name {
+ case "location":
+ if len(g.properties.Tools) == 0 && len(g.properties.Tool_files) == 0 {
+ return reportError("at least one `tools` or `tool_files` is required if $(location) is used")
+ }
+ loc := locationLabels[firstLabel]
+ paths := loc.Paths(cmd)
+ if len(paths) == 0 {
+ return reportError("default label %q has no files", firstLabel)
+ } else if len(paths) > 1 {
+ return reportError("default label %q has multiple files, use $(locations %s) to reference it",
+ firstLabel, firstLabel)
+ }
+ return proptools.ShellEscape(paths[0]), nil
+ case "in":
+ return strings.Join(proptools.ShellEscapeList(cmd.PathsForInputs(srcFiles)), " "), nil
+ case "out":
+ var sandboxOuts []string
+ for _, out := range task.out {
+ sandboxOuts = append(sandboxOuts, cmd.PathForOutput(out))
+ }
+ return strings.Join(proptools.ShellEscapeList(sandboxOuts), " "), nil
+ case "genDir":
+ return proptools.ShellEscape(cmd.PathForOutput(task.genDir)), nil
+ default:
+ if strings.HasPrefix(name, "location ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+ if loc, ok := locationLabels[label]; ok {
+ paths := loc.Paths(cmd)
+ if len(paths) == 0 {
+ return reportError("label %q has no files", label)
+ } else if len(paths) > 1 {
+ return reportError("label %q has multiple files, use $(locations %s) to reference it",
+ label, label)
+ }
+ return proptools.ShellEscape(paths[0]), nil
} else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], path.Path().String())
+ return reportError("unknown location label %q is not in srcs, out, tools or tool_files.", label)
+ }
+ } else if strings.HasPrefix(name, "locations ") {
+ label := strings.TrimSpace(strings.TrimPrefix(name, "locations "))
+ if loc, ok := locationLabels[label]; ok {
+ paths := loc.Paths(cmd)
+ if len(paths) == 0 {
+ return reportError("label %q has no files", label)
+ }
+ return proptools.ShellEscape(strings.Join(paths, " ")), nil
+ } else {
+ return reportError("unknown locations label %q is not in srcs, out, tools or tool_files.", label)
}
} else {
- ctx.ModuleErrorf("host tool %q missing output file", tool)
+ return reportError("unknown variable '$(%s)'", name)
}
}
})
- }
- // Get more information about each entry in "tool_files".
- for _, tool := range g.properties.Tool_files {
- toolPath := android.PathForModuleSrc(ctx, tool)
- implicitDeps = append(implicitDeps, toolPath)
- if _, exists := tools[tool]; !exists {
- tools[tool] = toolPath
+ if err != nil {
+ ctx.PropertyErrorf("cmd", "%s", err.Error())
+ return
+ }
+
+ g.rawCommands = append(g.rawCommands, rawCommand)
+
+ cmd.Text(rawCommand)
+ cmd.ImplicitOutputs(task.out)
+ cmd.Implicits(task.in)
+ cmd.ImplicitTools(tools)
+ cmd.ImplicitPackagedTools(packagedTools)
+
+ // Create the rule to run the genrule command inside sbox.
+ rule.Build(name, desc)
+
+ if len(task.copyTo) > 0 {
+ // If copyTo is set, multiple shards need to be copied into a single directory.
+ // task.out contains the per-shard paths, and copyTo contains the corresponding
+ // final path. The files need to be copied into the final directory by a
+ // single rule so it can remove the directory before it starts to ensure no
+ // old files remain. zipsync already does this, so build up zipArgs that
+ // zip all the per-shard directories into a single zip.
+ outputFiles = append(outputFiles, task.copyTo...)
+ copyFrom = append(copyFrom, task.out.Paths()...)
+ zipArgs.WriteString(" -C " + task.genDir.String())
+ zipArgs.WriteString(android.JoinWithPrefix(task.out.Strings(), " -f "))
} else {
- ctx.ModuleErrorf("multiple tools for %q, %q and %q", tool, tools[tool], toolPath.String())
+ outputFiles = append(outputFiles, task.out...)
}
}
- return
+
+ if len(copyFrom) > 0 {
+ // Create a rule that zips all the per-shard directories into a single zip and then
+ // uses zipsync to unzip it into the final directory.
+ ctx.Build(pctx, android.BuildParams{
+ Rule: gensrcsMerge,
+ Implicits: copyFrom,
+ Outputs: outputFiles,
+ Description: "merge shards",
+ Args: map[string]string{
+ "zipArgs": zipArgs.String(),
+ "tmpZip": android.PathForModuleGen(ctx, g.subDir+".zip").String(),
+ "genDir": android.PathForModuleGen(ctx, g.subDir).String(),
+ },
+ })
+ }
+
+ g.outputFiles = outputFiles.Paths()
}
-// expandCmd is an internal function to do some expansion and any additional
-// wrapping of the generator command line. Returns the command line to use and
-// an error value.
-func (g *waylandGenModule) expandCmd(ctx android.ModuleContext, tools map[string]android.Path) (cmd string) {
- cmd, err := android.Expand(proptools.String(g.properties.Cmd), func(name string) (string, error) {
- switch name {
- case "in":
- return "$in", nil
- case "out":
- // We need to use the sandbox out path instead
- //return "$sandboxOut", nil
- return "$out", nil
- case "location":
- if len(g.properties.Tools) > 0 {
- return tools[g.properties.Tools[0]].String(), nil
- } else {
- return tools[g.properties.Tool_files[0]].String(), nil
+func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ g.generateCommonBuildActions(ctx)
+
+ // When there are less than six outputs, we directly give those as the
+ // output dependency for this module. However, if there are more outputs,
+ // we inject a phony target. This potentially saves space in the generated
+ // ninja file, as well as simplifying any visualizations of the dependency
+ // graph.
+ if len(g.outputFiles) <= 6 {
+ g.outputDeps = g.outputFiles
+ } else {
+ phonyFile := android.PathForModuleGen(ctx, "genrule-phony")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: blueprint.Phony,
+ Output: phonyFile,
+ Inputs: g.outputFiles,
+ })
+ g.outputDeps = android.Paths{phonyFile}
+ }
+}
+
+// Part of android.MixedBuildBuildable.
+// Queues up Bazel cquery requests related to this module.
+func (g *Module) QueueBazelCall(ctx android.BaseModuleContext) {
+ bazelCtx := ctx.Config().BazelContext
+ bazelCtx.QueueBazelRequest(g.GetBazelLabel(ctx, g), cquery.GetOutputFiles,
+ android.GetConfigKey(ctx))
+}
+
+// Part of android.MixedBuildBuildable.
+// Returns true if Bazel can and should build this module in a mixed build.
+func (g *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+ return true
+}
+
+// Part of android.IDEInfo.
+// Collect information for opening IDE project files in java/jdeps.go.
+func (g *Module) IDEInfo(dpInfo *android.IdeInfo) {
+ dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...)
+ for _, src := range g.properties.Srcs {
+ if strings.HasPrefix(src, ":") {
+ src = strings.Trim(src, ":")
+ dpInfo.Deps = append(dpInfo.Deps, src)
+ }
+ }
+ dpInfo.Paths = append(dpInfo.Paths, g.modulePaths...)
+}
+
+// Ensure Module implements android.ApexModule
+// Note: gensrcs implements it but it's possible we do not actually need to.
+var _ android.ApexModule = (*Module)(nil)
+
+// Part of android.ApexModule.
+func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
+ sdkVersion android.ApiLevel) error {
+ // Because generated outputs are checked by client modules(e.g. cc_library, ...)
+ // we can safely ignore the check here.
+ return nil
+}
+
+func generatorFactory(taskGenerator taskFunc, props ...interface{}) *Module {
+ module := &Module{
+ taskGenerator: taskGenerator,
+ }
+
+ module.AddProperties(props...)
+ module.AddProperties(&module.properties)
+
+ module.ImageInterface = noopImageInterface{}
+
+ return module
+}
+
+type noopImageInterface struct{}
+
+func (x noopImageInterface) ImageMutatorBegin(android.BaseModuleContext) {}
+func (x noopImageInterface) CoreVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) RamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) VendorRamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) DebugRamdiskVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) RecoveryVariantNeeded(android.BaseModuleContext) bool { return false }
+func (x noopImageInterface) ExtraImageVariations(ctx android.BaseModuleContext) []string { return nil }
+func (x noopImageInterface) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
+
+// Constructs a Module for handling the code generation.
+func newCodegen() *Module {
+ properties := &codegenProperties{}
+
+ // finalSubDir is the name of the subdirectory that output files will be generated into.
+ // It is used so that per-shard directories can be placed alongside it an then finally
+ // merged into it.
+ const finalSubDir = "wayland_protocol_codegen"
+
+ // Code generation commands are sharded so that up to this many files
+ // are generated as part of one sandbox process.
+ const defaultShardSize = 100
+
+ taskGenerator := func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask {
+ shardSize := defaultShardSize
+
+ if len(srcFiles) == 0 {
+ ctx.ModuleErrorf("must have at least one source file")
+ return []generateTask{}
+ }
+
+ // wayland_protocol_codegen rules can easily hit command line limits by
+ // repeating the command for every input file. Shard the input files into
+ // groups.
+ shards := android.ShardPaths(srcFiles, shardSize)
+ var generateTasks []generateTask
+
+ distinctOutputs := make(map[string]android.Path)
+
+ for i, shard := range shards {
+ var commands []string
+ var outFiles android.WritablePaths
+ var copyTo android.WritablePaths
+
+ // When sharding is enabled (i.e. len(shards) > 1), the sbox rules for each
+ // shard will be write to their own directories and then be merged together
+ // into finalSubDir. If sharding is not enabled (i.e. len(shards) == 1),
+ // the sbox rule will write directly to finalSubDir.
+ genSubDir := finalSubDir
+ if len(shards) > 1 {
+ genSubDir = strconv.Itoa(i)
}
- default:
- if strings.HasPrefix(name, "location ") {
- label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
- if tool, ok := tools[label]; ok {
- return tool.String(), nil
- } else {
- return "", fmt.Errorf("unknown location label %q", label)
+
+ genDir := android.PathForModuleGen(ctx, genSubDir)
+ // NOTE: This TODO is copied from gensrcs, as applies here too.
+ // TODO(ccross): this RuleBuilder is a hack to be able to call
+ // rule.Command().PathForOutput. Replace this with passing the rule into the
+ // generator.
+ rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
+
+ for _, in := range shard {
+ outFileRaw := expandOutputPath(ctx, *properties, in)
+
+ if conflictWith, hasKey := distinctOutputs[outFileRaw]; hasKey {
+ ctx.ModuleErrorf("generation conflict: both '%v' and '%v' generate '%v'",
+ conflictWith.String(), in.String(), outFileRaw)
}
+
+ distinctOutputs[outFileRaw] = in
+
+ outFile := android.PathForModuleGen(ctx, finalSubDir, outFileRaw)
+
+ // If sharding is enabled, then outFile is the path to the output file in
+ // the shard directory, and copyTo is the path to the output file in the
+ // final directory.
+ if len(shards) > 1 {
+ shardFile := android.PathForModuleGen(ctx, genSubDir, outFileRaw)
+ copyTo = append(copyTo, outFile)
+ outFile = shardFile
+ }
+
+ outFiles = append(outFiles, outFile)
+
+ // pre-expand the command line to replace $in and $out with references to
+ // a single input and output file.
+ command, err := android.Expand(rawCommand, func(name string) (string, error) {
+ switch name {
+ case "in":
+ return in.String(), nil
+ case "out":
+ return rule.Command().PathForOutput(outFile), nil
+ default:
+ return "$(" + name + ")", nil
+ }
+ })
+ if err != nil {
+ ctx.PropertyErrorf("cmd", err.Error())
+ }
+
+ // escape the command in case for example it contains '#', an odd number of '"', etc
+ command = fmt.Sprintf("bash -c %v", proptools.ShellEscape(command))
+ commands = append(commands, command)
}
- return "", fmt.Errorf("unknown variable '$(%s)'", name)
+ fullCommand := strings.Join(commands, " && ")
+
+ generateTasks = append(generateTasks, generateTask{
+ in: shard,
+ out: outFiles,
+ copyTo: copyTo,
+ genDir: genDir,
+ cmd: fullCommand,
+ shard: i,
+ shards: len(shards),
+ })
}
- })
- if err != nil {
- ctx.PropertyErrorf("cmd", "%s", err.Error())
+
+ return generateTasks
}
- return
+
+ g := generatorFactory(taskGenerator, properties)
+ g.subDir = finalSubDir
+ return g
}
-// waylandCodegenModuleFactory creates an extension module instance.
-func waylandCodegenModuleFactory() android.Module {
- m := &waylandGenModule{}
- m.AddProperties(&m.properties)
+// Factory for code generation modules
+func codegenFactory() android.Module {
+ m := newCodegen()
android.InitAndroidModule(m)
+ android.InitBazelModule(m)
+ android.InitDefaultableModule(m)
return m
}
-// splitExt splits a base filename into (filename, ext) components, such that
-// input == filename + ext
-func splitExt(input string) (filename string, ext string) {
- // There is no filepath.SplitExt() or equivalent.
- dot := strings.LastIndex(input, ".")
- if dot != -1 {
- ext = input[dot:]
- filename = input[:dot]
- } else {
- ext = ""
- filename = input
+// The custom properties specific to this code generation module.
+type codegenProperties struct {
+ // The string to prepend to every protocol filename to generate the
+ // corresponding output filename. The empty string by default.
+ // Deprecated. Prefer "Output" instead.
+ Prefix *string
+
+ // The suffix to append to every protocol filename to generate the
+ // corresponding output filename. The empty string by default.
+ // Deprecated. Prefer "Output" instead.
+ Suffix *string
+
+ // The output filename template.
+ //
+ // This template string allows the output file name to be generated for
+ // each source file, using some limited properties of the source file.
+ //
+ // $(in:base): The base filename, no path or extension
+ // $(in:base.ext): The filename, no path
+ // $(in:path/base): The filename with path but no extension
+ // $(in:path/base.ext): The full source filename
+ // $(in): An alias for $(in:base) for the base filename, no extension
+ //
+ // Note that the path that is maintained is the relative path used when
+ // including the source in an Android.bp file.
+ //
+ // The template allows arbitrary prefixes and suffixes to be added to the
+ // output filename. For example, "a_$(in).d" would take an source filename
+ // of "b.c" and turn it into "a_b.d".
+ //
+ // The output template does not have to generate a unique filename,
+ // however the implementation will raise an error if the same output file
+ // is generated by more than one source file.
+ Output *string
+}
+
+// Expands the output path pattern to form the output path for the given
+// input path.
+func expandOutputPath(ctx android.ModuleContext, properties codegenProperties, in android.Path) string {
+ template := proptools.String(properties.Output)
+ if len(template) == 0 {
+ prefix := proptools.String(properties.Prefix)
+ suffix := proptools.String(properties.Suffix)
+ return prefix + removeExtension(in.Base()) + suffix
+ }
+
+ outPath, _ := android.Expand(template, func(name string) (string, error) {
+ // Report the error directly without returning an error to
+ // android.Expand to catch multiple errors in a single run.
+ reportError := func(fmt string, args ...interface{}) (string, error) {
+ ctx.PropertyErrorf("output", fmt, args...)
+ return "EXPANSION_ERROR", nil
+ }
+
+ switch name {
+ case "in":
+ return removeExtension(in.Base()), nil
+ case "in:base":
+ return removeExtension(in.Base()), nil
+ case "in:base.ext":
+ return in.Base(), nil
+ case "in:path/base":
+ return removeExtension(in.Rel()), nil
+ case "in:path/base.ext":
+ return in.Rel(), nil
+ default:
+ return reportError("unknown variable '$(%s)'", name)
+ }
+ })
+
+ return outPath
+}
+
+// Removes any extension from the final component of a path.
+func removeExtension(path string) string {
+ // Note: This implementation does not handle files like ".bashrc" correctly.
+ if dot := strings.LastIndex(path, "."); dot != -1 {
+ return path[:dot]
+ }
+ return path
+}
+
+// The attributes for the custom local ./bazel/gensrcs.bzl. See the .bzl file
+// for attribute documentation.
+type bazelGensrcsAttributes struct {
+ Srcs bazel.LabelListAttribute
+ Output string
+ Tools bazel.LabelListAttribute
+ Cmd string
+}
+
+// ConvertWithBp2build converts a Soong module -> Bazel target.
+func (m *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+ // Bazel only has the "tools" attribute.
+ tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
+ tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
+ tools_prop.Append(tool_files_prop)
+
+ tools := bazel.MakeLabelListAttribute(tools_prop)
+ srcs := bazel.LabelListAttribute{}
+ srcs_labels := bazel.LabelList{}
+ srcs_labels = android.BazelLabelForModuleSrcExcludes(
+ ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+ srcs = bazel.MakeLabelListAttribute(srcs_labels)
+
+ var allReplacements bazel.LabelList
+ allReplacements.Append(tools.Value)
+ allReplacements.Append(bazel.FirstUniqueBazelLabelList(srcs_labels))
+
+ // Convert the command line template.
+ var cmd string
+ if m.properties.Cmd != nil {
+ // $(in) becomes $(SRC) in our custom gensrcs.bzl
+ cmd = strings.ReplaceAll(*m.properties.Cmd, "$(in)", "$(SRC)")
+ // $(out) becomes $(OUT) in our custom gensrcs.bzl
+ cmd = strings.ReplaceAll(cmd, "$(out)", "$(OUT)")
+ // $(gendir) becomes $(RULEDIR) in our custom gensrcs.bzl
+ cmd = strings.Replace(cmd, "$(genDir)", "$(RULEDIR)", -1)
+
+ // $(location) or $(locations) becomes the more explicit
+ // $(location <default-tool-label>) in Bazel.
+ if len(tools.Value.Includes) > 0 {
+ cmd = strings.Replace(cmd, "$(location)",
+ fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
+ cmd = strings.Replace(cmd, "$(locations)",
+ fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
+ }
+
+ // Translate all the other $(location <name>) and $(locations <name>)
+ // expansion placeholders.
+ for _, l := range allReplacements.Includes {
+ bpLoc := fmt.Sprintf("$(location %s)", l.OriginalModuleName)
+ bpLocs := fmt.Sprintf("$(locations %s)", l.OriginalModuleName)
+ bazelLoc := fmt.Sprintf("$(location %s)", l.Label)
+ bazelLocs := fmt.Sprintf("$(locations %s)", l.Label)
+ cmd = strings.Replace(cmd, bpLoc, bazelLoc, -1)
+ cmd = strings.Replace(cmd, bpLocs, bazelLocs, -1)
+ }
}
- return
+
+ tags := android.ApexAvailableTags(m)
+
+ // The Output_extension prop is not in an immediately accessible field
+ // in the Module struct, so use GetProperties and cast it
+ // to the known struct prop.
+ var outputFileTemplate string
+ for _, propIntf := range m.GetProperties() {
+ if props, ok := propIntf.(*codegenProperties); ok {
+ // Convert the output path template.
+ outputFileTemplate = proptools.String(props.Output)
+ if len(outputFileTemplate) > 0 {
+ outputFileTemplate = strings.Replace(
+ outputFileTemplate, "$(in)", "$(SRC:BASE)", -1)
+ outputFileTemplate = strings.Replace(
+ outputFileTemplate, "$(in:path/base.ext)", "$(SRC:PATH/BASE.EXT)", -1)
+ outputFileTemplate = strings.Replace(
+ outputFileTemplate, "$(in:path/base)", "$(SRC:PATH/BASE)", -1)
+ outputFileTemplate = strings.Replace(
+ outputFileTemplate, "$(in:base.ext)", "$(SRC:BASE.EXT)", -1)
+ outputFileTemplate = strings.Replace(
+ outputFileTemplate, "$(in:base)", "$(SRC:BASE)", -1)
+ } else {
+ outputFileTemplate = proptools.String(props.Prefix) + "$(SRC:BASE)" +
+ proptools.String(props.Suffix)
+ }
+ break
+ }
+ }
+ props := bazel.BazelTargetModuleProperties{
+ Rule_class: "gensrcs",
+ Bzl_load_location: "//external/wayland-protocols/bazel:gensrcs.bzl",
+ }
+ attrs := &bazelGensrcsAttributes{
+ Srcs: srcs,
+ Output: outputFileTemplate,
+ Cmd: cmd,
+ Tools: tools,
+ }
+ ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+ Name: m.Name(),
+ Tags: tags,
+ }, attrs)
+}
+
+// Defaults module.
+type Defaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func defaultsFactory() android.Module {
+ return DefaultsFactory()
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+ module := &Defaults{}
+
+ module.AddProperties(props...)
+ module.AddProperties(
+ &generatorProperties{},
+ &codegenProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+
+ return module
}
diff --git a/wayland_protocol_codegen_test.go b/wayland_protocol_codegen_test.go
new file mode 100644
index 0000000..90f1200
--- /dev/null
+++ b/wayland_protocol_codegen_test.go
@@ -0,0 +1,488 @@
+// Copyright 2023 The Android Open Source Project
+//
+// 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.
+
+package soong_wayland_protocol_codegen
+
+import (
+ "os"
+ "regexp"
+ "testing"
+
+ "android/soong/android"
+)
+
+func TestMain(m *testing.M) {
+ os.Exit(m.Run())
+}
+
+var prepareForCodeGenTest = android.GroupFixturePreparers(
+ android.PrepareForTestWithArchMutator,
+ android.PrepareForTestWithDefaults,
+ android.PrepareForTestWithFilegroup,
+ android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerCodeGenBuildComponents),
+ ),
+ android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+ android.RegisterPrebuiltMutators(ctx)
+ ctx.RegisterModuleType("fake_android_host_tool", fakeAndroidHostToolFactory)
+ }),
+ android.FixtureMergeMockFs(android.MockFS{
+ "android_host_tool": nil,
+ "tool_src_file": nil,
+ "tool_src_file_1": nil,
+ "tool_src_file_2": nil,
+ "src_file": nil,
+ "src_file_1": nil,
+ "src_file_2": nil,
+ }),
+)
+
+func testCodeGenBp() string {
+ return `
+ fake_android_host_tool {
+ name: "host_tool",
+ }
+
+ filegroup {
+ name: "tool_single_source_file_filegroup",
+ srcs: [
+ "tool_src_file",
+ ],
+ }
+
+ filegroup {
+ name: "tool_multi_source_files_filegroup",
+ srcs: [
+ "tool_src_file_1",
+ "tool_src_file_2",
+ ],
+ }
+
+ filegroup {
+ name: "single_source_filegroup",
+ srcs: [
+ "src_file",
+ ],
+ }
+
+ filegroup {
+ name: "multi_source_filegroup",
+ srcs: [
+ "src_file_1",
+ "src_file_2",
+ ],
+ }
+
+ filegroup {
+ name: "empty_filegroup",
+ }
+ `
+}
+
+func TestWaylandCodeGen(t *testing.T) {
+ testcases := []struct {
+ name string
+ prop string
+
+ err string
+ cmds []string
+ files []string
+ }{
+ {
+ name: "single_source_with_host_tool",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["src_file"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
+ },
+ },
+ {
+ name: "multi_source_with_host_tool",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["src_file_1", "src_file_2"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
+ },
+ },
+ {
+ name: "single_source_filegroup_with_host_tool",
+ prop: `
+ tools: ["host_tool"],
+ srcs: [":single_source_filegroup"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
+ },
+ },
+ {
+ name: "multi_source_filegroup_with_host_tool",
+ prop: `
+ tools: ["host_tool"],
+ srcs: [":multi_source_filegroup"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
+ },
+ },
+ {
+ name: "single_source_with_single_tool_file",
+ prop: `
+ tool_files: ["tool_src_file"],
+ srcs: ["src_file"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location tool_src_file) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
+ },
+ },
+ {
+ name: "multi_source_with_single_tool_file",
+ prop: `
+ tool_files: ["tool_src_file"],
+ srcs: ["src_file_1", "src_file_2"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location tool_src_file) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
+ },
+ },
+ {
+ name: "single_source_filegroup_with_single_tool_file",
+ prop: `
+ tool_files: ["tool_src_file"],
+ srcs: [":single_source_filegroup"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location tool_src_file) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
+ },
+ },
+ {
+ name: "multi_source_filegroup_with_single_tool_file",
+ prop: `
+ tool_files: ["tool_src_file"],
+ srcs: [":multi_source_filegroup"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location tool_src_file) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_1 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_1_suffix' && bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file gen < src_file_2 > __SBOX_SANDBOX_DIR__/out/prefix_src_file_2_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_1_suffix",
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_2_suffix",
+ },
+ },
+ {
+ name: "multiple_tool_files",
+ prop: `
+ tool_files: ["tool_src_file_1", "tool_src_file_2"],
+ srcs: ["src_file"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location tool_src_file_1) $(location tool_src_file_2) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/tool_src_file_1 __SBOX_SANDBOX_DIR__/tools/src/tool_src_file_2 gen < src_file > __SBOX_SANDBOX_DIR__/out/prefix_src_file_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/prefix_src_file_suffix",
+ },
+ },
+ {
+ name: "output_template_explicit_base_only",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["txt/a/file.txt"],
+ output: "$(in:base)",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/file'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/file",
+ },
+ },
+ {
+ name: "output_template_explicit_base_and_ext",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["txt/a/file.txt"],
+ output: "$(in:base.ext)",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/file.txt'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/file.txt",
+ },
+ },
+ {
+ name: "output_template_explicit_path_and_base",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["txt/a/file.txt"],
+ output: "$(in:path/base)",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/txt/a/file'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/txt/a/file",
+ },
+ },
+ {
+ name: "output_template_explicit_path_and_base_and_ext",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["txt/a/file.txt"],
+ output: "$(in:path/base.ext)",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < txt/a/file.txt > __SBOX_SANDBOX_DIR__/out/txt/a/file.txt'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/txt/a/file.txt",
+ },
+ },
+ {
+ name: "single_source_file_does_not_need_distinct_outputs",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["src_file"],
+ output: "output",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/output'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/output",
+ },
+ },
+ {
+ name: "legacy_prefix_suffix",
+ prop: `
+ tools: ["host_tool"],
+ srcs: ["src_file"],
+ prefix: "legacy_prefix_",
+ suffix: "_legacy_suffix",
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ cmds: []string{
+ "bash -c '__SBOX_SANDBOX_DIR__/tools/src/out/host_tool gen < src_file > __SBOX_SANDBOX_DIR__/out/legacy_prefix_src_file_legacy_suffix'",
+ },
+ files: []string{
+ "out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/legacy_prefix_src_file_legacy_suffix",
+ },
+ },
+ {
+ name: "error_if_no_sources",
+ prop: `
+ tools: ["host_tool"],
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ err: "must have at least one source file",
+ },
+ {
+ name: "error_if_no_filegroup_sources",
+ prop: `
+ tools: ["host_tool"],
+ srcs: [":empty_filegroup"],
+ cmd: "$(location host_tool) gen < $(in) > $(out)",
+ `,
+ err: "must have at least one source file",
+ },
+ {
+ name: "error_if_in_outputs_are_not_distinct",
+ prop: `
+ tools: ["host_tool"],
+ tool_files: ["tool_src_file"],
+ srcs: ["src_file_1", "src_file_2"],
+ output: "not_unique",
+ cmd: "$(location)"
+ `,
+ err: "Android.bp:39:2: module \"codegen\": generation conflict: both 'src_file_1' and 'src_file_2' generate 'not_unique'",
+ },
+ {
+ name: "error_if_output_expansion_fails",
+ prop: `
+ tools: ["host_tool"],
+ tool_files: ["tool_src_file"],
+ srcs: ["src_file"],
+ output: "prefix_$(bad)_suffix",
+ cmd: "$(location)"
+ `,
+ err: "Android.bp:45:11: module \"codegen\": output: unknown variable '$(bad)'",
+ },
+ {
+ name: "error_if_cmd_expansion_fails",
+ prop: `
+ tools: ["host_tool"],
+ tool_files: ["tool_src_file"],
+ srcs: ["src_file"],
+ output: "prefix_$(in)_suffix",
+ cmd: "$(location bad_name)"
+ `,
+ err: "Android.bp:46:8: module \"codegen\": cmd: unknown location label \"bad_name\"",
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ bp := "wayland_protocol_codegen {\n"
+ bp += `name: "codegen",` + "\n"
+ bp += test.prop
+ bp += "}\n"
+
+ var expectedErrors []string
+ if test.err != "" {
+ expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
+ }
+
+ result := prepareForCodeGenTest.
+ ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
+ RunTestWithBp(t, testCodeGenBp()+bp)
+
+ if expectedErrors != nil {
+ return
+ }
+
+ gen := result.Module("codegen", "").(*Module)
+ android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)
+ android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
+ })
+ }
+}
+
+func TestGenruleWithBazel(t *testing.T) {
+ bp := `
+ wayland_protocol_codegen {
+ name: "mixed_codegen",
+ srcs: ["src_file"],
+ bazel_module: { label: "//example:bazel_codegen" },
+ }
+ `
+
+ result := android.GroupFixturePreparers(
+ prepareForCodeGenTest, android.FixtureModifyConfig(func(config android.Config) {
+ config.BazelContext = android.MockBazelContext{
+ OutputBaseDir: "outputbase",
+ LabelToOutputFiles: map[string][]string{
+ "//example:bazel_codegen": {"bazelone.txt", "bazeltwo.txt"}}}
+ })).RunTestWithBp(t, testCodeGenBp()+bp)
+
+ gen := result.Module("mixed_codegen", "").(*Module)
+
+ expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
+ "outputbase/execroot/__main__/bazeltwo.txt"}
+ android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
+ android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
+}
+
+func TestDefaults(t *testing.T) {
+ bp := `
+ wayland_protocol_codegen_defaults {
+ name: "gen_defaults1",
+ cmd: "cp $(in) $(out)",
+ output: "$(in).h",
+ }
+
+ wayland_protocol_codegen_defaults {
+ name: "gen_defaults2",
+ srcs: ["in1"],
+ }
+
+ wayland_protocol_codegen {
+ name: "codegen",
+ defaults: ["gen_defaults1", "gen_defaults2"],
+ }
+ `
+
+ result := prepareForCodeGenTest.RunTestWithBp(t, testCodeGenBp()+bp)
+
+ gen := result.Module("codegen", "").(*Module)
+
+ expectedCmd := "bash -c cp in1 __SBOX_SANDBOX_DIR__/out/in1.h"
+ android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
+
+ expectedSrcs := []string{"in1"}
+ android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
+
+ expectedFiles := []string{"out/soong/.intermediates/codegen/gen/wayland_protocol_codegen/in1.h"}
+ android.AssertPathsRelativeToTopEquals(t, "files", expectedFiles, gen.outputFiles)
+}
+
+type fakeAndroidHostTool struct {
+ android.ModuleBase
+ outputFile android.Path
+}
+
+func fakeAndroidHostToolFactory() android.Module {
+ module := &fakeAndroidHostTool{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func (t *fakeAndroidHostTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+}
+
+func (t *fakeAndroidHostTool) HostToolPath() android.OptionalPath {
+ return android.OptionalPathForPath(t.outputFile)
+}
+
+var _ android.HostToolProvider = (*fakeAndroidHostTool)(nil)