xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/gopackagesdriver/aspect.bzl (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1# Copyright 2021 The Bazel Go Rules Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load(
16    "//go/private:providers.bzl",
17    "GoArchive",
18    "GoStdLib",
19)
20load(
21    "@bazel_skylib//lib:paths.bzl",
22    "paths",
23)
24
25GoPkgInfo = provider()
26
27DEPS_ATTRS = [
28    "deps",
29    "embed",
30]
31
32PROTO_COMPILER_ATTRS = [
33    "compiler",
34    "compilers",
35    "library",
36]
37
38def bazel_supports_canonical_label_literals():
39    return str(Label("//:bogus")).startswith("@@")
40
41def is_file_external(f):
42    return f.owner.workspace_root != ""
43
44def file_path(f):
45    prefix = "__BAZEL_WORKSPACE__"
46    if not f.is_source:
47        prefix = "__BAZEL_EXECROOT__"
48    elif is_file_external(f):
49        prefix = "__BAZEL_OUTPUT_BASE__"
50    return paths.join(prefix, f.path)
51
52def _go_archive_to_pkg(archive):
53    return struct(
54        ID = str(archive.data.label),
55        PkgPath = archive.data.importpath,
56        ExportFile = file_path(archive.data.export_file),
57        GoFiles = [
58            file_path(src)
59            for src in archive.data.orig_srcs
60            if src.path.endswith(".go")
61        ],
62        CompiledGoFiles = [
63            file_path(src)
64            for src in archive.data.srcs
65            if src.path.endswith(".go")
66        ],
67        OtherFiles = [
68            file_path(src)
69            for src in archive.data.orig_srcs
70            if not src.path.endswith(".go")
71        ],
72        Imports = {
73            pkg.data.importpath: str(pkg.data.label)
74            for pkg in archive.direct
75        },
76    )
77
78def make_pkg_json(ctx, name, pkg_info):
79    pkg_json_file = ctx.actions.declare_file(name + ".pkg.json")
80    ctx.actions.write(pkg_json_file, content = pkg_info.to_json())
81    return pkg_json_file
82
83def _go_pkg_info_aspect_impl(target, ctx):
84    # Fetch the stdlib JSON file from the inner most target
85    stdlib_json_file = None
86
87    transitive_json_files = []
88    transitive_export_files = []
89    transitive_compiled_go_files = []
90
91    for attr in DEPS_ATTRS + PROTO_COMPILER_ATTRS:
92        deps = getattr(ctx.rule.attr, attr, []) or []
93
94        # Some attrs are not iterable, ensure that deps is always iterable.
95        if type(deps) != type([]):
96            deps = [deps]
97
98        for dep in deps:
99            if GoPkgInfo in dep:
100                pkg_info = dep[GoPkgInfo]
101                transitive_json_files.append(pkg_info.pkg_json_files)
102                transitive_compiled_go_files.append(pkg_info.compiled_go_files)
103                transitive_export_files.append(pkg_info.export_files)
104
105                # Fetch the stdlib json from the first dependency
106                if not stdlib_json_file:
107                    stdlib_json_file = pkg_info.stdlib_json_file
108
109    pkg_json_files = []
110    compiled_go_files = []
111    export_files = []
112
113    if GoArchive in target:
114        archive = target[GoArchive]
115        compiled_go_files.extend(archive.source.srcs)
116        export_files.append(archive.data.export_file)
117        pkg = _go_archive_to_pkg(archive)
118        pkg_json_files.append(make_pkg_json(ctx, archive.data.name, pkg))
119
120        if ctx.rule.kind == "go_test":
121            for dep_archive in archive.direct:
122                # find the archive containing the test sources
123                if archive.data.label == dep_archive.data.label:
124                    pkg = _go_archive_to_pkg(dep_archive)
125                    pkg_json_files.append(make_pkg_json(ctx, dep_archive.data.name, pkg))
126                    compiled_go_files.extend(dep_archive.source.srcs)
127                    export_files.append(dep_archive.data.export_file)
128                    break
129
130    # If there was no stdlib json in any dependencies, fetch it from the
131    # current go_ node.
132    if not stdlib_json_file:
133        stdlib_json_file = ctx.attr._go_stdlib[GoStdLib]._list_json
134
135    pkg_info = GoPkgInfo(
136        stdlib_json_file = stdlib_json_file,
137        pkg_json_files = depset(
138            direct = pkg_json_files,
139            transitive = transitive_json_files,
140        ),
141        compiled_go_files = depset(
142            direct = compiled_go_files,
143            transitive = transitive_compiled_go_files,
144        ),
145        export_files = depset(
146            direct = export_files,
147            transitive = transitive_export_files,
148        ),
149    )
150
151    return [
152        pkg_info,
153        OutputGroupInfo(
154            go_pkg_driver_json_file = pkg_info.pkg_json_files,
155            go_pkg_driver_srcs = pkg_info.compiled_go_files,
156            go_pkg_driver_export_file = pkg_info.export_files,
157            go_pkg_driver_stdlib_json_file = depset([pkg_info.stdlib_json_file] if pkg_info.stdlib_json_file else []),
158        ),
159    ]
160
161go_pkg_info_aspect = aspect(
162    implementation = _go_pkg_info_aspect_impl,
163    attr_aspects = DEPS_ATTRS + PROTO_COMPILER_ATTRS,
164    attrs = {
165        "_go_stdlib": attr.label(
166            default = "//:stdlib",
167        ),
168    },
169)
170