1*9bb1b549SSpandan Dasload("//go/private:sdk.bzl", "detect_host_platform", "go_download_sdk_rule", "go_host_sdk_rule", "go_multiple_toolchains") 2*9bb1b549SSpandan Dasload("//go/private:repositories.bzl", "go_rules_dependencies") 3*9bb1b549SSpandan Das 4*9bb1b549SSpandan Dasdef host_compatible_toolchain_impl(ctx): 5*9bb1b549SSpandan Das ctx.file("BUILD.bazel") 6*9bb1b549SSpandan Das ctx.file("defs.bzl", content = """ 7*9bb1b549SSpandan DasHOST_COMPATIBLE_SDK = Label({}) 8*9bb1b549SSpandan Das""".format(repr(ctx.attr.toolchain))) 9*9bb1b549SSpandan Das 10*9bb1b549SSpandan Dashost_compatible_toolchain = repository_rule( 11*9bb1b549SSpandan Das implementation = host_compatible_toolchain_impl, 12*9bb1b549SSpandan Das attrs = { 13*9bb1b549SSpandan Das # We cannot use attr.label for the `toolchain` attribute since the module extension cannot 14*9bb1b549SSpandan Das # refer to the repositories it creates by their apparent repository names. 15*9bb1b549SSpandan Das "toolchain": attr.string( 16*9bb1b549SSpandan Das doc = "The apparent label of a `ROOT` file in the repository of a host compatible toolchain created by the `go_sdk` extension", 17*9bb1b549SSpandan Das mandatory = True, 18*9bb1b549SSpandan Das ), 19*9bb1b549SSpandan Das }, 20*9bb1b549SSpandan Das doc = "An external repository to expose the first host compatible toolchain", 21*9bb1b549SSpandan Das) 22*9bb1b549SSpandan Das 23*9bb1b549SSpandan Das_download_tag = tag_class( 24*9bb1b549SSpandan Das attrs = { 25*9bb1b549SSpandan Das "name": attr.string(), 26*9bb1b549SSpandan Das "goos": attr.string(), 27*9bb1b549SSpandan Das "goarch": attr.string(), 28*9bb1b549SSpandan Das "sdks": attr.string_list_dict(), 29*9bb1b549SSpandan Das "urls": attr.string_list(default = ["https://dl.google.com/go/{}"]), 30*9bb1b549SSpandan Das "version": attr.string(), 31*9bb1b549SSpandan Das "strip_prefix": attr.string(default = "go"), 32*9bb1b549SSpandan Das }, 33*9bb1b549SSpandan Das) 34*9bb1b549SSpandan Das 35*9bb1b549SSpandan Das_host_tag = tag_class( 36*9bb1b549SSpandan Das attrs = { 37*9bb1b549SSpandan Das "name": attr.string(), 38*9bb1b549SSpandan Das "version": attr.string(), 39*9bb1b549SSpandan Das }, 40*9bb1b549SSpandan Das) 41*9bb1b549SSpandan Das 42*9bb1b549SSpandan Das# This limit can be increased essentially arbitrarily, but doing so will cause a rebuild of all 43*9bb1b549SSpandan Das# targets using any of these toolchains due to the changed repository name. 44*9bb1b549SSpandan Das_MAX_NUM_TOOLCHAINS = 9999 45*9bb1b549SSpandan Das_TOOLCHAIN_INDEX_PAD_LENGTH = len(str(_MAX_NUM_TOOLCHAINS)) 46*9bb1b549SSpandan Das 47*9bb1b549SSpandan Dasdef _go_sdk_impl(ctx): 48*9bb1b549SSpandan Das multi_version_module = {} 49*9bb1b549SSpandan Das for module in ctx.modules: 50*9bb1b549SSpandan Das if module.name in multi_version_module: 51*9bb1b549SSpandan Das multi_version_module[module.name] = True 52*9bb1b549SSpandan Das else: 53*9bb1b549SSpandan Das multi_version_module[module.name] = False 54*9bb1b549SSpandan Das 55*9bb1b549SSpandan Das # We remember the first host compatible toolchain declared by the download and host tags. 56*9bb1b549SSpandan Das # The order follows bazel's iteration over modules (the toolchains declared by the root module are considered first). 57*9bb1b549SSpandan Das # We know that at least `go_default_sdk` (which is declared by the `rules_go` module itself) is host compatible. 58*9bb1b549SSpandan Das first_host_compatible_toolchain = None 59*9bb1b549SSpandan Das host_detected_goos, host_detected_goarch = detect_host_platform(ctx) 60*9bb1b549SSpandan Das toolchains = [] 61*9bb1b549SSpandan Das for module in ctx.modules: 62*9bb1b549SSpandan Das for index, download_tag in enumerate(module.tags.download): 63*9bb1b549SSpandan Das # SDKs without an explicit version are fetched even when not selected by toolchain 64*9bb1b549SSpandan Das # resolution. This is acceptable if brought in by the root module, but transitive 65*9bb1b549SSpandan Das # dependencies should not slow down the build in this way. 66*9bb1b549SSpandan Das if not module.is_root and not download_tag.version: 67*9bb1b549SSpandan Das fail("go_sdk.download: version must be specified in non-root module " + module.name) 68*9bb1b549SSpandan Das 69*9bb1b549SSpandan Das # SDKs with an explicit name are at risk of colliding with those from other modules. 70*9bb1b549SSpandan Das # This is acceptable if brought in by the root module as the user is responsible for any 71*9bb1b549SSpandan Das # conflicts that arise. rules_go itself provides "go_default_sdk", which is used by 72*9bb1b549SSpandan Das # Gazelle to bootstrap itself. 73*9bb1b549SSpandan Das # TODO(https://github.com/bazelbuild/bazel-gazelle/issues/1469): Investigate whether 74*9bb1b549SSpandan Das # Gazelle can use the first user-defined SDK instead to prevent unnecessary downloads. 75*9bb1b549SSpandan Das if (not module.is_root and not module.name == "rules_go") and download_tag.name: 76*9bb1b549SSpandan Das fail("go_sdk.download: name must not be specified in non-root module " + module.name) 77*9bb1b549SSpandan Das 78*9bb1b549SSpandan Das name = download_tag.name or _default_go_sdk_name( 79*9bb1b549SSpandan Das module = module, 80*9bb1b549SSpandan Das multi_version = multi_version_module[module.name], 81*9bb1b549SSpandan Das tag_type = "download", 82*9bb1b549SSpandan Das index = index, 83*9bb1b549SSpandan Das ) 84*9bb1b549SSpandan Das go_download_sdk_rule( 85*9bb1b549SSpandan Das name = name, 86*9bb1b549SSpandan Das goos = download_tag.goos, 87*9bb1b549SSpandan Das goarch = download_tag.goarch, 88*9bb1b549SSpandan Das sdks = download_tag.sdks, 89*9bb1b549SSpandan Das urls = download_tag.urls, 90*9bb1b549SSpandan Das version = download_tag.version, 91*9bb1b549SSpandan Das ) 92*9bb1b549SSpandan Das 93*9bb1b549SSpandan Das if (not download_tag.goos or download_tag.goos == host_detected_goos) and (not download_tag.goarch or download_tag.goarch == host_detected_goarch): 94*9bb1b549SSpandan Das first_host_compatible_toolchain = first_host_compatible_toolchain or "@{}//:ROOT".format(name) 95*9bb1b549SSpandan Das 96*9bb1b549SSpandan Das toolchains.append(struct( 97*9bb1b549SSpandan Das goos = download_tag.goos, 98*9bb1b549SSpandan Das goarch = download_tag.goarch, 99*9bb1b549SSpandan Das sdk_repo = name, 100*9bb1b549SSpandan Das sdk_type = "remote", 101*9bb1b549SSpandan Das sdk_version = download_tag.version, 102*9bb1b549SSpandan Das )) 103*9bb1b549SSpandan Das 104*9bb1b549SSpandan Das for index, host_tag in enumerate(module.tags.host): 105*9bb1b549SSpandan Das # Dependencies can rely on rules_go providing a default remote SDK. They can also 106*9bb1b549SSpandan Das # configure a specific version of the SDK to use. However, they should not add a 107*9bb1b549SSpandan Das # dependency on the host's Go SDK. 108*9bb1b549SSpandan Das if not module.is_root: 109*9bb1b549SSpandan Das fail("go_sdk.host: cannot be used in non-root module " + module.name) 110*9bb1b549SSpandan Das 111*9bb1b549SSpandan Das name = host_tag.name or _default_go_sdk_name( 112*9bb1b549SSpandan Das module = module, 113*9bb1b549SSpandan Das multi_version = multi_version_module[module.name], 114*9bb1b549SSpandan Das tag_type = "host", 115*9bb1b549SSpandan Das index = index, 116*9bb1b549SSpandan Das ) 117*9bb1b549SSpandan Das go_host_sdk_rule( 118*9bb1b549SSpandan Das name = name, 119*9bb1b549SSpandan Das version = host_tag.version, 120*9bb1b549SSpandan Das ) 121*9bb1b549SSpandan Das 122*9bb1b549SSpandan Das toolchains.append(struct( 123*9bb1b549SSpandan Das goos = "", 124*9bb1b549SSpandan Das goarch = "", 125*9bb1b549SSpandan Das sdk_repo = name, 126*9bb1b549SSpandan Das sdk_type = "host", 127*9bb1b549SSpandan Das sdk_version = host_tag.version, 128*9bb1b549SSpandan Das )) 129*9bb1b549SSpandan Das first_host_compatible_toolchain = first_host_compatible_toolchain or "@{}//:ROOT".format(name) 130*9bb1b549SSpandan Das 131*9bb1b549SSpandan Das host_compatible_toolchain(name = "go_host_compatible_sdk_label", toolchain = first_host_compatible_toolchain) 132*9bb1b549SSpandan Das if len(toolchains) > _MAX_NUM_TOOLCHAINS: 133*9bb1b549SSpandan Das fail("more than {} go_sdk tags are not supported".format(_MAX_NUM_TOOLCHAINS)) 134*9bb1b549SSpandan Das 135*9bb1b549SSpandan Das # Toolchains in a BUILD file are registered in the order given by name, not in the order they 136*9bb1b549SSpandan Das # are declared: 137*9bb1b549SSpandan Das # https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/packages/Package.java;drc=8e41dce65b97a3d466d6b1e65005abc52a07b90b;l=156 138*9bb1b549SSpandan Das # We pad with an index that lexicographically sorts in the same order as if these toolchains 139*9bb1b549SSpandan Das # were registered using register_toolchains in their MODULE.bazel files. 140*9bb1b549SSpandan Das go_multiple_toolchains( 141*9bb1b549SSpandan Das name = "go_toolchains", 142*9bb1b549SSpandan Das prefixes = [ 143*9bb1b549SSpandan Das _toolchain_prefix(index, toolchain.sdk_repo) 144*9bb1b549SSpandan Das for index, toolchain in enumerate(toolchains) 145*9bb1b549SSpandan Das ], 146*9bb1b549SSpandan Das geese = [toolchain.goos for toolchain in toolchains], 147*9bb1b549SSpandan Das goarchs = [toolchain.goarch for toolchain in toolchains], 148*9bb1b549SSpandan Das sdk_repos = [toolchain.sdk_repo for toolchain in toolchains], 149*9bb1b549SSpandan Das sdk_types = [toolchain.sdk_type for toolchain in toolchains], 150*9bb1b549SSpandan Das sdk_versions = [toolchain.sdk_version for toolchain in toolchains], 151*9bb1b549SSpandan Das ) 152*9bb1b549SSpandan Das 153*9bb1b549SSpandan Dasdef _default_go_sdk_name(*, module, multi_version, tag_type, index): 154*9bb1b549SSpandan Das # Keep the version out of the repository name if possible to prevent unnecessary rebuilds when 155*9bb1b549SSpandan Das # it changes. 156*9bb1b549SSpandan Das return "{name}_{version}_{tag_type}_{index}".format( 157*9bb1b549SSpandan Das name = module.name, 158*9bb1b549SSpandan Das version = module.version if multi_version else "", 159*9bb1b549SSpandan Das tag_type = tag_type, 160*9bb1b549SSpandan Das index = index, 161*9bb1b549SSpandan Das ) 162*9bb1b549SSpandan Das 163*9bb1b549SSpandan Dasdef _toolchain_prefix(index, name): 164*9bb1b549SSpandan Das """Prefixes the given name with the index, padded with zeros to ensure lexicographic sorting. 165*9bb1b549SSpandan Das 166*9bb1b549SSpandan Das Examples: 167*9bb1b549SSpandan Das _toolchain_prefix( 2, "foo") == "_0002_foo_" 168*9bb1b549SSpandan Das _toolchain_prefix(2000, "foo") == "_2000_foo_" 169*9bb1b549SSpandan Das """ 170*9bb1b549SSpandan Das return "_{}_{}_".format(_left_pad_zero(index, _TOOLCHAIN_INDEX_PAD_LENGTH), name) 171*9bb1b549SSpandan Das 172*9bb1b549SSpandan Dasdef _left_pad_zero(index, length): 173*9bb1b549SSpandan Das if index < 0: 174*9bb1b549SSpandan Das fail("index must be non-negative") 175*9bb1b549SSpandan Das return ("0" * length + str(index))[-length:] 176*9bb1b549SSpandan Das 177*9bb1b549SSpandan Dasgo_sdk = module_extension( 178*9bb1b549SSpandan Das implementation = _go_sdk_impl, 179*9bb1b549SSpandan Das tag_classes = { 180*9bb1b549SSpandan Das "download": _download_tag, 181*9bb1b549SSpandan Das "host": _host_tag, 182*9bb1b549SSpandan Das }, 183*9bb1b549SSpandan Das) 184*9bb1b549SSpandan Das 185*9bb1b549SSpandan Dasdef _non_module_dependencies_impl(_ctx): 186*9bb1b549SSpandan Das go_rules_dependencies(force = True) 187*9bb1b549SSpandan Das 188*9bb1b549SSpandan Dasnon_module_dependencies = module_extension( 189*9bb1b549SSpandan Das implementation = _non_module_dependencies_impl, 190*9bb1b549SSpandan Das) 191