INTRODUCTION --------------------------------------------------------------------- Here will you find coding standards to the project. Requirements are listed in their respective files and can be considered an adendum to this document. CODING STYLE --------------------------------------------------------------------- See linux/Documentation/CodingStyle. Some highlights: - Outside of comments and documentation, never use spaces. Identation is done using tabs only. - Do not use tabs to align text documentation. Changing tab width should not interfere with the layout/alignment of code, only indentation. - functions are declared like this: char *function(const char *test) { } - if you use brackets INSIDE a function put them on the same line Wrong: if (condition) { do_something; } Right: if (condition) { do_something; } - Do not put actions in the same line of conditions: Wrong: if (condition) do_this; Right: if (condition) do_this; - Also it is a good practice to use always brackets in conditions: if (condition) { do_only_this; } - Variables are always in lower case (like tmp_buf) - New defined types are capitalized (like OSyncEngine) - Never use typedefs just to hide pointers - External APIs, used for integration between components may look like this: osync_engine_init() - Always add the osync_ prefix to your functions - Do not return function calls, like "return do_something();", instead, use a auxiliar variable (rationale: easy trace). - When doing error checking, use goto to help creating a single return point. Do not abuse goto usage though... never goto up, never create more than one label and do not "gotoo far". EMACS --------------------------------------------------------------------- For the above indentation rules, you can add the following to your .emacs file: ;; Additional setting to have linux kernel style ;; indentation. Argument lists in next line will only ;; be indented by tabs (defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (defun my-c-mode-hook () (setq indent-tabs-mode t) (c-set-style "linux") (c-set-offset 'arglist-cont-nonempty '(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) ) (add-hook 'c-mode-hook 'my-c-mode-hook ) CODE DOCUMENTATION --------------------------------------------------------------------- * Add FIXME, TODO and XXX comments appropriately. * Use Doxygen (http://www.stack.nl/~dimitri/doxygen/) to document your code * Add your doxygen annotations in the header files. This allows other developers to read the documentation without having installed the source. * Add simple READMEs and pictures as needed, but concentrate on doxygen. * Recommended to watch for leading and trailing empty spaces on comments (vim and emacs can be set to display them). * Try to describe the *why* of a function and not just the *what* (e.g. 'This function is required by module foo because of protocol X. It does something' Some examples: a) Say that you have a function (e.g. foo) and its declaration in a header file (foo.h), a good way to document that would be: - in foo.h /** @brief This functions does something funny. * * Here you can put further details. * * @todo something is missing * * @param bar This is the parameter, please refer \ref bar_func to create * this value. * @return Returns -1 for failure, 0 for success. */ char foo(int bar); - Implementation must be almost free of comments, because it helps to read the code. Only really trick parts should be commented (even better, re-written to be more simple). The wise use of XXX/FIXME/TODO can help a lot to identify hot spots in the code. So, say in foo.c void foo(int bar) { /* TODO: write a unit test */ /* XXX: a dirt trick to workaround a bug in protocol */ /* here goes the code */ /* FIXME: this should be moved to another module */ /* here goes the code */ } CODE INSTRUMENTATION ----------------------------------------------------------------- Always: * Use const; * Use static for internal functions; * Use safe glib functions where possible; * Check validity of all received parameters; * Use osync_assert() while developing; * Do not use alloca() or other non-recommended functions; * Check for return errors even from malloc() and other standard system calls; Regularly: * Use valgrind to profile you application and find memleaks About header files: * Source code has to be split into modules, which are defined as a collection of implementation files (.c) with an interface exported through a header file (.h). * The inclusion (#include) of headers must reflect the dependencies between modules in the implementation. The most important implications from this statement are: . implementation files (.c) *must* include *all* headers it directly depends; . implementation files (.c) *must not* include headers it doesn't directly depend; . headers should include headers only when needing a definition or declaration; . headers should never include other headers just to create a "single point of inclusion". These rules may sound obvious, but they're actually hard to follow. COMMITS AND CHANGELOGS --------------------------------------------------------------------- Descriptive and small. General rules: - *Always* do a svn diff and a svn status before a commit and document the diff(s) in the changelogs; - What matters is not what but why; - Several commits are usually better than a big one; - Do not commit unrelated modifications at once unless they're really trivial; - Commit ASAP. BUILD-SYSTEM --------------------------------------------------------------------- Standard instructions: Code should compile with no warnings, using the following GCC options: -Wall -Werror Recomended but not mandatory (for now): -W -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Wbad-function-cast -Wcast-qual -std=iso9899:1990 -D_FORTIFY_SOURCE=2 Hint: For developers using GCC there is a CMAKE_BUILD_TYPE "hacking" which sets the default compiler flags to a recommended compiler flag set. LOGS and TRACES --------------------------------------------------------------------- There are two types of logs that must be handled by almost all applications: * HIGH-LEVEL LOGS: these are standard, high-level logs usually enabled by default. Useful to advanced users, support-centers and alike. Should include basic information, including but not limited to: - start/end of application - errors - complex operations The requirements document specifies if logs are needed or not. * TRACES: traces are a particular kind of log used to debug the application. They're used mostly by black-box testers to submit failure reports. Traces should be enabled in a per-application basis using an environment variable or at compile time, to be yet defined. UNIT TESTS ----------------------------------------------------------------- * All code should be written together with unit-tests. The tool used to implement the tests is "check", available on http://check.sourceforge.net/. The build-system infra-structure provides a configure option to allow check usage. If possible use the CMake macro ADD_CHECK_TEST to introduce new tests, for building and integration as testrun in the build-environment. When using Make, the test can be locally run via: $ make test Results can also be sent to central testing dashboard CDash at http://opensync.org/testing. Command to run tests and send results to CDash: $ make Experimental The tests must be implemented inside a sub-directory called test with the following modules: check_ --> the test-manager check_ --> implements tests for interfaces exported by unit check_<...> Just to remember, an unit, or module, is a collection of souce-code files (.c) with an interface exported through a header file (.h). All interfaces exported by units must be tested (that is, all non-static functions). The tests should implement at least the following test cases: - standard usage - upper and bottom limits of buffers and variables as needed - error conditions - critical paths Use incremental tests, that is, test functions before using them in other test-cases (for example, if you need to call function A to prepare the environment to test function B, test function A first). Try to write the test-case before the unit or function itself. If the test needs an external entity to work (for example, it needs a device connected on a specific port), put the test inside a condition for an environment variable and document it in a README file.