1*60517a1eSAndroid Build Coastguard Worker# Copyright 2023 The Bazel Authors. All rights reserved. 2*60517a1eSAndroid Build Coastguard Worker# 3*60517a1eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*60517a1eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*60517a1eSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*60517a1eSAndroid Build Coastguard Worker# 7*60517a1eSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*60517a1eSAndroid Build Coastguard Worker# 9*60517a1eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*60517a1eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*60517a1eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*60517a1eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*60517a1eSAndroid Build Coastguard Worker# limitations under the License. 14*60517a1eSAndroid Build Coastguard Worker 15*60517a1eSAndroid Build Coastguard Worker"""This module provides the gazelle_python_manifest macro that contains targets 16*60517a1eSAndroid Build Coastguard Workerfor updating and testing the Gazelle manifest file. 17*60517a1eSAndroid Build Coastguard Worker""" 18*60517a1eSAndroid Build Coastguard Worker 19*60517a1eSAndroid Build Coastguard Workerload("@bazel_skylib//rules:diff_test.bzl", "diff_test") 20*60517a1eSAndroid Build Coastguard Workerload("@io_bazel_rules_go//go:def.bzl", "GoSource", "go_test") 21*60517a1eSAndroid Build Coastguard Workerload("@rules_python//python:defs.bzl", "py_binary") 22*60517a1eSAndroid Build Coastguard Worker 23*60517a1eSAndroid Build Coastguard Workerdef gazelle_python_manifest( 24*60517a1eSAndroid Build Coastguard Worker name, 25*60517a1eSAndroid Build Coastguard Worker modules_mapping, 26*60517a1eSAndroid Build Coastguard Worker requirements = [], 27*60517a1eSAndroid Build Coastguard Worker pip_repository_name = "", 28*60517a1eSAndroid Build Coastguard Worker pip_deps_repository_name = "", 29*60517a1eSAndroid Build Coastguard Worker manifest = ":gazelle_python.yaml", 30*60517a1eSAndroid Build Coastguard Worker **kwargs): 31*60517a1eSAndroid Build Coastguard Worker """A macro for defining the updating and testing targets for the Gazelle manifest file. 32*60517a1eSAndroid Build Coastguard Worker 33*60517a1eSAndroid Build Coastguard Worker Args: 34*60517a1eSAndroid Build Coastguard Worker name: the name used as a base for the targets. 35*60517a1eSAndroid Build Coastguard Worker modules_mapping: the target for the generated modules_mapping.json file. 36*60517a1eSAndroid Build Coastguard Worker requirements: the target for the requirements.txt file or a list of 37*60517a1eSAndroid Build Coastguard Worker requirements files that will be concatenated before passing on to 38*60517a1eSAndroid Build Coastguard Worker the manifest generator. If unset, no integrity field is added to the 39*60517a1eSAndroid Build Coastguard Worker manifest, meaning testing it is just as expensive as generating it, 40*60517a1eSAndroid Build Coastguard Worker but modifying it is much less likely to result in a merge conflict. 41*60517a1eSAndroid Build Coastguard Worker pip_repository_name: the name of the pip_install or pip_repository target. 42*60517a1eSAndroid Build Coastguard Worker pip_deps_repository_name: deprecated - the old pip_install target name. 43*60517a1eSAndroid Build Coastguard Worker manifest: the Gazelle manifest file. 44*60517a1eSAndroid Build Coastguard Worker defaults to the same value as manifest. 45*60517a1eSAndroid Build Coastguard Worker **kwargs: other bazel attributes passed to the generate and test targets 46*60517a1eSAndroid Build Coastguard Worker generated by this macro. 47*60517a1eSAndroid Build Coastguard Worker """ 48*60517a1eSAndroid Build Coastguard Worker if pip_deps_repository_name != "": 49*60517a1eSAndroid Build Coastguard Worker # buildifier: disable=print 50*60517a1eSAndroid Build Coastguard Worker print("DEPRECATED pip_deps_repository_name in //{}:{}. Please use pip_repository_name instead.".format( 51*60517a1eSAndroid Build Coastguard Worker native.package_name(), 52*60517a1eSAndroid Build Coastguard Worker name, 53*60517a1eSAndroid Build Coastguard Worker )) 54*60517a1eSAndroid Build Coastguard Worker pip_repository_name = pip_deps_repository_name 55*60517a1eSAndroid Build Coastguard Worker 56*60517a1eSAndroid Build Coastguard Worker if pip_repository_name == "": 57*60517a1eSAndroid Build Coastguard Worker # This is a temporary check while pip_deps_repository_name exists as deprecated. 58*60517a1eSAndroid Build Coastguard Worker fail("pip_repository_name must be set in //{}:{}".format(native.package_name(), name)) 59*60517a1eSAndroid Build Coastguard Worker 60*60517a1eSAndroid Build Coastguard Worker test_target = "{}.test".format(name) 61*60517a1eSAndroid Build Coastguard Worker update_target = "{}.update".format(name) 62*60517a1eSAndroid Build Coastguard Worker update_target_label = "//{}:{}".format(native.package_name(), update_target) 63*60517a1eSAndroid Build Coastguard Worker 64*60517a1eSAndroid Build Coastguard Worker manifest_genrule = name + ".genrule" 65*60517a1eSAndroid Build Coastguard Worker generated_manifest = name + ".generated_manifest" 66*60517a1eSAndroid Build Coastguard Worker manifest_generator = Label("//manifest/generate:generate") 67*60517a1eSAndroid Build Coastguard Worker manifest_generator_hash = Label("//manifest/generate:generate_lib_sources_hash") 68*60517a1eSAndroid Build Coastguard Worker 69*60517a1eSAndroid Build Coastguard Worker if requirements and type(requirements) == "list": 70*60517a1eSAndroid Build Coastguard Worker # This runs if requirements is a list or is unset (default value is empty list) 71*60517a1eSAndroid Build Coastguard Worker native.genrule( 72*60517a1eSAndroid Build Coastguard Worker name = name + "_requirements_gen", 73*60517a1eSAndroid Build Coastguard Worker srcs = sorted(requirements), 74*60517a1eSAndroid Build Coastguard Worker outs = [name + "_requirements.txt"], 75*60517a1eSAndroid Build Coastguard Worker cmd_bash = "cat $(SRCS) > $@", 76*60517a1eSAndroid Build Coastguard Worker cmd_bat = "type $(SRCS) > $@", 77*60517a1eSAndroid Build Coastguard Worker ) 78*60517a1eSAndroid Build Coastguard Worker requirements = name + "_requirements_gen" 79*60517a1eSAndroid Build Coastguard Worker 80*60517a1eSAndroid Build Coastguard Worker update_args = [ 81*60517a1eSAndroid Build Coastguard Worker "--manifest-generator-hash=$(execpath {})".format(manifest_generator_hash), 82*60517a1eSAndroid Build Coastguard Worker "--requirements=$(rootpath {})".format(requirements) if requirements else "--requirements=", 83*60517a1eSAndroid Build Coastguard Worker "--pip-repository-name={}".format(pip_repository_name), 84*60517a1eSAndroid Build Coastguard Worker "--modules-mapping=$(execpath {})".format(modules_mapping), 85*60517a1eSAndroid Build Coastguard Worker "--output=$(execpath {})".format(generated_manifest), 86*60517a1eSAndroid Build Coastguard Worker "--update-target={}".format(update_target_label), 87*60517a1eSAndroid Build Coastguard Worker ] 88*60517a1eSAndroid Build Coastguard Worker 89*60517a1eSAndroid Build Coastguard Worker native.genrule( 90*60517a1eSAndroid Build Coastguard Worker name = manifest_genrule, 91*60517a1eSAndroid Build Coastguard Worker outs = [generated_manifest], 92*60517a1eSAndroid Build Coastguard Worker cmd = "$(execpath {}) {}".format(manifest_generator, " ".join(update_args)), 93*60517a1eSAndroid Build Coastguard Worker tools = [manifest_generator], 94*60517a1eSAndroid Build Coastguard Worker srcs = [ 95*60517a1eSAndroid Build Coastguard Worker modules_mapping, 96*60517a1eSAndroid Build Coastguard Worker manifest_generator_hash, 97*60517a1eSAndroid Build Coastguard Worker ] + ([requirements] if requirements else []), 98*60517a1eSAndroid Build Coastguard Worker tags = ["manual"], 99*60517a1eSAndroid Build Coastguard Worker ) 100*60517a1eSAndroid Build Coastguard Worker 101*60517a1eSAndroid Build Coastguard Worker py_binary( 102*60517a1eSAndroid Build Coastguard Worker name = update_target, 103*60517a1eSAndroid Build Coastguard Worker srcs = [Label("//manifest:copy_to_source.py")], 104*60517a1eSAndroid Build Coastguard Worker main = Label("//manifest:copy_to_source.py"), 105*60517a1eSAndroid Build Coastguard Worker args = [ 106*60517a1eSAndroid Build Coastguard Worker "$(rootpath {})".format(generated_manifest), 107*60517a1eSAndroid Build Coastguard Worker "$(rootpath {})".format(manifest), 108*60517a1eSAndroid Build Coastguard Worker ], 109*60517a1eSAndroid Build Coastguard Worker data = [ 110*60517a1eSAndroid Build Coastguard Worker generated_manifest, 111*60517a1eSAndroid Build Coastguard Worker manifest, 112*60517a1eSAndroid Build Coastguard Worker ], 113*60517a1eSAndroid Build Coastguard Worker tags = kwargs.get("tags", []) + ["manual"], 114*60517a1eSAndroid Build Coastguard Worker **{k: v for k, v in kwargs.items() if k != "tags"} 115*60517a1eSAndroid Build Coastguard Worker ) 116*60517a1eSAndroid Build Coastguard Worker 117*60517a1eSAndroid Build Coastguard Worker if requirements: 118*60517a1eSAndroid Build Coastguard Worker attrs = { 119*60517a1eSAndroid Build Coastguard Worker "env": { 120*60517a1eSAndroid Build Coastguard Worker "_TEST_MANIFEST": "$(rootpath {})".format(manifest), 121*60517a1eSAndroid Build Coastguard Worker "_TEST_MANIFEST_GENERATOR_HASH": "$(rlocationpath {})".format(manifest_generator_hash), 122*60517a1eSAndroid Build Coastguard Worker "_TEST_REQUIREMENTS": "$(rootpath {})".format(requirements), 123*60517a1eSAndroid Build Coastguard Worker }, 124*60517a1eSAndroid Build Coastguard Worker "size": "small", 125*60517a1eSAndroid Build Coastguard Worker } 126*60517a1eSAndroid Build Coastguard Worker go_test( 127*60517a1eSAndroid Build Coastguard Worker name = test_target, 128*60517a1eSAndroid Build Coastguard Worker srcs = [Label("//manifest/test:test.go")], 129*60517a1eSAndroid Build Coastguard Worker data = [ 130*60517a1eSAndroid Build Coastguard Worker manifest, 131*60517a1eSAndroid Build Coastguard Worker requirements, 132*60517a1eSAndroid Build Coastguard Worker manifest_generator_hash, 133*60517a1eSAndroid Build Coastguard Worker ], 134*60517a1eSAndroid Build Coastguard Worker rundir = ".", 135*60517a1eSAndroid Build Coastguard Worker deps = [ 136*60517a1eSAndroid Build Coastguard Worker Label("//manifest"), 137*60517a1eSAndroid Build Coastguard Worker Label("@io_bazel_rules_go//go/runfiles"), 138*60517a1eSAndroid Build Coastguard Worker ], 139*60517a1eSAndroid Build Coastguard Worker # kwargs could contain test-specific attributes like size or timeout 140*60517a1eSAndroid Build Coastguard Worker **dict(attrs, **kwargs) 141*60517a1eSAndroid Build Coastguard Worker ) 142*60517a1eSAndroid Build Coastguard Worker else: 143*60517a1eSAndroid Build Coastguard Worker diff_test( 144*60517a1eSAndroid Build Coastguard Worker name = test_target, 145*60517a1eSAndroid Build Coastguard Worker file1 = generated_manifest, 146*60517a1eSAndroid Build Coastguard Worker file2 = manifest, 147*60517a1eSAndroid Build Coastguard Worker failure_message = "Gazelle manifest is out of date. Run 'bazel run {}' to update it.".format(native.package_relative_label(update_target)), 148*60517a1eSAndroid Build Coastguard Worker **kwargs 149*60517a1eSAndroid Build Coastguard Worker ) 150*60517a1eSAndroid Build Coastguard Worker 151*60517a1eSAndroid Build Coastguard Worker native.filegroup( 152*60517a1eSAndroid Build Coastguard Worker name = name, 153*60517a1eSAndroid Build Coastguard Worker srcs = [manifest], 154*60517a1eSAndroid Build Coastguard Worker tags = ["manual"], 155*60517a1eSAndroid Build Coastguard Worker visibility = ["//visibility:public"], 156*60517a1eSAndroid Build Coastguard Worker ) 157*60517a1eSAndroid Build Coastguard Worker 158*60517a1eSAndroid Build Coastguard Worker# buildifier: disable=provider-params 159*60517a1eSAndroid Build Coastguard WorkerAllSourcesInfo = provider(fields = {"all_srcs": "All sources collected from the target and dependencies."}) 160*60517a1eSAndroid Build Coastguard Worker 161*60517a1eSAndroid Build Coastguard Worker_rules_python_workspace = Label("@rules_python//:WORKSPACE") 162*60517a1eSAndroid Build Coastguard Worker 163*60517a1eSAndroid Build Coastguard Workerdef _get_all_sources_impl(target, ctx): 164*60517a1eSAndroid Build Coastguard Worker is_rules_python = target.label.workspace_name == _rules_python_workspace.workspace_name 165*60517a1eSAndroid Build Coastguard Worker if not is_rules_python: 166*60517a1eSAndroid Build Coastguard Worker # Avoid adding third-party dependency files to the checksum of the srcs. 167*60517a1eSAndroid Build Coastguard Worker return AllSourcesInfo(all_srcs = depset()) 168*60517a1eSAndroid Build Coastguard Worker srcs = depset( 169*60517a1eSAndroid Build Coastguard Worker target[GoSource].orig_srcs, 170*60517a1eSAndroid Build Coastguard Worker transitive = [dep[AllSourcesInfo].all_srcs for dep in ctx.rule.attr.deps], 171*60517a1eSAndroid Build Coastguard Worker ) 172*60517a1eSAndroid Build Coastguard Worker return [AllSourcesInfo(all_srcs = srcs)] 173*60517a1eSAndroid Build Coastguard Worker 174*60517a1eSAndroid Build Coastguard Worker_get_all_sources = aspect( 175*60517a1eSAndroid Build Coastguard Worker implementation = _get_all_sources_impl, 176*60517a1eSAndroid Build Coastguard Worker attr_aspects = ["deps"], 177*60517a1eSAndroid Build Coastguard Worker) 178*60517a1eSAndroid Build Coastguard Worker 179*60517a1eSAndroid Build Coastguard Workerdef _sources_hash_impl(ctx): 180*60517a1eSAndroid Build Coastguard Worker all_srcs = ctx.attr.go_library[AllSourcesInfo].all_srcs 181*60517a1eSAndroid Build Coastguard Worker hash_file = ctx.actions.declare_file(ctx.attr.name + ".hash") 182*60517a1eSAndroid Build Coastguard Worker args = ctx.actions.args() 183*60517a1eSAndroid Build Coastguard Worker args.add(hash_file) 184*60517a1eSAndroid Build Coastguard Worker args.add_all(all_srcs) 185*60517a1eSAndroid Build Coastguard Worker ctx.actions.run( 186*60517a1eSAndroid Build Coastguard Worker outputs = [hash_file], 187*60517a1eSAndroid Build Coastguard Worker inputs = all_srcs, 188*60517a1eSAndroid Build Coastguard Worker arguments = [args], 189*60517a1eSAndroid Build Coastguard Worker executable = ctx.executable._hasher, 190*60517a1eSAndroid Build Coastguard Worker ) 191*60517a1eSAndroid Build Coastguard Worker return [DefaultInfo( 192*60517a1eSAndroid Build Coastguard Worker files = depset([hash_file]), 193*60517a1eSAndroid Build Coastguard Worker runfiles = ctx.runfiles([hash_file]), 194*60517a1eSAndroid Build Coastguard Worker )] 195*60517a1eSAndroid Build Coastguard Worker 196*60517a1eSAndroid Build Coastguard Workersources_hash = rule( 197*60517a1eSAndroid Build Coastguard Worker _sources_hash_impl, 198*60517a1eSAndroid Build Coastguard Worker attrs = { 199*60517a1eSAndroid Build Coastguard Worker "go_library": attr.label( 200*60517a1eSAndroid Build Coastguard Worker aspects = [_get_all_sources], 201*60517a1eSAndroid Build Coastguard Worker providers = [GoSource], 202*60517a1eSAndroid Build Coastguard Worker ), 203*60517a1eSAndroid Build Coastguard Worker "_hasher": attr.label( 204*60517a1eSAndroid Build Coastguard Worker cfg = "exec", 205*60517a1eSAndroid Build Coastguard Worker default = Label("//manifest/hasher"), 206*60517a1eSAndroid Build Coastguard Worker executable = True, 207*60517a1eSAndroid Build Coastguard Worker ), 208*60517a1eSAndroid Build Coastguard Worker }, 209*60517a1eSAndroid Build Coastguard Worker) 210