The Dogfood and the Pitchfork #
Executive Summary #
- We suggest a standard physical interface between different libraries.
- Symbolic links are the easiest way to connect multiple libraries.
- Proposed changes to current PFL architecture are minimal
- A simple tool can manage the build process for many libraries.
Terminology and Rationale #
- UCCL - Uniform C Code Layout as described in this document
- PFL - the “Pitch Fork Layout” as described here.
- library - is the entity defined in section 3 of PFL, i.e. a collection of code exposed to library’s consumer.
- application - or app, is an executable that consumes a number of libraries.
- I - a generic developer who yearns for a simple life 😁
The usage scenario is a typical one: while developing an application I need to use a library. I find the library and I want to integrate it with my app with as little effort as possible.
Objectives:
- I don’t care at this stage about a particular version of the library. I want to grab the main/master branch of the git repo and use it.
- I don’t want to learn all the ins and outs of the new library. I hope to be able to use the public API of the library without diving into all the build subtleties.
- I want to iterate this process: if I create a library that depends on some other libraries, I want to be able to reuse it in another app including all the dependent libraries.
Proposed Changes to PFL #
Only separated header placement is recommended.
The biggest change is this:
All public headers of a library should be placed in a subdirectory of the
include
folder, with the same name as the library name.Sample layout:
my_library/ include/ my_lbrary/ unit1.hpp unit2.hpp .... src/ ....
There is one additional top level symbolic link to a directory called
lib
All executable tools are located in a top level directory called
bin
.
Usage of Symbolic Links #
Now, suppose I need to use another library your_library
that respects the same layout. I need to just place a symbolic link to include/your_library
it in the include/
directory. Denoting symbolic links with angle brackets, the layout is now:
my_library/
include/
my_library/
unit1.hpp
unit2.hpp
<your_library>/
unit1.hpp
unit3.hpp
View From the Source Code #
Assuming that, while compiling my_library
, the include/
folder has been placed on the search path, the source code will look like this:
#include <my_library/unit1.hpp>
#include <your_library/unit1.hpp>
#include <your_library/unit3.hpp>
Note that header file names have been automatically disambiguated. There are no possible conflicts between header file names in my_library
and your_library
Link Phase #
In order to have all link libraries available, there is one more top, top level folder called lib
, containing all the link libraries. Each library and application contains a symbolic link, also called lib
to this common directory.
In the end the full layout is this:
dev_root/
my_library/
include/
my_library/
unit1.hpp
unit2.hpp
<your_library>/
unit1.hpp
unit3.hpp
src/
unit1.cpp
unit2.cpp
<lib>/
my_library.a
your_library.a
your_library/
inlcude/
unit1.hpp
unit3.hpp
src/
unit1.cpp
unit3.cpp
<lib>/
my_library.a
your_library.a
....
lib/
my_library.a
your_library.a
....
The top level bin/
folder is assumed to be on the executable search path, hence all programs and tools placed here are accessible. This is important in environments like Windows where there are no locations guaranteed to be on the path.
Automation #
If the number of libraries involved becomes larger and they start having interdependencies, managing the symbolic links and the build process can become tedious. I have created a simple C/C++ Package Manager for this purpose.
To use it you just need to create a JSON file describing the interdependencies and build process and the tool will take care of all the details.
Toolchains #
Toolchains may require specific files. Most files should be placed in a directory with with a predefined name:
msc
- Visual Studio project filesclang
- CLang configuration and makefilesgcc
- GNU configuration and makefilescmake
- CMakeLists.txt files
The End Result #
With libraries that follow this layout, I can just clone the repositories in the dev_root
folder, create the symbolic links, and build the libraries.
- I have avoided name clashes between different projects
- I don’t need to create submodules or manipulate in any way the tree of other projects.
For more details you can check: