aboutsummaryrefslogtreecommitdiff
path: root/CONTRIBUTING.rst
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-08-31 10:33:58 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-08-31 10:33:58 +0200
commitbbf537f41c10c6b97909d3fea53990d28b292842 (patch)
treecb8b5d69685bce2e55183d98c808bde113e045d3 /CONTRIBUTING.rst
parent2d4bf461ea41cfaab38a2126570d5c810593f391 (diff)
downloadkernel-bbf537f41c10c6b97909d3fea53990d28b292842.tar.xz
kernel-bbf537f41c10c6b97909d3fea53990d28b292842.zip
doc: add additional contributor guidelines
Diffstat (limited to 'CONTRIBUTING.rst')
-rw-r--r--CONTRIBUTING.rst157
1 files changed, 117 insertions, 40 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index a0bfd8f8..7177f2a8 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,22 +1,99 @@
.. sectnum::
-TeachOS C++ Code Style Guide
-============================
+Contributing to TeachOS
+=======================
-This document codifies the C++ coding idioms used throughout the TeachOS kernel.
-It covers language usage, ownership and lifetime models, algorithm selection, error propagation, class design, and naming conventions.
-It does **not** cover token-level formatting, which is enforced automatically by the `.clang-format` configuration.
+Thank you for contributing to TeachOS.
+This document outlines the workflow for developing, testing, formatting, and submitting contributions, as well as the coding idioms enforced across the codebase.
+
+Development Workflow
+--------------------
+
+All **non-thesis** contributions follow a standard GitLab Merge Request workflow against the ``develop`` branch.
+When working on a semester of bachelor's thesis, first create a branch off ``develop`` with the name ``<SEMESTER_PREFIX>/develop`` (e.g., ``fa26/develop`` for a Fall 2026 thesis).
+Create feature branches off the thesis branch and submit Merge Requests against it, tagging your advisor as a reviewer if desired.
+Be prepared to rebase your thesis branch on top of ``develop`` periodically to keep up with changes in the mainline codebase.
+At the end of your thesis, submit a Merge Request from your thesis branch to ``develop`` for final review and integration.
+
+Branching and Merge Requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Base all feature branches off the latest ``develop`` or your thesis development branch.
+2. Keep Merge Requests focused on a single logical change or subsystem feature.
+3. Ensure CI pipelines pass cleanly without warnings or test regressions before requesting review.
+
+Commit Conventions
+~~~~~~~~~~~~~~~~~~
+
+Commit messages should be concise, written in the imperative mood, and scoped using a subsystem prefix:
+
+- Subsystems: ``kernel/vfs: <description>``, ``arch/x86_64: <description>``, ``kapi: <description>``, ``kstd: <description>``
+- Supporting areas: ``doc: <description>``, ``build: <description>``, ``chore: <description>``, ``ide: <description>``
+
+Building and Testing
+--------------------
+
+TeachOS utilizes CMake presets to standardize build and test configurations across local environments and CI runners.
+
+Build-Host Tests (BHT)
+~~~~~~~~~~~~~~~~~~~~~~
+
+Unit and host-executable integration tests use Catch2 with address, leak, and undefined behavior sanitizers enabled by default:
+
+.. code-block:: sh
+
+ cmake --preset bht
+ cmake --build --preset bht-dbg
+ ctest --preset bht-dbg
+
+For thread-safety and race condition analysis under ThreadSanitizer:
+
+.. code-block:: sh
+
+ cmake --preset bht-stress
+ cmake --build --preset bht-stress-dbg
+ ctest --preset bht-stress-dbg
+
+Bootable Kernel Targets
+~~~~~~~~~~~~~~~~~~~~~~~
+
+To build the bootable debug ISO images (e.g. for x86-64):
+
+.. code-block:: sh
+
+ cmake --preset x86_64
+ cmake --build --preset x86_64-dbg
+
+Quality and Compliance Verification
+-----------------------------------
+
+Before submitting a Merge Request, verify the following checks locally, replacing ``<ARCH>`` with the target architecture (e.g., ``x86_64``):
+
+1. **Header Interface Verification**:
+
+ .. code-block:: sh
+
+ cmake --build --preset bht-dbg --target all_verify_interface_header_sets
+ cmake --build --preset <ARCH> --target all_verify_interface_header_sets
+
+2. **Formatting**: Ensure all C++ source and header files are formatted according to ``.clang-format``.
+3. **Static Analysis**: Clang-Tidy runs during compilation when ``TEACHOS_ENABLE_LINTING`` is enabled (on by default). Builds must compile cleanly without warnings.
+4. **License Compliance**: TeachOS adheres to the `REUSE specification <https://reuse.software/>`_. Run ``reuse lint`` to ensure all new files have valid copyright and license metadata (BSD-3-Clause for kernel code, CC0-1.0 for documentation and configuration).
+
+Code Style and Idioms
+----------------------
+
+This subsection outlines the coding idioms and conventions enforced across the TeachOS codebase.
Language Standard and Vocabulary
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TeachOS targets **C++23** without compiler extensions.
Standard library features may be used freely in tests only.
All other parts of the codebase rely on a freestanding variant of the standard library as shipped with the toolchain.
-Types that are not part of the toolchain's freestanding standard library are provided by the `kstd` library.
-Below is an overview of `kstd` replacements to be used in the **non-test** kernel code, including the bundled support libraries.
-This list is does not claim completeness.
-
+Types that are not part of the toolchain's freestanding standard library are provided by the ``kstd`` library.
+Below is an overview of ``kstd`` replacements to be used in the **non-test** kernel code, including the bundled support libraries.
+This list does not claim completeness.
=================== ========================= =======================================
Concept Preferred Replaces
@@ -43,7 +120,7 @@ Common standard library parts that are available in the freestanding implementat
These parts may be used freely throughout the codebase.
Function Declarations — Trailing Return Types
----------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**All** functions and member functions use trailing return type syntax, including those returning `void`.
See the following code snippet for examples:
@@ -64,12 +141,12 @@ This rule applies to: free functions, member functions, lambdas with explicit re
The only exception is constructors and destructors, which have no return type at all.
Parameter Passing Conventions
------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The choice of passing convention encodes intent and must be consistent.
View and cheaply copyable types — pass by value
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Types that are designed to be non-owning views or are trivially copyable must be passed and returned **by value**.
Passing them by ``const &`` is redundant and adds a pointer indirection with no benefit.
@@ -98,7 +175,7 @@ See the following code snippet for examples:
auto resolve(kapi::capabilities::facet_id const & id, std::string_view const & name) -> std::observer_ptr<void>;
Large or non-trivially copyable types — pass by ``const &``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For types that own heap memory or are non-trivially copyable, use ``const &`` when the callee does not take ownership.
@@ -110,7 +187,7 @@ See the following code snippet for examples:
auto add_child(kstd::string const & child_name) -> void;
Sink parameters — pass by value and move
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When a function is designed to take **ownership** of an argument, accept it by value and move it into its destination.
This makes the transfer explicit at the call site.
@@ -129,7 +206,7 @@ See the following code snippet for examples:
}
Mutable subsystem references — pass by non-const reference
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Services and subsystems that are mutated in-place (e.g., ``page_mapper &``, ``driver_state &``, ``kapi::devices::bus &``) are passed by non-const reference.
This expresses that the function operates on a shared, mutable context.
@@ -142,14 +219,14 @@ See the following code snippet for examples:
auto add_directory_entry(inode & directory, inode & child, driver_state & state, write_batch & batch) -> kstd::result<void>;
Error Handling
---------------
+~~~~~~~~~~~~~~
TeachOS kernel code, and all library code used by it, cannot make use of exceptions.
Use of exception related keywords, `try`, `catch`, `throw` in kernel code will cause compilation to fail.
However, exceptions are allowed in test code.
Recoverable errors — ``kstd::result<T>``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Functions that can fail in an expected, recoverable way must return ``kstd::result<T>``.
Use the ``kstd::success()`` and ``kstd::failure()`` helpers consistently.
@@ -184,7 +261,7 @@ See the following code snippet for examples:
Monadic composition (``transform``, ``and_then``, ``or_else``) is preferred over manual if-check-and-return chains when it produces clearer code.
Unrecoverable errors — ``kapi::system::panic``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Violations of kernel invariants (e.g., a subsystem used before being initialized, OOM during boot) call ``kapi::system::panic(...)``.
The ``panic`` function is ``[[noreturn]]``, meaning control will never return from it.
@@ -202,16 +279,16 @@ See the following code snippet for examples:
}
Ownership and Lifetime
-----------------------
+~~~~~~~~~~~~~~~~~~~~~~
Shared ownership — ``kstd::shared_ptr``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use ``kstd::shared_ptr<T>`` when a resource is co-owned by multiple subsystems and its lifetime must be extended by any of them (e.g., ``device``, ``inode``, ``dentry``).
Weak back-references that must not extend lifetime use ``kstd::weak_ptr<T>``.
Non-owning references — ``kstd::observer_ptr`` and raw references
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Use ``kstd::observer_ptr<T>`` to express a non-owning pointer where null is a valid state and the holder has no say in the lifetime of the target.
Use a raw reference (``T &`` or ``T const &``) when null is not valid and the reference is short-lived (i.e. a function parameter or a local alias).
@@ -220,17 +297,17 @@ Never use raw ``T *`` to mean "sometimes I own this, sometimes I don't".
Ownership must be expressed unambiguously through the pointer type.
Device driver data — ``kstd::shared_ptr<void>``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Drivers attach their state to a ``device`` using an untyped ``kstd::shared_ptr<void>`` via ``device::set_driver_data``.
A driver retrieves its state via ``device::driver_data``.
This allows the device tree to destroy driver data automatically when the device is released, without the device knowing the concrete driver type.
Algorithm and Range Usage
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
Prefer ``std::ranges`` algorithms over manual loops
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When processing a range to search, filter, transform, or reduce, use the appropriate ``std::ranges`` algorithm or view pipeline instead of writing a raw ``for`` loop.
@@ -262,7 +339,7 @@ Acceptable uses of explicit loops include:
- Iterator-pair loops in library internals (``kstd::vector``, ``kstd::basic_string``).
Prefer view composition over intermediate containers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Build processing pipelines using ``std::views::filter``, ``std::views::transform``, ``std::views::reverse``, ``std::views::split``, and ``std::ranges::subrange`` rather than materialising intermediate vectors.
@@ -279,7 +356,7 @@ See the following code snippet for examples:
| std::views::transform(transform_module);
Do not call the same function twice to avoid storing the result
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If an intermediate value is needed more than once, store it in a local variable.
This applies especially to factory calls and heap allocations.
@@ -302,17 +379,17 @@ This applies especially to factory calls and heap allocations.
}
Class and Struct Design
------------------------
+~~~~~~~~~~~~~~~~~~~~~~~
Prefer ``struct`` over ``class``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The entire codebase uses ``struct`` for all type definitions with explicit ``private:``
sections where necessary.
Do not introduce ``class``.
Member ordering within a type
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Follow this ordering within a ``struct``:
@@ -347,7 +424,7 @@ See the following code snippet for examples:
};
``explicit`` on single-argument constructors
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Mark every single-argument constructor ``explicit`` unless an implicit conversion is intentional and documented.
A deliberate implicit constructor must be accompanied by a comment explaining the decision.
@@ -366,7 +443,7 @@ See the following code snippet for examples:
constexpr page(chunk other) : chunk{other} {}
Declare deleted special members explicitly
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If a type is not copyable or not movable, declare the deleted special members explicitly rather than relying on implicit suppression.
@@ -378,7 +455,7 @@ See the following code snippet for examples:
auto operator=(device const &) -> device & = delete;
Virtual destructors
-~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^
Every base class with virtual member functions must have a ``virtual`` destructor, defaulted if not otherwise needed.
@@ -390,7 +467,7 @@ See the following code snippet for examples:
virtual ~facet_registry_observer() = default;
Static Singletons
------------------
+~~~~~~~~~~~~~~~~~
Several subsystems expose a single global instance via an ``init()``/``get()`` pair.
Follow this pattern:
@@ -428,7 +505,7 @@ See the following code snippet for examples:
}
Enumerations
-------------
+~~~~~~~~~~~~
All enumerations use ``enum struct`` (scoped enums), never plain ``enum``.
Specify the underlying type explicitly when the representation matters (e.g., for hardware register fields).
@@ -449,7 +526,7 @@ See the following code snippet for examples:
enum state { uninitialized, present, bound };
Naming
-------
+~~~~~~
================================== ============================= =====================================
Symbol Convention Example
@@ -468,7 +545,7 @@ Namespaces ``lower_case`` ``kapi::device
Namespaces reflect directory structure: ``kernel::vfs``, ``kernel::filesystems::ext2``, ``arch::devices``, etc.
``[[nodiscard]]``
------------------
+~~~~~~~~~~~~~~~~~
Mark any function ``[[nodiscard]]`` whose return value the caller should not silently discard.
This includes in particular:
@@ -486,13 +563,13 @@ See the following code snippet for examples:
[[nodiscard]] auto request_resource(resource_type type, std::size_t index = 0) const -> kstd::result<resource>;
``constexpr`` and ``constinit``
--------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mark functions ``constexpr`` whenever they can be evaluated at compile time or in constant expressions, even if they are also called at runtime.
Mark module-scope variables ``constinit`` to guarantee zero-initialization before any dynamic initialization runs.
Documentation
--------------
+~~~~~~~~~~~~~
Every non-trivial public type, function, and data member must be documented with a Doxygen comment using the ``//!`` line style.
@@ -513,7 +590,7 @@ See the following code snippet for examples:
Doxygen grouping (``@name``, ``@{``, ``@}``) may be used to organize the API surface into logical sections visible in generated documentation.
Header Guards
--------------
+~~~~~~~~~~~~~
All headers use traditional include guards, not ``#pragma once``.
The guard name follows the pattern ``TEACHOS_<SUBPACKAGE>_<PATH>_HPP``, where each path component is uppercased and separators are replaced by ``_``.