| # Copyright 2020 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_toolchain/static_analysis_toolchain.gni") |
| import("$dir_pw_toolchain/universal_tools.gni") |
| |
| declare_args() { |
| # Scope defining the current toolchain. Contains all of the arguments required |
| # by the generate_toolchain template. This should NOT be manually modified. |
| pw_toolchain_SCOPE = { |
| } |
| |
| # Prefix for compilation commands (e.g. the path to a Goma or CCache compiler |
| # launcher). Example for ccache: |
| # gn gen out --args='pw_command_launcher="ccache"' |
| pw_command_launcher = "" |
| } |
| |
| # Creates a toolchain target. |
| # |
| # Args: |
| # ar: (required) String indicating the archive tool to use. |
| # cc: (required) String indicating the C compiler to use. |
| # cxx: (required) String indicating the C++ compiler to use. |
| # ld: (optional) String indicating the linking binary to use. |
| # is_host_toolchain: (optional) Boolean indicating if the outputs are meant |
| # for the $host_os. |
| # final_binary_extension: (optional) The extension to apply to final linked |
| # binaries. |
| # link_whole_archive: (optional) Boolean indicating if the linker should load |
| # all object files when resolving symbols. |
| # link_group: (optional) Boolean indicating if the linker should use |
| # a group to resolve circular dependencies between artifacts. |
| # generate_from: (optional) The full target name of the toolchain that can |
| # trigger this toolchain to be generated. GN only allows one toolchain to |
| # be generated at a given target path, so if multiple toolchains parse the |
| # same generate_toolchain target only one should declare a toolchain. This |
| # is primarily to allow generating sub-toolchains. Defaults to |
| # default_toolchain. |
| # defaults: (required) A scope setting GN build arg values to apply to GN |
| # targets in this toolchain. These take precedence over args.gni settings. |
| # |
| # The defaults scope should contain values for builtin GN arguments: |
| # current_cpu: The CPU of the toolchain. |
| # Well known values include "arm", "arm64", "x64", "x86", and "mips". |
| # current_os: The OS of the toolchain. Defaults to "". |
| # Well known values include "win", "mac", "linux", "android", and "ios". |
| # |
| # TODO(b/234891809): This should be renamed to pw_generate_toolchain. |
| template("generate_toolchain") { |
| assert(defined(invoker.defaults), "toolchain is missing 'defaults'") |
| |
| # On the default toolchain invocation, you typically need to generate all |
| # toolchains you encounter. For sub-toolchains, they must be generated from |
| # the context of their parent. |
| if (defined(invoker.generate_from)) { |
| _generate_toolchain = |
| get_label_info(invoker.generate_from, "label_no_toolchain") == |
| current_toolchain |
| } else { |
| _generate_toolchain = default_toolchain == current_toolchain |
| } |
| |
| if (_generate_toolchain) { |
| # TODO(amontanez): This should be renamed to build_args as "defaults" isn't |
| # sufficiently descriptive. |
| invoker_toolchain_args = invoker.defaults |
| |
| # These values should always be set as they influence toolchain |
| # behavior, but allow them to be unset as a transitional measure. |
| if (!defined(invoker_toolchain_args.current_cpu)) { |
| invoker_toolchain_args.current_cpu = "" |
| } |
| if (!defined(invoker_toolchain_args.current_os)) { |
| invoker_toolchain_args.current_os = "" |
| } |
| |
| # Determine OS of toolchain, which is the builtin argument "current_os". |
| toolchain_os = invoker_toolchain_args.current_os |
| |
| toolchain(target_name) { |
| # Uncomment this line to see which toolchains generate other toolchains. |
| # print("Generating toolchain: ${target_name} by ${current_toolchain}") |
| |
| assert(defined(invoker.cc), "toolchain is missing 'cc'") |
| tool("asm") { |
| if (pw_command_launcher != "") { |
| command_launcher = pw_command_launcher |
| } |
| depfile = "{{output}}.d" |
| command = string_join(" ", |
| [ |
| invoker.cc, |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{asmflags}}", |
| "{{cflags}}", |
| "{{defines}}", |
| "{{include_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) |
| 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", |
| ] |
| } |
| |
| tool("cc") { |
| if (pw_command_launcher != "") { |
| command_launcher = pw_command_launcher |
| } |
| depfile = "{{output}}.d" |
| command = string_join(" ", |
| [ |
| invoker.cc, |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{cflags}}", |
| "{{cflags_c}}", # Must come after {{cflags}}. |
| "{{defines}}", |
| "{{include_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) |
| depsformat = "gcc" |
| description = "cc {{output}}" |
| outputs = [ |
| "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", |
| ] |
| } |
| |
| assert(defined(invoker.cxx), "toolchain is missing 'cxx'") |
| tool("cxx") { |
| if (pw_command_launcher != "") { |
| command_launcher = pw_command_launcher |
| } |
| depfile = "{{output}}.d" |
| command = string_join(" ", |
| [ |
| invoker.cxx, |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{cflags}}", |
| "{{cflags_cc}}", # Must come after {{cflags}}. |
| "{{defines}}", |
| "{{include_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) |
| depsformat = "gcc" |
| description = "c++ {{output}}" |
| outputs = [ |
| "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", |
| ] |
| } |
| |
| tool("objc") { |
| if (pw_command_launcher != "") { |
| command_launcher = pw_command_launcher |
| } |
| depfile = "{{output}}.d" |
| command = |
| string_join(" ", |
| [ |
| invoker.cc, |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{cflags}}", |
| "{{cflags_objc}}", # Must come after {{cflags}}. |
| "{{defines}}", |
| "{{include_dirs}}", |
| "{{framework_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) |
| depsformat = "gcc" |
| description = "objc {{output}}" |
| outputs = [ |
| "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", |
| ] |
| } |
| |
| tool("objcxx") { |
| if (pw_command_launcher != "") { |
| command_launcher = pw_command_launcher |
| } |
| depfile = "{{output}}.d" |
| command = |
| string_join(" ", |
| [ |
| invoker.cxx, |
| "-MMD -MF $depfile", # Write out dependencies. |
| "{{cflags}}", |
| "{{cflags_objcc}}", # Must come after {{cflags}}. |
| "{{defines}}", |
| "{{include_dirs}}", |
| "{{framework_dirs}}", |
| "-c {{source}}", |
| "-o {{output}}", |
| ]) |
| depsformat = "gcc" |
| description = "objc++ {{output}}" |
| outputs = [ |
| "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", |
| ] |
| } |
| |
| assert(defined(invoker.ar), "toolchain is missing 'ar'") |
| tool("alink") { |
| if (host_os == "win") { |
| rspfile = "{{output}}.rsp" |
| rspfile_content = "{{inputs}}" |
| rm_command = "del /F /Q \"{{output}}\" 2> NUL" |
| command = "cmd /c \"($rm_command) & ${invoker.ar} {{arflags}} rcs {{output}} @$rspfile\"" |
| } else { |
| command = "rm -f {{output}} && ${invoker.ar} {{arflags}} rcs {{output}} {{inputs}}" |
| } |
| |
| 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" |
| } |
| |
| lib_switch = "-l" |
| lib_dir_switch = "-L" |
| |
| _link_outfile = |
| "{{output_dir}}/{{target_output_name}}{{output_extension}}" |
| _link_mapfile = "{{output_dir}}/{{target_output_name}}.map" |
| if (defined(invoker.ld)) { |
| _link_flags = [ |
| invoker.ld, |
| "{{ldflags}}", |
| ] |
| } else { |
| _link_flags = [ |
| invoker.cxx, |
| "{{ldflags}}", |
| ] |
| } |
| |
| if (toolchain_os == "mac" || toolchain_os == "ios") { |
| _link_flags += [ |
| # Output a map file that shows symbols and their location. |
| "-Wl,-map,$_link_mapfile", |
| ] |
| } else { |
| _link_flags += [ |
| # Output a map file that shows symbols and their location. |
| "-Wl,-Map,$_link_mapfile", |
| ] |
| } |
| |
| _rsp_file = "$_link_outfile.rsp" |
| _rsp_contents = [] |
| |
| _link_group = defined(invoker.link_group) && invoker.link_group |
| if (_link_group) { |
| _rsp_contents += [ "-Wl,--start-group" ] |
| } |
| _rsp_contents += [ "{{inputs}}" ] |
| _rsp_contents += [ "{{frameworks}}" ] |
| |
| if (defined(invoker.link_whole_archive) && invoker.link_whole_archive) { |
| # Load all object files from all libraries to resolve symbols. |
| # Short of living in the ideal world where all dependency graphs |
| # among static libs are acyclic and all developers diligently |
| # express such graphs in terms that GN understands, this is the |
| # safest option. |
| # Make sure you use this with --gc-sections, otherwise the |
| # resulting binary will contain every symbol defined in every |
| # input file and every static library. That could be quite a lot. |
| _rsp_contents += [ |
| "-Wl,--whole-archive", |
| "{{libs}}", |
| "-Wl,--no-whole-archive", |
| ] |
| } else { |
| _rsp_contents += [ "{{libs}}" ] |
| } |
| |
| if (_link_group) { |
| _rsp_contents += [ "-Wl,--end-group" ] |
| } |
| _rsp_command = string_join(" ", _rsp_contents) |
| |
| _link_flags += [ "@$_rsp_file" ] |
| _link_flags += [ "-o $_link_outfile" ] |
| |
| _link_command = string_join(" ", _link_flags) |
| |
| tool("link") { |
| command = _link_command |
| rspfile = _rsp_file |
| rspfile_content = _rsp_command |
| description = "ld $_link_outfile" |
| outputs = [ |
| _link_outfile, |
| _link_mapfile, |
| ] |
| default_output_dir = "{{target_out_dir}}/bin" |
| |
| if (defined(invoker.final_binary_extension)) { |
| default_output_extension = invoker.final_binary_extension |
| } else if (toolchain_os == "win") { |
| default_output_extension = ".exe" |
| } else { |
| default_output_extension = "" |
| } |
| } |
| |
| tool("solink") { |
| command = _link_command + " -shared" |
| rspfile = _rsp_file |
| rspfile_content = _rsp_command |
| description = "ld -shared $_link_outfile" |
| outputs = [ |
| _link_outfile, |
| _link_mapfile, |
| ] |
| 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, "*") |
| } |
| |
| # 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, "*") |
| } |
| _generate_rust_tools = defined(invoker.rustc) |
| |
| if (_generate_rust_tools) { |
| tool("rust_bin") { |
| _outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}" |
| |
| description = "rustc {{output}}" |
| command = |
| string_join(" ", |
| [ |
| # TODO(b/234872510): Ensure this works with Windows. |
| "RUST_BACKTRACE=1", |
| invoker.rustc, |
| "{{source}}", |
| "-o $_outfile", |
| "--color always", |
| "-D warnings", |
| ]) |
| |
| outputs = [ _outfile ] |
| default_output_dir = "{{target_out_dir}}/bin" |
| } |
| } |
| } |
| |
| _generate_static_analysis_toolchain = |
| defined(invoker.static_analysis) && invoker.static_analysis |
| |
| if (_generate_static_analysis_toolchain) { |
| pw_static_analysis_toolchain(target_name + ".static_analysis") { |
| forward_variables_from(invoker, "*") |
| } |
| } |
| } else { |
| not_needed(invoker, "*") |
| group(target_name) { |
| } |
| } |
| } |
| |
| # Creates a series of toolchain targets with common compiler options. |
| # |
| # Args: |
| # toolchains: List of scopes defining each of the desired toolchains. |
| # The scope must contain a "name" variable; other variables are forwarded to |
| # $generate_toolchain. |
| template("generate_toolchains") { |
| not_needed([ "target_name" ]) |
| assert(defined(invoker.toolchains), |
| "generate_toolchains must be called with a list of toolchains") |
| |
| # Create a target for each of the desired toolchains, appending its own cflags |
| # and ldflags to the common ones. |
| foreach(_toolchain, invoker.toolchains) { |
| generate_toolchain(_toolchain.name) { |
| forward_variables_from(_toolchain, "*", [ "name" ]) |
| } |
| } |
| } |