``dds`` has been designed from the very beginning as an extremely opinionated | ``dds`` has been designed from the very beginning as an extremely opinionated | ||||
hybrid *build system* and *package manager*. Unlike most build systems however, | hybrid *build system* and *package manager*. Unlike most build systems however, | ||||
``dds`` has a hyper-specific focus on a particular aspect of software | ``dds`` has a hyper-specific focus on a particular aspect of software | ||||
development: C++ libraries. | |||||
development: C and C++ libraries. | |||||
This may sound pointless, right? Libraries are useless unless we can use them | This may sound pointless, right? Libraries are useless unless we can use them | ||||
to build applications! | to build applications! | ||||
Indeed, applications *are* essential, but that is "not our job." | |||||
Indeed, applications *are* essential, but that is "not our job" with ``dds``. | |||||
Another design decision is that ``dds`` is built to be driven by automated | Another design decision is that ``dds`` is built to be driven by automated | ||||
tools as well as humans. ``dds`` will not build your AAA console game, nor will | |||||
it compile an OS kernel. Instead, the build system of your AAA console game or | |||||
OS kernel can *use* ``dds``. | |||||
tools as well as humans. ``dds`` is not designed to entirely replace existing | |||||
build systems and package management solutions. Rather, it is designed to be | |||||
easy to integrate *with* existing systems and tools. | |||||
Background | Background | ||||
********** | ********** | ||||
I'm going to say something somewhat controversial: C++ doesn't need "package | |||||
management." At least, not *generalize* "package management." C++ needs | |||||
*library* "package management." | |||||
I'm going to say something somewhat controversial: C and C++ don't need | |||||
"package management." At least, not *generalized* "package management." C++ | |||||
needs *library* "package management." | |||||
The C and C++ compilation model is inherently *more complex* than almost any | The C and C++ compilation model is inherently *more complex* than almost any | ||||
other language in use today. This isn't to say "bad," but rather than it is | other language in use today. This isn't to say "bad," but rather than it is | ||||
different, despite both using the same underlying "Build System." | different, despite both using the same underlying "Build System." | ||||
``dds`` takes a massive divergence at this point. One project using ``dds`` as | ``dds`` takes a massive divergence at this point. One project using ``dds`` as | ||||
their build system has an identical build process to every other project using | |||||
``dds``. Simply running :code:`dds -F` is enough to build *any* ``dds`` | |||||
project. | |||||
their build system has a nearly identical build process to every other project | |||||
using ``dds``. Simply running :code:`dds build -t <toolchain>` should be enough | |||||
to build *any* ``dds`` project. | |||||
In order to reach this uniformity and simplicity, ``dds`` drops almost all | In order to reach this uniformity and simplicity, ``dds`` drops almost all | ||||
aspects of project-by-project customizability. Instead, ``dds`` affords the | aspects of project-by-project customizability. Instead, ``dds`` affords the | ||||
on. | on. | ||||
``dds`` contains a minimal amount of functionality for building simple | ``dds`` contains a minimal amount of functionality for building simple | ||||
applications, but it is certainly not its primary purpose (See the ``--apps`` | |||||
flag). | |||||
applications, but it is certainly not its primary purpose. | |||||
.. _design.rules.change: | .. _design.rules.change: | ||||
.. note:: | .. note:: | ||||
These prescriptions are not as draconian as they may sound upon first | These prescriptions are not as draconian as they may sound upon first | ||||
reading. Refer to the :doc:`layout` page for more information. | |||||
reading. Refer to the :doc:`packages` page for more information. | |||||
.. _Pitchfork: https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs | .. _Pitchfork: https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs | ||||
Only ``src/`` and ``include/`` will ever be used as the basis for header | Only ``src/`` and ``include/`` will ever be used as the basis for header | ||||
resolution while building a library, so all ``#include`` directives should be | resolution while building a library, so all ``#include`` directives should be | ||||
relative to those directories. Refer to :ref:`guide.layout.include`. | |||||
relative to those directories. Refer to :ref:`pkg.source-root`. | |||||
.. _design.rules.uniform-compile: | .. _design.rules.uniform-compile: | ||||
identical set of options. Additionally, when DDS compiles a dependency tree, | identical set of options. Additionally, when DDS compiles a dependency tree, | ||||
every library in that dependency tree will be compiled with an identical set of | every library in that dependency tree will be compiled with an identical set of | ||||
options. Refer to the :doc:`toolchains` page for more information. | options. Refer to the :doc:`toolchains` page for more information. | ||||
Currently, the only exception to this rules is for flags that control compiler | |||||
warnings: Dependencies will be compiled without adding any warnings flags, | |||||
while the main project will be compiled with warnings enabled by default. |
:maxdepth: 2 | :maxdepth: 2 | ||||
design | design | ||||
layout | |||||
packages | |||||
toolchains | toolchains |
Project Layout | |||||
############## | |||||
The layout expected by ``dds`` is based on the `Pitchfork layout`_ (PFL). | |||||
``dds`` does not make use of every provision of the layout document, but the | |||||
features it does have are based on PFL. | |||||
.. _Pitchfork layout: https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs | |||||
In particular, the following directories are used: | |||||
- ``src/`` | |||||
- ``include/`` | |||||
- ``libs/`` | |||||
- ``_build/`` (the default build output directory used by ``dds``). | |||||
Note that the ``libs/*/`` directories can contain their own ``src/`` and | |||||
``include/`` directories, the purposes and behaviors of which match those of | |||||
their top-level counterparts. | |||||
.. _guide.layout.include: | |||||
Include Directories and Header Resolution | |||||
***************************************** | |||||
A compiler's "include path" is the list of directories in which it will attempt | |||||
to resolve ``#include`` directives. | |||||
The layout prescriptions permit either ``src/``, ``include/``, or both. In the | |||||
presence of both, the ``include/`` directory is used as the *public* include | |||||
directory, and ``src/`` is used as the *private* include directory. When only | |||||
one of either is present, that directory will be treated as the *public* | |||||
include directory (and there will be no *private* include directory). | |||||
.. _guide.layout.sources: | |||||
Source Files | |||||
************ | |||||
``dds`` distinguishes between *headers* and *compilable* sources. The heuristic | |||||
used is based on common file extensions: | |||||
The following are considered to be *header* source files: | |||||
- ``.h`` | |||||
- ``.hpp`` | |||||
- ``.hxx`` | |||||
- ``.inl`` | |||||
- ``.h++`` | |||||
While the following are considered to be *compilable* source files: | |||||
- ``.c`` | |||||
- ``.cpp`` | |||||
- ``.cc`` | |||||
- ``.cxx`` | |||||
- ``.c++`` | |||||
``dds`` will compile every compilable source file that appears in the ``src/`` | |||||
directory. ``dds`` will not compile compilable source files that appear in the | |||||
``include/`` directory and will issue a warning on each file found. | |||||
.. _guide.layout.apps-tests: | |||||
Applications and Tests | |||||
********************** | |||||
``dds`` will recognize certain compilable source files as belonging to | |||||
applications and tests. If a compilable source file stem ends with ``.main`` or | |||||
``.test``, that source file is assumed to correspond to an executable to | |||||
generate. The filename stem before the ``.main`` or ``.test`` will be used as | |||||
the name of the generated executable. For example: | |||||
- ``foo.main.cpp`` will generate an executable named ``foo``. | |||||
- ``bar.test.cpp`` will generate an executable named ``bar``. | |||||
- ``cat-meow.main.cpp`` will generate an executable named ``cat-meow``. | |||||
- ``cats.musical.test.cpp`` will generate an executable named ``cats.musical``. | |||||
.. note:: | |||||
``dds`` will automatically append the appropriate filename extension to the | |||||
generated executables based on the host and toolchain. | |||||
If the inner extension is ``.main``, then ``dds`` will assume the corresponding | |||||
executable to be an *application*. If the inner extension is ``.test``, ``dds`` | |||||
will assume the executable to be a test. | |||||
The building of tests and applications can be controlled when running | |||||
``dds build``. If tests are built, ``dds`` will automatically execute those | |||||
tests in parallel once the executables have been generated. | |||||
In any case, the executables are associated with a *library*, and, when those | |||||
executables are linked, the associated library (and its dependencies) will be | |||||
linked into the final executable. There is no need to manually specify this | |||||
linking behavior. | |||||
.. _guide.layout.libraries: | |||||
Libraries | |||||
********* | |||||
The *library* is a fundamental unit of consumable code, and ``dds`` is | |||||
specifically built to work with them. When you are in ``dds``, the library is | |||||
the center of everything. | |||||
A *source root* is a directory that contains the ``src/`` and/or ``include/`` | |||||
directories. The ``src/`` and ``include/`` directories are themselves | |||||
*source directories*. A single *source root* will always correspond to exactly | |||||
one library. If the library has any compilable sources then ``dds`` will use | |||||
those sources to generate a static library file that is linked into runtime | |||||
binaries. If a library contains only headers then ``dds`` will not generate an | |||||
archive to be included in downstream binaries, but it will still generate link | |||||
rules for the dependencies of a header-only library. | |||||
In the previous section, :ref:`guide.layout.apps-tests`, it was noted that | |||||
applications and tests are associated with a library. This association is | |||||
purely based on being collocated within the same source root. | |||||
When an executable is built within the context of a library, that library (and | |||||
all of its dependencies) will be linked into that executable. |
Package Layout | |||||
############## | |||||
The units of distribution in ``dds`` are *packages*. A single package consists | |||||
of one or more *libraries*. In the simplest case, a package will contain a | |||||
single library. | |||||
It may be easiest to work from the bottom-up when trying to understand how | |||||
``dds`` understands code. | |||||
The layout expected by ``dds`` is based on the `Pitchfork layout`_ (PFL). | |||||
``dds`` does not make use of every provision of the layout document, but the | |||||
features it does have are based on PFL. | |||||
.. _Pitchfork layout: https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/pitchfork/develop/data/spec.bs | |||||
In particular, the following directories are used: | |||||
- ``src/`` | |||||
- ``include/`` | |||||
- ``libs/`` | |||||
- ``_build/`` (the default build output directory used by ``dds``). | |||||
Note that the ``libs/*/`` directories can contain their own ``src/`` and | |||||
``include/`` directories, the purposes and behaviors of which match those of | |||||
their top-level counterparts. | |||||
Source Files | |||||
************ | |||||
The smallest subdivision of code that ``dds`` recognizes is the *source file*, | |||||
which is exactly as it sounds: A single file containing some amount of code. | |||||
Source files can be grouped on a few axes, the most fundamental of which is | |||||
"Is this compiled?" | |||||
``dds`` uses source file extensions to determine whether a source file should | |||||
be fed to the compiler. All of the common C and C++ file extensions are | |||||
supported: | |||||
.. list-table:: | |||||
- * Compiled as C | |||||
* ``.c`` and ``.C`` | |||||
- * Compiled as C++ | |||||
* ``.cpp``, ``.c++``, ``.cc``, and ``.cxx`` | |||||
- * Not compiled | |||||
* ``.H``, ``.H++``, ``.h``, ``.h++``, ``.hh``, ``.hpp``, ``.hxx``, and ``.inl`` | |||||
If a file's extension is not listed in the table above, ``dds`` will ignore it. | |||||
.. note:: | |||||
Although headers are not compiled, this does not mean they are ignored. | |||||
``dds`` still understands and respects headers, and they are collected | |||||
together as part of *source distribution*. | |||||
Applications and Tests | |||||
********************** | |||||
``dds`` will recognize certain compilable source files as belonging to | |||||
applications and tests. If a compilable source file stem ends with ``.main`` or | |||||
``.test``, that source file is assumed to correspond to an executable to | |||||
generate. The filename stem before the ``.main`` or ``.test`` will be used as | |||||
the name of the generated executable. For example: | |||||
- ``foo.main.cpp`` will generate an executable named ``foo``. | |||||
- ``bar.test.cpp`` will generate an executable named ``bar``. | |||||
- ``cat-meow.main.cpp`` will generate an executable named ``cat-meow``. | |||||
- ``cats.musical.test.cpp`` will generate an executable named ``cats.musical``. | |||||
.. note:: | |||||
``dds`` will automatically append the appropriate filename extension to the | |||||
generated executables based on the host and toolchain. | |||||
An *application* source file is a source file whose file stem ends with | |||||
``.main``. ``dds`` will assume this source file to contain a program entry | |||||
point function and not include it as part of the main library build. Instead, | |||||
when ``dds`` is generating applications, the source file will be compiled, and | |||||
the resulting object will be linked together with the enclosing library into an | |||||
executable. | |||||
A *test* source file is a source file whose file stem ends with ``.test``. Like | |||||
application sources, a *test* source file is omitted from the main library, and | |||||
it will be used to generate tests. The exact behavior of tests is determined by | |||||
the ``Test-Driver`` setting for the package, but the default is that each test | |||||
source file will generate a single test executable that is executed by ``dds`` | |||||
when running unit tests. | |||||
The building of tests and applications can be controlled when running | |||||
``dds build``. If tests are built, ``dds`` will automatically execute those | |||||
tests in parallel once the executables have been generated. | |||||
In any case, the executables are associated with a *library*, and, when those | |||||
executables are linked, the associated library (and its dependencies) will be | |||||
linked into the final executable. There is no need to manually specify this | |||||
linking behavior. | |||||
.. _pkg.source-root: | |||||
Source Roots | |||||
************ | |||||
Source files are collected as children of some *source root*. A *source | |||||
root* is a single directory that contains some *portable* bundle of source | |||||
files. The word "portable" is important: It is what distinguishes the | |||||
source root from its child directories. | |||||
Portability | |||||
=========== | |||||
By saying that a source root is "portable", It indicates that the directory | |||||
itself can be moved, renamed, or copied without breaking the ``#include`` | |||||
directives of its children or of the directory's referrers. | |||||
As a practical example, let's examine such a directory, which we'll call | |||||
``src/`` for the purposes of this example. Suppose we have a directory named | |||||
``src`` with the following structure: | |||||
.. code-block:: text | |||||
<path>/src/ | |||||
animals/ | |||||
mammal/ | |||||
mammal.hpp | |||||
cat/ | |||||
cat.hpp | |||||
sound.hpp | |||||
sound.cpp | |||||
dog/ | |||||
dog.hpp | |||||
sound.hpp | |||||
sound.cpp | |||||
In this example, ``src/`` is a *source root*, but ``src/animals/``, | |||||
``src/animals/cat/``, and ``src/animals/dog/`` are **not** source roots. | |||||
While they may be directories that contain source files, they are not "roots." | |||||
Suppose now that ``dog.hpp`` contains an ``#include`` directive: | |||||
.. code-block:: c++ | |||||
#include <animals/mammal/mammal.hpp> | |||||
or even a third-party user that wants to use our library: | |||||
.. code-block:: c++ | |||||
#include <animals/dog/dog.hpp> | |||||
#include <animals/dog/sound.hpp> | |||||
In order for any code to compile and resolve these ``#include`` directives, the | |||||
``src/`` directory must be added to their *include search path*. | |||||
Because the ``#include`` directives are based on the *portable* source root, | |||||
the exactly location of ``src/`` is not important to the content of the | |||||
consuming source code, and can thus be relocated and renamed as necessary. | |||||
Consumers only need to update the path of the *include search path* in a single | |||||
location rather than modifying their source code. | |||||
Source Roots in ``dds`` | |||||
======================= | |||||
To avoid ambiguity and aide in portability, the following rules should be | |||||
strictly adhered to: | |||||
#. Source roots may not contain other source roots. | |||||
#. Only source roots will be added to the *include-search-path*. | |||||
#. All ``#include``-directives are relative to a source root. | |||||
By construction, ``dds`` cannot build a project that has nested source roots, | |||||
and it will only ever add source roots to the *include-search-path*. | |||||
``dds`` supports either one or two source roots in a library. | |||||
Library Roots | |||||
************* | |||||
In ``dds``, a *library root* is a directory that contains a ``src/`` directory, | |||||
an ``include/`` directory, or both. ``dds`` will treat both directories as | |||||
source roots, but behaves differently between the two. The ``src/`` and | |||||
``include/`` directories are themselves *source roots*. | |||||
``dds`` distinguishes between a *public* include-directory, and a *private* | |||||
include-directory. When ``dds`` is compiling a library, both its *private* and | |||||
its *public* include-paths will be added to the compiler's | |||||
*include-search-path*. When a downstream user of a library is compiling against | |||||
a library managed by ``dds``, only the *public* include-directory will be | |||||
added to the compiler's *include-search-path*. This has the effect that only | |||||
the files that are children of the source root that is the *public* | |||||
include-directory will be available when compiling consumers. | |||||
.. warning:: | |||||
Because only the *public* include-directory is available when compiling | |||||
consumers, it is essential that no headers within the *public* | |||||
include-directory attempt to use headers from the *private* | |||||
include-directory, as they **will not** be visible. | |||||
If both ``src/`` and ``include/`` are present in a library root, then ``dds`` | |||||
will use ``include/`` as the *public* include-directory and ``src/`` as the | |||||
*private* include-directory. If only one of the two is present, then that | |||||
directory will be treated as the *public* include-directory, and there will be | |||||
no *private* include-directory. | |||||
When ``dds`` exports a library, the header files from the *public* | |||||
include-directory source root will be collected together and distributed as | |||||
that library's header tree. The path to the individual header files relative to | |||||
their source root will be retained as part of the library distribution. | |||||
``dds`` will compile every compilable source file that appears in the ``src/`` | |||||
directory. ``dds`` will not compile compilable source files that appear in the | |||||
``include/`` directory and will issue a warning on each file found. | |||||
Libraries | |||||
********* | |||||
The *library* is a fundamental unit of consumable code, and ``dds`` is | |||||
specifically built to work with them. When you are in ``dds``, the library is | |||||
the center of everything. | |||||
A single *library root* will always correspond to exactly one library. If the | |||||
library has any compilable sources then ``dds`` will use those sources to | |||||
generate a static library file that is linked into runtime binaries. If a | |||||
library contains only headers then ``dds`` will not generate an archive to be | |||||
included in downstream binaries, but it will still generate link rules for the | |||||
dependencies of a header-only library. | |||||
In order for ``dds`` to be able to distribute and interlink libraries, a | |||||
``library.dds`` file must be present at the corresponding library root. | |||||
Package Roots | |||||
************* | |||||
A *package root* is a directory that contains some number of library roots. If | |||||
the package root contains a ``src/`` and/or ``include/`` directory then the | |||||
package root is itself a library root, and a library is defined at the root of | |||||
the package. This is intended to be the most common and simplest method of | |||||
creating libraries with ``dds``. | |||||
If the package root contains a ``libs/`` directory, then each subdirectory of | |||||
the ``libs/`` directory is checked to be a library root. Each direct child of | |||||
the ``libs/`` directory that is also a library root is added as a child of the | |||||
owning package. | |||||
Packages | |||||
******** | |||||
A package is defined by some *package root*, and contains some number of | |||||
*libraries*. In order for a package to be exported by ``dds`` it must have a | |||||
``package.dds`` file at its package root. |
DDS | DDS | ||||
###### | ###### | ||||
**dds** is the Drop-Dead-Simple Build and Library Management Tool. | |||||
**dds** is the Drop-Dead-Simple build and library management tool. | |||||
dds is a hybrid build system and package manager with a unique twist. | |||||
There's a lot to learn, but I'm glad you're here! I hope you find ``dds`` | |||||
useful to you and your projects. | |||||
dds is a hybrid build system and package manager with a few distinguishing | |||||
design decisions that set it apart from current offerings. There's a lot to | |||||
learn, but I'm glad you're here! I hope you find ``dds`` useful to you and your | |||||
projects. | |||||
If you're completely new and have no idea what the project is about, check out | If you're completely new and have no idea what the project is about, check out | ||||
the :doc:`guide/design` page to get started. | the :doc:`guide/design` page to get started. |