From 068115e4cf974c8a5e83f0db3f9c79b6b3c027f8 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Mon, 26 Feb 2024 10:16:23 -0700 Subject: [PATCH] POC - go.mod replace with ModulePath --- internal/bzlmod/go_deps.bzl | 16 +++++++-- internal/bzlmod/go_mod.bzl | 9 ++--- internal/go_repository.bzl | 69 ++++++++++++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/internal/bzlmod/go_deps.bzl b/internal/bzlmod/go_deps.bzl index 21e8f152b..86debd13e 100644 --- a/internal/bzlmod/go_deps.bzl +++ b/internal/bzlmod/go_deps.bzl @@ -473,6 +473,7 @@ def _go_deps_impl(module_ctx): version = new_version, raw_version = replace.version, ) + if path in root_versions: if replace != replace.to_path: # If the root module replaces a Go module with a completely different one, do @@ -522,7 +523,6 @@ def _go_deps_impl(module_ctx): # Do not create a go_repository for a dep shared with the non-isolated instance of # go_deps. continue - go_repository_args = { "name": module.repo_name, "importpath": path, @@ -549,6 +549,13 @@ def _go_deps_impl(module_ctx): "version": "v" + module.raw_version, }) + # TODO: remove the need for a sentinel value, make it explicit instead + if module.raw_version == "999.999.999": + go_repository_args.update({ + "version": None, + "path": module.replace, + }) + go_repository(**go_repository_args) # Create a synthetic WORKSPACE file that lists all Go repositories created @@ -593,8 +600,11 @@ def _get_sum_from_module(path, module, sums): entry = (module.replace, module.raw_version) if entry not in sums: - # TODO: if no sum exist, this is probably because a go mod tidy was missed - fail("No sum for {}@{} found".format(path, module.raw_version)) + if module.raw_version == "999.999.999": + return "" + else: + # TODO: if no sum exist, this is probably because a go mod tidy was missed + fail("No sum for {}@{} found".format(path, module.raw_version)) return sums[entry] diff --git a/internal/bzlmod/go_mod.bzl b/internal/bzlmod/go_mod.bzl index 024bf170b..deb1f99cc 100644 --- a/internal/bzlmod/go_mod.bzl +++ b/internal/bzlmod/go_mod.bzl @@ -252,10 +252,11 @@ def _parse_replace_directive(state, tokens, path, line_no): version = _canonicalize_raw_version(tokens[4]), ) else: - fail( - "{}:{}: replace directive must follow pattern: ".format(path, line_no) + - "'replace from_path from_version => to_path to_version' or " + - "'replace from_path => to_path to_version'", + state["replace"][from_path] = struct( + from_version = None, + to_path = tokens[2], + # TODO: should version be None here? or some sentinel Null value version? + version = _canonicalize_raw_version("999.999.999"), ) def _tokenize_line(line, path, line_no): diff --git a/internal/go_repository.bzl b/internal/go_repository.bzl index 8dac6eb9b..4ce8430e4 100644 --- a/internal/go_repository.bzl +++ b/internal/go_repository.bzl @@ -181,6 +181,8 @@ def _go_repository_impl(ctx): "-version=" + ctx.attr.version, "-sum=" + ctx.attr.sum, ] + elif ctx.attr.path: + pass else: fail("one of urls, commit, tag, or importpath must be specified") @@ -237,6 +239,58 @@ def _go_repository_impl(ctx): env.update({k: ctx.os.environ[k] for k in env_keys if k in ctx.os.environ}) + if ctx.attr.path: + local_path_env = dict(env) + local_path_env["GOSUMDB"] = "off" + + # Override external GO111MODULE, because it is needed by module mode, no-op in repository mode + local_path_env["GO111MODULE"] = "on" + + if hasattr(ctx, "watch_tree"): + print("hi") + # https://github.com/bazelbuild/bazel/commit/fffa0affebbacf1961a97ef7cd248be64487d480 + ctx.watch_tree(ctx.attr.path) + else: + print(""" + WARNING: go.mod replace directives to module paths is only supported in bazel 7.1.0-rc1 or later, + Because of this changes to %s will not be detected by bazel in previous versions.""" % ctx.attr.path) + + command = ["cp", "-r", "%s/" % ctx.attr.path, ctx.path("")] + result = env_execute( + ctx, + command, + environment = local_path_env, + timeout = _GO_REPOSITORY_TIMEOUT, + ) + + # use "go mod download" to satisfy the dependencies of this module + if ctx.attr.debug_mode and result.stderr: + print("copy mod: %s", result.stderr) + + if result.return_code: + fail(command) + + # TODO: this needs to have a go mod with the same go version as the + # project, or it may fail as not all go versions are available + # locally + command = ["go", "mod", "download", "-json", "-modcacherw"] + result = env_execute( + ctx, + command, + environment = local_path_env, + timeout = _GO_REPOSITORY_TIMEOUT, + working_directory = "%s" % ctx.path(""), + ) + + # use "go mod download" to satisfy the dependencies of this module + if result.stderr: + print("mod download: %s", result.stderr) + + if result.return_code: + fail(command) + + # use "go mod download" to satisfy the dependencies of this module + if fetch_repo_args: # Disable sumdb in fetch_repo. In module mode, the sum is a mandatory # attribute of go_repository, so we don't need to look it up. @@ -252,10 +306,12 @@ def _go_repository_impl(ctx): environment = fetch_repo_env, timeout = _GO_REPOSITORY_TIMEOUT, ) + + if result.stderr: + print("copy_repo: " + result.stderr) + if result.return_code: - fail("failed to fetch %s: %s" % (ctx.name, result.stderr)) - if ctx.attr.debug_mode and result.stderr: - print("fetch_repo: " + result.stderr) + fail("%s: %s" % (ctx.name, result.stderr)) # Repositories are fetched. Determine if build file generation is needed. build_file_names = ctx.attr.build_file_name.split(",") @@ -307,7 +363,7 @@ def _go_repository_impl(ctx): "-repo_config", repo_config, ] - if ctx.attr.version: + if ctx.attr.version or ctx.attr.path: cmd.append("-go_repository_module_mode") if ctx.attr.build_file_name: cmd.extend(["-build_file_name", ctx.attr.build_file_name]) @@ -421,6 +477,11 @@ go_repository = repository_rule( doc = _AUTH_PATTERN_DOC, ), + # Attributes for a module that should be loaded from the local file system. + "path": attr.string( + doc = """ If specified, `go_repository` will load the module from this local directory""", + ), + # Attributes for a module that should be downloaded with the Go toolchain. "version": attr.string( doc = """If specified, `go_repository` will download the module at this version