1
0
Fork 0
llvm-premerge-checks/scripts/premerge_checks.py
2020-06-04 14:10:08 +02:00

150 lines
6 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright 2020 Google LLC
#
# Licensed under the the Apache License v2.0 with LLVM Exceptions (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://llvm.org/LICENSE.txt
#
# 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.
# Runs all check on buildkite agent.
import argparse
import json
import logging
import os
import pathlib
import shutil
import subprocess
import time
from typing import Callable
import clang_format_report
import clang_tidy_report
import run_cmake
import test_results_report
from buildkite.utils import upload_file
from phabtalk.add_url_artifact import maybe_add_url_artifact
from phabtalk.phabtalk import Report, PhabTalk, Step
def ninja_all_report(step: Step, _: Report):
print('Full will be available in Artifacts "ninja-all.log"')
r = subprocess.run(f'ninja all | '
f'tee {artifacts_dir}/ninja-all.log | '
f'grep -vE "\\[.*] (Building|Linking|Copying|Generating|Creating)"',
shell=True, cwd=build_dir)
logging.debug(f'ninja all: returned {r.returncode}, stderr: "{r.stderr}"')
step.set_status_from_exit_code(r.returncode)
def ninja_check_all_report(step: Step, _: Report):
print('Full will be available in Artifacts "ninja-check-all.log"')
r = subprocess.run(f'ninja check-all | tee {artifacts_dir}/ninja-check-all.log | '
f'grep -vE "^\\[.*] (Building|Linking)" | '
f'grep -vE "^(PASS|XFAIL|UNSUPPORTED):"', shell=True, cwd=build_dir)
logging.debug(f'ninja check-all: returned {r.returncode}, stderr: "{r.stderr}"')
step.set_status_from_exit_code(r.returncode)
test_results_report.run(build_dir, 'test-results.xml', step, report)
def run_step(name: str, report: Report, thunk: Callable[[Step, Report], None]) -> Step:
start = time.time()
print(f'--- {name}') # New section in Buildkite log.
step = Step()
step.name = name
thunk(step, report)
step.duration = time.time() - start
# Expand section if it failed.
if not step.success:
print('^^^ +++')
report.steps.append(step)
return step
def cmake_report(projects: str, step: Step, _: Report):
global build_dir
cmake_result, build_dir, cmake_artifacts = run_cmake.run(projects, os.getcwd())
for file in cmake_artifacts:
if os.path.exists(file):
shutil.copy2(file, artifacts_dir)
step.set_status_from_exit_code(cmake_result)
def as_dict(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Runs premerge checks8')
parser.add_argument('--log-level', type=str, default='WARNING')
parser.add_argument('--check-clang-format', action='store_true')
parser.add_argument('--check-clang-tidy', action='store_true')
parser.add_argument('--projects', type=str, default='detect',
help="Projects to select, either a list or projects like 'clang;libc', or "
"'detect' to automatically infer proejcts from the diff, or "
"'default' to add all enabled projects")
args = parser.parse_args()
logging.basicConfig(level=args.log_level, format='%(levelname)-7s %(message)s')
build_dir = ''
step_key = os.getenv("BUILDKITE_STEP_KEY")
scripts_dir = pathlib.Path(__file__).parent.absolute()
artifacts_dir = os.path.join(os.getcwd(), 'artifacts')
os.makedirs(artifacts_dir, exist_ok=True)
report_path = f'{step_key}_result.json'
report = Report()
report.os = f'{os.getenv("BUILDKITE_AGENT_META_DATA_OS")}'
report.name = step_key
report.success = False
# Create report with failure in case something below fails.
with open(report_path, 'w') as f:
json.dump(report.__dict__, f, default=as_dict)
report.success = True
cmake = run_step('cmake', report, lambda s, r: cmake_report(args.projects, s, r))
if cmake.success:
ninja_all = run_step('ninja all', report, ninja_all_report)
if ninja_all.success:
run_step('ninja check-all', report, ninja_check_all_report)
if args.check_clang_tidy:
run_step('clang-tidy', report,
lambda s, r: clang_tidy_report.run('HEAD~1', os.path.join(scripts_dir, 'clang-tidy.ignore'), s, r))
if args.check_clang_format:
run_step('clang-format', report,
lambda s, r: clang_format_report.run('HEAD~1', os.path.join(scripts_dir, 'clang-format.ignore'), s, r))
logging.debug(report)
for s in report.steps:
mark = 'OK '
if not s.success:
report.success = False
mark = 'FAIL '
msg = ''
if len(s.messages):
msg = ': ' + '\n '.join(s.messages)
print(f'{mark} {s.name}{msg}')
ph_target_phid = os.getenv('ph_target_phid')
ph_buildable_diff = os.getenv('ph_buildable_diff')
if ph_target_phid is not None:
phabtalk = PhabTalk(os.getenv('CONDUIT_TOKEN'), 'https://reviews.llvm.org/api/', False)
for u in report.unit:
u['engine'] = step_key
phabtalk.update_build_status(ph_buildable_diff, ph_target_phid, True, report.success, report.lint, report.unit)
for a in report.artifacts:
url = upload_file(a['dir'], a['file'])
if url is not None:
maybe_add_url_artifact(phabtalk, ph_target_phid, url, f'{a["name"]} ({step_key})')
else:
logging.warning('No phabricator phid is specified. Will not update the build status in Phabricator')
with open(report_path, 'w') as f:
json.dump(report.__dict__, f, default=as_dict)
if not report.success:
print('Build completed with failures')
exit(1)