xref: /aosp_15_r20/art/tools/test_presubmit.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright 2017, The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker#
18*795d594fSAndroid Build Coastguard Worker# There are many run-tests which generate their sources automatically.
19*795d594fSAndroid Build Coastguard Worker# It is desirable to keep the checked-in source code, as we re-run generators very rarely.
20*795d594fSAndroid Build Coastguard Worker#
21*795d594fSAndroid Build Coastguard Worker# This script will re-run the generators only if their dependent files have changed and then
22*795d594fSAndroid Build Coastguard Worker# complain if the outputs no longer matched what's in the source tree.
23*795d594fSAndroid Build Coastguard Worker#
24*795d594fSAndroid Build Coastguard Worker
25*795d594fSAndroid Build Coastguard Workerimport os
26*795d594fSAndroid Build Coastguard Workerimport pathlib
27*795d594fSAndroid Build Coastguard Workerimport subprocess
28*795d594fSAndroid Build Coastguard Workerimport sys
29*795d594fSAndroid Build Coastguard Workerimport tempfile
30*795d594fSAndroid Build Coastguard Worker
31*795d594fSAndroid Build Coastguard WorkerTHIS_PATH = os.path.dirname(os.path.realpath(__file__))
32*795d594fSAndroid Build Coastguard Worker
33*795d594fSAndroid Build Coastguard WorkerTOOLS_GEN_SRCS = [
34*795d594fSAndroid Build Coastguard Worker    # tool -> path to a script to generate a file
35*795d594fSAndroid Build Coastguard Worker    # reference_files -> list of files that the script can generate
36*795d594fSAndroid Build Coastguard Worker    # args -> lambda(path) that generates arguments the 'tool' in order to output to 'path'
37*795d594fSAndroid Build Coastguard Worker    # interesting_files -> which files much change in order to re-run the tool.
38*795d594fSAndroid Build Coastguard Worker    # interesting_to_reference_files: lambda(x,reference_files)
39*795d594fSAndroid Build Coastguard Worker    #                                 given the interesting file 'x' and a list of reference_files,
40*795d594fSAndroid Build Coastguard Worker    #                                 return exactly one reference file that corresponds to it.
41*795d594fSAndroid Build Coastguard Worker    { 'tool' : 'test/988-method-trace/gen_srcs.py',
42*795d594fSAndroid Build Coastguard Worker      'reference_files' : ['test/988-method-trace/src/art/Test988Intrinsics.java'],
43*795d594fSAndroid Build Coastguard Worker      'args' : lambda output_path: [output_path],
44*795d594fSAndroid Build Coastguard Worker      'interesting_files' : ['compiler/intrinsics_list.h'],
45*795d594fSAndroid Build Coastguard Worker      'interesting_to_reference_file' : lambda interesting, references: references[0],
46*795d594fSAndroid Build Coastguard Worker    },
47*795d594fSAndroid Build Coastguard Worker]
48*795d594fSAndroid Build Coastguard Worker
49*795d594fSAndroid Build Coastguard WorkerDEBUG = False
50*795d594fSAndroid Build Coastguard Worker
51*795d594fSAndroid Build Coastguard Workerdef debug_print(msg):
52*795d594fSAndroid Build Coastguard Worker  if DEBUG:
53*795d594fSAndroid Build Coastguard Worker    print("[DEBUG]: " + msg, file=sys.stderr)
54*795d594fSAndroid Build Coastguard Worker
55*795d594fSAndroid Build Coastguard Workerdef is_interesting(f, tool_dict):
56*795d594fSAndroid Build Coastguard Worker  """
57*795d594fSAndroid Build Coastguard Worker  Returns true if this is a file we want to run this tool before uploading. False otherwise.
58*795d594fSAndroid Build Coastguard Worker  """
59*795d594fSAndroid Build Coastguard Worker  path = pathlib.Path(f)
60*795d594fSAndroid Build Coastguard Worker  return str(path) in tool_dict['interesting_files']
61*795d594fSAndroid Build Coastguard Worker
62*795d594fSAndroid Build Coastguard Workerdef get_changed_files(commit):
63*795d594fSAndroid Build Coastguard Worker  """
64*795d594fSAndroid Build Coastguard Worker  Gets the files changed in the given commit.
65*795d594fSAndroid Build Coastguard Worker  """
66*795d594fSAndroid Build Coastguard Worker  return subprocess.check_output(
67*795d594fSAndroid Build Coastguard Worker      ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit],
68*795d594fSAndroid Build Coastguard Worker      stderr=subprocess.STDOUT,
69*795d594fSAndroid Build Coastguard Worker      universal_newlines=True).split()
70*795d594fSAndroid Build Coastguard Worker
71*795d594fSAndroid Build Coastguard Workerdef command_line_for_tool(tool_dict, output):
72*795d594fSAndroid Build Coastguard Worker  """
73*795d594fSAndroid Build Coastguard Worker  Calculate the command line for this tool when ran against the output file 'output'.
74*795d594fSAndroid Build Coastguard Worker  """
75*795d594fSAndroid Build Coastguard Worker  proc_args = [tool_dict['tool']] + tool_dict['args'](output)
76*795d594fSAndroid Build Coastguard Worker  return proc_args
77*795d594fSAndroid Build Coastguard Worker
78*795d594fSAndroid Build Coastguard Workerdef run_tool(tool_dict, output):
79*795d594fSAndroid Build Coastguard Worker  """
80*795d594fSAndroid Build Coastguard Worker  Execute this tool by passing the tool args to the tool.
81*795d594fSAndroid Build Coastguard Worker  """
82*795d594fSAndroid Build Coastguard Worker  proc_args = command_line_for_tool(tool_dict, output)
83*795d594fSAndroid Build Coastguard Worker  debug_print("PROC_ARGS: %s" %(proc_args))
84*795d594fSAndroid Build Coastguard Worker  succ = subprocess.call(proc_args)
85*795d594fSAndroid Build Coastguard Worker  return succ
86*795d594fSAndroid Build Coastguard Worker
87*795d594fSAndroid Build Coastguard Workerdef get_reference_file(changed_file, tool_dict):
88*795d594fSAndroid Build Coastguard Worker   """
89*795d594fSAndroid Build Coastguard Worker   Lookup the file that the tool is generating in response to changing an interesting file
90*795d594fSAndroid Build Coastguard Worker   """
91*795d594fSAndroid Build Coastguard Worker   return tool_dict['interesting_to_reference_file'](changed_file, tool_dict['reference_files'])
92*795d594fSAndroid Build Coastguard Worker
93*795d594fSAndroid Build Coastguard Workerdef run_diff(changed_file, tool_dict, original_file):
94*795d594fSAndroid Build Coastguard Worker  ref_file = get_reference_file(changed_file, tool_dict)
95*795d594fSAndroid Build Coastguard Worker
96*795d594fSAndroid Build Coastguard Worker  return subprocess.call(["diff", ref_file, original_file]) != 0
97*795d594fSAndroid Build Coastguard Worker
98*795d594fSAndroid Build Coastguard Workerdef run_gen_srcs(files):
99*795d594fSAndroid Build Coastguard Worker  """
100*795d594fSAndroid Build Coastguard Worker  Runs test tools only for interesting files that were changed in this commit.
101*795d594fSAndroid Build Coastguard Worker  """
102*795d594fSAndroid Build Coastguard Worker  if len(files) == 0:
103*795d594fSAndroid Build Coastguard Worker    return
104*795d594fSAndroid Build Coastguard Worker
105*795d594fSAndroid Build Coastguard Worker  success = 0  # exit code 0 = success, >0 error.
106*795d594fSAndroid Build Coastguard Worker  had_diffs = False
107*795d594fSAndroid Build Coastguard Worker
108*795d594fSAndroid Build Coastguard Worker  for tool_dict in TOOLS_GEN_SRCS:
109*795d594fSAndroid Build Coastguard Worker    tool_ran_at_least_once = False
110*795d594fSAndroid Build Coastguard Worker    for f in files:
111*795d594fSAndroid Build Coastguard Worker      if is_interesting(f, tool_dict):
112*795d594fSAndroid Build Coastguard Worker        tmp_file = tempfile.mktemp()
113*795d594fSAndroid Build Coastguard Worker        reference_file = get_reference_file(f, tool_dict)
114*795d594fSAndroid Build Coastguard Worker
115*795d594fSAndroid Build Coastguard Worker        # Generate the source code with a temporary file as the output.
116*795d594fSAndroid Build Coastguard Worker        success = run_tool(tool_dict, tmp_file)
117*795d594fSAndroid Build Coastguard Worker        if success != 0:
118*795d594fSAndroid Build Coastguard Worker          # Immediately abort if the tool fails with a non-0 exit code, do not go any further.
119*795d594fSAndroid Build Coastguard Worker          print("[FATAL] Error when running tool (return code %s)" %(success), file=sys.stderr)
120*795d594fSAndroid Build Coastguard Worker          print("$> %s" %(" ".join(command_line_for_tool(tool_dict, tmp_file))), file=sys.stderr)
121*795d594fSAndroid Build Coastguard Worker          sys.exit(success)
122*795d594fSAndroid Build Coastguard Worker        if run_diff(f, tool_dict, tmp_file):
123*795d594fSAndroid Build Coastguard Worker          # If the tool succeeded, but there was a diff, then the generated code has diverged.
124*795d594fSAndroid Build Coastguard Worker          # Output the diff information and continue to the next files/tools.
125*795d594fSAndroid Build Coastguard Worker          had_diffs = True
126*795d594fSAndroid Build Coastguard Worker          print("-----------------------------------------------------------", file=sys.stderr)
127*795d594fSAndroid Build Coastguard Worker          print("File '%s' diverged from generated file; please re-run tools:" %(reference_file), file=sys.stderr)
128*795d594fSAndroid Build Coastguard Worker          print("$> %s" %(" ".join(command_line_for_tool(tool_dict, reference_file))), file=sys.stderr)
129*795d594fSAndroid Build Coastguard Worker        else:
130*795d594fSAndroid Build Coastguard Worker          debug_print("File %s is consistent with tool %s" %(reference_file, tool_dict['tool']))
131*795d594fSAndroid Build Coastguard Worker
132*795d594fSAndroid Build Coastguard Worker        tool_ran_at_least_once = True
133*795d594fSAndroid Build Coastguard Worker
134*795d594fSAndroid Build Coastguard Worker    if not tool_ran_at_least_once:
135*795d594fSAndroid Build Coastguard Worker      debug_print("Interesting files %s unchanged, skipping tool '%s'" %(tool_dict['interesting_files'], tool_dict['tool']))
136*795d594fSAndroid Build Coastguard Worker
137*795d594fSAndroid Build Coastguard Worker  if had_diffs:
138*795d594fSAndroid Build Coastguard Worker    success = 1
139*795d594fSAndroid Build Coastguard Worker  # Always return non-0 exit code when there were diffs so that the presubmit hooks are FAILED.
140*795d594fSAndroid Build Coastguard Worker
141*795d594fSAndroid Build Coastguard Worker  return success
142*795d594fSAndroid Build Coastguard Worker
143*795d594fSAndroid Build Coastguard Worker
144*795d594fSAndroid Build Coastguard Workerdef main():
145*795d594fSAndroid Build Coastguard Worker  if 'PREUPLOAD_COMMIT' in os.environ:
146*795d594fSAndroid Build Coastguard Worker    commit = os.environ['PREUPLOAD_COMMIT']
147*795d594fSAndroid Build Coastguard Worker  else:
148*795d594fSAndroid Build Coastguard Worker    print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'", file=sys.stderr)
149*795d594fSAndroid Build Coastguard Worker    commit = "HEAD"
150*795d594fSAndroid Build Coastguard Worker
151*795d594fSAndroid Build Coastguard Worker  os.chdir(os.path.join(THIS_PATH, '..')) # run tool relative to 'art' directory
152*795d594fSAndroid Build Coastguard Worker  debug_print("CWD: %s" %(os.getcwd()))
153*795d594fSAndroid Build Coastguard Worker
154*795d594fSAndroid Build Coastguard Worker  changed_files = get_changed_files(commit)
155*795d594fSAndroid Build Coastguard Worker  debug_print("Changed files: %s" %(changed_files))
156*795d594fSAndroid Build Coastguard Worker  return run_gen_srcs(changed_files)
157*795d594fSAndroid Build Coastguard Worker
158*795d594fSAndroid Build Coastguard Workerif __name__ == '__main__':
159*795d594fSAndroid Build Coastguard Worker  sys.exit(main())
160