Contributing

How to contribute

All contributions are welcome.

  • If you have seen a bug or error and you may know how to fix it

  • If you have some suggestions, proposals to add a new feature or improvement

Then, you can contribute by :

  • open an issue

  • open a new pull request

Requirement

The contributions :

  • Has to be entirely compatible with MIT License.

  • Has to conform to our Coding Style Guide

  • Has to follow the requirement from our Documentation Guide

  • Has to be sufficiently commented for people to understand its structure and how its works

  • In case of multiple commits, ensure they are grouped together into logical changes

Coding Style Guide

Conventions

PascalCase convention : a compound word that uses capital letters to delineate words. Includes the first letter.

  • example : SomeName

snake_case convention : when all the words in a compound word are lower case but delimited by an underscore.

  • example : some_name

Directories Names

  • If the directory is a module directory, use ‘pascal case’ convention.

  • If the directory is not a module directory, use ‘snake case’ convention.

Files Names

  • Files name should use the snake_case convention. The first character of the name should be a letter and all others should be letters and/or numbers.

  • C source file names must end in .c

  • C header file names must end in .h

  • Assembler source file names must end in .s

Include files

  • Use the <name> construction for getting them from a standard place, and use “” for custom libraries or define them relative to the current directory.

#include <stdio.h>
#include "my_module_header.h"

Prefer the “include-path” option of the C compiler to handle extensive private libraries of header files. Never use absolute pathnames for header files.

Header file

  • Header files should have preprocessor guards.

#ifndef _FILE_NAME_H_
#define _FILE_NAME_H_
...
#endif // _FILE_NAME_H_
  • Always include check for C++ with extern keyword in header file.

#ifdef __cplusplus
extern "C" {
#endif

/* declarations go here */

#ifdef __cplusplus
}
#endif
Note that the following order should be used :
  • preprocessor guards

  • then any #include statements

  • then extern “C” guards:

Source file

  • Always include check for C++ with extern keyword in header file.

#ifdef __cplusplus
extern "C" {
#endif

/* declarations go here */

#ifdef __cplusplus
}
#endif

Module organization

Softwares modules are organized with src/ and include/ directories, that respectively hold source and header files. In module root folder a “CMakeList.txt” file defines one or more build target in order to be able to build that module. Furthermore, when is required and module is unitary “testable”, a “unittest” directory containing unit test source code and its CMakeList.txt is present.

Typical module look like this :

Module
├── CMakeLists.txt
├── include
│   ├── module_private.h
│   └── module.h
├── src
│   └── module.c
└── unittest
   ├── CMakeLists.txt
   ├── module_mock.yml
   ├── TestGrpRunModule.c
   └── TestModule.c

Naming convention

For simple type, we use the (u)intN_t convention from “stdint.h”. That is to say, the basic type are (u)int8_t, (u)int16_t, (u)int32_t, (u)int64_t, float, double.

Variables Names

The generic rule is that variables follow the PascalCase convention. Furthermore, variables are prefixed with their type.

For simple types, the variables are prefixed with their type in their “short” version :

uint8_t u8MyVar;
int8_t i8MyVar;
uint16_t u16MyVar;
int16_t i16MyVar;
uint32_t u32MyVar;
int32_t i32MyVar;
uint64_t u64MyVar;
int64_t i64MyVar;
float fMyVar;
double dMyVar;

The same rule is applied for structure, enum, pointer and array type.

Example with structure and enum :

my_struct_t sMyVar; /* this a structure */
my_enum_e   eMyVar; /* this an enum */

Example with pointer and array :

uint8_t *pMyVar;    /* this a pointer */
uint8_t aMyVar[nb element]/* this an array */

Note

Pointer is different from array or table in the sense that array is allocated, pointer is not.

Static variable follow the snake_case convention with _ (underscore as prefix and suffix :

static uint8_t _u8_my_static_var_; /* this is my static variable */

Structures Names

Structure declaration follows the snake_case convention suffixed with _s or _t if it is a type:

struct my_struct_s
{
   ...
}

typedef struct
{
   ...
} my_struct_type_t

Structure instantiation follows the PascalCase convention prefixed with s:

struct my_struct_s sMyStruct;
my_struct_type_t sMyStructType;

Enumerate Names

Enumerate declaration follows the snake_case convention suffixed with _e :

enum my_enum_e
{
   ...
}

typedef enum
{
   ...
} my_enum_type_e

Enumerate instantiation follows the PascalCase convention prefixed with e:

my_enum_type_e eMyEnumType;
Functions Names

For public or API functions, its name is prefixed by the module name :

void MyModuleName_MyFunctionName(...)

The module name prefix and function name use the PascalCase convention.

Note

Sometimes, it is necessary or it could be better to be able to identify some structural functionality. In these cases, use a structural name as identifier :

For example, in case of a Board Support Package (BSP). The BSP is not really a single module, but more a collection module. All BSP files are grouped under, let say, “bsp” directory. Some of them will deal with flash memory while other ones will deal with SPI.

Example :

bsp_flash :

BSP_Flash_Erase(...)

bsp_spi :

BSP_Spi_Read(...)

For private or static function, ‘_’ (underscore) is added as prefix and suffix.

static void _my_function_name_(...)

Passed variable in function declaration/implementation must use the PascalCase convention.

void MyModuleName_MyFunctionName(uint8_t u8MyVarName);

void MyModuleName_MyFunctionName(uint8_t u8MyVarName)
{
   ...
}

static void _my_function_name(uint8_t u8MyVarName);


static void _my_function_name(uint8_t u8MyVarName)
{
   ...
}

Other General rules

  • Use C99

  • Do not use tabs, use 4 spaces instead

  • Use 1 space between keywords like if, do, while, switch and opening bracket

    if (condition)
    {
       ...
    }
    
  • Use single space before and after comparison and assignment operators

    uint8_t i = 1;
    if (i == 3)
    {
       ...
    }
    
  • Do not use space between function name and opening bracket

  • Use single space after every comma

  • Do not initialize static and global variables to 0 (or NULL), let compiler do it for you

  • Avoid variable assignment with function call in declaration

  • Always use brackets with sizeof operator

  • Use const for pointer if function should not modify memory pointed to by pointer

  • Use const for function parameter or variable, if it should not be modified

/* When pData could be modified, data pointed to by pData could not be modified */
void my_func(const void* pData) {
...
}

/* When pData should not be modified inside function, only data pointed to by pData
 * could be modified
 */
void my_func(void* const pData) {
...
}

/* When pData and data pointed to by pData both could not be modified */
void my_func(const void* const pData) {
...
}
  • Preprocessor macro use always upper case and, if required, underscore.

    #define MY_DEFINE
    
  • Use // or /* * / block for single line comments.

    // My one line comment
    /* My other one line comment */
    
  • Use /* */ block for multi-line comments.

    /*
     * My multi line comment
     */
    
  • Try to avoid global variable.

  • Always respect code style already used in project or library (even if style is not clear or not obvious)

  • Every function must include doxygen-enabled comment, even if function is static

  • Use English names/text for functions, variables, comments

  • Commits should only contain files with LF (Unix style) endings.

Documentation Guide

Files

Each source and header file must begin with the following :

 1/*!
 2  * @file filename.[c/h]
 3  * @brief A brief file description
 4  *
 5  * @details Optionaly, describe details about this file
 6  *
 7  * @copyright 2022, Company Name, Inc.  All rights reserved.
 8  *
 9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted (subject to the limitations in the disclaimer
11  * below) provided that the following conditions are met:
12  *    - Redistributions of source code must retain the above copyright notice,
13  *      this list of conditions and the following disclaimer.
14  *    - Redistributions in binary form must reproduce the above copyright
15  *      notice, this list of conditions and the following disclaimer in the
16  *      documentation and/or other materials provided with the distribution.
17  *    - Neither the name of GRDF, Inc. nor the names of its contributors
18  *      may be used to endorse or promote products derived from this software
19  *      without specific prior written permission.
20  *
21  *
22  * @par Revision history
23  *
24  * @par 1.0.0 : 2021/03/01 [ABC]
25  * Initial version
26  *
27  * @par 1.0.1 : 2022/03/01 [CBA]
28  * Changes in function X to take into account the feature Z
29  *
30  */

Revision history is formatted as follow :

* @par MAJ.MIN.REV : DATE [AUTHOR]
* Describe the revisions changes

Where, “MAJ.MIN.REV” is respectively major, minor and revision version number. This must be followed by the date and author. Then on the next line, describe the revision changes.

Functions

All functions must begin with its documentation block :

/*!
  * @brief Brief documentation of this function
  *
  * @details Optionaly, give some details about this function.
  *
  * @param [in]      in_param    Input Parameter description.
  * @param [out]     out_param   Output Parameter description.
  * @param [in, out] inout_param Input/Output Parameter description.
  *
  * @retval return value x if everything is fine
  *         return value y if something goes wrong
  */

If the function is private add @static at the begining, just before @brief.

/*!
  * @static
  * @brief ...
  *

If required, place some documentation part under @cond block.

/*!
 * @cond INTERNAL
 * @{
 */

Here the content that should appear in documention only if INTERNAL is defined

/*!
 * @}
 * @endcond
 */

In order to organize the documentation, add a group to your module.

/*!
 * @addtogroup groupe_name
 * @{
 *
 */

   ... body of this module ...

/*! @} */

Structures and enumeration

/*!
 * @brief Describe your enum
 */
typedef enum{
   ENUM_0,        /*!< Enum 0 decritption */
   ENUM_1,        /*!< Enum 0 decritption */

   /* Optionaly add a "marker" */
   ENUM_NB
} my_enum_e;
/*!
 * @brief Describe your structure
 */
typedef struct  {
   uint8_t u8Field1;  /*! Field 1 description */
   int16_t i16Field2; /*! Field 2 description */
} my_struct_t
/*!
 * @brief
 */
#define MY_DEFINE 1
/*!
 * @brief
 */
extern my_struct_t sMyStruct;
/*!
 * @static
 * @brief
 */
static my_struct_t _my_struct_;