mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 00:49:45 +01:00
afb16bb705
CODING_CONVENTIONS.md: Add IWYU policy
503 lines
17 KiB
Markdown
503 lines
17 KiB
Markdown
# Coding Conventions
|
|
|
|
## General
|
|
|
|
* Code shall be [C11](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)
|
|
compliant.
|
|
* Avoid dynamic memory allocation (malloc/free, new, etc.)! It will break
|
|
real-time guarantees, increase code complexity, and make it more likely to use
|
|
more memory than available.
|
|
* Avoid the use of floating point arithmetic. Not every MCU has a FPU and software
|
|
floating point libraries cause unnecessary overhead.
|
|
Instead use fixed-point integers and transform equations so that they stay within
|
|
the range of integer math.
|
|
An easy way to ensure this is by multiplying by a constant factor, ideally a power
|
|
of two - this is a simple shift operation.
|
|
Take care that intermediate values do not exceed the range of the data type you are using.
|
|
When writing drivers, do not convert the measurement data into float, but instead
|
|
choose an appropriate integer format / SI prefix.
|
|
* Please obey the Linux coding style as described in
|
|
https://www.kernel.org/doc/Documentation/process/coding-style.rst with the
|
|
following modifications and additions:
|
|
* Line length: aim for no more than 80 characters per line, the absolute
|
|
maximum should be 100 characters per line.
|
|
* All line endings shall be set to LF (`\n`). (How to handle line endings in
|
|
Git: https://help.github.com/articles/dealing-with-line-endings)
|
|
* There must be no trailing whitespace in any line.
|
|
The script `/dist/tools/whitespacecheck/check.sh master || exit` can be
|
|
used to detect whitespaces at the end of line(s) that would lead to
|
|
*Murdock* build error(s).
|
|
* Use empty braces for empty while loops waiting for a hardware register
|
|
instead of semicolon.
|
|
`while (HW_STATUS != STATUS_OK) {}` is correct,
|
|
`while (HW_STATUS != STATUS_OK);` is wrong.
|
|
* Our policy regarding `typedef`s is
|
|
[completely different](https://www.kernel.org/doc/html/latest/process/coding-style.html#typedefs)
|
|
(see below) (BTW: Do we have any reason to do so?)
|
|
* Comments should be C-style comments (see below)
|
|
* In order to follow Linux's recommendation on
|
|
[conditional compilation](https://www.kernel.org/doc/html/latest/process/coding-style.html#conditional-compilation)
|
|
make use of `IS_ACTIVE` and `IS_USED` macros from `kernel_defines.h` with C
|
|
conditionals. If a symbol is not going to be defined under a certain
|
|
condition, the usage of preprocessor `#if defined()` is fine.
|
|
* You can use [uncrustify](http://uncrustify.sourceforge.net/) with the provided
|
|
option files: https://github.com/RIOT-OS/RIOT/blob/master/uncrustify-riot.cfg
|
|
|
|
## Types
|
|
|
|
* Be careful with platform dependent type sizes like `int` or `long`. Use data
|
|
types that include the bit length in the name like `uint16_t` when you need to
|
|
make sure that a certain variable is exactly of this length.
|
|
* The use of typedefs for structs and pointers is allowed.
|
|
* Type definitions (using `typedef`) always end on "_t".
|
|
* If a typedef is used for a struct, it has to be specified at the struct
|
|
definition (i.e., not as a separate line). E.g.:
|
|
```
|
|
typedef struct {
|
|
uint8_t a;
|
|
uint8_t b;
|
|
} foobar_t;
|
|
```
|
|
* Use of a separate line typedef for structs is allowed for forward
|
|
declarations, e.g.,
|
|
```
|
|
typedef struct mystruct mystruct_t;
|
|
[...]
|
|
struct mystruct {
|
|
[...]
|
|
};
|
|
```
|
|
* Guidelines for pointer types (as long as it is reasonable):
|
|
- use `char *` for strings and only for strings
|
|
- use `uint8_t[]` as type for arbitrary byte buffers, but use `void *` to
|
|
pass them around. `uint8_t[]` because we're dealing with bytes and not
|
|
characters, `void *` to avoid unnecessary casting shall the need arise to
|
|
have their content to have a certain type
|
|
- use `uint8_t *` to pass "typed" byte buffers, e.g., link-layer addresses,
|
|
where it avoids unnecessary temporary variable
|
|
- use `void *` for generic typing
|
|
|
|
## Variables
|
|
|
|
* Do NOT use global variables unless it is unavoidable.
|
|
* If you declare a variable within a header file, you MUST use the keyword
|
|
`extern`.
|
|
|
|
## Functions
|
|
|
|
* Every function needs a prototype in addition to its definition. If a prototype
|
|
is specified within a .c file it has to be declared BEFORE any function
|
|
definitions.
|
|
* If the scope of a function is limited to one file, it MUST be declared static.
|
|
* Functions without parameters must be specified with `(void)`.
|
|
* Keep functions short! As a rule of thumb, the function's body should not
|
|
exceed one screen.
|
|
* Do NOT use global macros defining more than one line of code. Use inline
|
|
functions instead.
|
|
|
|
## Return values
|
|
|
|
* Every function must return one of the following values or none (void):
|
|
* logical value (zero or not zero)
|
|
* an error code (given as a negative number or zero) or a positive status
|
|
value
|
|
* the count of read or written bytes/values for I/O functions
|
|
* the position or address (for search functions)
|
|
* a pointer
|
|
* `NULL` indicates an error case, too.
|
|
* Do NOT return structs or other larger types! These would get copied to the
|
|
stack, resulting in expensive operations. Moreover, some compilers have
|
|
trouble with larger return types. Use pointers to structs instead and take
|
|
care of the structs lifetime.
|
|
* If possible, prefer signed types over unsigned ones in order to be able to add
|
|
error codes later on.
|
|
|
|
## Naming
|
|
|
|
* Names of all public functions and variables must start with the name of the
|
|
corresponding library, e.g.:
|
|
```
|
|
thread_getpid(void);
|
|
hwtimer_init_comp(uint32_t fcpu);
|
|
int transceiver_pid;
|
|
```
|
|
* Private functions and variables do NOT have this library prefix.
|
|
* Do NOT use CamelCase. Function, variable and file names as well as enums,
|
|
structs or typedefs are written in lowercase with underscores.
|
|
```
|
|
/* instead of: */
|
|
void CamelCaseNamedFunction(int camelCaseNamedVar);
|
|
|
|
/* write: */
|
|
void camel_case_named_function(int camel_case_named_var);
|
|
```
|
|
* When implementing constants or variables that are defined in third party
|
|
documents such as RFCs, add a prefix to those names based on the RIOT coding
|
|
conventions. If you use a name in the RIOT code that is different from the one
|
|
in the third party document, you must add a reference to the original name of
|
|
the constant or variable in the Doxygen documentation.
|
|
|
|
## Indentation and braces
|
|
|
|
* Indentations are four spaces (i.e., NO tab characters).
|
|
* As an exception to the Linux coding style, the closing brace is empty on a
|
|
line of its own when followed by an else, too. When followed by a `while` in a
|
|
`do`-statement, it goes into the same line.
|
|
* Use curly braces even for one-line blocks. This improves debugging and later
|
|
additions.
|
|
```
|
|
/* instead of: */
|
|
if (debug) println("DEBUG");
|
|
else println("DEBUG ELSE");
|
|
|
|
/* write: */
|
|
if (debug) {
|
|
println("DEBUG");
|
|
}
|
|
else {
|
|
println("DEBUG ELSE");
|
|
}
|
|
```
|
|
* Commas are always followed by a space.
|
|
* For complex statements it is always good to use more parentheses - or split up
|
|
the statement and simplify it.
|
|
|
|
## Indentation of Preprocessor Directives
|
|
|
|
Add two spaces of indent *after* the `#` per level of indent. Increment the
|
|
indent when entering conditional compilation using `#if`/`#ifdef`/`#ifndef`
|
|
(except for the include guard, which does not add to the indent). Treat indent
|
|
for C language statements and C preprocessor directives independently.
|
|
|
|
```
|
|
/* BAD: */
|
|
#if XOSC1
|
|
#define XOSC XOSC1
|
|
#define XOSC_NUM 1
|
|
#elif XOSC2
|
|
#define XOSC XSOC2
|
|
#define XOSC_NUM 2
|
|
#endif /* XOSC1/XOSC2 */
|
|
```
|
|
|
|
```
|
|
/* GOOD: */
|
|
#if XOSC1
|
|
# define XOSC XOSC1
|
|
# define XOSC_NUM 1
|
|
#elif XOSC2
|
|
# define XOSC XSOC2
|
|
# define XOSC_NUM 2
|
|
#endif
|
|
```
|
|
|
|
```
|
|
/* BAD: */
|
|
void init_foo(uint32_t param)
|
|
{
|
|
(void)param;
|
|
#if HAS_FOO
|
|
switch (param) {
|
|
case CASE1:
|
|
do_foo_init_for_case1;
|
|
break;
|
|
#if HAS_CASE_2
|
|
case CASE2:
|
|
do_foo_init_for_case2;
|
|
break;
|
|
#endif
|
|
#endif
|
|
}
|
|
```
|
|
|
|
```
|
|
/* GOOD: */
|
|
void init_foo(uint32_t param)
|
|
{
|
|
(void)param;
|
|
#if HAS_FOO
|
|
switch (param) {
|
|
case CASE1:
|
|
do_foo_init_for_case1;
|
|
break;
|
|
# if HAS_CASE_2
|
|
case CASE2:
|
|
do_foo_init_for_case2;
|
|
break;
|
|
# endif
|
|
#endif
|
|
}
|
|
```
|
|
|
|
### Reasoning
|
|
|
|
Adding the indent does improve readability a lot, more than adding comments.
|
|
Hence, we prefer the indent to allow reviewers to quickly grasp the structure
|
|
of the code.
|
|
|
|
Adding spaces before the `#` is not in compliance with the C standard (even
|
|
though in practice compilers will be just fine with whitespace in front), but
|
|
adding spaces afterwards is standard compliant. In either case, having the `#`
|
|
at the beginning of the line makes it visually stand out from C statements,
|
|
which eases reading the code.
|
|
|
|
Using an indent width of 2 makes preprocessor directives visually more
|
|
distinctive from C code, which helps to quickly understand the structure
|
|
of code.
|
|
|
|
## Includes
|
|
|
|
* The include of system headers (in <>-brackets) always precedes RIOT specific
|
|
includes (in quotes).
|
|
* Optional headers must only be included if their corresponding module is
|
|
selected/being build. In other words: always put an `#ifdef MODULE_...`
|
|
statement around includes of optional headers:
|
|
```c
|
|
#ifdef MODULE_ABC
|
|
# include "abc.h"
|
|
#endif
|
|
```
|
|
|
|
### Include What You Use (IWYU)
|
|
|
|
`#include` directives that are not actually needed should be removed to reduce
|
|
clutter and improve compilation speed. Similar: Try to add the corresponding
|
|
`#include`s for all the functions, macros, types, etc. used and do not rely on
|
|
`bar.h` to implicitly include `foo.h`, unless this is documented behavior.
|
|
|
|
Tools such as [clang's Include Cleaner][clangd-include-cleaner] can help with
|
|
that. These tools may show false positives in cases where headers are *expected*
|
|
to be included indirectly: E.g. if `foo.h` is the public header that contains
|
|
common helpers and implementations, but a per platform `foo_arch.h` is included
|
|
from within `foo.h` for platform specific implementations. If in this scenario
|
|
only functions provided by `foo_arch.h` are included, the `#include` of `foo.h`
|
|
is considered as unused. To avoid this, one should add
|
|
[`/* IWYU pragma: export */`](https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md) after `#include "foo_arch.h"` in `foo.h`.
|
|
|
|
[clangd-include-cleaner]: https://clangd.llvm.org/design/include-cleaner
|
|
|
|
## Header Guards
|
|
|
|
All files are required to have header guards of the form
|
|
|
|
```c
|
|
#ifndef PATH_TO_FILE_FILENAME_H
|
|
#define PATH_TO_FILE_FILENAME_H
|
|
|
|
...
|
|
#endif /* PATH_TO_FILE_FILENAME_H */
|
|
```
|
|
|
|
Rules for generating the guard name:
|
|
|
|
1. take the file name
|
|
2. if there's ```include/``` in the file's pathname, include the path from there
|
|
on.
|
|
3. replace "/" and "." with "_"
|
|
4. convert to uppercase letters
|
|
5. if the produced guard starts with "_", prefix "PRIV"
|
|
|
|
Examples:
|
|
|
|
- "core/include/msg.h" -> "MSG_H"
|
|
- "sys/include/net/gnrc/pkt.h" -> NET_GNRC_PKT_H
|
|
- "drivers/abcd0815/abcd0815_params.h" -> ABCD0815_PARAMS_H
|
|
- "sys/module/_internal.h" -> PRIV_INTERNAL_H
|
|
|
|
Note: these rules will be enforced by the CI.
|
|
|
|
## C++ compatibility
|
|
|
|
* C Header files should be always wrapped for C++ compatibility to prevent
|
|
issues with name mangling, i.e. mark all the containing functions and
|
|
definitions as `extern "C"`
|
|
``` C
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
... all your function declarations, global variables and defines belong here
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
```
|
|
|
|
* use `__restrict` instead of `restrict` in headers (compare
|
|
https://github.com/RIOT-OS/RIOT/pull/2042)
|
|
|
|
## Absolute values
|
|
|
|
* Absolute values must be specified as macros or enums, not as literals, i.e.
|
|
instead of
|
|
```
|
|
int timeout = 7 * 1000000;
|
|
```
|
|
write
|
|
```
|
|
int timeout = TIMEOUT_INTERVAL * USEC_PER_SEC;
|
|
```
|
|
## Comments
|
|
* All comments should be written as C-style comments.
|
|
|
|
E.g:
|
|
```
|
|
/* This is a C-style comment */
|
|
```
|
|
Wrong:
|
|
```
|
|
// C++ comment here
|
|
```
|
|
|
|
## Documentation
|
|
|
|
* All documentation must be in English.
|
|
* All files contain the copyright note and the author.
|
|
* Doxygen documentation is mandatory for all header files.
|
|
* Every header file includes a general description about the provided
|
|
functionality.
|
|
* Every function must be documented - including parameters and return value.
|
|
|
|
An exemplary doxygen documentation in a header file can look like this.
|
|
|
|
```
|
|
/*
|
|
* Copyright (C) 2014 Peter Schmerzl <peter@schmerzl-os.org>
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser General
|
|
* Public License v2.1. See the file LICENSE in the top level directory for more
|
|
* details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup foobar
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Definitions for foo and bar functions.
|
|
*
|
|
* More detailed information about the file and the functionality implemented.
|
|
*
|
|
* @author Peter Schmerzl <peter@schmerzl-os.org>
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @brief Set the state of foobar.
|
|
*
|
|
* @param[in] state The new state of foobar.
|
|
* @param[out] old_state The old state of foobar is written to this variable.
|
|
*
|
|
* @return 1 if setting the state was successful, 0 otherwise.
|
|
*/
|
|
int set_foobar(int state, int *old_state);
|
|
```
|
|
|
|
## Common compilation warnings
|
|
|
|
Some solutions to correctly handle compilation warnings.
|
|
|
|
### -Wformat
|
|
|
|
Solution for string formatting errors:
|
|
|
|
* When printing a `size_t`
|
|
* use `PRIuSIZE` from `architecture.h` because `newlib-nano` does not support `%zu`
|
|
* When printing an `unsigned char/uint8_t`
|
|
* Use `%u` because `newlib-nano` does not support `%hu/PRIu8`
|
|
[example](https://github.com/RIOT-OS/RIOT/pull/4851)
|
|
* When printing an `uint32_t`
|
|
* Use `PRIu32` print format
|
|
[example](https://github.com/RIOT-OS/RIOT/blob/4c74db4e7a6cf1e3be1edb3c10cdba14ba032513/drivers/sx127x/sx127x_getset.c#L126)
|
|
* When printing `64bit` variables
|
|
* It is not correctly supported by `newlib-nano` as
|
|
[said here](https://github.com/RIOT-OS/RIOT/issues/1891). It is
|
|
recommended to use `fmt` module for these.
|
|
[Example](https://github.com/RIOT-OS/RIOT/blob/e19f6463c09fc22c76c5b855799054cf27a697f1/tests/posix_semaphore/main.c#L277)
|
|
|
|
### -Wformat-nonliteral
|
|
|
|
For a `printf` style function with the following error: `error: format string is
|
|
not a string literal`.
|
|
|
|
* Function using a variable number of arguments:
|
|
* Use `__attribute__((__format__ (__printf__, 3, 4)))`, where here `3` is
|
|
the number of the argument with the format and `4` the format arguments,
|
|
starting from 1. See
|
|
[example](https://github.com/miri64/RIOT/blob/d6cdf4d06f2aeed05dcf86a5437254e2403e147b/pkg/openthread/contrib/platform_logging.c#L31-L32)
|
|
* Function using `va_list`:
|
|
* Use `__attribute__((__format__ (__printf__, 1, 0)))`, where here `1` is
|
|
the number of the argument with the format and `0` as there is no variable
|
|
numbers of arguments. See
|
|
[example](https://github.com/miri64/RIOT/blob/ad133da2096c44e001ee65071cb36db60a54e215/cpu/native/syscalls.c#L268-L271)
|
|
|
|
|
|
## Git
|
|
|
|
* Make one commit per change.
|
|
* The first line of the commit message describes the main feature of the commit.
|
|
|
|
## Continuous Integration
|
|
* If the CI tests fail due to errors these errors need to be addressed.
|
|
* If the CI tests fail due to warnings/errors emitted by cppcheck you should try
|
|
to fix the error. If the error is definitely a false positive there is the
|
|
possibility to suppress this warning/error. You MUST do so by adding a
|
|
comment, including a rationale why it is a false positive and why the code
|
|
can't be fixed otherwise, in the following format:
|
|
```
|
|
/* cppcheck-suppress <category of error/warning>
|
|
* (reason: cppcheck is being really silly. this is certainly not a
|
|
* null-pointer dereference */
|
|
```
|
|
|
|
## Python coding convention
|
|
|
|
* Code shall be compliant with Python 3.5 minimum because this is the default
|
|
Python 3 version in Ubuntu 16.04 (used as the reference system for CI)
|
|
* Code shall report no error when running the
|
|
[Flake8](http://flake8.pycqa.org/en/latest/) tool, e.g:
|
|
* for style checks described in
|
|
[PEP8](https://www.python.org/dev/peps/pep-0008/),
|
|
* for lint checks provided by
|
|
[Pyflakes](https://pypi.python.org/pypi/pyflakes),
|
|
* for complexity checks provided by the
|
|
[McCabe project](https://pypi.python.org/pypi/mccabe)
|
|
* A line length of maximum of 120 is allowed instead of the pep8 79: this
|
|
increases tests readability as they can expects long line of output.
|
|
* Only runnable scripts shall start with `#!/usr/bin/env python3`
|
|
* Runnable scripts shall use the following scheme:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
|
|
# Copyright (C) <your copyright>
|
|
#
|
|
# This file is subject to the terms and conditions of the GNU Lesser
|
|
# General Public License v2.1. See the file LICENSE in the top level
|
|
# directory for more details.
|
|
|
|
# put the module imports first
|
|
# see https://www.python.org/dev/peps/pep-0008/#imports
|
|
# for more details
|
|
import module1
|
|
import module2
|
|
|
|
# Optional global variables
|
|
GLOBAL_VARIABLE = "I'm global"
|
|
|
|
|
|
# local functions, if required
|
|
def local_func():
|
|
# Put your local function code here
|
|
|
|
|
|
# The main function
|
|
def main_func():
|
|
# Put your main code here
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Call the main function from here:
|
|
main_func()
|
|
```
|