# 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. # 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. r"""A script that compares 2 CToolchains from proto format. This script accepts two files in either a CROSSTOOL proto text format or a CToolchain proto text format. It then locates the CToolchains with the given toolchain_identifier and checks if the resulting CToolchain objects in Java are the same. Example usage: bazel run \ @rules_cc//tools/migration:ctoolchain_comparator -- \ --before=/path/to/CROSSTOOL1 \ --after=/path/to/CROSSTOOL2 \ --toolchain_identifier=id """ import os from absl import app from absl import flags from google.protobuf import text_format from third_party.com.github.bazelbuild.bazel.src.main.protobuf import crosstool_config_pb2 from tools.migration.ctoolchain_comparator_lib import compare_ctoolchains flags.DEFINE_string( "before", None, ("A text proto file containing the relevant CTooclchain before the change, " "either a CROSSTOOL file or a single CToolchain proto text")) flags.DEFINE_string( "after", None, ("A text proto file containing the relevant CToolchain after the change, " "either a CROSSTOOL file or a single CToolchain proto text")) flags.DEFINE_string("toolchain_identifier", None, "The identifier of the CToolchain that is being compared.") flags.mark_flag_as_required("before") flags.mark_flag_as_required("after") def _to_absolute_path(path): path = os.path.expanduser(path) if os.path.isabs(path): return path else: if "BUILD_WORKING_DIRECTORY" in os.environ: return os.path.join(os.environ["BUILD_WORKING_DIRECTORY"], path) else: return path def _find_toolchain(crosstool, toolchain_identifier): for toolchain in crosstool.toolchain: if toolchain.toolchain_identifier == toolchain_identifier: return toolchain return None def _read_crosstool_or_ctoolchain_proto(input_file, toolchain_identifier=None): """Reads a proto file and finds the CToolchain with the given identifier.""" with open(input_file, "r") as f: text = f.read() crosstool_release = crosstool_config_pb2.CrosstoolRelease() c_toolchain = crosstool_config_pb2.CToolchain() try: text_format.Merge(text, crosstool_release) if toolchain_identifier is None: print("CROSSTOOL proto needs a 'toolchain_identifier' specified in " "order to be able to select the right toolchain for comparison.") return None toolchain = _find_toolchain(crosstool_release, toolchain_identifier) if toolchain is None: print(("Cannot find a CToolchain with an identifier '%s' in CROSSTOOL " "file") % toolchain_identifier) return None return toolchain except text_format.ParseError as crosstool_error: try: text_format.Merge(text, c_toolchain) if (toolchain_identifier is not None and c_toolchain.toolchain_identifier != toolchain_identifier): print(("Expected CToolchain with identifier '%s', got CToolchain with " "identifier '%s'" % (toolchain_identifier, c_toolchain.toolchain_identifier))) return None return c_toolchain except text_format.ParseError as toolchain_error: print(("Error parsing file '%s':" % input_file)) # pylint: disable=superfluous-parens print("Attempt to parse it as a CROSSTOOL proto:") # pylint: disable=superfluous-parens print(crosstool_error) # pylint: disable=superfluous-parens print("Attempt to parse it as a CToolchain proto:") # pylint: disable=superfluous-parens print(toolchain_error) # pylint: disable=superfluous-parens return None def main(unused_argv): before_file = _to_absolute_path(flags.FLAGS.before) after_file = _to_absolute_path(flags.FLAGS.after) toolchain_identifier = flags.FLAGS.toolchain_identifier toolchain_before = _read_crosstool_or_ctoolchain_proto( before_file, toolchain_identifier) toolchain_after = _read_crosstool_or_ctoolchain_proto(after_file, toolchain_identifier) if not toolchain_before or not toolchain_after: print("There was an error getting the required toolchains.") exit(1) found_difference = compare_ctoolchains(toolchain_before, toolchain_after) if found_difference: exit(1) if __name__ == "__main__": app.run(main)