Thank you for considering a contribution to Koilo Engine. This document explains the rules every pull-request must follow.
Koilo Engine is a header / source c++17 library that targets
- Arduino-class mcus (avr, cortex-m, esp32)
- Bare-metal embedded c++
- Desktop (clang / gcc / msvc)
Install the following tooling before you begin:
- CMake 3.21+ and a C++17 compiler (clang, gcc, or msvc)
- Python 3.10+ for code generation scripts under
scripts/
-
Fork & Clone
git clone https://github.com/coelacant1/koiloengine.git cd koiloengine -
Create a Topic Branch:
git checkout -b feat/<short-topic>(use prefixesfeat/,fix/,docs/, orrefactor/). -
Configure & Build (desktop/native)
cmake -S . -B build cmake --build build -j
Running ./build.sh is an alternative wrapper that performs the same steps plus code generation.
-
Run Tests
cmake --build build -j --target koilo_tests cd build ctest --output-on-failure -
Make Changes
-
Follow the coding rules below, including Doxygen comment requirements.
-
Regenerate reflection bindings if you touched registry descriptors:
python scripts/generatekoiloall.py
-
-
Commit & Push
git commit -m "Feat: Short summary (fixes #123)" git push --set-upstream origin feat/<short-topic>
-
Open a Pull Request against
main, filling in the template checklist.
- Lower-case, no underscores:
triangle2d.hpp,triangle2d.cpp.
- No namespaces for now. Every type lives in the root scope - this may change later.
| item | style example |
|---|---|
| Function names | ComputeNormals() |
| Member vars | float edgeLength; |
| Local vars | float baryCenter; |
| Public vars | float BaryCenter; |
| Braces | void Foo() { |
All contributions must retain and extend the project-wide Doxygen documentation. Follow these rules:
- File header block - start every translation unit and header with a
/** ... */block containing at least:
@file- canonical file name@brief- one-sentence summary- Optional supporting lines (
@date,@version,@author, etc.) - Additional prose paragraphs may follow the tagged lines to explain context or usage.
- Class / struct documentation - place a
/** */block immediately above each type declaration with:
@class(or@struct) and@brief- Optional
@detailssection describing design intent, invariants, ownership rules, threading expectations, etc. - Enumerate important invariants as bullet points inside the comment when relevant.
- Functions and methods - each member function (including constructors/destructors) requires:
@brief@detailswhen behaviour warrants explanation beyond one sentence@paramtags for every parameter (use[in],[out],[in,out]annotations when clarity helps)@returnfor non-voidfunctions, describing ownership and lifetime semantics of returned pointers/references.
-
Data members - add
///<trailing comments (or preceding/** */blocks when multiline description is required) explaining meaning, ownership, ranges, and invariants. -
Do not remove existing documentation. You may tighten wording or reflow text, but never drop semantic information. When existing comments become outdated, update them instead of deleting.
-
Minimalism is fine. If behaviour is obvious, keep the comment concise but still present.
/**
* @file Foo.hpp
* @brief Short summary of the file contents.
*
* Additional paragraphs may elaborate on runtime expectations, usage scenarios,
* or references to related modules.
*/
/**
* @class Foo
* @brief Summarises the role of the type.
* @details Optional multi-line description including invariants such as:
* - bullet points for ownership rules
* - constraints on method call ordering
*/
class Foo {
public:
/**
* @brief Construct a Foo from a data source.
* @param[in] source Pointer to valid source data (must not be nullptr).
*/
explicit Foo(const Source* source);
/**
* @brief Process the stored data.
* @param[in] delta Time step in seconds.
* @return Resulting processed value (caller owns the copy).
*/
Result Process(float delta);
private:
Result cache; ///< Cached output reused across frames.
};Use this skeleton as the baseline for new files and when refactoring existing ones.
- Only
<stdint.h>,<stddef.h>,<math.h>, and other c++17 headers. - Never
Arduino.hin public headers. Use it only inside#if defined(ARDUINO)guards in.cpp.
Headers under engine/koilo/ are automatically scanned by the code-generation
scripts (scripts/generatekoiloall.py, scripts/generatereflectionentry.py).
Files that contain certain C++ constructs are silently excluded from reflection.
The following rules apply to any .hpp that should be accessible from KoiloScript:
| Construct | Effect | Workaround |
|---|---|---|
template<...> anywhere in the file |
Excluded - even in comments, using aliases, or unused specializations |
Move template code to a separate non-reflected header, or refactor to runtime sizing |
virtual keyword anywhere in the file |
Excluded - even in comments (// virtual dispatch) |
Use "polymorphic" or "overridable" in comments; remove virtual methods from script-facing classes |
The filter uses word-boundary regex (\btemplate\s*< and \bvirtual\b), so:
// This replaces the old virtual interface-> triggers filter (use "polymorphic" instead)template <size_t N> using Alias = Concrete;-> triggers filter (remove the alias)std::vector<Color888>-> safe (notemplatekeyword)
Quick check: Run python3 scripts/validate_reflection_headers.py to see which files
are currently excluded and why.
- Prefer stack allocation and deterministic lifetimes. Avoid heap churn inside real-time loops.
std::vectorandstd::stringare allowed when they meaningfully reduce complexity, but:- Reserve capacity up front (
reserve()/resize()), - Reuse instances instead of re-allocating per frame,
- Do not perform dynamic growth inside the hot path of render or control loops.
- Reserve capacity up front (
- For predictable workloads (tight MCU loops, DSP kernels), favour fixed-size containers or caller-supplied buffers.
- Never leak ownership; pair each
newwith strict RAII wrappers or smart pointers. Rawnew/deleteshould be avoided outside low-level allocators.
Format your code before committing:
clang-format -i path/to/your/file.cppA .clang-format file is provided at the repository root. Most IDEs can auto-format on save.
-
Every feature or bug fix must include coverage in the desktop suite (
koilo_tests) or KoiloScript language tests (koilo_script_language_tests). -
Desktop workflow
cmake --build build -j --target koilo_tests cd build ctest --output-on-failure -
Add or update tests under
tests/(Unity-based) as appropriate. Tests should be deterministic and avoid timing-based assertions.
- Keep commits atomic and focused on one logical change.
- Prefix messages with conventional tags:
Feat:for new functionalityFix:for bug fixesRefactor:for structural changesDocs:for documentation-only updates
- Suffix with issue references where applicable:
Fix: Stabilise quadtree build (fixes #231). - Re-run formatting (
clang-format) and relevant generators before committing.
- Squash noisy WIP commits before requesting review.
- Update CHANGELOG.md for user-visible changes.
- Include summaries of:
- Why the change is needed
- How it was implemented (mention scripts or regeneration steps)
- The verification performed (build + test commands)
Before marking a PR ready for review, confirm the following:
-
cmake --build build -jsucceeds without warnings introduced by your change. -
ctest --output-on-failure(and, if applicable, relevantpio testinvocations) pass. - Doxygen headers/classes/methods are present and updated; no documentation removed.
- Reflection/code generation scripts (
scripts/generatekoiloall.py, etc.) have been run when registry descriptors changed. -
clang-format -ihas been applied to touched C++ files. - CHANGELOG entries, README snippets, and sample code updated when behaviour changes.
- New files follow the canonical skeleton from section 3.4.
- No stray debugging output, TODOs, or commented-out code remain.
Attach the checklist (checked items) in the PR description.
- The maintainer reviews every pull request.
- Feel free to push follow-up commits.
- When CI passes and approvals are given, the maintainer will merge your PR.
Again, thank you for considering to contribute or contributing. Open an issue any time you need clarification.