| # Copyright 2019 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # TECHNICAL NOTE: |
| # |
| # This file is used to build the sysroot directories used to build C++, |
| # Rust and Go binaries with the Fuchsia build system. |
| # |
| # A "sysroot" is a directory that should provide system headers, libraries |
| # and support files for a given target system. In the case of Fuchsia, |
| # its layout will look like: |
| # |
| # include/ |
| # # C library and Zircon VDSO headers. |
| # |
| # lib/ |
| # libc.so |
| # libdl.so |
| # libm.so |
| # libpthread.so |
| # librt.so |
| # libzircon.so |
| # Scrt1.o |
| # |
| # When building Fuchsia binaries, the sysroot is passed to the final linker |
| # (i.e. 'lld') with the '--sysroot=<dir>' argument, which has the following |
| # effect: |
| # |
| # - Adding the $SYSROOT/lib directory at the end of the library search |
| # path. This allows linker arguments like '-lc' or '-lzircon' to |
| # work properly. |
| # |
| # - When generating an executable, the linker will, by default, inject |
| # the $SYSROOT/lib/Scrt1.o to the link. |
| # |
| # NOTE: The 'libdl.so', 'libm.so', 'libpthread.so' and 'librt.so' libraries |
| # are "legacy" system libraries, because all their APIs are implemented |
| # by 'libc.so' on Fuchsia. However, some of our third_party targets expect |
| # them to be available (e.g. when their BUILD.gn file adds explicit '-lm' |
| # linker flags to link to the Math library, or the Go compiler will inject |
| # an '-lpthread' when calling the linker). To support these, we create |
| # them as empty linker scripts. |
| # |
| # Each sysroot is toolchain-specific (because the C library must always be |
| # built with a variant that is compatible with the current toolchain instance |
| # used to build Fuchsia user binaries). |
| # |
| # To avoid too much duplicate work and copies, each sysroot directory is |
| # created as follows: |
| # |
| # - A common include/ directory is created in a special GN toolchain |
| # instance (//build/zircon:sysroot_toolchain), which contains all |
| # C library and VDSO headers. |
| # |
| # - Each '$SYSROOT/include' contains a copy of the headers listed by |
| # various sysroot_entries.gni files in our build, which define |
| # sysroot_FOO_entries lists of scopes describing a part of the |
| # sysroot content. |
| # |
| # - Files under '$SYSROOT/lib' are all tiny linker scripts that are either |
| # empty, or redirect to the right library binary, whose path depends |
| # on the current toolchain variant. For example, when in the |
| # //build/toolchain/fuchsia:x64-asan toolchain, libc.so would |
| # redirect to 'user.libc_x64/-asan/libc.so.debug' (where '.debug' |
| # means this is the unstripped version). |
| # |
| # The non-empty linker scripts are created through a generated_file() |
| # target that depends on the destination library target. |
| # |
| # Note that C++, Rust and Go require different content in their sysroot: |
| # |
| # - Go requires the paths used in linker scripts to be absolute, because |
| # building Go binaries requires running the "go" tool in a |
| # target-specific directory. |
| # |
| # C++ and Rust do not require this, so relative paths are used instead |
| # to help keep the build more hermetic. |
| # |
| # - Rust requires an empty $SYSROOT/lib/Scrt1.o linker stub. Otherwise, |
| # the file will be added twice to the 'lld' command invoked by the 'rustc' |
| # compiler. This is due to the way GN computes native dependencies for |
| # Rust targets. |
| # |
| # The C++ compiler also requires it by default. Technically, this could |
| # be omitted by adding '-nostartfiles' to the ldflags of the |
| # //build/config/fuchsia:compiler_sysroot config. But using an |
| # empty file instead is less complicated and more consistent with |
| # the Rust case. |
| # |
| # The Go compiler absolutely requires a valid Scrt1.o file though, |
| # so one redirection linker stub is generated instead. |
| # |
| |
| import("//zircon/system/ulib/c/libc_toolchain.gni") |
| import("cpp.gni") |
| import("go.gni") |
| |
| # Import the scope definitions that list all sysroot headers. |
| import("//src/zircon/lib/zircon/sysroot_entries.gni") |
| import("//zircon/system/ulib/c/sysroot_entries.gni") |
| sysroot_entries = |
| sysroot_uninstrumented_libc_entries + sysroot_instrumented_libc_entries + |
| sysroot_crt1_entries + sysroot_vdso_entries |
| |
| # Write sysroot_entries to a JSON file that will get parsed |
| # by our populate_sysroot_headers.py script below, since using it |
| # is faster than creating hundred of copy() targets in each toolchain, |
| # and ensures the destination directory is cleaned up from stale header |
| # files from a previous build. |
| sysroot_entries_json_file = "$root_out_dir/sysroot-entries.json" |
| |
| generated_file("sysroot-entries-json") { |
| visibility = [ ":*" ] |
| contents = sysroot_entries |
| outputs = [ sysroot_entries_json_file ] |
| output_conversion = "json" |
| } |
| |
| # Parse the sysroot_entries list of scopes. |
| # |
| # For historical reasons, only the scopes with an `sdk` key are considered |
| # here. Each value associated with an `sdk` key is a scope itself, with the |
| # following schema: |
| # |
| # If `include_dir` is present then: |
| # |
| # include_dir (optional) |
| # [GN path] A GN path to a subdirectory containing zero or more headers |
| # to copy to the sysroot's include directory. |
| # |
| # headers (optional) |
| # [list of paths] Required if `include_dir` is specified, ignored otherwise. |
| # A list of header sub-paths, relative to `include_dir`, that must be |
| # copied to the sysroot directory. |
| # |
| # no_export (optional) |
| # [boolean] Ignored if `include_dir` is not used. A flag that is set to |
| # indicate that the set of headers described in the current entry should |
| # not be exported to the SDK sysroot (though they will still be copied |
| # to the platform's sysroot). This is useful for <zircon/device/*.h> |
| # headers, as well as `cdecls-next.inc` and `testonly-cdecls.inc`. |
| # |
| # Otherwise, if `include_dir` is _not_ present: |
| # |
| # source (optional) |
| # [path] A path, relative to the current root build directory, where |
| # to find the source file to be copied into the sysroot, where destination |
| # is specified by one of the ` link`, `debug` or `dist` keys described |
| # below. Ignored if `include_dir` is present. |
| # |
| # debug (optional) |
| # dist (optional) |
| # link (optional) |
| # [path] A path relative to the sysroot directory, that specifies where |
| # the `source` file needs to be copied into the SDK. Only one of these |
| # keys can be used per entry. For the platform SDK, onle `dist` and `link` |
| # are used. The SDK sysroot will use all three though. |
| # |
| # deps (optional) |
| # [list of labels] A list of labels to dependencies for this entry, |
| # this should correspond to the GN target that built the `source` |
| # file, once the sysroot generation is moved to the Fuchsia build. |
| # |
| # IMPORTANT: The populate_sysroot_headers.py script in this directory relies |
| # on this exact schema definition. Keep it in sync if the schema changes! |
| # |
| sysroot_headers = [] |
| sysroot_headers_deps = [] |
| foreach(entry, sysroot_entries) { |
| if (defined(entry.sdk)) { |
| sdk = { |
| } |
| sdk = entry.sdk |
| if (defined(sdk.headers)) { |
| dir = rebase_path(sdk.include_dir, "", root_build_dir) |
| foreach(file, sdk.headers) { |
| sysroot_headers += [ "include/$file" ] |
| if (defined(sdk.deps)) { |
| sysroot_headers_deps += sdk.deps |
| } |
| } |
| } |
| } |
| } |
| |
| # An additional dependency for executable() Fuchsia user binaries. It adds |
| # the C runtime startup object code. |
| group("crt1_deps") { |
| deps = [ "//zircon/system/ulib/c:crt1" ] |
| } |
| |
| # Internal template used to populate a language-specific sysroot directory. |
| # The content will be customized according to misc arguments described below |
| # to meet the needs of the C++, Rust and Go compilers. |
| # |
| # This will populate 'sysroot_dir' with an 'include' symlink, and a 'lib' |
| # directory containing linker stubs or dummy files. See technical note |
| # above for details. |
| # |
| # Arguments |
| # sysroot_dir (required) |
| # [GN path] Path to the destination directory to populate. |
| # |
| # absolute_stub_paths (optional) |
| # [boolean] Set to true to use absolute paths in linker redirection |
| # stubs. This is required for the Go sysroot. |
| # |
| # add_crt1 (optional) |
| # [boolean] Set to true to create an Scrt1.o linker stub that |
| # redirects to the actual file. By default this creates an empty |
| # file, and it is up to the caller to add the right dependency to |
| # the actual object through "crt1_deps" above. |
| # |
| template("_create_sysroot") { |
| prefix = target_name |
| |
| assert(defined(invoker.sysroot_dir), |
| "sysroot_dir must be defined when calling this template") |
| _sysroot_dir = invoker.sysroot_dir |
| |
| _libc_target = sysroot_libc_stub_target |
| _libc_file = rebase_path(sysroot_libc_stub, root_build_dir) |
| |
| _vdso_target = "//src/zircon/lib/zircon:zircon.stub($default_toolchain)" |
| _vdso_path = get_label_info(_vdso_target, "target_out_dir") + "/libzircon.so" |
| |
| _crt1_file = rebase_path(sysroot_crt1_obj, root_build_dir) |
| |
| if (defined(invoker.absolute_stub_paths) && invoker.absolute_stub_paths) { |
| _libc_file = rebase_path(_libc_file, "", root_build_dir) |
| _crt1_file = rebase_path(_crt1_file, "", root_build_dir) |
| } |
| |
| # Generate a '$sysroot_dir/include' directory. |
| # |
| # This simply copies the common sysroot directory from |
| # $root_build_dir/sysroot_toolchain/sysroot/include into |
| # $sysroot_dir/include. |
| # |
| # NOTE: This used to be a simple symlink creation, but a recent upstream |
| # GN change prevents using this technique, because it ruins the Ninja no-op |
| # rebuild check. |
| # |
| # For more context, upstream GN used to get rid of action() timestamp files, |
| # but this got recently reverted due to some issues with the Chrome build. |
| # |
| # Timestamp files prevent symlinking directories properly, because Ninja uses |
| # stat() to get a directory's timestamp, which will return the symlink's target |
| # timestamp instead (see https://github.com/ninja-build/ninja/issues/1186). |
| # |
| # When GN gets rid of timestamp files again, a simple symlink will be |
| # enough. For now, the symptoms are: |
| # |
| # - On the first fx build, the common sysroot is created under |
| # $root_build_dir/sysroot_toolchain/sysroot/include by the action("headers") |
| # below, in the $sysroot_toolchain toolchain. |
| # |
| # GN also tells Ninja to generate a timestamp file for this action as |
| # $root_build_dir/sysroot_toolchain/obj/zircon/public/sysroot/headers.stamp |
| # |
| # - A symlink is created by the action() below from $sysroot_dir/include to |
| # the common sysroot. The build completes successfully. |
| # |
| # - On the next 'ninja' invokation, the tool will determine that the timestamp |
| # value for $sysroot_dir/include is that of its target, not that of the |
| # symlink itself (see https://github.com/ninja-build/ninja/issues/1186). |
| # |
| # It will then determine that this value is older than the value for |
| # the timestamp file (i.e. .../headers.stamp), which it depends on |
| # and then needs to be rebuilt!! |
| # |
| action("${prefix}_headers") { |
| # TODO(https://fxbug.dev/42148117): self-caching outputs is intended populate_sysroot_headers.py |
| |
| inputs = [ sysroot_entries_json_file ] |
| outputs = [] |
| foreach(header, sysroot_headers) { |
| outputs += [ "${_sysroot_dir}/$header" ] |
| } |
| script = "populate_sysroot_headers.py" |
| depfile = "$target_gen_dir/${prefix}_headers.d" |
| args = [ |
| "--src-dir", |
| rebase_path("//", root_build_dir), |
| "--sysroot-json", |
| rebase_path(inputs[0], root_build_dir), |
| "--sysroot-dir", |
| rebase_path(_sysroot_dir, root_build_dir), |
| "--dep-file", |
| rebase_path(depfile, root_build_dir), |
| |
| # The --debug flag can be used to print the script's operations for debugging. |
| #"--debug", |
| ] |
| deps = [ ":sysroot-entries-json" ] + sysroot_headers_deps |
| |
| visibility = [ ":*" ] |
| } |
| |
| _sysroot_deps = [ ":${prefix}_headers" ] |
| |
| # Generate a linker file redirecting to the C library. |
| generated_file("${prefix}_libc") { |
| _relative_libc_file = |
| rebase_path("$_libc_file", _sysroot_dir, root_build_dir) |
| outputs = [ "$_sysroot_dir/lib/libc.so" ] |
| contents = [ "INPUT(=$_relative_libc_file)" ] |
| deps = [ _libc_target ] |
| } |
| _sysroot_deps += [ ":${prefix}_libc" ] |
| |
| # Generate a linker file redirecting to the Zircon VDSO |
| copy("${prefix}_libzircon") { |
| outputs = [ "$_sysroot_dir/lib/libzircon.so" ] |
| sources = [ _vdso_path ] |
| deps = [ _vdso_target ] |
| } |
| _sysroot_deps += [ ":${prefix}_libzircon" ] |
| |
| # Generate empty linker stubs for legacy system libraries that |
| # some compilers or runtimes depend on. Their APIs are implemented |
| # by the C library anyway. |
| foreach(stub, |
| [ |
| "libdl", |
| "libm", |
| "libpthread", |
| "librt", |
| ]) { |
| generated_file("${prefix}_${stub}") { |
| outputs = [ "$_sysroot_dir/lib/${stub}.so" ] |
| contents = [ "/* EMPTY */" ] |
| } |
| _sysroot_deps += [ ":${prefix}_${stub}" ] |
| } |
| |
| # Generate a linker file redirecting to the C runtime startup object |
| # or is empty, depending on invoker.add_crt1. |
| generated_file("${prefix}_crt1") { |
| outputs = [ "$_sysroot_dir/lib/Scrt1.o" ] |
| if (defined(invoker.add_crt1) && invoker.add_crt1) { |
| _relative_crt1_file = |
| rebase_path("$_crt1_file", _sysroot_dir, root_build_dir) |
| contents = [ "INPUT(=$_relative_crt1_file)" ] |
| deps = [ sysroot_crt1_target ] |
| } else { |
| contents = [ "/* EMPTY */" ] |
| not_needed([ "_crt1_file" ]) |
| } |
| } |
| |
| _sysroot_deps += [ ":${prefix}_crt1" ] |
| |
| # The parent group() that depends on everything else. |
| group(target_name) { |
| deps = _sysroot_deps |
| if (defined(invoker.deps)) { |
| deps += invoker.deps |
| } |
| |
| if (defined(invoker.data_deps)) { |
| data_deps = invoker.data_deps |
| } |
| } |
| } |
| |
| # Create the sysroot directory used by the C++ compiler. |
| # Any Fuchsia binary target must depend on this target to be able |
| # to include C library and VDSO headers, as well as link ELF shared |
| # libraries or executables. |
| # |
| # Note that executables should also depend on the 'crt1_deps' target |
| # to get the proper dependency on the C runtime startup object |
| # (the one created in this sysroot is intentionally empty). |
| # |
| _create_sysroot("cpp_binary_deps") { |
| sysroot_dir = cpp_sysroot_dir |
| deps = [ "//zircon/system/ulib/c" ] |
| data_deps = [ sysroot_libc_target ] |
| } |
| |
| # Rust compilation can now use the C++ sysroot directly, so just |
| # redirect to the corresponding target. If you change this, don't |
| # forget to update sysroot_rust.gni as well. |
| group("rust_binary_deps") { |
| deps = [ ":cpp_binary_deps" ] |
| } |
| |
| # Create the sysroot directory used by the Go compiler. |
| # |
| # Note that this does include an Scrt1.o linker stub that redirects and |
| # depends on the real file, so Go executables should _not_ depend on |
| # 'crt1_deps' at all. |
| # |
| _create_sysroot("go_binary_deps") { |
| sysroot_dir = go_sysroot_dir |
| add_crt1 = true |
| absolute_stub_paths = true |
| deps = [ "//zircon/system/ulib/c" ] |
| data_deps = [ sysroot_libc_target ] |
| } |
| |
| # Depend on this to only require the Zircon headers, but not link to the |
| # C library or Zircon VDSO. Should only be used for source_set() and |
| # static_library() targets. |
| group("headers") { |
| # Only depend on the target that creates the include directory. |
| deps = [ ":cpp_binary_deps_headers" ] |
| } |
| |
| # A special dependency that ensures that the uninstrumented C library is |
| # available in the system package as /lib/ld.so.1. For more details read |
| # //zircon/system/ulib/c/libc_toolchain.gni |
| # |
| # In practice, should only be used to build the system package, and some |
| # prebuilt driver packages. |
| group("system_libc_deps") { |
| public_deps = [ system_libc_target ] |
| } |