"Setup jq toolchain repositories and rules" load(":repo_utils.bzl", "repo_utils") # Platform names follow the platform naming convention in @aspect_bazel_lib//:lib/private/repo_utils.bzl JQ_PLATFORMS = { "darwin_amd64": struct( release_platform = "osx-amd64", compatible_with = [ "@platforms//os:macos", # JQ only ships a universal binary; it should work on # Apple Silicon (amd64) as well. #"@platforms//cpu:x86_64", ], ), "linux_amd64": struct( release_platform = "linux64", compatible_with = [ "@platforms//os:linux", "@platforms//cpu:x86_64", ], ), "windows_amd64": struct( release_platform = "win64", compatible_with = [ "@platforms//os:windows", "@platforms//cpu:x86_64", ], ), } DEFAULT_JQ_VERSION = "1.6" # https://github.com/stedolan/jq/releases # # The integrity hashes can be computed with # shasum -b -a 384 [downloaded file] | awk '{ print $1 }' | xxd -r -p | base64 JQ_VERSIONS = { "1.6": { "linux64": "sha384-+K6tuwxrC/P4WBYRJ7YXcpeLS7GesbbnUhq4r9w7k0lCUC1KlhyXXf0sFQgOg0dI", "osx-amd64": "sha384-ZLZljM9OyKCJbJbv7s1SRYSeMbVxfRc6kFNUlk9U/IL10Xm2xr4cxx3SZKv93QFO", "win64": "sha384-O4qdyhb+0zU1XAuUKc1Mil5hlbSbCUcPQOGRtkJUqryv7X0IeKcMCIuZw97q9WGr", }, "1.5": { "linux64": "sha384-/Su0ihtb867nCQTzQlTHjve+KpwfzsPws5ILj6hl7k33Qw+FwnyxAVITDh/pOOYw", "osx-amd64": "sha384-X3VGwLkqaLafis82SySkqFPGIiJMdWdzcHPWLJ0q87XF+MGVc/e2n65a1yMBW6Nf", "win64": "sha384-NtaejeSFoKaXxxT1nPqxdOWRmIZAFF8wFTKjqs/4W0qYMYLohmO73AGKKR2XIg84", }, } JqInfo = provider( doc = "Provide info for executing jq", fields = { "bin": "Executable jq binary", }, ) def _jq_toolchain_impl(ctx): binary = ctx.attr.bin.files.to_list()[0] # Make the $(JQ_BIN) variable available in places like genrules. # See https://docs.bazel.build/versions/main/be/make-variables.html#custom_variables template_variables = platform_common.TemplateVariableInfo({ "JQ_BIN": binary.path, }) default_info = DefaultInfo( files = depset([binary]), runfiles = ctx.runfiles(files = [binary]), ) jq_info = JqInfo( bin = binary, ) # Export all the providers inside our ToolchainInfo # so the resolved_toolchain rule can grab and re-export them. toolchain_info = platform_common.ToolchainInfo( jqinfo = jq_info, template_variables = template_variables, default = default_info, ) return [default_info, toolchain_info, template_variables] jq_toolchain = rule( implementation = _jq_toolchain_impl, attrs = { "bin": attr.label( mandatory = True, allow_single_file = True, ), }, ) def _jq_toolchains_repo_impl(rctx): # Expose a concrete toolchain which is the result of Bazel resolving the toolchain # for the execution or target platform. # Workaround for https://github.com/bazelbuild/bazel/issues/14009 starlark_content = """# @generated by @aspect_bazel_lib//lib/private:jq_toolchain.bzl # Forward all the providers def _resolved_toolchain_impl(ctx): toolchain_info = ctx.toolchains["@aspect_bazel_lib//lib:jq_toolchain_type"] return [ toolchain_info, toolchain_info.default, toolchain_info.jqinfo, toolchain_info.template_variables, ] # Copied from java_toolchain_alias # https://cs.opensource.google/bazel/bazel/+/master:tools/jdk/java_toolchain_alias.bzl resolved_toolchain = rule( implementation = _resolved_toolchain_impl, toolchains = ["@aspect_bazel_lib//lib:jq_toolchain_type"], incompatible_use_toolchain_transition = True, ) """ rctx.file("defs.bzl", starlark_content) build_content = """# @generated by @aspect_bazel_lib//lib/private:jq_toolchain.bzl # # These can be registered in the workspace file or passed to --extra_toolchains flag. # By default all these toolchains are registered by the jq_register_toolchains macro # so you don't normally need to interact with these targets. load(":defs.bzl", "resolved_toolchain") resolved_toolchain(name = "resolved_toolchain", visibility = ["//visibility:public"]) """ for [platform, meta] in JQ_PLATFORMS.items(): build_content += """ toolchain( name = "{platform}_toolchain", exec_compatible_with = {compatible_with}, toolchain = "@{user_repository_name}_{platform}//:jq_toolchain", toolchain_type = "@aspect_bazel_lib//lib:jq_toolchain_type", ) """.format( platform = platform, user_repository_name = rctx.attr.user_repository_name, compatible_with = meta.compatible_with, ) # Base BUILD file for this repository rctx.file("BUILD.bazel", build_content) jq_toolchains_repo = repository_rule( _jq_toolchains_repo_impl, doc = """Creates a repository with toolchain definitions for all known platforms which can be registered or selected.""", attrs = { "user_repository_name": attr.string(doc = "Base name for toolchains repository"), }, ) def _jq_platform_repo_impl(rctx): is_windows = rctx.attr.platform.startswith("windows_") meta = JQ_PLATFORMS[rctx.attr.platform] release_platform = meta.release_platform if hasattr(meta, "release_platform") else rctx.attr.platform url = "https://github.com/stedolan/jq/releases/download/jq-{0}/jq-{1}{2}".format( rctx.attr.version, release_platform, ".exe" if is_windows else "", ) rctx.download( url = url, output = "jq.exe" if is_windows else "jq", executable = True, integrity = JQ_VERSIONS[rctx.attr.version][release_platform], ) build_content = """# @generated by @aspect_bazel_lib//lib/private:jq_toolchain.bzl load("@aspect_bazel_lib//lib/private:jq_toolchain.bzl", "jq_toolchain") exports_files(["{0}"]) jq_toolchain(name = "jq_toolchain", bin = "{0}", visibility = ["//visibility:public"]) """.format("jq.exe" if is_windows else "jq") # Base BUILD file for this repository rctx.file("BUILD.bazel", build_content) jq_platform_repo = repository_rule( implementation = _jq_platform_repo_impl, doc = "Fetch external tools needed for jq toolchain", attrs = { "version": attr.string(mandatory = True, values = JQ_VERSIONS.keys()), "platform": attr.string(mandatory = True, values = JQ_PLATFORMS.keys()), }, ) def _jq_host_alias_repo(rctx): ext = ".exe" if repo_utils.is_windows(rctx) else "" # Base BUILD file for this repository rctx.file("BUILD.bazel", """# @generated by @aspect_bazel_lib//lib/private:jq_toolchain.bzl package(default_visibility = ["//visibility:public"]) exports_files(["jq{ext}"]) """.format( ext = ext, )) rctx.symlink("../{name}_{platform}/jq{ext}".format( name = rctx.attr.name, platform = repo_utils.platform(rctx), ext = ext, ), "jq{ext}".format(ext = ext)) jq_host_alias_repo = repository_rule( _jq_host_alias_repo, doc = """Creates a repository with a shorter name meant for the host platform, which contains a BUILD.bazel file that exports symlinks to the host platform's binaries """, )