# Copyright (C) 2017 The Android Open Source Project # # 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 # # http://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("//gn/perfetto.gni") import("//gn/standalone/android.gni") import("//gn/standalone/wasm.gni") import("llvm.gni") import("msvc.gni") # This file is evaluated once, within the context of the default toolchain, # which is the target toolchain. # Note: This means that is_android=true even on a mac when cross-compiling for # Android. assert(current_os == target_os && current_cpu == target_cpu, "Assumptions on current_xxx in this file have been violated") declare_args() { cc_wrapper = "" # These apply to both target and host toolchains. extra_cflags = "" extra_cxxflags = "" extra_ldflags = "" # These apply only to the target toolchain. extra_target_cflags = "" extra_target_cxxflags = "" extra_target_ldflags = "" # These apply only to the host toolchain. extra_host_cflags = "" extra_host_cxxflags = "" extra_host_ldflags = "" } # First of all determine the host toolchain. The user can override this by: # 1. setting ar/cc/cxx vars in args.gn. # 2. setting is_system_compiler=true in args.gn and the env vars AR/CC/CXX. # This is used by OSSFuzz and CrOS ebuilds. declare_args() { sysroot = "" gcc_toolchain = "" ar = "ar" linker = "" if (is_linux_host) { linker = "gold" } if (is_clang) { if (is_linux_host && !is_system_compiler) { cc = linux_clang_bin cxx = linux_clangxx_bin linker = linux_clang_linker } else if (is_win_host && !is_system_compiler) { cc = win_clang_bin cxx = win_clangxx_bin linker = win_clang_linker } else { cc = "clang" cxx = "clang++" linker = "" } } else if (is_win) { # MSVC cc = "${win_msvc_bin_dir}\\cl.exe" cxx = "${win_msvc_bin_dir}\\cl.exe" linker = "${win_msvc_bin_dir}\\link.exe" } else { # GCC cc = "gcc" cxx = "g++" } } # Then determine the target toolchain. declare_args() { target_sysroot = sysroot target_gcc_toolchain = "" # |target_triplet| is the variable that the user can set via GN args. The user # doesn't have to necessarily set it though. In most cases we can infer it # by looking at target_os and target_cpu. # |_target_triplet| is the final argument passed to the toolchain. if (target_triplet != "") { assert(is_cross_compiling) # If the user provides the target_triplet in gn args, respect that. # Otherwise guess it looking at the target os and cpu variables. _target_triplet = target_triplet } else if (!is_cross_compiling) { _target_triplet = "" } else if (target_os == "mac" && target_cpu == "x64") { _target_triplet = "x86_64-apple-darwin" } else if (target_os == "mac" && target_cpu == "x86") { _target_triplet = "i686-apple-darwin" } else if (target_os == "linux" && target_cpu == "arm64") { _target_triplet = "aarch64-linux-gnu" } else if (target_os == "linux" && target_cpu == "arm") { _target_triplet = "arm-linux-gnueabi" } else if (target_os == "linux" && target_cpu == "x64") { _target_triplet = "x86_64-linux-gnu" } else if (target_os == "linux" && target_cpu == "x86") { _target_triplet = "i686-linux-gnu" } else if (target_os == "android" && target_cpu == "arm64") { _target_triplet = "aarch64-linux-android" } else if (target_os == "android" && target_cpu == "arm") { _target_triplet = "arm-linux-androideabi" } else if (target_os == "android" && target_cpu == "x86") { _target_triplet = "i686-linux-android" } else if (target_os == "android" && target_cpu == "x64") { _target_triplet = "x86_64-linux-android" } else { assert( false, "Cannot guess the target triplet from the target_os and target_cpu combination. Please set the target_triplet GN arg explicitly. See https://clang.llvm.org/docs/CrossCompilation.html#target-triple") } } declare_args() { if (is_linux || is_android) { target_linker = "gold" } else { target_linker = "" } if (!is_cross_compiling || is_perfetto_build_generator || is_system_compiler) { target_ar = ar target_cc = cc target_cxx = cxx target_linker = linker } else { target_ar = "ar" if (is_android) { target_ar = "$android_toolchain_root/bin/$android_abi_target-ar" target_cc = "$android_llvm_dir/bin/clang" target_cxx = "$android_llvm_dir/bin/clang++" target_linker = "$android_llvm_dir/bin/ld.lld" } else { assert(_target_triplet != "", "target_triplet must be non-empty when cross-compiling") if (is_clang) { if (is_linux_host) { target_cc = "${linux_clang_bin} --target=${_target_triplet}" target_cxx = "${linux_clangxx_bin} --target=${_target_triplet}" target_linker = "${linux_clang_linker} --target=${_target_triplet}" } else { target_cc = "clang --target=${_target_triplet}" target_cxx = "clang++ --target=${_target_triplet}" } } else { # GCC target_ar = "${_target_triplet}-ar" target_cc = "${_target_triplet}-gcc" target_cxx = "${_target_triplet}-g++" } } } } template("gcc_like_toolchain") { toolchain(target_name) { ar = invoker.ar cc = invoker.cc cxx = invoker.cxx lib_switch = "-l" lib_dir_switch = "-L" ld_arg = "" external_cflags = "" external_cxxflags = "" external_ldflags = "" if (defined(invoker.linker) && invoker.linker != "") { _invoker_linker = invoker.linker ld_arg = "-fuse-ld=$_invoker_linker" } if (defined(invoker.sysroot) && invoker.sysroot != "") { _invoker_sysroot = invoker.sysroot cc = "$cc --sysroot=$_invoker_sysroot" cxx = "$cxx --sysroot=$_invoker_sysroot" } if (defined(invoker.gcc_toolchain) && invoker.gcc_toolchain != "") { assert(is_clang, "gcc_toolchain can be used only when using clang") _invoker_gcc_toolchain = invoker.gcc_toolchain ld_arg = "$ld_arg --gcc-toolchain=$_invoker_gcc_toolchain" } if (defined(invoker.external_cflags)) { external_cflags = invoker.external_cflags } if (defined(invoker.external_cxxflags)) { external_cxxflags = invoker.external_cxxflags } if (defined(invoker.external_ldflags)) { external_ldflags = invoker.external_ldflags } tool("cc") { depfile = "{{output}}.d" command = "$cc_wrapper $cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} ${external_cflags} -c {{source}} -o {{output}}" depsformat = "gcc" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ] description = "compile {{source}}" } tool("cxx") { depfile = "{{output}}.d" command = "$cc_wrapper $cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} ${external_cflags} ${external_cxxflags} -c {{source}} -o {{output}}" depsformat = "gcc" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ] description = "compile {{source}}" } tool("asm") { depfile = "{{output}}.d" command = "$cc_wrapper $cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}" depsformat = "gcc" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ] description = "assemble {{source}}" } tool("alink") { rspfile = "{{output}}.rsp" if (is_mac && ar != "suppress_unused_ar_variable_warning") { rspfile_content = "{{inputs_newline}}" command = "rm -f {{output}} && libtool -static {{arflags}} -o {{output}} -filelist $rspfile" } else { rspfile_content = "{{inputs}}" command = "rm -f {{output}} && $ar rcsD {{output}} @$rspfile" } outputs = [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ] default_output_extension = ".a" output_prefix = "lib" description = "link {{output}}" } tool("solink") { soname = "{{target_output_name}}{{output_extension}}" rpath = "-Wl,-soname,$soname" if (is_mac) { rpath = "-Wl,-install_name,@rpath/$soname" } command = "$cc_wrapper $cxx $ld_arg -shared {{ldflags}} ${external_ldflags} {{inputs}} {{solibs}} {{libs}} $rpath -o {{output}}" outputs = [ "{{root_out_dir}}/$soname" ] output_prefix = "lib" default_output_extension = ".so" description = "link {{output}}" } tool("link") { command = "$cc_wrapper $cxx $ld_arg {{ldflags}} ${external_ldflags} {{inputs}} {{solibs}} {{libs}} -o {{output}}" outputs = [ "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" ] description = "link {{output}}" } tool("stamp") { command = "touch {{output}}" description = "stamp {{output}}" } tool("copy") { command = "cp -af {{source}} {{output}}" description = "COPY {{source}} {{output}}" } toolchain_args = { current_cpu = invoker.cpu current_os = invoker.os } } } gcc_like_toolchain("gcc_like") { cpu = current_cpu os = current_os ar = target_ar cc = target_cc cxx = target_cxx linker = target_linker sysroot = target_sysroot gcc_toolchain = target_gcc_toolchain external_cflags = string_join(" ", [ extra_cflags, extra_target_cflags, ]) external_cxxflags = string_join(" ", [ extra_cxxflags, extra_target_cxxflags, ]) external_ldflags = string_join(" ", [ extra_ldflags, extra_target_ldflags, ]) } gcc_like_toolchain("gcc_like_host") { cpu = host_cpu os = host_os ar = ar cc = cc cxx = cxx linker = linker sysroot = sysroot gcc_toolchain = gcc_toolchain external_cflags = string_join(" ", [ extra_cflags, extra_host_cflags, ]) external_cxxflags = string_join(" ", [ extra_cxxflags, extra_host_cxxflags, ]) external_ldflags = string_join(" ", [ extra_ldflags, extra_host_ldflags, ]) } gcc_like_toolchain("wasm") { # emsdk_dir and em_config are defined in wasm.gni. cpu = host_cpu os = host_os ar = "$emsdk_dir/emscripten/emar --em-config $em_config" cc = "$emsdk_dir/emscripten/emcc --em-config $em_config" cxx = "$emsdk_dir/emscripten/em++ --em-config $em_config" } # This is used both for MSVC anc clang-cl. clang-cl cmdline interface pretends # to be MSVC's cl.exe. toolchain("msvc") { lib_switch = "" lib_dir_switch = "/LIBPATH:" sys_lib_flags = "/LIBPATH:\"${win_sdk_lib_dir}\\ucrt\\x64\" " sys_lib_flags += "/LIBPATH:\"${win_sdk_lib_dir}\\um\\x64\" " sys_lib_flags += "/LIBPATH:\"${win_msvc_lib_dir}\" " # Note: /showIncludes below is required for ninja, to build a complete # dependency graph for headers. Removing it breaks incremental builds. tool("cc") { precompiled_header_type = "msvc" pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb" command = "$cc_wrapper $cc /nologo /showIncludes /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\"" depsformat = "msvc" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ] description = "compile {{source}}" } tool("cxx") { precompiled_header_type = "msvc" pdbname = "{{target_out_dir}}/{{label_name}}_c.pdb" command = "$cc_wrapper $cxx /nologo /showIncludes /FC {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} /c {{source}} /Fo{{output}} /Fd\"$pdbname\"" depsformat = "msvc" outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj" ] description = "compile {{source}}" } tool("alink") { rspfile = "{{output}}.rsp" command = "$linker /lib /nologo /ignore:4221 {{arflags}} /OUT:{{output}} @$rspfile" outputs = [ # Ignore {{output_extension}} and always use .lib, there's no reason to # allow targets to override this extension on Windows. "{{root_out_dir}}/{{target_output_name}}{{output_extension}}", ] default_output_extension = ".lib" default_output_dir = "{{target_out_dir}}" # inputs_newline works around a fixed per-line buffer size in the linker. rspfile_content = "{{inputs_newline}}" description = "link {{output}}" } tool("solink") { dllname = "{{output_dir}}/{{target_output_name}}{{output_extension}}" libname = "${dllname}.lib" pdbname = "${dllname}.pdb" rspfile = "${dllname}.rsp" command = "$linker /nologo /IMPLIB:$libname ${sys_lib_flags} /DLL /OUT:$dllname /PDB:$pdbname @$rspfile" outputs = [ dllname, libname, pdbname, ] default_output_extension = ".dll" default_output_dir = "{{root_out_dir}}" link_output = libname depend_output = libname runtime_outputs = [ dllname, pdbname, ] # Since the above commands only updates the .lib file when it changes, ask # Ninja to check if the timestamp actually changed to know if downstream # dependencies should be recompiled. restat = true # inputs_newline works around a fixed per-line buffer size in the linker. rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}" description = "link {{output}}" } tool("link") { exename = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" pdbname = "$exename.pdb" rspfile = "$exename.rsp" command = "$linker /nologo /OUT:$exename ${sys_lib_flags} /PDB:$pdbname @$rspfile" default_output_extension = ".exe" default_output_dir = "{{root_out_dir}}" outputs = [ exename ] # inputs_newline works around a fixed per-line buffer size in the linker. rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}" description = "link {{output}}" } tool("stamp") { command = "cmd /c type nul > \"{{output}}\"" description = "stamp {{output}}" } tool("copy") { cp_py = rebase_path("../cp.py") command = "cmd.exe /c python \"$cp_py\" {{source}} {{output}}" description = "copy {{source}} {{output}}" } }