libfossil  HackersGuide

Hacker's Guide

See also: building, AmalgamationBuild

This page primarily contains notes for anyone who's actively hacking on libfossil internals, e.g. changing fsl_cx's structure.

An exceedingly brief intro to the source tree:

  • The primary "exported" distributable is the AmalgamationBuild. The other sources and headers are essentially components for creating that deliverable.
  • Public headers go in include/fossil-scm/. There is at least one pseudo-private header (fossil-internal.h) there as well, but that one is under consideration for being moved to...
  • Private impl files and headers go in src/.
  • Demo/test apps can be found in f-apps/.
  • The truly adventurous might wand to explore the other various subdirs, like sql/ (schema files), cpp/ (one potential C++ binding) and s2/ (script bindings).
  • The "native" build platform requires GNU Make 3.81 or higher and the conventional set of Unix-side build tools. Contributions of control files (or READMEs) for other build platforms are of course welcomed.
  • The overall style is a direct artifact (as it were) of fossil(1), and we ask that all contributors maintain the general overall style. We are not picky about placement of spaces around parenthesis and whatnot, mainly just the overall indentation and {bracing} styles, as well as the naming style for public symbols (e.g. a prefix of "fsl_" on most symbols). The one notable exception is how APIs are documented: libfossil uses Doxygen style (and Doxygen cannot swallow fossil's native comment style).

An Introduction to structs...

All (or almost all) structs in the library are accompanied by other constructs which help ensure (insure?) consistency regarding how library-level structs are to be initialized. The general pattern looks like this:

struct fsl_foo { ... };
typedef struct fsl_foo fsl_foo;
extern const fsl_foo_empty;
#define fsl_foo_empty_m { ...member initializers... }

The purpose of the "empty_m" macro is to define a cleanly-initialized const state for that struct. The fsl_foo_empty instance is guaranteed to be initialized using fsl_foo_empty_m, but both the "empty" and "empty_m" variants are provided because code requires different initializers in different contexts (examples are provided below).

The "_m" suffix denotes "macro", by the way.

All non-opaque structs can and should be initialized in code like this:

fsl_buffer buf = fsl_buffer_empty;
fsl_deck deck = fsl_deck_empty;

// The "empty_m" macros are required mainly for in-struct initialization:
struct {
  fsl_buffer buf;
  fsl_deck deck;
} foo = {
  fsl_buffer_empty_m,
  fsl_deck_empty_m
};

That ensures (insures?) that all struct members get set to known default values (which need not be NULL/0), and helps harden the code against uninitialized memory access (since all members get initialized to whatever the struct developer deemed sensible, and NULL is generally the sensible default for pointer members).

When updating structs...

When changing the members of structs, it is critical that the accompanying "empty_m" macro (described above) also be updated. Failing to do so can lead to undefined results (best case is a compilation failure when the shared struct "empty" instance is initialized from the "empty_m" macro).

After changing a struct, make sure to do a full/clean rebuild or you might see really weird results in code compiled against older struct.