diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-08-31 10:33:58 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-08-31 10:33:58 +0200 |
| commit | bbf537f41c10c6b97909d3fea53990d28b292842 (patch) | |
| tree | cb8b5d69685bce2e55183d98c808bde113e045d3 | |
| parent | 2d4bf461ea41cfaab38a2126570d5c810593f391 (diff) | |
| download | kernel-bbf537f41c10c6b97909d3fea53990d28b292842.tar.xz kernel-bbf537f41c10c6b97909d3fea53990d28b292842.zip | |
doc: add additional contributor guidelines
| -rw-r--r-- | CONTRIBUTING.rst | 157 |
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 ``_``. |
