# Copyright 2017 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. load( "@bazel_skylib//lib:shell.bzl", "shell", ) load( "@bazel_gazelle_is_bazel_module//:defs.bzl", "GAZELLE_IS_BAZEL_MODULE", ) load( "//internal:go_repository.bzl", _go_repository = "go_repository", ) load( "//internal:overlay_repository.bzl", _git_repository = "git_repository", _http_archive = "http_archive", ) load( "//internal:gazelle_binary.bzl", _gazelle_binary = "gazelle_binary_wrapper", ) load( "//internal/generationtest:generationtest.bzl", _gazelle_generation_test = "gazelle_generation_test", ) go_repository = _go_repository git_repository = _git_repository http_archive = _http_archive gazelle_binary = _gazelle_binary gazelle_generation_test = _gazelle_generation_test DEFAULT_LANGUAGES = [ Label("//language/proto:go_default_library"), Label("//language/go:go_default_library"), ] def _valid_env_variable_name(name): """ Returns if a string is in the regex [a-zA-Z_][a-zA-Z0-9_]* Given that bazel lacks support of regex, we need to implement a poor man validation """ if not name: return False for i, c in enumerate(name.elems()): if c.isalpha() or c == "_" or (i > 0 and c.isdigit()): continue return False return True def _rlocation_path(ctx, file): if file.short_path.startswith("../"): return file.short_path[3:] else: return ctx.workspace_name + "/" + file.short_path def _gazelle_runner_impl(ctx): args = [ctx.attr.command] if ctx.attr.mode: args.extend(["-mode", ctx.attr.mode]) if ctx.attr.external: args.extend(["-external", ctx.attr.external]) if ctx.attr.prefix: args.extend(["-go_prefix", ctx.attr.prefix]) if ctx.attr.build_tags: args.extend(["-build_tags", ",".join(ctx.attr.build_tags)]) if GAZELLE_IS_BAZEL_MODULE: args.append("-bzlmod") args.extend([ctx.expand_location(arg, ctx.attr.data) for arg in ctx.attr.extra_args]) for key in ctx.attr.env: if not _valid_env_variable_name(key): fail("Invalid environmental variable name: '%s'" % key) env = "\n".join(["export %s=%s" % (x, shell.quote(y)) for (x, y) in ctx.attr.env.items()]) out_file = ctx.actions.declare_file(ctx.label.name + ".bash") go_tool = ctx.toolchains["@io_bazel_rules_go//go:toolchain"].sdk.go repo_config = ctx.file._repo_config substitutions = { "@@ARGS@@": shell.array_literal(args), "@@GAZELLE_PATH@@": shell.quote(_rlocation_path(ctx, ctx.executable.gazelle)), "@@GENERATED_MESSAGE@@": """ # Generated by {label} # DO NOT EDIT """.format(label = str(ctx.label)), "@@GOTOOL@@": shell.quote(_rlocation_path(ctx, go_tool)), "@@ENV@@": env, "@@REPO_CONFIG_PATH@@": shell.quote(_rlocation_path(ctx, repo_config)) if repo_config else "", } ctx.actions.expand_template( template = ctx.file._template, output = out_file, substitutions = substitutions, is_executable = True, ) runfiles = ctx.runfiles(files = [ ctx.executable.gazelle, go_tool, ] + ([repo_config] if repo_config else [])).merge( ctx.attr.gazelle[DefaultInfo].default_runfiles, ) for d in ctx.attr.data: runfiles = runfiles.merge(d[DefaultInfo].default_runfiles) return [DefaultInfo( files = depset([out_file]), runfiles = runfiles, executable = out_file, )] _gazelle_runner = rule( implementation = _gazelle_runner_impl, attrs = { "gazelle": attr.label( allow_single_file = True, default = "//cmd/gazelle", executable = True, cfg = "exec", ), "command": attr.string( values = [ "fix", "update", "update-repos", ], default = "update", ), "mode": attr.string( values = ["", "print", "fix", "diff"], default = "", ), "external": attr.string( values = ["", "external", "static", "vendored"], default = "", ), "build_tags": attr.string_list(), "prefix": attr.string(), "extra_args": attr.string_list(), "data": attr.label_list(allow_files = True), "env": attr.string_dict(), "_repo_config": attr.label( default = "@bazel_gazelle_go_repository_config//:WORKSPACE" if GAZELLE_IS_BAZEL_MODULE else None, allow_single_file = True, ), "_template": attr.label( default = "//internal:gazelle.bash.in", allow_single_file = True, ), }, executable = True, toolchains = ["@io_bazel_rules_go//go:toolchain"], ) def gazelle(name, **kwargs): if "args" in kwargs: # The args attribute has special meaning for executable rules, but we # always want extra_args here instead. if "extra_args" in kwargs: fail("{}: both args and extra_args were provided".format(name)) kwargs["extra_args"] = kwargs["args"] kwargs.pop("args") visibility = kwargs.pop("visibility", default = None) tags_set = {t: "" for t in kwargs.pop("tags", [])} tags_set["manual"] = "" tags = [k for k in tags_set.keys()] runner_name = name + "-runner" _gazelle_runner( name = runner_name, tags = tags, **kwargs ) native.sh_binary( name = name, srcs = [runner_name], tags = tags, visibility = visibility, deps = ["@bazel_tools//tools/bash/runfiles"], data = kwargs["data"] if "data" in kwargs else [], )