diff options
author | Abhishek Arya <inferno@chromium.org> | 2020-06-12 14:39:16 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-12 14:39:16 -0700 |
commit | 7101ec4d4bac7f4e97e6b0e12d077a00b7e105b2 (patch) | |
tree | de19698c32e43accd80f3ee372d98861cf86ba96 /infra/ci/build.py | |
parent | ad4557e4ad194d91cd2e9a4815aacbcd1cc1fbf6 (diff) | |
download | oss-fuzz-7101ec4d4bac7f4e97e6b0e12d077a00b7e105b2.tar.gz |
Add Github Actions CI. (#3971)
Diffstat (limited to 'infra/ci/build.py')
-rwxr-xr-x | infra/ci/build.py | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/infra/ci/build.py b/infra/ci/build.py new file mode 100755 index 000000000..f93c18a09 --- /dev/null +++ b/infra/ci/build.py @@ -0,0 +1,148 @@ +#!/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 sys +import subprocess +import yaml + +DEFAULT_ARCHITECTURES = ['x86_64'] +DEFAULT_ENGINES = ['afl', 'honggfuzz', 'libfuzzer'] +DEFAULT_SANITIZERS = ['address', 'undefined'] + + +def get_modified_buildable_projects(): + """Returns a list of all the projects modified in this commit that have a + build.sh file.""" + output = subprocess.check_output(['git', 'diff', '--name-only', + 'FETCH_HEAD']).decode() + projects_regex = '.*projects/(?P<name>.*)/.*\n' + modified_projects = set(re.findall(projects_regex, output)) + projects_dir = os.path.join(get_oss_fuzz_root(), 'projects') + # Filter out projects without Dockerfile files since new projects and reverted + # projects frequently don't have them. In these cases we don't want Travis's + # builds to fail. + modified_buildable_projects = [] + for project in modified_projects: + if not os.path.exists(os.path.join(projects_dir, project, 'Dockerfile')): + print('Project {0} does not have Dockerfile. skipping build.'.format( + project)) + continue + modified_buildable_projects.append(project) + return modified_buildable_projects + + +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 + print('Running command: %s' % ' '.join(command)) + subprocess.check_call(command) + + +def build_fuzzers(project, engine, sanitizer, architecture): + """Execute helper.py's build_fuzzers command on |project|. Build the fuzzers + with |engine| and |sanitizer| for |architecture|.""" + execute_helper_command([ + 'build_fuzzers', project, '--engine', engine, '--sanitizer', sanitizer, + '--architecture', architecture + ]) + + +def check_build(project, engine, sanitizer, architecture): + """Execute helper.py's check_build command on |project|, assuming it was most + recently built with |engine| and |sanitizer| for |architecture|.""" + execute_helper_command([ + 'check_build', project, '--engine', engine, '--sanitizer', sanitizer, + '--architecture', architecture + ]) + + +def should_build(project_yaml): + """Is the build specified by travis enabled in the |project_yaml|?""" + + def is_enabled(env_var, yaml_name, defaults): + """Is the value of |env_var| enabled in |project_yaml| (in the |yaml_name| + section)? Uses |defaults| if |yaml_name| section is unspecified.""" + return os.getenv(env_var) in project_yaml.get(yaml_name, defaults) + + return (is_enabled('ENGINE', 'fuzzing_engines', DEFAULT_ENGINES) and + is_enabled('SANITIZER', 'sanitizers', DEFAULT_SANITIZERS) and + is_enabled('ARCHITECTURE', 'architectures', DEFAULT_ARCHITECTURES)) + + +def build_project(project): + """Do the build of |project| that is specified by the environment variables - + SANITIZER, ENGINE, and ARCHITECTURE.""" + root = get_oss_fuzz_root() + project_yaml_path = os.path.join(root, 'projects', project, 'project.yaml') + with open(project_yaml_path) as file_handle: + project_yaml = yaml.safe_load(file_handle) + + if project_yaml.get('disabled', False): + print('Project {0} is disabled, skipping build.'.format(project)) + return + + engine = os.getenv('ENGINE') + sanitizer = os.getenv('SANITIZER') + architecture = os.getenv('ARCHITECTURE') + + if not should_build(project_yaml): + print(('Specified build: engine: {0}, sanitizer: {1}, architecture: {2} ' + 'not enabled for this project: {3}. skipping build.').format( + engine, sanitizer, architecture, project)) + + return + + print('Building project', project) + build_fuzzers(project, engine, sanitizer, architecture) + + if engine != 'none': + check_build(project, engine, sanitizer, architecture) + + +def main(): + """Build modified projects on travis.""" + projects = get_modified_buildable_projects() + failed_projects = [] + for project in projects: + try: + build_project(project) + except subprocess.CalledProcessError: + failed_projects.append(project) + + if failed_projects: + print('Failed projects:', ' '.join(failed_projects)) + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) |