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.
- 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
includefolder, with the same name as the library name.
my_library/ include/ my_lbrary/ unit1.hpp unit2.hpp .... src/ ....
There is one additional top level symbolic link to a directory called
All executable tools are located in a top level directory called
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
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
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.
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 may require specific files. Most files should be placed in a directory with with a predefined name:
msc- Visual Studio project files
clang- CLang configuration and makefiles
gcc- GNU configuration and makefiles
cmake- 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: