blob: f86ecf83b16ae511c78a3eb2aacb4ff918016cdb [file] [log] [blame]
# Copyright (C) 2022 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.
"""Rules for defining a DDK (Driver Development Kit) module."""
load(":ddk/ddk_conditional_filegroup.bzl", "flatten_conditional_srcs")
load(":ddk/ddk_module_config.bzl", "ddk_module_config")
load(":ddk/makefiles.bzl", "makefiles")
load(":kernel_module.bzl", "kernel_module")
visibility("//build/kernel/kleaf/...")
def ddk_module(
name,
kernel_build,
srcs = None,
deps = None,
hdrs = None,
textual_hdrs = None,
includes = None,
conditional_srcs = None,
crate_root = None,
linux_includes = None,
out = None,
local_defines = None,
copts = None,
removed_copts = None,
asopts = None,
linkopts = None,
config = None,
kconfig = None,
defconfig = None,
generate_btf = None,
autofdo_profile = None,
debug_info_for_profiling = None,
**kwargs):
"""
Defines a DDK (Driver Development Kit) module.
Example:
```
ddk_module(
name = "my_module",
srcs = ["my_module.c", "private_header.h"],
out = "my_module.ko",
# Exported headers
hdrs = ["include/my_module_exported.h"],
textual_hdrs = ["my_template.c"],
includes = ["include"],
)
```
Note: Local headers should be specified in one of the following ways:
- In a `ddk_headers` target in the same package, if you need to auto-generate `-I` ccflags.
In that case, specify the `ddk_headers` target in `deps`.
- Otherwise, in `srcs` if you don't need the `-I` ccflags.
Exported headers should be specified in one of the following ways:
- In a separate `ddk_headers` target in the same package. Then specify the
target in `hdrs`. This is recommended if there
are multiple `ddk_module`s depending on a
[`glob`](https://bazel.build/reference/be/functions#glob) of headers or a large list
of headers.
- Using `hdrs`, `textual_hdrs` and `includes` of this target.
For details, see `build/kernel/kleaf/tests/ddk_examples/README.md`.
`hdrs`, `textual_hdrs` and `includes` have the same semantics as [`ddk_headers`](#ddk_headers).
That is, this target effectively acts as a `ddk_headers` target when specified in the `deps`
attribute of another `ddk_module`. In other words, the following code snippet:
```
ddk_module(name = "module_A", hdrs = [...], includes = [...], ...)
ddk_module(name = "module_B", deps = ["module_A"], ...)
```
... is effectively equivalent to the following:
```
ddk_headers(name = "module_A_hdrs, hdrs = [...], includes = [...], ...)
ddk_module(name = "module_A", ...)
ddk_module(name = "module_B", deps = ["module_A", "module_A_hdrs"], ...)
```
**Submodules**
See [ddk_submodule](#ddk_submodule).
If `deps` contains a `ddk_submodule` target, the `ddk_module` target must not specify
anything except:
- `kernel_build`
- `linux_includes`
It is not recommended that a `ddk_submodule` depends on a `ddk_headers` target that specifies
`linux_includes`. If a `ddk_submodule` does depend on a `ddk_headers` target
that specifies `linux_includes`, all submodules below the same directory (i.e. sharing the same
`Kbuild` file) gets these `linux_includes`. This is because `LINUXINCLUDE` is set for the whole
`Kbuild` file, not per compilation unit.
In particular, a `ddk_submodule` should not depend on `//common:all_headers`.
Instead, the dependency should come from the `kernel_build`; that is, the `kernel_build` of
the `ddk_module`, or the `base_kernel`, should specify
`ddk_module_headers = "//common:all_headers"`.
To avoid confusion, the dependency on this `ddk_headers` target with `linux_includes` should
be moved to the top-level `ddk_module`. In this case, all submodules of this `ddk_module`
receives the said `LINUXINCLUDE` from the `ddk_headers` target.
Example:
```
# //common
kernel_build(name = "kernel_aarch64", ddk_module_headers = ":all_headers_aarch64")
ddk_headers(
name = "all_headers_aarch64",
linux_includes = [
"arch/arm64/include",
"arch/arm64/include/uapi",
"include",
"include/uapi",
],
)
```
```
# //device
kernel_build(name = "tuna", base_kernel = "//common:kernel_aarch64")
ddk_headers(name = "uapi", linux_includes = ["uapi/include"])
ddk_module(
name = "mymodule",
kernel_build = ":tuna",
deps = [
":mysubmodule"
# Specify dependency on :uapi in the top level ddk_module
":uapi",
],
)
ddk_submodule(
name = "mysubmodule",
deps = [
# Not recommended to specify dependency on :uapi since it contains
# linux_includes
# No need tp specify dependency on //common:all_headers_aarch64
# since it comes from :tuna -> //common:kernel_aarch64
]
)
```
**Ordering of `includes`**
**The best practice is to not have conflicting header names and search paths.**
But if you do, see below for ordering of include directories to be
searched for header files.
A [`ddk_module`](#ddk_module) is compiled with the following order of include directories
(`-I` options):
1. Traverse depedencies for `linux_includes`:
1. All `linux_includes` of this target, in the specified order
2. All `linux_includes` of `deps`, in the specified order (recursively apply #1.3 on each target)
3. All `linux_includes` of `hdrs`, in the specified order (recursively apply #1.3 on each target)
4. All `linux_includes` from kernel_build:
1. All `linux_includes` from `ddk_module_headers` of the `base_kernel` of the
`kernel_build` of this `ddk_module`;
2. All `linux_includes` from `ddk_module_headers` of the `kernel_build` of this
`ddk_module`;
2. `LINUXINCLUDE` (See `${KERNEL_DIR}/Makefile`)
3. Traverse depedencies for `includes`:
1. All `includes` of this target, in the specified order
2. All `includes` of `deps`, in the specified order (recursively apply #3.1 and #3.3 on each target)
3. All `includes` of `hdrs`, in the specified order (recursively apply #3.1 and #3.3 on each target)
4. All `includes` from kernel_build:
1. All `includes` from `ddk_module_headers` of the `base_kernel` of the
`kernel_build` of this `ddk_module`;
2. All `includes` from `ddk_module_headers` of the `kernel_build` of this
`ddk_module`;
In other words, #1 and #3 uses the `preorder` of
[depset](https://bazel.build/rules/lib/depset).
"In the specified order" means that order matters within these lists.
To prevent buildifier from sorting these lists, use the `# do not sort` magic line.
To export a target `:x` in `hdrs` before other targets in `deps`
(that is, if you need #3.3 before #3.2, or #1.2 before #1.1),
specify `:x` in the `deps` list in the position you want. See example below.
To export an include directory in `includes` that needs to be included
after other targets in `hdrs` or `deps` (that is, if you need #3.1 after #3.2
or #3.3), specify the include directory in a separate `ddk_headers` target,
then specify this `ddk_headers` target in `hdrs` and/or `deps` based on
your needs.
For example:
```
ddk_headers(name = "base_ddk_headers", includes = ["base"], linux_includes = ["uapi/base"])
ddk_headers(name = "device_ddk_headers", includes = ["device"], linux_includes = ["uapi/device"])
kernel_build(
name = "kernel_aarch64",
ddk_module_headers = [":base_ddk_headers"],
)
kernel_build(
name = "device",
base_kernel = ":kernel_aarch64",
ddk_module_headers = [":device_ddk_headers"],
)
ddk_headers(name = "dep_a", includes = ["dep_a"], linux_includes = ["uapi/dep_a"])
ddk_headers(name = "dep_b", includes = ["dep_b"])
ddk_headers(name = "dep_c", includes = ["dep_c"], hdrs = ["dep_a"])
ddk_headers(name = "hdrs_a", includes = ["hdrs_a"], linux_includes = ["uapi/hdrs_a"])
ddk_headers(name = "hdrs_b", includes = ["hdrs_b"])
ddk_headers(name = "x", includes = ["x"])
ddk_module(
name = "module",
kernel_build = ":device",
deps = [":dep_b", ":x", ":dep_c"],
hdrs = [":hdrs_a", ":x", ":hdrs_b"],
linux_includes = ["uapi/module"],
includes = ["self_1", "self_2"],
)
```
Then `":module"` is compiled with these flags, in this order:
```
# 1.1 linux_includes
-Iuapi/module
# 1.2 deps, linux_includes, recursively
-Iuapi/dep_a
# 1.3 hdrs, linux_includes, recursively
-Iuapi/hdrs_a
# 1.4 linux_includes from kernel_build and base_kernel
-Iuapi/device
-Iuapi/base
# 2.
$(LINUXINCLUDE)
# 3.1 includes
-Iself_1
-Iself_2
# 3.2. deps, recursively
-Idep_b
-Ix
-Idep_a # :dep_c depends on :dep_a, so include dep_a/ first
-Idep_c
# 3.3. hdrs, recursively
-Ihdrs_a
# x is already included, skip
-Ihdrs_b
# 3.4. includes from kernel_build and base_kernel
-Idevice
-Ibase
```
A dependent module automatically gets #1.1, #1.3, #3.1, #3.3, in this order. For example:
```
ddk_module(
name = "child",
kernel_build = ":device",
deps = [":module"],
# ...
)
```
Then `":child"` is compiled with these flags, in this order:
```
# 1.2. linux_includes of deps, recursively
-Iuapi/module
-Iuapi/hdrs_a
# 1.4 linux_includes from kernel_build and base_kernel
-Iuapi/device
-Iuapi/base
# 2.
$(LINUXINCLUDE)
# 3.2. includes of deps, recursively
-Iself_1
-Iself_2
-Ihdrs_a
-Ix
-Ihdrs_b
# 3.4. includes from kernel_build and base_kernel
-Idevice
-Ibase
```
Args:
name: Name of target. This should usually be name of the output `.ko` file without the
suffix.
srcs: sources or local headers.
Source files (`.c`, `.S`, `.rs`) must be in the package of
this `ddk_module` target, or in subpackages.
Generated source files (`.c`, `.S`, `.rs`) are accepted as long as
they are in the package of this `ddk_module` target, or in
subpackages.
Header files specified here are only visible to this `ddk_module`
target, but not dependencies. To export a header so dependencies
can use it, put it in `hdrs` and set `includes` accordingly.
Generated header files are accepted.
deps: A list of dependent targets. Each of them must be one of the following:
- [`kernel_module`](#kernel_module)
- [`ddk_module`](#ddk_module)
- [`ddk_headers`](#ddk_headers).
- [`ddk_prebuilt_object`](#ddk_prebuilt_object)
- [`ddk_library`](#ddk_library)
If [`config`](#ddk_module-config) is set, if some `deps` of this target have `kconfig`
/ `defconfig` set (including transitive dependencies), you may need to duplicate these
targets in `ddk_config.deps`. Inconsistent configs are disallowed; if the resulting
`.config` is not the same as the one from [`config`](#ddk_module-config), you get a
build error.
hdrs: See [`ddk_headers.hdrs`](#ddk_headers-hdrs)
If [`config`](#ddk_module-config) is set, if some `hdrs` of this target have `kconfig`
/ `defconfig` set (including transitive dependencies), you may need to duplicate these
targets in `ddk_config.deps`. Inconsistent configs are disallowed; if the resulting
`.config` is not the same as the one from [`config`](#ddk_module-config), you get a
build error.
textual_hdrs: See [`ddk_headers.textual_hdrs`](#ddk_headers-textual_hdrs). DEPRECATED. Use `hdrs`.
includes: See [`ddk_headers.includes`](#ddk_headers-includes)
linux_includes: See [`ddk_headers.linux_includes`](#ddk_headers-linux_includes)
Unlike `ddk_headers.linux_includes`, `ddk_module.linux_includes` is **NOT**
applied to dependent `ddk_module`s.
kernel_build: [`kernel_build`](#kernel_build)
conditional_srcs: A dictionary that specifies sources conditionally compiled based on configs.
Example:
```
conditional_srcs = {
"CONFIG_FOO": {
True: ["foo.c"],
False: ["notfoo.c"]
}
}
```
In the above example, if `CONFIG_FOO` is `y` or `m`, `foo.c` is compiled.
Otherwise, `notfoo.c` is compiled instead.
crate_root: For Rust modules, the file that will be passed to rustc to
be used for building this module.
Currently, each `.ko` may only contain a single Rust crate. Modules with multiple crates
are not yet supported. Hence, only a single file may be passed into crate_root.
Unlike `rust_binary`, this must always be set for Rust modules. No defaults are assumed.
out: The output module file. This should usually be `"{name}.ko"`.
This is required if this target does not contain submodules.
local_defines: List of defines to add to the compile and assemble command line.
**Order matters**. To prevent buildifier from sorting the list, use the
`# do not sort` magic line.
Each string is prepended with `-D` and added to the compile/assemble command
line for this target, but not to its dependents.
Unlike
[`cc_library.local_defines`](https://bazel.build/reference/be/c-cpp#cc_library.local_defines),
this is not subject to
["Make" variable substitution](https://bazel.build/reference/be/make-variables) or
[`$(location)` substitution](https://bazel.build/reference/be/make-variables#predefined_label_variables).
Each string is treated as a single Bourne shell token. Unlike
[`cc_library.local_defines`](https://bazel.build/reference/be/c-cpp#cc_library.local_defines),
this is not subject to
[Bourne shell tokenization](https://bazel.build/reference/be/common-definitions#sh-tokenization).
The behavior is similar to `cc_library` with the `no_copts_tokenization`
[feature](https://bazel.build/reference/be/functions#package.features).
For details about `no_copts_tokenization`, see
[`cc_library.copts`](https://bazel.build/reference/be/c-cpp#cc_library.copts).
copts: Add these options to the compilation command.
**Order matters**. To prevent buildifier from sorting the list, use the
`# do not sort` magic line.
Subject to
[`$(location)` substitution](https://bazel.build/reference/be/make-variables#predefined_label_variables).
The flags take effect only for compiling this target, not its
dependencies, so be careful about header files included elsewhere.
All host paths should be provided via
[`$(location)` substitution](https://bazel.build/reference/be/make-variables#predefined_label_variables).
See "Implementation detail" section below.
Each `$(location)` expression should occupy its own token; optional argument key is
allowed as a prefix. For example:
```
# Good
copts = ["-include", "$(location //other:header.h)"]
copts = ["-include=$(location //other:header.h)"]
# BAD - Don't do this! Split into two tokens.
copts = ["-include $(location //other:header.h)"]
# BAD - Don't do this! Split into two tokens.
copts = ["$(location //other:header.h) -Werror"]
# BAD - Don't do this! Split into two tokens.
copts = ["$(location //other:header.h) $(location //other:header2.h)"]
```
Unlike
[`cc_library.local_defines`](https://bazel.build/reference/be/c-cpp#cc_library.local_defines),
this is not subject to
["Make" variable substitution](https://bazel.build/reference/be/make-variables).
Each string is treated as a single Bourne shell token. Unlike
[`cc_library.copts`](https://bazel.build/reference/be/c-cpp#cc_library.copts)
this is not subject to
[Bourne shell tokenization](https://bazel.build/reference/be/common-definitions#sh-tokenization).
The behavior is similar to `cc_library` with the `no_copts_tokenization`
[feature](https://bazel.build/reference/be/functions#package.features).
For details about `no_copts_tokenization`, see
[`cc_library.copts`](https://bazel.build/reference/be/c-cpp#cc_library.copts).
Because each string is treated as a single Bourne shell token, if
a plural `$(locations)` expression expands to multiple paths, they
are treated as a single Bourne shell token, which is likely an
undesirable behavior. To avoid surprising behaviors, use singular
`$(location)` expressions to ensure that the label only expands to one
path. For differences between the `$(locations)` and `$(location)`, see
[`$(location)` substitution](https://bazel.build/reference/be/make-variables#predefined_label_variables).
**Implementation detail**: Unlike usual `$(location)` expansion,
`$(location)` in `copts` is expanded to a path relative to the current
package before sending to the compiler.
For example:
```
# package: //package
ddk_module(
name = "my_module",
copts = ["-include", "$(location //other:header.h)"],
srcs = ["//other:header.h", "my_module.c"],
)
```
Then the content of generated Makefile is semantically equivalent to:
```
CFLAGS_my_module.o += -include ../other/header.h
```
The behavior is such because the generated `Makefile` is located in
`package/Makefile`, and `make` is executed under `package/`. In order
to find `other/header.h`, its path relative to `package/` is given.
removed_copts: Similar to `copts` but for flags **removed** from the
compilation command.
For example:
```
ddk_module(
name = "my_module",
removed_copts = ["-Werror"],
srcs = ["my_module.c"],
)
```
Then the content of generated Makefile is semantically equivalent to:
```
CFLAGS_REMOVE_my_module.o += -Werror
```
Note: Due to implementation details of Kleaf flags in `copts` are written to a file and
provided to the compiler with the `@<arg_file>` syntax, so they are not affected
by `removed_copts` implemented by `CFLAGS_REMOVE_`. To remove flags from the Bazel
`copts` list, do so directly.
asopts: Similar to `copts` but for assembly.
For example:
```
ddk_module(
name = "my_module",
asopts = ["-ansi"],
srcs = ["my_module.S"],
)
```
Then the content of generated Makefile is semantically equivalent to:
```
AFLAGS_my_module.o += -ansi
```
linkopts: Similar to `copts` but for linking the module.
For example:
```
ddk_module(
name = "my_module",
linkopts = ["-lc"],
out = "my_module.ko",
# ...
)
```
Then the content of generated Makefile is semantically equivalent to:
```
LDFLAGS_my_module.ko += -lc
```
config: **EXPERIMENTAL**. The parent [ddk_config](#ddk_config) that encapsulates
Kconfig/defconfig.
kconfig: The Kconfig files for this external module.
See
[`Documentation/kbuild/kconfig-language.rst`](https://www.kernel.org/doc/html/latest/kbuild/kconfig.html)
for its format.
Kconfig is optional for a `ddk_module`. The final Kconfig known by
this module consists of the following:
- Kconfig from `kernel_build`
- Kconfig from dependent modules, if any
- Kconfig of this module, if any
For legacy reasons, this is singular and accepts a single target. If multiple `Kconfig`
files should be added, use a
[`filegroup`](https://bazel.build/reference/be/general#filegroup) to wrap the files.
defconfig: The `defconfig` file.
Items must already be declared in `kconfig`. An item not declared
in Kconfig and inherited Kconfig files is silently dropped.
An item declared in `kconfig` without a specific value in `defconfig`
uses default value specified in `kconfig`.
generate_btf: Allows generation of BTF type information for the module.
See [kernel_module.generate_btf](#kernel_module-generate_btf)
autofdo_profile: Label to an AutoFDO profile.
debug_info_for_profiling: If true, enables extra debug information to be emitted to make
profile matching during AutoFDO more accurate.
**kwargs: Additional attributes to the internal rule.
See complete list
[here](https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes).
"""
if textual_hdrs:
# buildifier: disable=print
print("\nWARNING: textual_hdrs deprecated, use `hdrs` instead.")
module_hdrs = (hdrs or []) + (textual_hdrs or [])
ddk_module_config(
name = name + "_config",
parent = config,
defconfig = defconfig,
kconfig = kconfig,
kernel_build = kernel_build,
module_deps = deps,
module_hdrs = module_hdrs,
generate_btf = generate_btf,
**kwargs
)
kernel_module(
name = name,
kernel_build = kernel_build,
srcs = [],
# Set it to empty list, not None, so kernel_module() doesn't fallback to {name}.ko.
# _kernel_module_impl infers the list of outs from internal_ddk_makefiles_dir.
outs = [],
generate_btf = generate_btf,
internal_ddk_makefiles_dir = ":{name}_makefiles".format(name = name),
# This is used in build_cleaner.
internal_module_symvers_name = "{name}_Module.symvers".format(name = name),
internal_drop_modules_order = True,
internal_exclude_kernel_build_module_srcs = True,
internal_ddk_config = name + "_config",
internal_mnemonic = "DDK module",
**kwargs
)
private_kwargs = dict(kwargs)
private_kwargs["visibility"] = ["//visibility:private"]
flattened_conditional_srcs = flatten_conditional_srcs(
module_name = name,
conditional_srcs = conditional_srcs,
**private_kwargs
)
makefiles(
name = name + "_makefiles",
kernel_build = kernel_build,
module_srcs = (srcs or []) + flattened_conditional_srcs,
module_crate_root = crate_root,
module_hdrs = module_hdrs,
module_includes = includes,
module_linux_includes = linux_includes,
module_out = out,
module_deps = deps,
module_local_defines = local_defines,
module_copts = copts,
module_removed_copts = removed_copts,
module_asopts = asopts,
module_linkopts = linkopts,
module_autofdo_profile = autofdo_profile,
module_debug_info_for_profiling = debug_info_for_profiling,
target_type = "module",
top_level_makefile = True,
kbuild_has_linux_include = True,
**private_kwargs
)