| # Copyright 2022 The Pigweed Authors |
| # |
| # 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 |
| # |
| # https://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. |
| |
| import("//build_overrides/pigweed.gni") |
| |
| import("$dir_pw_compilation_testing/negative_compilation_test.gni") |
| import("$dir_pw_third_party/boringssl/boringssl.gni") |
| import("$dir_pw_third_party/chre/chre.gni") |
| import("$dir_pw_third_party/googletest/googletest.gni") |
| import("$dir_pw_third_party/mbedtls/mbedtls.gni") |
| import("$dir_pw_toolchain/universal_tools.gni") |
| |
| declare_args() { |
| # Regular expressions matching the paths of the source files to be excluded |
| # from the analysis. clang-tidy provides no alternative option. |
| # |
| # For example, the following disables clang-tidy on all source files in the |
| # third_party directory: |
| # |
| # pw_toolchain_STATIC_ANALYSIS_SKIP_SOURCES_RES = ["third_party/.*"] |
| # |
| pw_toolchain_STATIC_ANALYSIS_SKIP_SOURCES_RES = [] |
| |
| # Disable clang-tidy for specific include paths. In the clang-tidy command, |
| # include paths that end with one of these, or match as a regular expression, |
| # are switched from -I to -isystem, which causes clang-tidy to ignore them. |
| # Unfortunately, clang-tidy provides no other way to filter header files. |
| # |
| # For example, the following ignores header files in "repo/include": |
| # |
| # pw_toolchain_STATIC_ANALYSIS_SKIP_INCLUDE_PATHS = ["repo/include"] |
| # |
| # While the following ignores all third-party header files: |
| # |
| # pw_toolchain_STATIC_ANALYSIS_SKIP_INCLUDE_PATHS = [".*/third_party/.*"] |
| # |
| pw_toolchain_STATIC_ANALYSIS_SKIP_INCLUDE_PATHS = [] |
| } |
| |
| # Third-party software with Pigweed-supported build files that do not pass all |
| # clang-tidy checks. |
| _excluded_third_party_dirs = [ |
| dir_pw_third_party_mbedtls, |
| dir_pw_third_party_boringssl, |
| dir_pw_third_party_googletest, |
| dir_pw_third_party_chre, |
| ] |
| |
| # Creates a toolchain target for static analysis. |
| # |
| # The generated toolchain runs clang-tidy on all source files that are not |
| # excluded by pw_toolchain_STATIC_ANALYSIS_SKIP_SOURCES_RES or |
| # pw_toolchain_STATIC_ANALYSIS_SKIP_INCLUDE_PATHS. |
| # |
| # Args: |
| # cc: (required) String indicating the C compiler to use. |
| # cxx: (required) String indicating the C++ compiler to use. |
| # static_analysis: (required) A scope defining args to apply to the |
| # static_analysis toolchain. |
| # static_analysis.enabled: (required) Bool used to indicate whether |
| # static_analysis should be enabled for the toolchain where scope is |
| # applied to. Note that static_analysis.enabled must be set in order to |
| # use this toolchain. |
| # static_analysis.clang_tidy_path: (optional) String indicating clang-tidy bin |
| # to use. |
| # static_analysis.cc_post: (optional) String defining additional commands to |
| # append to cc tool's command list (i.e command(s) to run after cc command |
| # chain). |
| # static_analysis.cxx_post: (optional) String defining additional commands to |
| # append to cxx tool's command list (i.e command(s) to run after cxx |
| # command chain). |
| template("pw_static_analysis_toolchain") { |
| invoker_toolchain_args = invoker.defaults |
| assert(defined(invoker.static_analysis), "static_analysis scope missing.") |
| _static_analysis_args = invoker.static_analysis |
| assert(defined(_static_analysis_args.enabled), |
| "static_analysis.enabled is missing") |
| assert(_static_analysis_args.enabled, |
| "static_analysis.enabled must be true to use this toolchain.") |
| |
| _skipped_regexps = [] |
| _skipped_include_paths = [] |
| foreach(third_party_dir, _excluded_third_party_dirs) { |
| if (third_party_dir != "") { |
| _skipped_include_paths += [ |
| third_party_dir + "/include", |
| third_party_dir, |
| ] |
| } |
| } |
| |
| _skipped_regexps += pw_toolchain_STATIC_ANALYSIS_SKIP_SOURCES_RES |
| _skipped_include_paths += pw_toolchain_STATIC_ANALYSIS_SKIP_INCLUDE_PATHS |
| |
| # Clang tidy is invoked by a wrapper script which implements the missing |
| # option --source-filter. |
| _clang_tidy_py_path = |
| rebase_path("$dir_pw_toolchain/py/pw_toolchain/clang_tidy.py", |
| root_build_dir) |
| _clang_tidy_py = "${python_path} ${_clang_tidy_py_path}" |
| _source_root = rebase_path("//", root_build_dir) |
| _source_exclude = "" |
| foreach(pattern, _skipped_regexps) { |
| _source_exclude = _source_exclude + " --source-exclude '${pattern}'" |
| } |
| _skip_include_path = "" |
| foreach(pattern, _skipped_include_paths) { |
| _skip_include_path = |
| _skip_include_path + " --skip-include-path '${pattern}'" |
| } |
| _clang_tidy_path = "" |
| if (defined(_static_analysis_args.clang_tidy_path)) { |
| _clang_tidy_path = |
| "--clang-tidy " + |
| rebase_path(_static_analysis_args.clang_tidy_path, root_build_dir) |
| } |
| |
| toolchain(target_name) { |
| # Uncomment this line to see which toolchains generate other toolchains. |
| # print("Generating toolchain: ${target_name} by ${current_toolchain}") |
| |
| tool("asm") { |
| depfile = "{{output}}.d" |
| command = pw_universal_stamp.command |
| depsformat = "gcc" |
| description = "as {{output}}" |
| outputs = [ |
| # Use {{source_file_part}}, which includes the extension, instead of |
| # {{source_name_part}} so that object files created from <file_name>.c |
| # and <file_name>.cc sources are unique. |
| "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", |
| ] |
| } |
| |
| assert(defined(invoker.cc), "toolchain is missing 'cc'") |
| tool("cc") { |
| _post_command_hook = "" |
| if (defined(_static_analysis_args.cc_post) && |
| _static_analysis_args.cc_post != "") { |
| _post_command_hook += " && " + _static_analysis_args.cc_post |
| } |
| |
| depfile = "{{output}}.d" |
| command = string_join(" ", |
| [ |
| _clang_tidy_py, |
| _source_exclude, |
| _skip_include_path, |
| _clang_tidy_path, |
| "--source-file {{source}}", |
| "--source-root '${_source_root}'", |
| "--export-fixes {{output}}.yaml", |
| "--", |
| invoker.cc, |
| "END_OF_INVOKER", |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{cflags}}", |
| "{{cflags_c}}", # Must come after {{cflags}}. |
| "{{defines}}", |
| "{{include_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) + " && touch {{output}}" + _post_command_hook |
| depsformat = "gcc" |
| description = "clang-tidy {{source}}" |
| outputs = |
| [ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ] |
| } |
| |
| assert(defined(invoker.cxx), "toolchain is missing 'cxx'") |
| tool("cxx") { |
| _post_command_hook = "" |
| if (defined(_static_analysis_args.cxx_post) && |
| _static_analysis_args.cxx_post != "") { |
| _post_command_hook += " && " + _static_analysis_args.cxx_post |
| } |
| |
| depfile = "{{output}}.d" |
| command = string_join(" ", |
| [ |
| _clang_tidy_py, |
| _source_exclude, |
| _skip_include_path, |
| _clang_tidy_path, |
| "--source-file {{source}}", |
| "--source-root '${_source_root}'", |
| "--export-fixes {{output}}.yaml", |
| "--", |
| invoker.cxx, |
| "END_OF_INVOKER", |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{cflags}}", |
| "{{cflags_cc}}", # Must come after {{cflags}}. |
| "{{defines}}", |
| "{{include_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) + " && touch {{output}}" + _post_command_hook |
| depsformat = "gcc" |
| description = "clang-tidy {{source}}" |
| outputs = |
| [ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ] |
| } |
| |
| tool("objc") { |
| depfile = "{{output}}.d" |
| command = pw_universal_stamp.command |
| depsformat = "gcc" |
| description = "objc {{source}}" |
| outputs = |
| [ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ] |
| } |
| |
| tool("objcxx") { |
| depfile = "{{output}}.d" |
| command = pw_universal_stamp.command |
| depsformat = "gcc" |
| description = "objc++ {{output}}" |
| outputs = |
| [ "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o" ] |
| } |
| |
| tool("alink") { |
| command = "rm -f {{output}} && touch {{output}}" |
| description = "ar {{target_output_name}}{{output_extension}}" |
| outputs = [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ] |
| default_output_extension = ".a" |
| default_output_dir = "{{target_out_dir}}/lib" |
| } |
| |
| tool("link") { |
| if (host_os == "win") { |
| # Force the extension to '.bat', empty bat scripts are still |
| # executable and will not raise errors. |
| _output = "{{output_dir}}/{{target_output_name}}.bat" |
| command = pw_universal_stamp.command |
| default_output_extension = ".bat" |
| } else { |
| default_output_extension = "" |
| _output = "{{output_dir}}/{{target_output_name}}{{output_extension}}" |
| command = "touch {{output}} && chmod +x {{output}}" |
| } |
| description = "ld $_output" |
| outputs = [ _output ] |
| default_output_dir = "{{target_out_dir}}/bin" |
| } |
| |
| tool("solink") { |
| _output = "{{output_dir}}/{{target_output_name}}{{output_extension}}" |
| command = pw_universal_stamp.command |
| description = "ld -shared $_output" |
| outputs = [ _output ] |
| default_output_dir = "{{target_out_dir}}/lib" |
| default_output_extension = ".so" |
| } |
| |
| tool("stamp") { |
| # GN-ism: GN gets mad if you directly forward the contents of |
| # pw_universal_stamp. |
| _stamp = pw_universal_stamp |
| forward_variables_from(_stamp, "*") |
| } |
| |
| tool("copy") { |
| # GN-ism: GN gets mad if you directly forward the contents of |
| # pw_universal_copy. |
| _copy = pw_universal_copy |
| forward_variables_from(_copy, "*") |
| } |
| |
| tool("rust_bin") { |
| _output = "{{output_dir}}/{{target_output_name}}{{output_extension}}" |
| command = pw_universal_stamp.command |
| description = "rustc {{output}}" |
| outputs = [ _output ] |
| default_output_dir = "{{target_out_dir}}/bin" |
| } |
| |
| # Build arguments to be overridden when compiling cross-toolchain: |
| # |
| # pw_toolchain_defaults: A scope setting defaults to apply to GN targets |
| # in this toolchain. It is analogous to $pw_target_defaults in |
| # $dir_pigweed/pw_vars_default.gni. |
| # |
| # pw_toolchain_SCOPE: A copy of the invoker scope that defines the |
| # toolchain. Used for generating derivative toolchains. |
| # |
| toolchain_args = { |
| pw_toolchain_SCOPE = { |
| } |
| pw_toolchain_SCOPE = { |
| forward_variables_from(invoker, "*") |
| name = target_name |
| } |
| forward_variables_from(invoker_toolchain_args, "*") |
| |
| # Disable compilation testing for static analysis toolchains. |
| pw_compilation_testing_NEGATIVE_COMPILATION_ENABLED = false |
| |
| # Always disable coverage generation since we will not actually run the |
| # instrumented binaries to produce a profraw file. |
| pw_toolchain_COVERAGE_ENABLED = false |
| } |
| } |
| } |