#!/usr/bin/env python # Copyright 2019 Google Inc. # # 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. # ################################################################################ """Build modified projects.""" from __future__ import print_function import os import re import subprocess import yaml DEFAULT_FUZZING_ENGINES = ['afl', 'libfuzzer'] def get_modified_projects(): """Get a list of all the projects modified in this commit.""" output = subprocess.check_output(['git', 'diff', 'origin/master', '--name-status']) projects_regex = '.*projects/(?P.*)/.*\n' return set(re.findall(projects_regex, output)) def get_oss_fuzz_root(): """Get the absolute path of the root of the oss-fuzz checkout.""" script_path = os.path.realpath(__file__) return os.path.abspath( os.path.dirname(os.path.dirname(os.path.dirname(script_path)))) def execute_helper_command(helper_command): """Execute |helper_command| using helper.py.""" root = get_oss_fuzz_root() script_path = os.path.join(root, 'infra', 'helper.py') command = ['python', script_path] + helper_command subprocess.check_call(command) def build_fuzzers(project, sanitizer, engine): """Execute helper.py's build_fuzzers command on |project|. Build the fuzzers with |sanitizer| and |engine|.""" execute_helper_command( ['build_fuzzers', project, '--engine', engine, '--sanitizer', sanitizer]) def check_build(project, sanitizer, engine): """Execute helper.py's check_build command on |project|, assuming it was most recently built with |sanitizer| and |engine|.""" execute_helper_command( ['check_build', project, '--engine', engine, '--sanitizer', sanitizer]) def build_project(project): """Do all build of |project|.""" print('Building project', project) root = get_oss_fuzz_root() project_yaml_path = os.path.join(root, 'projects', project, 'project.yaml') with open(project_yaml_path) as fp: project_yaml = yaml.safe_load(fp) if project_yaml.get('disabled', False): return fuzzing_engines = project_yaml.get('fuzzing_engines', DEFAULT_FUZZING_ENGINES) if 'none' in fuzzing_engines: # no engine builds always use ASAN. build_fuzzers(project, 'address', 'none') return if 'afl' in fuzzing_engines: # AFL builds always use ASAN. build_fuzzers(project, 'address', 'afl') check_build(project, 'address', 'afl') if 'libfuzzer' not in fuzzing_engines: return for sanitizer in project_yaml['sanitizers']: build_fuzzers(project, sanitizer, 'libfuzzer') check_build(project, sanitizer, 'libfuzzer') def main(): projects = get_modified_projects() failures = [] for project in projects: try: build_project(project) except subprocess.CalledProcessError: failures.append(project) if failures: print('Failures:', ' '.join(failures)) exit(1) if __name__ == '__main__': main()