# Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Defines fuzzer_test. # import("//build/config/features.gni") import("//build/config/sanitizers/sanitizers.gni") import("//testing/test.gni") # fuzzer_test is used to define individual libfuzzer tests. # # Supported attributes: # - (required) sources - fuzzer test source files # - deps - test dependencies # - additional_configs - additional configs to be used for compilation # - dict - a dictionary file for the fuzzer. # - libfuzzer_options - options for the fuzzer (e.g. -max_len or -timeout). # - seed_corpus - a directory with seed corpus. # # If use_libfuzzer gn flag is defined, then proper fuzzer would be build. # Without use_libfuzzer a unit-test style binary would be built on linux # and the whole target is a no-op otherwise. # # The template wraps test() target with appropriate dependencies. # If any test run-time options are present (dict or libfuzzer_options), then a # config (.options file) file would be generated or modified in root output # dir (next to test). template("fuzzer_test") { if (!disable_libfuzzer && (use_libfuzzer || use_drfuzz || is_linux)) { assert(defined(invoker.sources), "Need sources in $target_name.") test_deps = [ "//testing/libfuzzer:libfuzzer_main" ] if (defined(invoker.deps)) { test_deps += invoker.deps } if (defined(invoker.libfuzzer_options) && !defined(invoker.dict)) { # Copy libfuzzer_options to output if dict is not provided. copy(target_name + "_libfuzzer_options_copy") { sources = [ invoker.libfuzzer_options, ] outputs = [ "$root_build_dir/" + target_name + ".options", ] } test_deps += [ ":" + target_name + "_libfuzzer_options_copy" ] } if (defined(invoker.seed_corpus)) { depfile = "$root_build_dir/$target_name" + ".seed_corpus.d" out = "$root_build_dir/$target_name" + "_seed_corpus.zip" action(target_name + "_seed_corpus") { script = "//testing/libfuzzer/archive_corpus.py" args = [ "--depfile", rebase_path(depfile), "--corpus", rebase_path(invoker.seed_corpus), "--output", rebase_path(out), "--fuzzer", rebase_path("$root_build_dir/$target_name"), ] depfile = depfile outputs = [ out, ] deps = [ "//testing/libfuzzer:seed_corpus", ] } test_deps += [ ":" + target_name + "_seed_corpus" ] } if (defined(invoker.dict)) { # Copy dictionary to output. copy(target_name + "_dict_copy") { sources = [ invoker.dict, ] outputs = [ "$root_build_dir/" + target_name + ".dict", ] } test_deps += [ ":" + target_name + "_dict_copy" ] # Generate new .options file or update an existing one. config_name = target_name + ".options" action(config_name) { script = "//testing/libfuzzer/gen_fuzzer_config.py" args = [ "--config", rebase_path("$root_build_dir/" + config_name), "--dict", rebase_path("$root_build_dir/" + invoker.target_name + ".dict"), ] if (defined(invoker.libfuzzer_options)) { args += [ "--libfuzzer_options", rebase_path(invoker.libfuzzer_options), ] } outputs = [ "$root_build_dir/$config_name", ] } test_deps += [ ":" + config_name ] } test(target_name) { forward_variables_from(invoker, [ "sources", "include_dirs", ]) deps = test_deps if (defined(invoker.additional_configs)) { configs += invoker.additional_configs } } } else { # noop on unsupported platforms. # mark attributes as used. assert(invoker.sources == [] || invoker.sources != []) if (defined(invoker.additional_configs)) { assert( invoker.additional_configs == [] || invoker.additional_configs != []) } if (defined(invoker.deps)) { assert(invoker.deps == [] || invoker.deps != []) } if (defined(invoker.dict)) { assert(invoker.dict == [] || invoker.dict != []) } if (defined(invoker.libfuzzer_options)) { assert(invoker.libfuzzer_options == [] || invoker.libfuzzer_options != []) } if (defined(invoker.seed_corpus)) { assert(invoker.seed_corpus == [] || invoker.seed_corpus != []) } group(target_name) { } } }