1# Copyright 2018 The Bazel Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14r"""A script that compares 2 CToolchains from proto format. 15 16This script accepts two files in either a CROSSTOOL proto text format or a 17CToolchain proto text format. It then locates the CToolchains with the given 18toolchain_identifier and checks if the resulting CToolchain objects in Java 19are the same. 20 21Example usage: 22 23bazel run \ 24@rules_cc//tools/migration:ctoolchain_comparator -- \ 25--before=/path/to/CROSSTOOL1 \ 26--after=/path/to/CROSSTOOL2 \ 27--toolchain_identifier=id 28""" 29 30import os 31from absl import app 32from absl import flags 33from google.protobuf import text_format 34from third_party.com.github.bazelbuild.bazel.src.main.protobuf import crosstool_config_pb2 35from tools.migration.ctoolchain_comparator_lib import compare_ctoolchains 36 37flags.DEFINE_string( 38 "before", None, 39 ("A text proto file containing the relevant CTooclchain before the change, " 40 "either a CROSSTOOL file or a single CToolchain proto text")) 41flags.DEFINE_string( 42 "after", None, 43 ("A text proto file containing the relevant CToolchain after the change, " 44 "either a CROSSTOOL file or a single CToolchain proto text")) 45flags.DEFINE_string("toolchain_identifier", None, 46 "The identifier of the CToolchain that is being compared.") 47flags.mark_flag_as_required("before") 48flags.mark_flag_as_required("after") 49 50 51def _to_absolute_path(path): 52 path = os.path.expanduser(path) 53 if os.path.isabs(path): 54 return path 55 else: 56 if "BUILD_WORKING_DIRECTORY" in os.environ: 57 return os.path.join(os.environ["BUILD_WORKING_DIRECTORY"], path) 58 else: 59 return path 60 61 62def _find_toolchain(crosstool, toolchain_identifier): 63 for toolchain in crosstool.toolchain: 64 if toolchain.toolchain_identifier == toolchain_identifier: 65 return toolchain 66 return None 67 68 69def _read_crosstool_or_ctoolchain_proto(input_file, toolchain_identifier=None): 70 """Reads a proto file and finds the CToolchain with the given identifier.""" 71 with open(input_file, "r") as f: 72 text = f.read() 73 crosstool_release = crosstool_config_pb2.CrosstoolRelease() 74 c_toolchain = crosstool_config_pb2.CToolchain() 75 try: 76 text_format.Merge(text, crosstool_release) 77 if toolchain_identifier is None: 78 print("CROSSTOOL proto needs a 'toolchain_identifier' specified in " 79 "order to be able to select the right toolchain for comparison.") 80 return None 81 toolchain = _find_toolchain(crosstool_release, toolchain_identifier) 82 if toolchain is None: 83 print(("Cannot find a CToolchain with an identifier '%s' in CROSSTOOL " 84 "file") % toolchain_identifier) 85 return None 86 return toolchain 87 except text_format.ParseError as crosstool_error: 88 try: 89 text_format.Merge(text, c_toolchain) 90 if (toolchain_identifier is not None and 91 c_toolchain.toolchain_identifier != toolchain_identifier): 92 print(("Expected CToolchain with identifier '%s', got CToolchain with " 93 "identifier '%s'" % (toolchain_identifier, 94 c_toolchain.toolchain_identifier))) 95 return None 96 return c_toolchain 97 except text_format.ParseError as toolchain_error: 98 print(("Error parsing file '%s':" % input_file)) # pylint: disable=superfluous-parens 99 print("Attempt to parse it as a CROSSTOOL proto:") # pylint: disable=superfluous-parens 100 print(crosstool_error) # pylint: disable=superfluous-parens 101 print("Attempt to parse it as a CToolchain proto:") # pylint: disable=superfluous-parens 102 print(toolchain_error) # pylint: disable=superfluous-parens 103 return None 104 105 106def main(unused_argv): 107 108 before_file = _to_absolute_path(flags.FLAGS.before) 109 after_file = _to_absolute_path(flags.FLAGS.after) 110 toolchain_identifier = flags.FLAGS.toolchain_identifier 111 112 toolchain_before = _read_crosstool_or_ctoolchain_proto( 113 before_file, toolchain_identifier) 114 toolchain_after = _read_crosstool_or_ctoolchain_proto(after_file, 115 toolchain_identifier) 116 117 if not toolchain_before or not toolchain_after: 118 print("There was an error getting the required toolchains.") 119 exit(1) 120 121 found_difference = compare_ctoolchains(toolchain_before, toolchain_after) 122 if found_difference: 123 exit(1) 124 125 126if __name__ == "__main__": 127 app.run(main) 128