Uniform C Code Layout

UCCL - Uniform C Code Layout #

(aka “The Dogfood Layout” 1)

Motivation #

A Uniform code layout facilitates a developer’s access to an unfamiliar code base, saving valuable time and effort. At the same time it makes easier to integrate and reuse different projects.

This layout proposal uses symbolic links, called symlinks for short, to provide a uniform method for access between projects. Symbolic links are used only at directory level, not at file level.

Definitions #

Package ≡ Repository ≈ Library
A unit of software that resides in a unique source control repository. In general a package generates a binary library, unless it is a header-only package. All files in a package are located in one directory (that can have any number of sub-directories).
Development tree
A directory that contains all packages. It’s location is arbitrary but we will assume it is indicated by the $DEV_ROOT environment variable.
Artifacts
Files, other than libraries produced by a package. These can include programs, data files, etc.

Layout Description #

Following is a semi-formal description of the proposed layout.

1. Basic rules #

1.1 Location of header files #

All header files of a package are placed in a subdirectory of the package directory. The name of the subdirectory is include/.

1.2 Location of API header files #

All header files that compose the public API of a package are located in a subdirectory of the include/ directory. The name of the subdirectory must be the same as the package name.

1.3 Location of library files #

All library files are placed in the lib/ subdirectory of $DEV_ROOT directory. Each package directory contains a symbolic link, named also lib/ to this subdirectory. Library variants for different platforms or configurations may be placed in different subdirectories. The naming scheme <platform>/<configuration> is recommended.

With these basic rules, the layout of a package is like this:

2. References to other packages #

2.1 Location of external header files #

A package can reference another package by placing a symlink to its public API in the include folder. The symlink has the same name as the referenced package.

Example: The package package_a references package_b

Libraries created by other packages are implicitly visible in the lib/ directory

3. Other artifacts #

3.1 Location of executable artifacts #

Programs and related components are located in the bin/ subdirectory of the $DEV_ROOT directory. Packages that produce these artifacts as well as packages that need them, should have a symbolic link, also named bin/ pointing to this location.

`$DEV_ROOT/bin` can be added to `PATH` to make these programs accessible from anywhere.

4. Source #

4.1 Location of source files #

Source files are located in the src/ folder of the package directory.

4.2 Referencing header files #

The include/ directory of a package should be added to the compiler search path. If the layout rules have been respected, there should be no ambiguity or name clashing between the different header files.

Example: Consider a filesource.cpp in package_a

// Reference to a package_a API file
#include <package_a/api.h>

// Reference to a package_b API file
#include <package_b/api.h>

//Reference to a package_a internal header file
#include <internal.h>

5. Other directories #

The following directories can appear at the top level in any package, in addition to the ones discussed before. None of them are required.

build/
Used for storing intermediate build artifacts (object files, symbols, etc.)
data/
Any data files required by a package.
docs/
Documentation and documentation-related files.
fab/
build scripts and other build-related files.
examples/
Sample program files.
tests/
Unit tests and other test files.

Recommendations #

The following guidelines are born from experience with this layout.

  1. Minimize the number of files at top level of a package. In general only well-known files (README, LICENSE, AUTHORS, etc.) should be kept at top level. This is not always possible as some toolchains require specific files to be also placed at top level.

  2. Place files related to the used toolchain in a subdirectory of the fab/ directory, with a name that clearly indicates the toolchain used. Some suggested toolset names are:

    • clang/ for CLang compiler
    • gcc/ for GCC tools
    • msc/ for Visual Studio C/C++ compiler
  3. Use a scheme like <platform name>/<toolset>/<configuration> for subdirectories of lib/; keep the library name unchanged.

    Example: $DEV_ROOT/lib/x64/msc/debug/package_a.lib the library name created by package_a when compiled in debug mode for x64 platform using Visual Studio C/C++ compiler.

    Some suggested platform names are:

    • x64
    • x86
    • arm

Layout Summary #

FolderContent
$DEV_ROOT/Root of development tree
   lib/Directory of static link libraries
   package/Package root directory
      build/Build artifacts
      data/Data files
      docs/Documentation files
      examples/Example files
      fab/Build scripts and related files
      include/Header files
         package/Public package header files
      lib/Symlink to $DEV_ROOT/lib directory
      src/Source files
      tests/Test files

  1. Initially I gave this proposal a nice buttoned-down name: Uniform C/C++ Code Layout. It seems however that every self-respecting open source project must have a more or less funky name. Bob Pike named his layout proposal Pitchfork Layout, so I figured this one could be named Dogfood Layout based on the title of the first CodeProject article Eat your Own Dogfood where I describe it. ↩︎