| # Copyright 2023 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. |
| |
| import("//build/testing/golden_files.gni") |
| import("//build/zircon/c_utils.gni") |
| |
| # Internal template used to preprocess a DTS file. |
| # |
| # Parameters |
| # |
| # * sources |
| # - Required: DTS file to preprocess. |
| # - Type: list(path). Must only have one entry. |
| # |
| # * deps |
| # - Optional: Usual GN meaning. |
| # |
| template("_preprocessed_dts") { |
| toolchain_utils_action(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "sources", |
| "deps", |
| ]) |
| |
| assert([ sources[0] ] == sources, "Only one source file can be passed.") |
| |
| utils = [ "cc" ] |
| |
| source_root_relative = get_path_info(rebase_path(sources[0], "//"), "dir") |
| source_name = get_path_info(sources[0], "name") |
| |
| # This is done to remove the '.S' extension added to the files and also |
| # place it in a relatively equivalent path to that of the source. This is |
| # required to allow includes that contain the full source path. |
| outputs = |
| [ "$target_gen_dir/$target_name/$source_root_relative/$source_name" ] |
| depfile = "$target_gen_dir/$source_name.d" |
| |
| configs = [ |
| "//build/config:relative_paths", |
| "//build/config:default_include_dirs", |
| ] |
| |
| args = [ |
| "-undef", |
| "-MD", |
| "-MF", |
| rebase_path(depfile, root_build_dir), |
| "-E", |
| "-P", |
| "{{include_dirs}}", |
| "{{defines}}", |
| "-x", |
| "assembler-with-cpp", |
| rebase_path(sources[0], root_build_dir), |
| "-o", |
| rebase_path(outputs[0], root_build_dir), |
| ] |
| } |
| } |
| |
| # Defines a devicetree source file that can be included by other devicetree files. |
| # |
| # Parameters |
| # |
| # * sources |
| # - Required: Device tree source include file (.dtsi/.dtsi.S). Source files |
| # that include C header files should end with the extension `.dtsi.S` and |
| # it will be preprocessed by C compiler. |
| # - Type: list(path). Must only have one entry. |
| # |
| # * deps |
| # - Optional: Other Devicetree fragment targets referenced by this fragment. |
| # - Type: list(label) |
| # - Default: [] |
| # |
| template("devicetree_fragment") { |
| dtsi_file = invoker.sources |
| assert([ dtsi_file[0] ] == invoker.sources, |
| "Devicetree fragment can only have one source file") |
| extension = get_path_info(dtsi_file[0], "extension") |
| dtsi_extension = |
| get_path_info(get_path_info(dtsi_file[0], "name"), "extension") |
| assert(extension == "dtsi" || (extension == "S" && dtsi_extension == "dtsi"), |
| "Devicetree fragment can only have .dtsi or .dtsi.S source file.") |
| not_needed([ "dtsi_extension" ]) |
| |
| if (extension == "S") { |
| processed_dts_target = "_devicetree_fragment.$target_name.preprocessed" |
| _preprocessed_dts(processed_dts_target) { |
| forward_variables_from(invoker, |
| [ |
| "sources", |
| "deps", |
| ]) |
| } |
| processed_dts_outputs = get_target_outputs(":$processed_dts_target") |
| processed_dts_output_dir = get_path_info(processed_dts_outputs[0], "dir") |
| includes = [ |
| # For dtsi includes referring to paths relative to `//`. |
| "$target_gen_dir/$processed_dts_target", |
| |
| # For dtsi includes referring to current directory. |
| "$processed_dts_output_dir", |
| ] |
| } else { |
| includes = [ get_path_info(dtsi_file[0], "dir") ] |
| } |
| |
| group(target_name) { |
| forward_variables_from(invoker, [ "deps" ]) |
| if (!defined(deps)) { |
| deps = [] |
| } |
| |
| if (extension == "S") { |
| deps += [ ":$processed_dts_target" ] |
| } |
| |
| metadata = { |
| devicetree_barrier = [] |
| devicetree_includes = includes |
| } |
| } |
| } |
| |
| # Defines a devicetree blob builder. |
| # |
| # Parameters |
| # |
| # * sources |
| # - Required: Device tree source file (.dts/.dts.S). Source file that |
| # include C header files should end with the extension `.dts.S` and it |
| # will be preprocessed by C compiler before invoking the devicetree compiler. |
| # - Type: List(path). Must only have one entry. |
| # |
| # * deps |
| # - Optional: Usual GN meaning. |
| # |
| # * dtcflags |
| # - Optional: Flags to be passed to dtc compiler. |
| # - Type: List(flags). |
| # |
| # * outputs |
| # - Optional: The singleton list containing the output blob path. This value |
| # can be accessed as `get_target_outputs(target_name)`. |
| # - Type: list(path). Must only have one entry. |
| # - Default: target_out_dir + "/" + get_path_info(sources[0], "name) + ".dtb" |
| # |
| template("dtb") { |
| dts_file = invoker.sources |
| assert([ dts_file[0] ] == invoker.sources, |
| "devicetree can only have a single input file") |
| |
| extension = get_path_info(dts_file[0], "extension") |
| dts_extension = get_path_info(get_path_info(dts_file[0], "name"), "extension") |
| assert(extension == "dts" || (extension == "S" && dts_extension == "dts"), |
| "Only .dts or .dts.S source files accepted.") |
| not_needed([ "dts_extension" ]) |
| |
| processed_dts_target = "_dtb.$target_name.preprocessed" |
| dts_include_dirs_target = "_dtb.$target_name.includes" |
| dts_include_dirs_filename = dts_include_dirs_target + ".txt" |
| |
| # Each devicetree_fragment() call puts a devicetree in a directory tree that |
| # mimics the source tree, so that we can properly restrict /include/ |
| # statements in DTC. This generates a file containing paths to all of these |
| # directory trees, which we use below when calling DTC. |
| generated_file(dts_include_dirs_target) { |
| forward_variables_from(invoker, [ "deps" ]) |
| data_keys = [ "devicetree_includes" ] |
| outputs = [ "$target_gen_dir/$dts_include_dirs_filename" ] |
| output_conversion = "list lines" |
| rebase = root_build_dir |
| } |
| |
| # This passes the DTS files through the preprocessor to handle any C constants. |
| _preprocessed_dts(processed_dts_target) { |
| forward_variables_from(invoker, |
| [ |
| "sources", |
| "deps", |
| ]) |
| } |
| |
| # This actually compiles the DTB. |
| action(target_name) { |
| deps = [ ":$dts_include_dirs_target" ] |
| |
| if (extension == "S") { |
| deps += [ ":$processed_dts_target" ] |
| sources = get_target_outputs(":$processed_dts_target") |
| |
| # Extract name from <name>.dts.S. |
| source_name = get_path_info(sources[0], "name") |
| source_name = get_path_info(source_name, "name") |
| } else { |
| sources = invoker.sources |
| source_name = get_path_info(sources[0], "name") |
| } |
| |
| depfile = "$target_gen_dir/$source_name.d" |
| script = "//build/devicetree/dtc.sh" |
| inputs = [ "//prebuilt/third_party/dtc/${host_platform}/dtc" ] |
| include_dirs = get_target_outputs(":$dts_include_dirs_target") |
| sources += include_dirs |
| |
| if (defined(invoker.outputs)) { |
| outputs = invoker.outputs |
| } else { |
| outputs = [ "$target_out_dir/$source_name.dtb" ] |
| } |
| |
| args = [ |
| rebase_path(inputs[0], root_build_dir), |
| rebase_path(include_dirs[0], root_build_dir), |
| "-i", |
| rebase_path("//", root_build_dir), |
| rebase_path(sources[0], root_build_dir), |
| "-o", |
| rebase_path(outputs[0], root_build_dir), |
| "-d", |
| rebase_path(depfile, root_build_dir), |
| ] |
| |
| if (defined(invoker.dtcflags)) { |
| args += invoker.dtcflags |
| } |
| } |
| } |
| |
| # Defines a devicetree blob decompiler. |
| # |
| # Parameters |
| # |
| # * sources |
| # - Required: Device tree blob file (.dtb). |
| # - Type: List(path). Must only have one entry. |
| # |
| # * deps |
| # - Optional: Usual GN meaning. |
| # |
| # * dtcflags |
| # - Optional: Flags to be passed to dtc compiler. |
| # - Type: List(flags). |
| # |
| # * outputs |
| # - Optional: The singleton list containing the output dts path. This value |
| # can be accessed as `get_target_outputs(target_name)`. |
| # - Type: list(path). Must only have one entry. |
| # - Default: target_out_dir + "/" + get_path_info(sources[0], "name) + ".dts" |
| # |
| template("dts") { |
| action(target_name) { |
| forward_variables_from(invoker, |
| [ |
| "sources", |
| "deps", |
| ]) |
| assert([ sources[0] ] == sources, "Only one input file accepted.") |
| assert(get_path_info(sources[0], "extension") == "dtb", |
| "Only .dtb source files accepted.") |
| |
| source_name = get_path_info(sources[0], "name") |
| script = "//build/devicetree/dtc.sh" |
| inputs = [ "//prebuilt/third_party/dtc/${host_platform}/dtc" ] |
| |
| if (defined(invoker.outputs)) { |
| outputs = invoker.outputs |
| } else { |
| outputs = [ "$target_out_dir/$source_name.dts" ] |
| } |
| |
| args = [ |
| rebase_path(inputs[0], root_build_dir), |
| "--", |
| "-I", |
| "dtb", |
| "-O", |
| "dts", |
| "--sort", |
| rebase_path(sources[0], root_build_dir), |
| "-o", |
| rebase_path(outputs[0], root_build_dir), |
| ] |
| |
| if (defined(invoker.dtcflags)) { |
| args += invoker.dtcflags |
| } |
| } |
| } |
| |
| # Defines a devicetree to be built into a devicetree blob. |
| # |
| # There are three public targets - |
| # 1. The main target compiles the devicetree source and runs all the necessary |
| # verification checks. |
| # 2. The target "$target_name.dtb" can be used to get the output devicetree |
| # blob using `get_target_output("$target_name.dtb")`. |
| # 3. The target "$target_name.assembly_inputs" can be used to include the devicetree |
| # into assembly artifacts. |
| # |
| # Parameters |
| # |
| # * sources |
| # - Required: Device tree source file (.dts) |
| # - Type: List(path). Must only have one entry. |
| # |
| # * golden |
| # - Required: Path to the golden device tree source file |
| # (created by the template if not existing). It will be compared against |
| # the current dts after resolving all includes. This is useful to review |
| # changes done to the device tree either directly by the dts or |
| # indirectly by the includes and headers. |
| # |
| # * deps |
| # - Optional: Other devicetree fragments and header file targets referenced |
| # by the source. |
| # - Type: list(label) |
| # - Default: [] |
| # |
| # * dtcflags |
| # - Optional: Flags to be passed to dtc compiler. |
| # - Type: List(flags). |
| # |
| # * outputs |
| # - Optional: The singleton list containing the output blob path. This value |
| # can be accessed as `get_target_outputs(target_name)`. |
| # - Type: list(path). Must only have one entry. |
| # - Default: target_out_dir + "/" + target_name + ".dtb" |
| # |
| template("devicetree") { |
| compiled_dtb_target = "$target_name.dtb" |
| |
| decompiled_dts_target = "_devicetree.$target_name.decompiled" |
| |
| golden_dts_target = "_devicetree.$target_name.golden" |
| |
| assembly_inputs_target = "$target_name.assembly_inputs" |
| |
| _dtb_path = [] |
| if (defined(invoker.outputs)) { |
| _dtb_path = invoker.outputs |
| } else { |
| _dtb_path = [ "$target_out_dir/$compiled_dtb_target" ] |
| } |
| |
| # Compile the dts |
| dtb(compiled_dtb_target) { |
| forward_variables_from(invoker, |
| [ |
| "sources", |
| "deps", |
| "public_deps", |
| "dtcflags", |
| ]) |
| outputs = _dtb_path |
| } |
| |
| # Decompile the compiled DTS so we can use it for the golden comparison. |
| # We use the decompiled DTS instead of the original DTS as all includes and |
| # C headers are resolved in the final dtb. |
| dts(decompiled_dts_target) { |
| forward_variables_from(invoker, [ "dtcflags" ]) |
| deps = [ ":$compiled_dtb_target" ] |
| sources = get_target_outputs(":$compiled_dtb_target") |
| } |
| |
| # Compare golden dts and decompiled dts file. |
| golden_files(golden_dts_target) { |
| deps = [ ":$decompiled_dts_target" ] |
| dts_outputs = get_target_outputs(":$decompiled_dts_target") |
| comparisons = [ |
| { |
| forward_variables_from(invoker, [ "golden" ]) |
| candidate = dts_outputs[0] |
| }, |
| ] |
| } |
| |
| generated_file(assembly_inputs_target) { |
| contents = [ |
| { |
| source = rebase_path(_dtb_path[0], root_build_dir) |
| destination = "built/artifacts/obj/devicetree/$compiled_dtb_target" |
| }, |
| ] |
| output_conversion = "json" |
| deps = [ ":${compiled_dtb_target}" ] |
| |
| outputs = [ "${target_out_dir}/assembly_inputs.json" ] |
| |
| metadata = { |
| assembly_inputs = [ |
| { |
| path = rebase_path(outputs[0], root_build_dir) |
| }, |
| ] |
| } |
| } |
| |
| group(target_name) { |
| deps = [ |
| ":$compiled_dtb_target", |
| ":$golden_dts_target", |
| ] |
| } |
| } |