blob: f230def36bd3fd8639e6db793af0814ebba2b5e9 [file] [log] [blame]
# Copyright 2024 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.
load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
load("@pigweed//pw_build:binary_tools.bzl", "run_action_on_executable")
load("@pigweed//pw_build/bazel_internal:pigweed_internal.bzl", "compile_cc")
load("@rules_cc//cc:action_names.bzl", "CPP_LINK_EXECUTABLE_ACTION_NAME", "C_COMPILE_ACTION_NAME")
def _get_inputs(ctx):
all_input_files = [ctx.file.overlay]
for label in ctx.attr.references:
if type(label) == "Target" and label.files.to_list():
all_input_files.extend(label.files.to_list())
else:
fail("Unsupported target kind in references:", label)
return depset(all_input_files)
def _package_exec_path(ctx):
workspace_name = ctx.label.workspace_name
if workspace_name == "":
return "."
return "external/" + workspace_name + "/" + ctx.label.package
def _dts_library_impl(ctx):
sources = [ctx.file.overlay]
include_paths = ctx.attr.includes
output_header = ctx.actions.declare_file(ctx.label.name + ".dts")
cc_toolchain = find_cpp_toolchain(ctx)
workspace_root = ctx.label.workspace_root
zephyr_root = Label("@zephyr//:west.yml").workspace_root
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
cxx_compiler_path = cc_common.get_tool_for_action(
feature_configuration = feature_configuration,
action_name = C_COMPILE_ACTION_NAME,
)
print("Declaring file: ", output_header.path)
print("workspace_name: ", ctx.label.workspace_root)
for dep in ctx.attr.deps:
if hasattr(dep, "files"):
sources.extend(dep.files)
else:
fail("Dependency", dep, "does not contain files.")
args = [
"-x",
"assembler-with-cpp",
"-nostdinc",
]
source_count = 0
for src in sources:
if source_count != 0:
args.append("-include")
args.append(src.path)
source_count += 1
root_path = _package_exec_path(ctx)
# TODO revisit this, cc_library doesn't allow labels in the include paths,
# it's possible we should use the deps and inherit includes depending on
# the providers.
for path in include_paths:
if path.startswith("@"):
label = Label(path)
flag = "-I" + label.workspace_root + "/" + label.package
else:
flag = "-I" + root_path + "/" + path
print(" flag=%s" % (flag))
args.append(flag)
args.extend([
"-I",
"-D__DTS__",
"-E",
"-o",
output_header.path,
])
print("Running: ", cxx_compiler_path, " ".join(args))
ctx.actions.run(
inputs = _get_inputs(ctx),
outputs = [output_header],
arguments = args,
executable = cxx_compiler_path,
tools = cc_toolchain.all_files,
mnemonic = "DtsPreCompile",
)
return [
DefaultInfo(files = depset([output_header])),
]
dts_library = rule(
implementation = _dts_library_impl,
attrs = {
"deps": attr.label_list(allow_files = False),
"includes": attr.string_list(),
"overlay": attr.label(mandatory = True, allow_single_file = True),
"references": attr.label_list(allow_files = True),
"_cc_toolchain": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
},
toolchains = use_cpp_toolchain(),
fragments = ["cpp"],
)
def _dts_cc_library_impl(ctx):
output_dts = ctx.actions.declare_file("zephyr.dts")
output_edt_pickle = ctx.actions.declare_file("edt.pickle")
output_header_name = "zephyr/devicetree_generated.h"
output_header = ctx.actions.declare_file(output_header_name)
dts_file = ctx.attr.dts_lib[DefaultInfo].files.to_list()[0]
bindings_files = []
bindings_dirs = []
for bindings_file_provider in ctx.attr.bindings:
provider = bindings_file_provider[DefaultInfo].files.to_list()
bindings_files.extend(provider)
bindings_dirs += [f.dirname for f in provider]
gen_defines_target = ctx.attr._gen_defines[DefaultInfo]
gen_edt_target = ctx.attr._gen_edt[DefaultInfo]
inputs = gen_defines_target.files.to_list() + gen_edt_target.files.to_list() + [dts_file] + bindings_files
ctx.actions.run(
inputs = inputs,
outputs = [output_edt_pickle, output_dts],
executable = gen_edt_target.files_to_run.executable,
arguments = [
"--edt-pickle-out",
output_edt_pickle.path,
"--dts",
dts_file.path,
"--bindings-dirs",
] + depset(bindings_dirs).to_list() + [
# "--bindings-dirs",
# "external/zephyr/dts/bindings",
"--dts-out",
output_dts.path,
"--dtc-flags",
"Wno-simple_bus_reg",
],
mnemonic = "DtsGenEdtPickle",
progress_message = "Generating EDT pickle",
)
args = [
"--header-out",
output_header.path,
"--edt-pickle",
output_edt_pickle.path,
]
ctx.actions.run(
inputs = inputs + [output_edt_pickle],
outputs = [output_header],
executable = gen_defines_target.files_to_run.executable,
arguments = args,
mnemonic = "DtsGenDefines",
progress_message = "Running DTS definition generator",
)
return [
DefaultInfo(
files = depset([output_header]),
),
CcInfo(
compilation_context = cc_common.create_compilation_context(
includes = depset([output_header.path[:-len(output_header_name)]]),
headers = depset([output_header]),
),
),
]
dts_cc_library = rule(
implementation = _dts_cc_library_impl,
attrs = {
"dts_lib": attr.label(allow_files = False),
"bindings": attr.label_list(allow_files = True),
"_gen_defines": attr.label(
default = "//scripts/dts:gen_defines",
executable = True,
cfg = "exec",
),
"_gen_edt": attr.label(
default = "//scripts/dts:gen_edt",
executable = True,
cfg = "exec",
),
},
provides = [CcInfo],
)
def _syscall_library_files_impl(ctx):
print(",".join([str(f.path) for f in ctx.files.hdrs]))
syscalls_json = ctx.actions.declare_file("syscalls.json")
struct_tags_json = ctx.actions.declare_file("struct_tags.json")
syscall_file_list = ctx.actions.declare_file("syscalls_file_list.txt")
# Generated sources
syscall_dispatcher_c = ctx.actions.declare_file("include/generated/zephyr/syscall_dispatch.c")
syscall_list_h = ctx.actions.declare_file("include/generated/zephyr/syscall_list.h")
syscall_export_llext_c = ctx.actions.declare_file("include/generated/zephyr/syscall_export_llext.c")
# TODO This file is always generated but only compiled if CONFIG_LLEXT=y
syscall_weakdefs_llext_c = ctx.actions.declare_file("syscall_weakdefs_llext.c")
ctx.actions.write(
output = syscall_file_list,
content = ";".join([f.path for f in ctx.files.hdrs]),
)
scan_dirs = depset([f.dirname for f in ctx.files.hdrs])
scan_list = []
include_list = []
for d in scan_dirs.to_list():
scan_list += ["--scan", d]
include_list += ["--include", d]
ctx.actions.run(
inputs = ctx.files.hdrs + [syscall_file_list],
outputs = [syscalls_json, struct_tags_json],
executable = ctx.attr._parse_syscalls[DefaultInfo].files_to_run.executable,
arguments = scan_list + include_list + [
"--json-file",
syscalls_json.path,
"--tag-struct-file",
struct_tags_json.path,
"--file-list",
syscall_file_list.path,
],
mnemonic = "ParseSyscalls",
progress_message = "Parsing syscalls",
)
syscall_headers = [
ctx.actions.declare_file("include/generated/zephyr/syscalls/" + f.basename)
for f in ctx.files.hdrs
]
base_output_dir = syscall_headers[0].path
base_output_dir = base_output_dir[:-len(ctx.files.hdrs[0].basename)]
syscall_include_path = base_output_dir[:-len("zephyr/syscalls/")]
print("base_output_dir = " + base_output_dir)
print("syscall_include_path = " + syscall_include_path)
ctx.actions.run(
inputs = [syscalls_json],
outputs = [
syscall_dispatcher_c,
syscall_list_h,
syscall_export_llext_c,
syscall_weakdefs_llext_c,
] + syscall_headers,
executable = ctx.attr._gen_syscalls[DefaultInfo].files_to_run.executable,
arguments = [
"--json-file",
syscalls_json.path,
"--base-output",
base_output_dir,
"--syscall-dispatch",
syscall_dispatcher_c.path,
"--syscall-exports-llext",
syscall_export_llext_c.path,
"--syscall-weakdefs-llext",
syscall_weakdefs_llext_c.path,
"--syscall-list",
syscall_list_h.path,
],
mnemonic = "GenSyscalls",
progress_message = "Generating syscalls",
)
return [
DefaultInfo(
files = depset([
syscall_file_list,
syscalls_json,
struct_tags_json,
syscall_dispatcher_c,
syscall_list_h,
syscall_export_llext_c,
] + syscall_headers),
),
]
syscall_library_files = rule(
implementation = _syscall_library_files_impl,
attrs = {
"hdrs": attr.label_list(allow_files = True),
"_parse_syscalls": attr.label(
default = "//scripts/build:parse_syscalls",
executable = True,
cfg = "exec",
),
"_gen_syscalls": attr.label(
default = "//scripts/build:gen_syscalls",
executable = True,
cfg = "exec",
),
},
output_to_genfiles = True,
)
def _syscall_library_impl(ctx):
print(",".join([str(f.path) for f in ctx.files.hdrs]))
syscalls_json = ctx.actions.declare_file("syscalls.json")
struct_tags_json = ctx.actions.declare_file("struct_tags.json")
# include/generated/zephyr/
syscall_dispatcher_c = ctx.actions.declare_file("syscall_dispatch.c")
syscall_list_h = ctx.actions.declare_file("include/generated/zephyr/syscall_list.h")
syscall_export_llext_c = ctx.actions.declare_file("syscall_export_llext.c")
syscall_file_list = ctx.actions.declare_file("syscalls_file_list.txt")
# output_file = ctx.actions.declare_file(ctx.label.name + ".a")
ctx.actions.write(
output = syscall_file_list,
content = ";".join([f.path for f in ctx.files.hdrs]),
)
scan_dirs = depset([f.dirname for f in ctx.files.hdrs])
scan_list = []
include_list = []
for d in scan_dirs.to_list():
scan_list += ["--scan", d]
include_list += ["--include", d]
print("Running:\n" + " ".join(scan_list + include_list + [
"--json-file",
syscalls_json.path,
"--tag-struct-file",
struct_tags_json.path,
"--file-list",
syscall_file_list.path,
]))
ctx.actions.run(
inputs = ctx.files.hdrs + [syscall_file_list],
outputs = [syscalls_json, struct_tags_json],
executable = ctx.attr._parse_syscalls[DefaultInfo].files_to_run.executable,
arguments = scan_list + include_list + [
"--json-file",
syscalls_json.path,
"--tag-struct-file",
struct_tags_json.path,
"--file-list",
syscall_file_list.path,
],
mnemonic = "ParseSyscalls",
progress_message = "Parsing syscalls",
)
syscall_headers = [
ctx.actions.declare_file("include/generated/zephyr/syscalls/" + f.basename)
for f in ctx.files.hdrs
]
base_output_dir = syscall_headers[0].path
base_output_dir = base_output_dir[:-len(ctx.files.hdrs[0].basename)]
syscall_include_path = base_output_dir[:-len("zephyr/syscalls/")]
print("base_output_dir = " + base_output_dir)
print("syscall_include_path = " + syscall_include_path)
ctx.actions.run(
inputs = [syscalls_json],
outputs = [
syscall_dispatcher_c,
syscall_list_h,
syscall_export_llext_c,
] + syscall_headers,
executable = ctx.attr._gen_syscalls[DefaultInfo].files_to_run.executable,
arguments = [
"--json-file",
syscalls_json.path,
"--base-output",
base_output_dir,
"--syscall-dispatch",
syscall_dispatcher_c.path,
"--syscall-export-llext",
syscall_export_llext_c.path,
"--syscall-list",
syscall_list_h.path,
],
mnemonic = "GenSyscalls",
progress_message = "Generating syscalls",
)
return compile_cc(
ctx = ctx,
srcs = [syscall_dispatcher_c],
hdrs = [syscall_list_h, syscall_export_llext_c] + syscall_headers,
deps = ctx.attr.deps,
includes = [syscall_include_path],
) + [
DefaultInfo(
files = depset([syscall_list_h]),
),
]
syscall_library = rule(
implementation = _syscall_library_impl,
attrs = {
"hdrs": attr.label_list(allow_files = True),
"deps": attr.label_list(allow_files = False),
"_parse_syscalls": attr.label(
default = "//scripts/build:parse_syscalls",
executable = True,
cfg = "exec",
),
"_gen_syscalls": attr.label(
default = "//scripts/build:gen_syscalls",
executable = True,
cfg = "exec",
),
"_cc_toolchain": attr.label(
default = Label("@rules_cc//cc:current_cc_toolchain"),
),
},
toolchains = use_cpp_toolchain(),
fragments = ["cpp"],
output_to_genfiles = True,
provides = [CcInfo],
)
def _zephyr_final_binary_impl(ctx):
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
cxx_linker_path = cc_common.get_tool_for_action(
feature_configuration = feature_configuration,
action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
)
output_elf = ctx.actions.declare_file(ctx.label.name)
ctx.actions.run(
inputs = depset(
direct = [ctx.executable.pre0_elf, ctx.file.lds],
transitive = [cc_toolchain.all_files],
),
outputs = [output_elf],
arguments = [
"-T",
ctx.file.lds.path,
ctx.executable.pre0_elf.path,
"-o",
output_elf.path,
],
executable = cxx_linker_path,
tools = cc_toolchain.all_files,
mnemonic = "Linking",
)
return [
DefaultInfo(
files = depset([output_elf, ctx.executable.pre0_elf]),
executable = output_elf,
),
]
zephyr_final_binary = rule(
implementation = _zephyr_final_binary_impl,
attrs = {
"lds": attr.label(
mandatory = True,
allow_single_file = True,
),
"pre0_elf": attr.label(
mandatory = True,
executable = True,
cfg = "target",
),
"_cc_toolchain": attr.label(
default = Label("@rules_cc//cc:current_cc_toolchain"),
),
},
executable = True,
toolchains = use_cpp_toolchain(),
fragments = ["cpp"],
)
def _zephyr_offset_header_impl(ctx):
cc_info = ctx.attr.lib[DefaultInfo]
library_archive = cc_info.files.to_list()[0]
linker_output = ctx.actions.declare_file("offsets.o")
output_file = ctx.actions.declare_file(ctx.attr.output)
run_action_on_executable(
ctx = ctx,
action_name = ACTION_NAMES.cpp_link_static_library,
action_args = "x {input} --output={output_dir} offsets.o".format(
input = str(library_archive.path),
output_dir = str(linker_output.dirname),
),
inputs = [library_archive],
output = linker_output,
additional_outputs = [],
output_executable = False,
)
ctx.actions.run(
outputs = [output_file],
inputs = [linker_output],
executable = ctx.executable.tool,
arguments = [
"-i",
linker_output.path,
"-o",
output_file.path,
],
)
return [DefaultInfo(files = depset([output_file]))]
zephyr_offset_header = rule(
implementation = _zephyr_offset_header_impl,
attrs = {
"lib": attr.label(mandatory = True, allow_files = False, providers = [CcInfo]),
"tool": attr.label(executable = True, allow_files = False, mandatory = True, cfg = "exec"),
"output": attr.string(mandatory = True),
},
toolchains = use_cpp_toolchain(),
fragments = ["cpp"],
)