1# Copyright 2020 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14 15import("//build_overrides/pigweed.gni") 16 17import("$dir_pw_toolchain/universal_tools.gni") 18 19declare_args() { 20 # Scope defining the current toolchain. Contains all of the arguments required 21 # by the generate_toolchain template. This should NOT be manually modified. 22 pw_toolchain_SCOPE = { 23 } 24 25 # Prefix for compilation commands (e.g. the path to a Goma or CCache compiler 26 # launcher). Example for ccache: 27 # gn gen out --args='pw_command_launcher="ccache"' 28 pw_command_launcher = "" 29} 30 31# Creates a toolchain target. 32# 33# Args: 34# ar: (required) String indicating the archive tool to use. 35# cc: (required) String indicating the C compiler to use. 36# cxx: (required) String indicating the C++ compiler to use. 37# is_host_toolchain: (optional) Boolean indicating if the outputs are meant 38# for the $host_os. 39# final_binary_extension: (optional) The extension to apply to final linked 40# binaries. 41# link_whole_archive: (optional) Boolean indicating if the linker should load 42# all object files when resolving symbols. 43# link_group: (optional) Boolean indicating if the linker should use 44# a group to resolve circular dependencies between artifacts. 45# generate_from: (optional) The full target name of the toolchain that can 46# trigger this toolchain to be generated. GN only allows one toolchain to 47# be generated at a given target path, so if multiple toolchains parse the 48# same generate_toolchain target only one should declare a toolchain. This 49# is primarily to allow generating sub-toolchains. Defaults to 50# default_toolchain. 51# defaults: (required) A scope setting GN build arg values to apply to GN 52# targets in this toolchain. These take precedence over args.gni settings. 53# 54# The defaults scope should contain values for builtin GN arguments: 55# current_cpu: The CPU of the toolchain. 56# Well known values include "arm", "arm64", "x64", "x86", and "mips". 57# current_os: The OS of the toolchain. Defaults to "". 58# Well known values include "win", "mac", "linux", "android", and "ios". 59# 60# TODO(pwbug/333): This should be renamed to pw_generate_toolchain. 61template("generate_toolchain") { 62 assert(defined(invoker.defaults), "toolchain is missing 'defaults'") 63 64 # On the default toolchain invocation, you typically need to generate all 65 # toolchains you encounter. For sub-toolchains, they must be generated from 66 # the context of their parent. 67 if (defined(invoker.generate_from)) { 68 _generate_toolchain = 69 get_label_info(invoker.generate_from, "label_no_toolchain") == 70 current_toolchain 71 } else { 72 _generate_toolchain = default_toolchain == current_toolchain 73 } 74 75 if (_generate_toolchain) { 76 # TODO(amontanez): This should be renamed to build_args as "defaults" isn't 77 # sufficiently descriptive. 78 invoker_toolchain_args = invoker.defaults 79 80 # These values should always be set as they influence toolchain 81 # behavior, but allow them to be unset as a transitional measure. 82 if (!defined(invoker_toolchain_args.current_cpu)) { 83 invoker_toolchain_args.current_cpu = "" 84 } 85 if (!defined(invoker_toolchain_args.current_os)) { 86 invoker_toolchain_args.current_os = "" 87 } 88 89 # Determine OS of toolchain, which is the builtin argument "current_os". 90 toolchain_os = invoker_toolchain_args.current_os 91 92 toolchain(target_name) { 93 # Uncomment this line to see which toolchains generate other toolchains. 94 # print("Generating toolchain: ${target_name} by ${current_toolchain}") 95 96 assert(defined(invoker.cc), "toolchain is missing 'cc'") 97 tool("asm") { 98 if (pw_command_launcher != "") { 99 command_launcher = pw_command_launcher 100 } 101 depfile = "{{output}}.d" 102 command = string_join(" ", 103 [ 104 invoker.cc, 105 "-MMD -MF $depfile", # Write out dependencies. 106 "{{asmflags}}", 107 "{{cflags}}", 108 "{{defines}}", 109 "{{include_dirs}}", 110 "-c {{source}}", 111 "-o {{output}}", 112 ]) 113 depsformat = "gcc" 114 description = "as {{output}}" 115 outputs = [ 116 # Use {{source_file_part}}, which includes the extension, instead of 117 # {{source_name_part}} so that object files created from <file_name>.c 118 # and <file_name>.cc sources are unique. 119 "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", 120 ] 121 } 122 123 tool("cc") { 124 if (pw_command_launcher != "") { 125 command_launcher = pw_command_launcher 126 } 127 depfile = "{{output}}.d" 128 command = string_join(" ", 129 [ 130 invoker.cc, 131 "-MMD -MF $depfile", # Write out dependencies. 132 "{{cflags}}", 133 "{{cflags_c}}", # Must come after {{cflags}}. 134 "{{defines}}", 135 "{{include_dirs}}", 136 "-c {{source}}", 137 "-o {{output}}", 138 ]) 139 depsformat = "gcc" 140 description = "cc {{output}}" 141 outputs = [ 142 "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", 143 ] 144 } 145 146 assert(defined(invoker.cxx), "toolchain is missing 'cxx'") 147 tool("cxx") { 148 if (pw_command_launcher != "") { 149 command_launcher = pw_command_launcher 150 } 151 depfile = "{{output}}.d" 152 command = string_join(" ", 153 [ 154 invoker.cxx, 155 "-MMD -MF $depfile", # Write out dependencies. 156 "{{cflags}}", 157 "{{cflags_cc}}", # Must come after {{cflags}}. 158 "{{defines}}", 159 "{{include_dirs}}", 160 "-c {{source}}", 161 "-o {{output}}", 162 ]) 163 depsformat = "gcc" 164 description = "c++ {{output}}" 165 outputs = [ 166 "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", 167 ] 168 } 169 170 tool("objc") { 171 if (pw_command_launcher != "") { 172 command_launcher = pw_command_launcher 173 } 174 depfile = "{{output}}.d" 175 command = 176 string_join(" ", 177 [ 178 invoker.cc, 179 "-MMD -MF $depfile", # Write out dependencies. 180 "{{cflags}}", 181 "{{cflags_objc}}", # Must come after {{cflags}}. 182 "{{defines}}", 183 "{{include_dirs}}", 184 "{{framework_dirs}}", 185 "-c {{source}}", 186 "-o {{output}}", 187 ]) 188 depsformat = "gcc" 189 description = "objc {{output}}" 190 outputs = [ 191 "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", 192 ] 193 } 194 195 tool("objcxx") { 196 if (pw_command_launcher != "") { 197 command_launcher = pw_command_launcher 198 } 199 depfile = "{{output}}.d" 200 command = 201 string_join(" ", 202 [ 203 invoker.cxx, 204 "-MMD -MF $depfile", # Write out dependencies. 205 "{{cflags}}", 206 "{{cflags_objcc}}", # Must come after {{cflags}}. 207 "{{defines}}", 208 "{{include_dirs}}", 209 "{{framework_dirs}}", 210 "-c {{source}}", 211 "-o {{output}}", 212 ]) 213 depsformat = "gcc" 214 description = "objc++ {{output}}" 215 outputs = [ 216 "{{source_out_dir}}/{{target_output_name}}.{{source_file_part}}.o", 217 ] 218 } 219 220 assert(defined(invoker.ar), "toolchain is missing 'ar'") 221 tool("alink") { 222 if (host_os == "win") { 223 rspfile = "{{output}}.rsp" 224 rspfile_content = "{{inputs}}" 225 rm_command = "del /F /Q \"{{output}}\" 2> NUL" 226 command = "cmd /c \"($rm_command) & ${invoker.ar} {{arflags}} rcs {{output}} @$rspfile\"" 227 } else { 228 command = "rm -f {{output}} && ${invoker.ar} {{arflags}} rcs {{output}} {{inputs}}" 229 } 230 231 description = "ar {{target_output_name}}{{output_extension}}" 232 outputs = 233 [ "{{output_dir}}/{{target_output_name}}{{output_extension}}" ] 234 default_output_extension = ".a" 235 default_output_dir = "{{target_out_dir}}/lib" 236 } 237 238 lib_switch = "-l" 239 lib_dir_switch = "-L" 240 241 _link_outfile = 242 "{{output_dir}}/{{target_output_name}}{{output_extension}}" 243 _link_mapfile = "{{output_dir}}/{{target_output_name}}.map" 244 _link_flags = [ 245 invoker.cxx, 246 "{{ldflags}}", 247 ] 248 249 if (toolchain_os == "mac" || toolchain_os == "ios") { 250 _link_flags += [ 251 # Output a map file that shows symbols and their location. 252 "-Wl,-map,$_link_mapfile", 253 254 # Delete unreferenced sections. Helpful with -ffunction-sections. 255 "-Wl,-dead_strip", 256 ] 257 } else { 258 _link_flags += [ 259 # Output a map file that shows symbols and their location. 260 "-Wl,-Map,$_link_mapfile", 261 262 # Delete unreferenced sections. Helpful with -ffunction-sections. 263 "-Wl,--gc-sections", 264 ] 265 } 266 267 _link_group = defined(invoker.link_group) && invoker.link_group 268 if (_link_group) { 269 _link_flags += [ "-Wl,--start-group" ] 270 } 271 _link_flags += [ "{{inputs}}" ] 272 _link_flags += [ "{{frameworks}}" ] 273 274 if (defined(invoker.link_whole_archive) && invoker.link_whole_archive) { 275 # Load all object files from all libraries to resolve symbols. 276 # Short of living in the ideal world where all dependency graphs 277 # among static libs are acyclic and all developers diligently 278 # express such graphs in terms that GN understands, this is the 279 # safest option. 280 # Make sure you use this with --gc-sections, otherwise the 281 # resulting binary will contain every symbol defined in every 282 # input file and every static library. That could be quite a lot. 283 _link_flags += [ 284 "-Wl,--whole-archive", 285 "{{libs}}", 286 "-Wl,--no-whole-archive", 287 ] 288 } else { 289 _link_flags += [ "{{libs}}" ] 290 } 291 292 if (_link_group) { 293 _link_flags += [ "-Wl,--end-group" ] 294 } 295 _link_flags += [ "-o $_link_outfile" ] 296 297 _link_command = string_join(" ", _link_flags) 298 299 tool("link") { 300 command = _link_command 301 description = "ld $_link_outfile" 302 outputs = [ 303 _link_outfile, 304 _link_mapfile, 305 ] 306 default_output_dir = "{{target_out_dir}}/bin" 307 308 if (defined(invoker.final_binary_extension)) { 309 default_output_extension = invoker.final_binary_extension 310 } else if (toolchain_os == "win") { 311 default_output_extension = ".exe" 312 } else { 313 default_output_extension = "" 314 } 315 } 316 317 tool("solink") { 318 command = _link_command + " -shared" 319 description = "ld -shared $_link_outfile" 320 outputs = [ 321 _link_outfile, 322 _link_mapfile, 323 ] 324 default_output_dir = "{{target_out_dir}}/lib" 325 default_output_extension = ".so" 326 } 327 328 tool("stamp") { 329 # GN-ism: GN gets mad if you directly forward the contents of 330 # pw_universal_stamp. 331 _stamp = pw_universal_stamp 332 forward_variables_from(_stamp, "*") 333 } 334 335 tool("copy") { 336 # GN-ism: GN gets mad if you directly forward the contents of 337 # pw_universal_copy. 338 _copy = pw_universal_copy 339 forward_variables_from(_copy, "*") 340 } 341 342 # Build arguments to be overridden when compiling cross-toolchain: 343 # 344 # pw_toolchain_defaults: A scope setting defaults to apply to GN targets 345 # in this toolchain. It is analogous to $pw_target_defaults in 346 # $dir_pigweed/pw_vars_default.gni. 347 # 348 # pw_toolchain_SCOPE: A copy of the invoker scope that defines the 349 # toolchain. Used for generating derivative toolchains. 350 # 351 toolchain_args = { 352 pw_toolchain_SCOPE = { 353 } 354 pw_toolchain_SCOPE = { 355 forward_variables_from(invoker, "*") 356 name = target_name 357 } 358 forward_variables_from(invoker_toolchain_args, "*") 359 } 360 } 361 } else { 362 not_needed(invoker, "*") 363 group(target_name) { 364 } 365 } 366} 367 368# Creates a series of toolchain targets with common compiler options. 369# 370# Args: 371# toolchains: List of scopes defining each of the desired toolchains. 372# The scope must contain a "name" variable; other variables are forwarded to 373# $generate_toolchain. 374template("generate_toolchains") { 375 not_needed([ "target_name" ]) 376 assert(defined(invoker.toolchains), 377 "generate_toolchains must be called with a list of toolchains") 378 379 # Create a target for each of the desired toolchains, appending its own cflags 380 # and ldflags to the common ones. 381 foreach(_toolchain, invoker.toolchains) { 382 generate_toolchain(_toolchain.name) { 383 forward_variables_from(_toolchain, "*", [ "name" ]) 384 } 385 } 386} 387