diff --git a/go/private/repositories.bzl b/go/private/repositories.bzl index d2d75097e2..c9d767add2 100644 --- a/go/private/repositories.bzl +++ b/go/private/repositories.bzl @@ -16,6 +16,7 @@ load("@io_bazel_rules_go//go/private:common.bzl", "MINIMUM_BAZEL_VERSION") load("@io_bazel_rules_go//go/private:compat/compat_repo.bzl", "go_rules_compat") +load("@io_bazel_rules_go//go/private:skylib/lib/unittest.bzl", "register_unittest_toolchains") load("@io_bazel_rules_go//go/private:skylib/lib/versions.bzl", "versions") load("@io_bazel_rules_go//go/private:nogo.bzl", "DEFAULT_NOGO", "go_register_nogo") load("@io_bazel_rules_go//go/platform:list.bzl", "GOOS_GOARCH") @@ -28,6 +29,8 @@ def go_rules_dependencies(): if getattr(native, "bazel_version", None): versions.check(MINIMUM_BAZEL_VERSION, bazel_version = native.bazel_version) + register_unittest_toolchains() + # Compatibility layer, needed to support older versions of Bazel. _maybe( go_rules_compat, diff --git a/go/private/skylib/README.rst b/go/private/skylib/README.rst index 2a2db0c70f..2009643564 100644 --- a/go/private/skylib/README.rst +++ b/go/private/skylib/README.rst @@ -1,5 +1,7 @@ -This directory is a copy of github.com/bazelbuild/bazel-skylib/lib. -Version 0.5.0, retrieved on 2018-11-26. +This directory is a copy of github.com/bazelbuild/bazel-skylib/{lib,toolchains} +Version 0.8.0, retrieved on 2019-05-29, with the following changes: +- lib/BUILD is omitted +- all labels (e.g. in load() statements) are updated for this repository This is needed only until nested workspaces works. It has to be copied in because we use the functionality inside code that diff --git a/go/private/skylib/lib/collections.bzl b/go/private/skylib/lib/collections.bzl index de612ff0d0..f41eea2e6b 100644 --- a/go/private/skylib/lib/collections.bzl +++ b/go/private/skylib/lib/collections.bzl @@ -60,7 +60,10 @@ def _uniq(iterable): A new list with all unique elements from `iterable`. """ unique_elements = {element: None for element in iterable} - return unique_elements.keys() + + # list() used here for python3 compatibility. + # TODO(bazel-team): Remove when testing frameworks no longer require python compatibility. + return list(unique_elements.keys()) collections = struct( after_each = _after_each, diff --git a/go/private/skylib/lib/dicts.bzl b/go/private/skylib/lib/dicts.bzl index f702fa92c5..3f8e661a28 100644 --- a/go/private/skylib/lib/dicts.bzl +++ b/go/private/skylib/lib/dicts.bzl @@ -14,7 +14,7 @@ """Skylib module containing functions that operate on dictionaries.""" -def _add(*dictionaries): +def _add(*dictionaries, **kwargs): """Returns a new `dict` that has all the entries of the given dictionaries. If the same key is present in more than one of the input dictionaries, the @@ -27,6 +27,7 @@ def _add(*dictionaries): Args: *dictionaries: Zero or more dictionaries to be added. + **kwargs: Additional dictionary passed as keyword args. Returns: A new `dict` that has all the entries of the given dictionaries. @@ -34,6 +35,7 @@ def _add(*dictionaries): result = {} for d in dictionaries: result.update(d) + result.update(kwargs) return result dicts = struct( diff --git a/go/private/skylib/lib/new_sets.bzl b/go/private/skylib/lib/new_sets.bzl index 3e203c1979..3eb103bf74 100644 --- a/go/private/skylib/lib/new_sets.bzl +++ b/go/private/skylib/lib/new_sets.bzl @@ -20,7 +20,7 @@ values in the set can be retrieved using `sets.to_list(my_set)`. """ -load(":skylib/lib/dicts.bzl", "dicts") +load("@io_bazel_rules_go//go/private:skylib/lib/dicts.bzl", "dicts") def _make(elements = None): """Creates a new set. diff --git a/go/private/skylib/lib/old_sets.bzl b/go/private/skylib/lib/old_sets.bzl new file mode 100644 index 0000000000..2ac6c6ba3d --- /dev/null +++ b/go/private/skylib/lib/old_sets.bzl @@ -0,0 +1,160 @@ +# 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. + +"""Skylib module containing common set algorithms. + +CAUTION: Operating on sets, particularly sets contained in providers, may +asymptotically slow down the analysis phase. While constructing large sets with +addition/union is fast (there is no linear-time copy involved), the +`difference` function and various comparison predicates involve linear-time +traversals. + +For convenience, the functions in this module can take either sets or lists as +inputs; operations that take lists treat them as if they were sets (i.e., +duplicate elements are ignored). Functions that return new sets always return +them as the `set` type, regardless of the types of the inputs. +""" + +_depset_type = type(depset()) +_list_type = type([]) + +def _precondition_only_sets_or_lists(*args): + """Verifies that all arguments are either sets or lists. + + The build will fail if any of the arguments is neither a set nor a list. + + Args: + *args: A list of values that must be sets or lists. + """ + for a in args: + t = type(a) + if t not in (_depset_type, _list_type): + fail("Expected arguments to be depset or list, but found type %s: %r" % + (t, a)) + +def _depset_to_list(val): + """Converts a depset to a list. + + If the given value is a depset, will return the list representation of + the depset. Otherwise, will return the value itself. + + Args: + val: The value to be optionally converted and returned. + + Returns: + The converted value. + """ + if type(val) == _depset_type: + return val.to_list() + else: + return val + +def _is_equal(a, b): + """Returns whether two sets are equal. + + Args: + a: A depset or a list. + b: A depset or a list. + + Returns: + True if `a` is equal to `b`, False otherwise. + """ + _precondition_only_sets_or_lists(a, b) + + # Convert both values to a depset then back to a list to remove duplicates. + a = _depset_to_list(depset(a)) + b = _depset_to_list(depset(b)) + return sorted(a) == sorted(b) + +def _is_subset(a, b): + """Returns whether `a` is a subset of `b`. + + Args: + a: A depset or a list. + b: A depset or a list. + + Returns: + True if `a` is a subset of `b`, False otherwise. + """ + _precondition_only_sets_or_lists(a, b) + for e in _depset_to_list(a): + if e not in _depset_to_list(b): + return False + return True + +def _disjoint(a, b): + """Returns whether two sets are disjoint. + + Two sets are disjoint if they have no elements in common. + + Args: + a: A set or list. + b: A set or list. + + Returns: + True if `a` and `b` are disjoint, False otherwise. + """ + _precondition_only_sets_or_lists(a, b) + for e in _depset_to_list(a): + if e in _depset_to_list(b): + return False + return True + +def _intersection(a, b): + """Returns the intersection of two sets. + + Args: + a: A set or list. + b: A set or list. + + Returns: + A set containing the elements that are in both `a` and `b`. + """ + _precondition_only_sets_or_lists(a, b) + return depset([e for e in _depset_to_list(a) if e in _depset_to_list(b)]) + +def _union(*args): + """Returns the union of several sets. + + Args: + *args: An arbitrary number of sets or lists. + + Returns: + The set union of all sets or lists in `*args`. + """ + _precondition_only_sets_or_lists(*args) + args_deps = [depset(x) if type(x) == _list_type else x for x in args] + return depset(transitive = args_deps) + +def _difference(a, b): + """Returns the elements in `a` that are not in `b`. + + Args: + a: A set or list. + b: A set or list. + + Returns: + A set containing the elements that are in `a` but not in `b`. + """ + _precondition_only_sets_or_lists(a, b) + return depset([e for e in _depset_to_list(a) if e not in _depset_to_list(b)]) + +sets = struct( + difference = _difference, + disjoint = _disjoint, + intersection = _intersection, + is_equal = _is_equal, + is_subset = _is_subset, + union = _union, +) diff --git a/go/private/skylib/lib/partial.bzl b/go/private/skylib/lib/partial.bzl index d70aa0f092..e2f24b7922 100644 --- a/go/private/skylib/lib/partial.bzl +++ b/go/private/skylib/lib/partial.bzl @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Skylark module for working with partial function objects. +"""Starlark module for working with partial function objects. Partial function objects allow some parameters are bound before the call. diff --git a/go/private/skylib/lib/selects.bzl b/go/private/skylib/lib/selects.bzl index 4f9fea4479..1c589d3d35 100755 --- a/go/private/skylib/lib/selects.bzl +++ b/go/private/skylib/lib/selects.bzl @@ -54,7 +54,7 @@ def _with_or(input_dict, no_match_error = ""): def _with_or_dict(input_dict): """Variation of `with_or` that returns the dict of the `select()`. - Unlike `select()`, the contents of the dict can be inspected by Skylark + Unlike `select()`, the contents of the dict can be inspected by Starlark macros. Args: diff --git a/go/private/skylib/lib/sets.bzl b/go/private/skylib/lib/sets.bzl index 0c300fa2fd..08c25b5136 100644 --- a/go/private/skylib/lib/sets.bzl +++ b/go/private/skylib/lib/sets.bzl @@ -1,4 +1,4 @@ -# Copyright 2017 The Bazel Authors. All rights reserved. +# Copyright 2018 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. @@ -12,125 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Skylib module containing common set algorithms. +"""Skylib module reexporting deprecated set algorithms.""" -CAUTION: Operating on sets, particularly sets contained in providers, may -asymptotically slow down the analysis phase. While constructing large sets with -addition/union is fast (there is no linear-time copy involved), the -`difference` function and various comparison predicates involve linear-time -traversals. +load("@io_bazel_rules_go//go/private:skylib/lib/old_sets.bzl", _sets = "sets") -For convenience, the functions in this module can take either sets or lists as -inputs; operations that take lists treat them as if they were sets (i.e., -duplicate elements are ignored). Functions that return new sets always return -them as the `set` type, regardless of the types of the inputs. -""" - -def _precondition_only_sets_or_lists(*args): - """Verifies that all arguments are either sets or lists. - - The build will fail if any of the arguments is neither a set nor a list. - - Args: - *args: A list of values that must be sets or lists. - """ - for a in args: - t = type(a) - if t not in ("depset", "list"): - fail("Expected arguments to be depset or list, but found type %s: %r" % - (t, a)) - -def _is_equal(a, b): - """Returns whether two sets are equal. - - Args: - a: A depset or a list. - b: A depset or a list. - - Returns: - True if `a` is equal to `b`, False otherwise. - """ - _precondition_only_sets_or_lists(a, b) - return sorted(depset(a)) == sorted(depset(b)) - -def _is_subset(a, b): - """Returns whether `a` is a subset of `b`. - - Args: - a: A depset or a list. - b: A depset or a list. - - Returns: - True if `a` is a subset of `b`, False otherwise. - """ - _precondition_only_sets_or_lists(a, b) - for e in a: - if e not in b: - return False - return True - -def _disjoint(a, b): - """Returns whether two sets are disjoint. - - Two sets are disjoint if they have no elements in common. - - Args: - a: A set or list. - b: A set or list. - - Returns: - True if `a` and `b` are disjoint, False otherwise. - """ - _precondition_only_sets_or_lists(a, b) - for e in a: - if e in b: - return False - return True - -def _intersection(a, b): - """Returns the intersection of two sets. - - Args: - a: A set or list. - b: A set or list. - - Returns: - A set containing the elements that are in both `a` and `b`. - """ - _precondition_only_sets_or_lists(a, b) - return depset([e for e in a if e in b]) - -def _union(*args): - """Returns the union of several sets. - - Args: - *args: An arbitrary number of sets or lists. - - Returns: - The set union of all sets or lists in `*args`. - """ - _precondition_only_sets_or_lists(*args) - args_deps = [depset(x) if type(x) == type([]) else x for x in args] - return depset(transitive = args_deps) - -def _difference(a, b): - """Returns the elements in `a` that are not in `b`. - - Args: - a: A set or list. - b: A set or list. - - Returns: - A set containing the elements that are in `a` but not in `b`. - """ - _precondition_only_sets_or_lists(a, b) - return depset([e for e in a if e not in b]) - -sets = struct( - difference = _difference, - disjoint = _disjoint, - intersection = _intersection, - is_equal = _is_equal, - is_subset = _is_subset, - union = _union, -) +sets = _sets diff --git a/go/private/skylib/lib/types.bzl b/go/private/skylib/lib/types.bzl index 54061eb01d..78248931f8 100644 --- a/go/private/skylib/lib/types.bzl +++ b/go/private/skylib/lib/types.bzl @@ -14,16 +14,19 @@ """Skylib module containing functions checking types.""" # create instance singletons to avoid unnecessary allocations -_a_bool = True -_a_dict = {} -_a_list = [] -_a_string = "" -_a_tuple = () -_an_int = 1 +_a_bool_type = type(True) +_a_dict_type = type({}) +_a_list_type = type([]) +_a_string_type = type("") +_a_tuple_type = type(()) +_an_int_type = type(1) +_a_depset_type = type(depset()) def _a_function(): pass +_a_function_type = type(_a_function) + def _is_list(v): """Returns True if v is an instance of a list. @@ -33,7 +36,7 @@ def _is_list(v): Returns: True if v is an instance of a list, False otherwise. """ - return type(v) == type(_a_list) + return type(v) == _a_list_type def _is_string(v): """Returns True if v is an instance of a string. @@ -44,7 +47,7 @@ def _is_string(v): Returns: True if v is an instance of a string, False otherwise. """ - return type(v) == type(_a_string) + return type(v) == _a_string_type def _is_bool(v): """Returns True if v is an instance of a bool. @@ -55,7 +58,7 @@ def _is_bool(v): Returns: True if v is an instance of a bool, False otherwise. """ - return type(v) == type(_a_bool) + return type(v) == _a_bool_type def _is_none(v): """Returns True if v has the type of None. @@ -77,7 +80,7 @@ def _is_int(v): Returns: True if v is an instance of a signed integer, False otherwise. """ - return type(v) == type(_an_int) + return type(v) == _an_int_type def _is_tuple(v): """Returns True if v is an instance of a tuple. @@ -88,7 +91,7 @@ def _is_tuple(v): Returns: True if v is an instance of a tuple, False otherwise. """ - return type(v) == type(_a_tuple) + return type(v) == _a_tuple_type def _is_dict(v): """Returns True if v is an instance of a dict. @@ -99,7 +102,7 @@ def _is_dict(v): Returns: True if v is an instance of a dict, False otherwise. """ - return type(v) == type(_a_dict) + return type(v) == _a_dict_type def _is_function(v): """Returns True if v is an instance of a function. @@ -110,7 +113,18 @@ def _is_function(v): Returns: True if v is an instance of a function, False otherwise. """ - return type(v) == type(_a_function) + return type(v) == _a_function_type + +def _is_depset(v): + """Returns True if v is an instance of a `depset`. + + Args: + v: The value whose type should be checked. + + Returns: + True if v is an instance of a `depset`, False otherwise. + """ + return type(v) == _a_depset_type types = struct( is_list = _is_list, @@ -121,4 +135,5 @@ types = struct( is_tuple = _is_tuple, is_dict = _is_dict, is_function = _is_function, + is_depset = _is_depset, ) diff --git a/go/private/skylib/lib/unittest.bzl b/go/private/skylib/lib/unittest.bzl index 0862130f5d..3d73dd5e30 100644 --- a/go/private/skylib/lib/unittest.bzl +++ b/go/private/skylib/lib/unittest.bzl @@ -19,8 +19,66 @@ functions to declare and define unit tests, and `asserts` which contains the assertions used to within tests. """ -load(":skylib/lib/sets.bzl", "sets") -load(":skylib/lib/new_sets.bzl", new_sets = "sets") +load("@io_bazel_rules_go//go/private:skylib/lib/new_sets.bzl", new_sets = "sets") +load("@io_bazel_rules_go//go/private:skylib/lib/sets.bzl", "sets") +load("@io_bazel_rules_go//go/private:skylib/lib/types.bzl", "types") + +# The following function should only be called from WORKSPACE files and workspace macros. +def register_unittest_toolchains(): + """Registers the toolchains for unittest users.""" + native.register_toolchains( + "@io_bazel_rules_go//go/private/skylib/toolchains/unittest:cmd_toolchain", + "@io_bazel_rules_go//go/private/skylib/toolchains/unittest:bash_toolchain", + ) + +TOOLCHAIN_TYPE = "@io_bazel_rules_go//go/private/skylib/toolchains/unittest:toolchain_type" + +_UnittestToolchainInfo = provider( + doc = "Execution platform information for rules in the bazel_skylib repository.", + fields = ["file_ext", "success_templ", "failure_templ", "join_on"], +) + +def _unittest_toolchain_impl(ctx): + return [ + platform_common.ToolchainInfo( + unittest_toolchain_info = _UnittestToolchainInfo( + file_ext = ctx.attr.file_ext, + success_templ = ctx.attr.success_templ, + failure_templ = ctx.attr.failure_templ, + join_on = ctx.attr.join_on, + ), + ), + ] + +unittest_toolchain = rule( + implementation = _unittest_toolchain_impl, + attrs = { + "failure_templ": attr.string(mandatory = True), + "file_ext": attr.string(mandatory = True), + "join_on": attr.string(mandatory = True), + "success_templ": attr.string(mandatory = True), + }, +) + +def _impl_function_name(impl): + """Derives the name of the given rule implementation function. + + This can be used for better test feedback. + + Args: + impl: the rule implementation function + + Returns: + The name of the given function + """ + + # Starlark currently stringifies a function as "", so we use + # that knowledge to parse the "NAME" portion out. If this behavior ever + # changes, we'll need to update this. + # TODO(bazel-team): Expose a ._name field on functions to avoid this. + impl_name = str(impl) + impl_name = impl_name.partition("")[0] def _make(impl, attrs = None): """Creates a unit test rule from its implementation function. @@ -41,7 +99,7 @@ def _make(impl, attrs = None): # Assert statements go here - unittest.end(env) + return unittest.end(env) your_test = unittest.make(_your_test) ``` @@ -57,24 +115,96 @@ def _make(impl, attrs = None): A rule definition that should be stored in a global whose name ends in `_test`. """ - - # Derive the name of the implementation function for better test feedback. - # Skylark currently stringifies a function as "", so we use - # that knowledge to parse the "NAME" portion out. If this behavior ever - # changes, we'll need to update this. - # TODO(bazel-team): Expose a ._name field on functions to avoid this. - impl_name = str(impl) - impl_name = impl_name.partition("")[0] - attrs = dict(attrs) if attrs else {} - attrs["_impl_name"] = attr.string(default = impl_name) + attrs["_impl_name"] = attr.string(default = _impl_function_name(impl)) return rule( impl, attrs = attrs, _skylark_testable = True, test = True, + toolchains = [TOOLCHAIN_TYPE], + ) + +_ActionInfo = provider(fields = ["actions"]) + +def _action_retrieving_aspect_impl(target, ctx): + _ignore = [ctx] + return [_ActionInfo(actions = target.actions)] + +_action_retrieving_aspect = aspect( + attr_aspects = [], + implementation = _action_retrieving_aspect_impl, +) + +# TODO(cparsons): Provide more full documentation on analysis testing in README. +def _make_analysis_test(impl, expect_failure = False, config_settings = {}): + """Creates an analysis test rule from its implementation function. + + An analysis test verifies the behavior of a "real" rule target by examining + and asserting on the providers given by the real target. + + Each analysis test is defined in an implementation function that must then be + associated with a rule so that a target can be built. This function handles + the boilerplate to create and return a test rule and captures the + implementation function's name so that it can be printed in test feedback. + + An example of an analysis test: + + ``` + def _your_test(ctx): + env = analysistest.begin(ctx) + + # Assert statements go here + + return analysistest.end(env) + + your_test = analysistest.make(_your_test) + ``` + + Recall that names of test rules must end in `_test`. + + Args: + impl: The implementation function of the unit test. + expect_failure: If true, the analysis test will expect the target_under_test + to fail. Assertions can be made on the underlying failure using asserts.expect_failure + config_settings: A dictionary of configuration settings to change for the target under + test and its dependencies. This may be used to essentially change 'build flags' for + the target under test, and may thus be utilized to test multiple targets with different + flags in a single build + + Returns: + A rule definition that should be stored in a global whose name ends in + `_test`. + """ + attrs = {} + attrs["_impl_name"] = attr.string(default = _impl_function_name(impl)) + + changed_settings = dict(config_settings) + if expect_failure: + changed_settings["//command_line_option:allow_analysis_failures"] = "True" + + if changed_settings: + test_transition = analysis_test_transition( + settings = changed_settings, + ) + attrs["target_under_test"] = attr.label( + aspects = [_action_retrieving_aspect], + cfg = test_transition, + mandatory = True, + ) + else: + attrs["target_under_test"] = attr.label( + aspects = [_action_retrieving_aspect], + mandatory = True, + ) + + return rule( + impl, + attrs = attrs, + test = True, + toolchains = [TOOLCHAIN_TYPE], + analysis_test = True, ) def _suite(name, *test_rules): @@ -151,26 +281,49 @@ def _begin(ctx): """ return struct(ctx = ctx, failures = []) +def _end_analysis_test(env): + """Ends an analysis test and logs the results. + + This must be called and returned at the end of an analysis test implementation function so + that the results are reported. + + Args: + env: The test environment returned by `analysistest.begin`. + + Returns: + A list of providers needed to automatically register the analysis test result. + """ + return [AnalysisTestResultInfo( + success = (len(env.failures) == 0), + message = "\n".join(env.failures), + )] + def _end(env): """Ends a unit test and logs the results. - This must be called before the end of a unit test implementation function so + This must be called and returned at the end of a unit test implementation function so that the results are reported. Args: env: The test environment returned by `unittest.begin`. + + Returns: + A list of providers needed to automatically register the test result. """ - cmd = "\n".join([ - "cat << EOF", - "\n".join(env.failures), - "EOF", - "exit %d" % len(env.failures), - ]) + + tc = env.ctx.toolchains[TOOLCHAIN_TYPE].unittest_toolchain_info + testbin = env.ctx.actions.declare_file(env.ctx.label.name + tc.file_ext) + if env.failures: + cmd = tc.failure_templ % tc.join_on.join(env.failures) + else: + cmd = tc.success_templ + env.ctx.actions.write( - output = env.ctx.outputs.executable, + output = testbin, content = cmd, is_executable = True, ) + return [DefaultInfo(executable = testbin)] def _fail(env, msg): """Unconditionally causes the current test to fail. @@ -267,7 +420,61 @@ def _assert_new_set_equals(env, expected, actual, msg = None): full_msg = expectation_msg _fail(env, full_msg) +def _expect_failure(env, expected_failure_msg = ""): + """Asserts that the target under test has failed with a given error message. + + This requires that the analysis test is created with `analysistest.make()` and + `expect_failures = True` is specified. + + Args: + env: The test environment returned by `analysistest.begin`. + expected_failure_msg: The error message to expect as a result of analysis failures. + """ + dep = _target_under_test(env) + if AnalysisFailureInfo in dep: + actual_errors = "" + for cause in dep[AnalysisFailureInfo].causes.to_list(): + actual_errors += cause.message + "\n" + if actual_errors.find(expected_failure_msg) < 0: + expectation_msg = "Expected errors to contain '%s' but did not. " % expected_failure_msg + expectation_msg += "Actual errors:%s" % actual_errors + _fail(env, expectation_msg) + else: + _fail(env, "Expected failure of target_under_test, but found success") + +def _target_actions(env): + """Returns a list of actions registered by the target under test. + + Args: + env: The test environment returned by `analysistest.begin`. + + Returns: + A list of actions registered by the target under test + """ + + # Validate? + dep = _target_under_test(env) + return dep[_ActionInfo].actions + +def _target_under_test(env): + """Returns the target under test. + + Args: + env: The test environment returned by `analysistest.begin`. + + Returns: + The target under test. + """ + result = getattr(env.ctx.attr, "target_under_test") + if types.is_list(result): + if result: + return result[0] + else: + fail("test rule does not have a target_under_test") + return result + asserts = struct( + expect_failure = _expect_failure, equals = _assert_equals, false = _assert_false, set_equals = _assert_set_equals, @@ -282,3 +489,12 @@ unittest = struct( end = _end, fail = _fail, ) + +analysistest = struct( + make = _make_analysis_test, + begin = _begin, + end = _end_analysis_test, + fail = _fail, + target_actions = _target_actions, + target_under_test = _target_under_test, +) diff --git a/go/private/skylib/lib/versions.bzl b/go/private/skylib/lib/versions.bzl index 0298ef4053..f5a3066905 100644 --- a/go/private/skylib/lib/versions.bzl +++ b/go/private/skylib/lib/versions.bzl @@ -89,10 +89,12 @@ def _check_bazel_version(minimum_bazel_version, maximum_bazel_version = None, ba """ if not bazel_version: if "bazel_version" not in dir(native): - fail("\nCurrent Bazel version is lower than 0.2.1, expected at least %s\n" % minimum_bazel_version) + fail("Current Bazel version is lower than 0.2.1; expected at least {}".format( + minimum_bazel_version, + )) elif not native.bazel_version: - print("\nCurrent Bazel is not a release version, cannot check for compatibility.") - print("Make sure that you are running at least Bazel %s.\n" % minimum_bazel_version) + print("Current Bazel is not a release version; cannot check for compatibility. " + + "Make sure that you are running at least Bazel {}.".format(minimum_bazel_version)) return else: bazel_version = native.bazel_version @@ -101,7 +103,7 @@ def _check_bazel_version(minimum_bazel_version, maximum_bazel_version = None, ba threshold = minimum_bazel_version, version = bazel_version, ): - fail("\nCurrent Bazel version is {}, expected at least {}\n".format( + fail("Current Bazel version is {}; expected at least {}".format( bazel_version, minimum_bazel_version, )) @@ -111,7 +113,7 @@ def _check_bazel_version(minimum_bazel_version, maximum_bazel_version = None, ba threshold = maximum_bazel_version, version = bazel_version, ): - fail("\nCurrent Bazel version is {}, expected at most {}\n".format( + fail("Current Bazel version is {}; expected at most {}".format( bazel_version, maximum_bazel_version, )) diff --git a/go/private/skylib/toolchains/unittest/BUILD b/go/private/skylib/toolchains/unittest/BUILD new file mode 100644 index 0000000000..fe08564814 --- /dev/null +++ b/go/private/skylib/toolchains/unittest/BUILD @@ -0,0 +1,62 @@ +load( + "@io_bazel_rules_go//go/private:skylib/lib/unittest.bzl", + "TOOLCHAIN_TYPE", + "unittest_toolchain", +) + +licenses(["notice"]) + +toolchain_type( + name = "toolchain_type", + visibility = ["//visibility:public"], +) + +unittest_toolchain( + name = "cmd", + failure_templ = """@echo off +echo %s +exit /b 1 +""", + file_ext = ".bat", + join_on = "\necho ", + success_templ = "@exit /b 0", + visibility = ["//visibility:public"], +) + +toolchain( + name = "cmd_toolchain", + exec_compatible_with = [ + "@bazel_tools//platforms:windows", + ], + toolchain = ":cmd", + toolchain_type = TOOLCHAIN_TYPE, +) + +unittest_toolchain( + name = "bash", + failure_templ = """#!/bin/sh +cat <<'EOF' +%s +EOF +exit 1 +""", + file_ext = ".sh", + join_on = "\n", + success_templ = "#!/bin/sh\nexit 0", + visibility = ["//visibility:public"], +) + +toolchain( + name = "bash_toolchain", + toolchain = ":bash", + toolchain_type = TOOLCHAIN_TYPE, +) + +filegroup( + name = "test_deps", + testonly = True, + srcs = [ + "BUILD", + ], + visibility = ["//:__subpackages__"], +)