blob: 5143e029b55bb10542de92636ac19127762b88e0 [file] [log] [blame]
rosica175f81c2019-01-08 14:29:28 +01001# 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")
rosica175f81c2019-01-08 14:29:28 +010049
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
Googlere68ae762019-08-09 05:42:58 -070069def _read_crosstool_or_ctoolchain_proto(input_file, toolchain_identifier=None):
rosica175f81c2019-01-08 14:29:28 +010070 """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)
Googlere68ae762019-08-09 05:42:58 -070077 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
rosica175f81c2019-01-08 14:29:28 +010081 toolchain = _find_toolchain(crosstool_release, toolchain_identifier)
82 if toolchain is None:
Googlerda4639b2019-01-18 11:20:12 -080083 print(("Cannot find a CToolchain with an identifier '%s' in CROSSTOOL "
rosica175f81c2019-01-08 14:29:28 +010084 "file") % toolchain_identifier)
85 return None
rosicaf64680e2019-01-18 07:37:01 -080086 return toolchain
rosica175f81c2019-01-08 14:29:28 +010087 except text_format.ParseError as crosstool_error:
88 try:
89 text_format.Merge(text, c_toolchain)
Googlere68ae762019-08-09 05:42:58 -070090 if (toolchain_identifier is not None and
91 c_toolchain.toolchain_identifier != toolchain_identifier):
Googlerda4639b2019-01-18 11:20:12 -080092 print(("Expected CToolchain with identifier '%s', got CToolchain with "
rosica175f81c2019-01-08 14:29:28 +010093 "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:
Googlerda4639b2019-01-18 11:20:12 -0800118 print("There was an error getting the required toolchains.")
119 exit(1)
rosica175f81c2019-01-08 14:29:28 +0100120
rosicaf64680e2019-01-18 07:37:01 -0800121 found_difference = compare_ctoolchains(toolchain_before, toolchain_after)
122 if found_difference:
123 exit(1)
rosica175f81c2019-01-08 14:29:28 +0100124
125
126if __name__ == "__main__":
127 app.run(main)