1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190

build / nocompile.gni [blame]

# Copyright 2011 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# This file is meant to be included into an target to create a unittest that
# invokes a set of no-compile tests.  A no-compile test is a test that asserts
# a particular construct will not compile.
#
# Usage:
#
# 1. Create a GN target:
#
#    import("//build/nocompile.gni")
#
#    nocompile_source_set("base_nocompile_tests") {
#      sources = [
#        "functional/one_not_equal_two_nocompile.nc",
#      ]
#      deps = [
#        ":base"
#      ]
#    }
#
#    Note that by convention, nocompile tests use the `.nc` extension rather
#    than the standard `.cc` extension: this is because the expectation lines
#    often exceed 80 characters, which would make clang-format unhappy.
#
# 2. Add a dep from a related test binary to the nocompile source set:
#
#    test("base_unittests") {
#      ...
#      deps += [ ":base_nocompile_tests" ]
#    }
#
# 3. Populate the .nc file with test cases. Expected compile failures should be
#    annotated with a comment of the form:
#
#    // expected-error {{<expected error string here>}}
#
#    For example:
#
#    void OneDoesNotEqualTwo() {
#      static_assert(1 == 2);  // expected-error {{static assertion failed due to requirement '1 == 2'}}
#    }
#
#    The verification logic is built as part of clang; full documentation is at
#    https://clang.llvm.org/docs/InternalsManual.html#specifying-diagnostics.
#
# Also see:
#   http://dev.chromium.org/developers/testing/no-compile-tests
#
import("//build/config/clang/clang.gni")
if (is_win) {
  import("//build/toolchain/win/win_toolchain_data.gni")
}

declare_args() {
  enable_nocompile_tests = is_clang && !is_nacl
}

if (enable_nocompile_tests) {
  template("nocompile_source_set") {
    action_foreach(target_name) {
      testonly = true

      script = "//tools/nocompile/wrapper.py"
      sources = invoker.sources
      if (defined(invoker.deps)) {
        deps = invoker.deps
      }

      # An action is not a compiler, so configs is empty until it is explicitly
      # set.
      configs = default_compiler_configs
      if (defined(invoker.configs)) {
        configs += invoker.configs
      }

      # Disable the checks that the Chrome style plugin normally enforces to
      # reduce the amount of boilerplate needed in nocompile tests.
      configs -= [ "//build/config/clang:find_bad_constructs" ]

      if (is_win) {
        result_path =
            "$target_out_dir/$target_name/{{source_name_part}}_placeholder.obj"
      } else {
        result_path =
            "$target_out_dir/$target_name/{{source_name_part}}_placeholder.o"
      }
      rebased_obj_path = rebase_path(result_path, root_build_dir)

      depfile = "${result_path}.d"
      rebased_depfile_path = rebase_path(depfile, root_build_dir)
      outputs = [ result_path ]

      if (is_win) {
        if (host_os == "win") {
          cxx = "clang-cl.exe"
        } else {
          cxx = "clang-cl"
        }
      } else {
        cxx = "clang++"
      }

      args = []

      if (is_win) {
        # ninja normally parses /showIncludes output, but the depsformat
        # variable can only be set in compiler tools, not for custom actions.
        # Unfortunately, this means the clang wrapper needs to generate the
        # depfile itself.
        args += [ "--generate-depfile" ]
      }

      args += [
        rebase_path("$clang_base_path/bin/$cxx", root_build_dir),
        "{{source}}",
        rebased_obj_path,
        rebased_depfile_path,
        "--",
        "{{cflags}}",
        "{{cflags_cc}}",
        "{{defines}}",
        "{{include_dirs}}",

        # No need to generate an object file for nocompile tests.
        "-Xclang",
        "-fsyntax-only",

        # Enable clang's VerifyDiagnosticConsumer:
        # https://clang.llvm.org/doxygen/classclang_1_1VerifyDiagnosticConsumer.html
        "-Xclang",
        "-verify",

        # But don't require expected-note comments since that is not the
        # primary point of the nocompile tests.
        "-Xclang",
        "-verify-ignore-unexpected=note",

        # Disable the error limit so that nocompile tests do not need to be
        # arbitrarily split up when they hit the default error limit.
        "-ferror-limit=0",

        # So funny characters don't show up in error messages.
        "-fno-color-diagnostics",

        # Always treat warnings as errors.
        "-Werror",
      ]

      if (!is_win) {
        args += [
          # On non-Windows platforms, clang can generate the depfile.
          "-MMD",
          "-MF",
          rebased_depfile_path,
          "-MT",
          rebased_obj_path,

          # Non-Windows clang uses file extensions to determine how to treat
          # various inputs, so explicitly tell it to treat all inputs (even
          # those with weird extensions like .nc) as C++ source files.
          "-x",
          "c++",
        ]
      } else {
        # For some reason, the Windows includes are not part of the default
        # compiler configs. Set it explicitly here, since things like libc++
        # depend on the VC runtime.
        if (target_cpu == "x86") {
          win_toolchain_data = win_toolchain_data_x86
        } else if (target_cpu == "x64") {
          win_toolchain_data = win_toolchain_data_x64
        } else if (target_cpu == "arm64") {
          win_toolchain_data = win_toolchain_data_arm64
        } else {
          error("Unsupported target_cpu, add it to win_toolchain_data.gni")
        }
        args += win_toolchain_data.include_flags_imsvc_list
        args += [ "/showIncludes:user" ]
      }

      # Note: for all platforms, the depfile only lists user includes, and not
      # system includes. If system includes change, the compiler flags are
      # expected to artificially change in some way to invalidate and force the
      # nocompile tests to run again.
    }
  }
}