@@ -0,0 +1,27 @@ | |||
.SILENT: | |||
.PHONY: docs docs-server docs-watch docs-sync-server | |||
_invalid: | |||
echo "Specify a target name to execute" | |||
docs: | |||
sphinx-build -b html \ | |||
docs \ | |||
_build/docs \ | |||
-Wqaj8 | |||
echo "Docs generated to _build/docs" | |||
docs-server: docs | |||
echo "Docs are visible on http://localhost:9794/" | |||
cd _build/docs && \ | |||
python -m http.server 9794 | |||
docs-watch: docs | |||
+sh tools/docs-watch.sh | |||
docs-sync-server: docs | |||
cd _build/docs && \ | |||
browser-sync start --server \ | |||
--reload-delay 300 \ | |||
--watch **/*.html |
@@ -0,0 +1,31 @@ | |||
# -*- coding: utf-8 -*- | |||
# Refer: http://www.sphinx-doc.org/en/master/config | |||
# -- Project information ----------------------------------------------------- | |||
project = 'dds' | |||
copyright = '2019, vector-of-bool' | |||
author = 'vector-of-bool' | |||
# The short X.Y version | |||
version = '' | |||
# The full version, including alpha/beta/rc tags | |||
release = '0.1.0' | |||
# -- General configuration --------------------------------------------------- | |||
extensions = [] | |||
templates_path = ['_templates'] | |||
source_suffix = '.rst' | |||
master_doc = 'index' | |||
language = None | |||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] | |||
pygments_style = None | |||
# -- Options for HTML output ------------------------------------------------- | |||
html_theme = 'pyramid' | |||
html_theme_options = {} | |||
html_static_path = ['_static'] | |||
html_sidebars = {} | |||
def setup(app): | |||
app.add_stylesheet('tweaks.css') |
@@ -0,0 +1,195 @@ | |||
``dds`` Design and Rationale | |||
############################ | |||
``dds`` has been designed from the very beginning as an extremely opinionated | |||
hybrid *build system* and *package manager*. Unlike most build systems however, | |||
``dds`` has a hyper-specific focus on a particular aspect of software | |||
development: C and C++ libraries. | |||
This may sound pointless, right? Libraries are useless unless we can use them | |||
to build applications! | |||
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 | |||
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 | |||
********** | |||
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 | |||
other language in use today. This isn't to say "bad," but rather than it is | |||
built to meet extremely high and strange demands. It also comes with a large | |||
burden of *legacy*. Meeting both of these requirements simultaneously presents | |||
incredible implementation challenges. | |||
Despite the vast amount of work put into build systems and tooling, virtually | |||
all developers are using them *incorrectly* and/or *dangerously* without | |||
realizing it. Despite this work, we seem to be a great distance from a unified | |||
library package distribution and consumption mechanism. | |||
Tabula Rasa | |||
*********** | |||
``dds`` attempts to break from the pattern of legacy demands and strange usage | |||
demands in a few ways. The major differences between ``dds`` and other build | |||
systems like CMake, Meson, build2, SCons, MSBuild, etc. is that of *tradeoffs*. | |||
If you opt-in to have your library built by ``dds``, you forgoe | |||
*customizability* in favor of *simplicity* and *ease*. | |||
``dds`` takes a look at what is needed to build and develop *libraries* and | |||
hyper-optimizes for that use case. It is also built with a very strong, very | |||
opinionated idea of *how* libraries should be constructed and used. These | |||
prescriptions are not at all arbitrary, though. They are built upon the | |||
observations of the strengths and weaknesses of build systems in use throughout | |||
industry and community. | |||
There is some ambiguity on the term "build system." It can mean one of two | |||
things: | |||
1. A *proper noun* "Build System," such as CMake, Meson, Autotools, or even | |||
Gulp, WebPack, and Mix. These are specific tools that have been developed | |||
for the implementation of the second definition: | |||
2. A general noun "build system" refers to the particular start-to-finish | |||
process through which a specific piece of software is mapped from its raw | |||
*inputs* (source code, resource libraries, toolchains) to the outputs | |||
(applications, appliances, libraries, or web sites). | |||
For example, LLVM and Blender both use the CMake "Build System," but their | |||
"build system" is not the same. The "build system" for each is wildly | |||
different, despite both using the same underlying "Build System." | |||
``dds`` takes a massive divergence at this point. One project using ``dds`` as | |||
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 | |||
aspects of project-by-project customizability. Instead, ``dds`` affords the | |||
developer a contract: | |||
If you play by my rules, you get to play in my space. | |||
.. _design.rules: | |||
The Rules | |||
********* | |||
We've talked an awful lot about the "rules" and "restrictions" that ``dds`` | |||
imposes, but what are they? | |||
.. _design.rules.not-apps: | |||
``dds`` Is not Made for Complex Applications | |||
=============================================== | |||
Alright, this one isn't a "rule" as much as a recommendation: If you are | |||
building an application that *needs* some build process functionality that | |||
``dds`` does not provide, ``dds`` is only open to changes that do not | |||
violate any of the other existing rules. | |||
.. note:: | |||
**However:** If you are a *library* author and you find that ``dds`` | |||
cannot correctly build your library without violating other rules, we may | |||
have to take a look. This is certainly not to say it will allow arbitrary | |||
customization features to permit the rules to be bent arbitrarily: Read | |||
on. | |||
``dds`` contains a minimal amount of functionality for building simple | |||
applications, but it is certainly not its primary purpose. | |||
.. _design.rules.change: | |||
*Your* Code Should Be Changed Before ``dds`` Should Be Changed | |||
================================================================= | |||
The wording of this rule means that the onus is on the library developer to | |||
meet the expectations that ``dds`` prescribes in order to make the build | |||
work. | |||
If your library meets all the requirements outlined in this document but you | |||
still find trouble in making your build work, this is grounds for change in | |||
``dds``, either in clarifying the rules or tweaking ``dds`` functionality. | |||
.. _design.rules.layout: | |||
Library Projects Must Meet the Layout Requirements | |||
================================================== | |||
This is a very concrete requirement. ``dds`` prescribes a particular project | |||
structure layout with minimal differing options. ``dds`` prescribes the | |||
`Pitchfork`_ layout requirements. | |||
.. note:: | |||
These prescriptions are not as draconian as they may sound upon first | |||
reading. Refer to the :doc:`guide/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 | |||
.. _design.rules.no-cond-compile: | |||
A Library Build Must Successfully Compile All Source Files | |||
========================================================== | |||
Almost all Build Systems have a concept of *conditionally* adding a source file | |||
to a build. ``dds`` elides this feature in place of relying on in-source | |||
conditional compilation. | |||
.. _design.rules.no-lazy-code-gen: | |||
All Code Must Be in Place Before Building | |||
========================================= | |||
``dds`` does not provide code-generation functionality. Instead, any | |||
generated code should be generated and committed to the repository to be only | |||
ever modified through such generation scripts. | |||
.. _design.rules.one-binary-per-src: | |||
All Compilable Files in a ``src/`` Directory Must Link Together | |||
=============================================================== | |||
As part of the prescribed project layout, the ``src/`` project directory | |||
contains source files. ``dds`` requires that *all* source files in a given | |||
``src/`` directory should link together cleanly. Practically, this means that | |||
every ``src/`` directory must correspond to *exactly* one library. | |||
.. _design.rules.include: | |||
No Arbitrary ``#include`` Directories | |||
===================================== | |||
Only ``src/`` and ``include/`` will ever be used as the basis for header | |||
resolution while building a library, so all ``#include`` directives should be | |||
relative to those directories. Refer to :ref:`pkg.source-root`. | |||
.. _design.rules.uniform-compile: | |||
All Files Compile with the Same Options | |||
======================================= | |||
When DDS compiles a library, every source file will be compiled with an | |||
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 | |||
options. Refer to the :doc:`guide/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. |
@@ -0,0 +1,116 @@ | |||
The Package Catalog | |||
################### | |||
``dds`` stores a catalog of available packages, along with their dependency | |||
statements and information about how a source distribution thereof may be | |||
maintained. | |||
Viewing Catalog Contents | |||
************************ | |||
The default catalog database is stored in a user-local location, and the | |||
package IDs available can be listed with ``dds catalog list``. This will only | |||
list the IDs of the packages, but none of the additional metadata about them. | |||
Adding Packages to the Catalog | |||
****************************** | |||
There are two primary ways to add entries to the package catalog. | |||
Adding Individual Packages | |||
========================== | |||
A single package can be added to the catalog with the ``dds catalog add`` | |||
command: | |||
.. code-block:: text | |||
dds catalog add <package-id> | |||
[--depends <requirement> [--depends <requirement> [...]]] | |||
[--git-url <url>] | |||
[--git-ref <ref>] | |||
[--auto-lib <Namespace>/<Name>] | |||
The ``<package-id>`` positional arguments is the ``name@version`` package ID | |||
that will be added to the catalog. The following options are supported: | |||
``--depends <requirement>`` | |||
This argument, which can be specified multiple times to represent multiple | |||
dependencies, sets the dependencies of the package within the catalog. If | |||
the obtained package root contains a ``package.dds``, then the dependencies | |||
listed here must be identical to those listed in ``package.dds``, or | |||
dependency resolution may yield unexpected results. | |||
``--git-url <url>`` | |||
Specify a Git URL to clone from to obtain the package. The root of the | |||
cloned repository must be a package root, but does not necessarily need to | |||
have the ``package.dds`` and ``library.dds`` files if relying on the | |||
``--auto-lib`` parameter. | |||
``--git-ref`` **must** be passed with ``--git-url``. | |||
``--git-ref <ref>`` | |||
Specify a Git ref to clone. The remote must support cloning by the ref that | |||
is specified here. Most usually this should be a Git tag. | |||
``dds`` will perform a shallow clone of the package at the specified | |||
Git reference. | |||
``--auto-lib`` | |||
This option must be provided if the upstream does not already contain the | |||
``dds`` files that are necessary to export the library information. This | |||
can only be specified for packages that contain a single library root at | |||
the package root. | |||
The form of the argument is that of ``<Namespapce>/<Name>``, where | |||
``Namespace`` and ``Name`` are the usage requirement keys that should be | |||
generated for the library. | |||
Bulk Imports via JSON | |||
===================== | |||
The ``dds catalog import`` supports a ``--json`` flag that specifies a JSON | |||
file from which catalog entries will be generated. | |||
.. note:: | |||
The ``--json`` flag can be passed more than once to import multiple JSON | |||
files at once. | |||
The JSON file has the following structure: | |||
.. code-block:: javascript | |||
{ | |||
// Import version spec. | |||
"version": 1, | |||
// Packages section | |||
"packages": { | |||
// Subkeys are package names | |||
"acme-gadgets": { | |||
// Keys within the package names are the versions that are | |||
// available for each package. | |||
"0.4.2": { | |||
// `depends` is an object of dependencies for this | |||
// particular version of the package. | |||
"depends": { | |||
// A mapping of package names to version ranges | |||
"acme-widgets": "^1.4.1" | |||
}, | |||
// Specify the Git remote information | |||
"git": { | |||
// `url` and `ref` are required. | |||
"url": "http://example.com/git/repo/acme-gadgets.git", | |||
"ref": "v0.4.2-stable", | |||
// The `auto-lib` is optional, to specify an automatic | |||
// library name/namespace pair to generate for the | |||
// root library | |||
"auto-lib": "Acme/Gadgets" | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
.. _guide: | |||
User Guide | |||
########## | |||
.. toctree:: | |||
:maxdepth: 2 | |||
packages | |||
toolchains | |||
source-dists | |||
repo | |||
catalog | |||
interdeps |
@@ -0,0 +1,116 @@ | |||
.. highlight:: yaml | |||
Library and Package Dependencies | |||
################################ | |||
``dds`` considers that all libraries belong to a single *package*, but a single | |||
package may contain one or more *libraries*. For this reason, and to better | |||
interoperate with other build and packaging tools, we consider the issues of | |||
package dependencies and library dependencies separately. | |||
.. _deps.pkg-deps: | |||
Package Dependencies | |||
******************** | |||
Consider that we are creating a package ``acme-gadgets@4.3.6``. We declare the | |||
name and version in the ``package.dds`` in the package root: | |||
.. code-block:: | |||
Name: acme-gadgets | |||
Version: 4.3.6 | |||
Namespace: acme | |||
.. note:: | |||
The ``Namespace`` field is required, but will be addressed in the | |||
:ref:`deps.lib-deps` section. | |||
Suppose that our package's libraries build upon the libraries in the | |||
``acme-widgets`` package, and that we require version ``1.4.3`` or newer, but | |||
not as new as ``2.0.0``. Such a dependency can be declared with the ``Depends`` | |||
key: | |||
.. code-block:: | |||
:emphasize-lines: 5 | |||
Name: acme-gadgets | |||
Version: 4.3.6 | |||
Namespace: acme | |||
Depends: acme-widgets ^1.4.3 | |||
If we wish to declare additional dependencies, we simply declare them with | |||
additional ``Depends`` keys | |||
.. code-block:: | |||
:emphasize-lines: 5-7 | |||
Name: acme-gadgets | |||
Version: 4.3.6 | |||
Namespace: acme | |||
Depends: acme-widgets ^1.4.3 | |||
Depends: acme-gizmos ~5.6.5 | |||
Depends: acme-utils ^3.3.0 | |||
When ``dds`` attempts to build a project, it will first build the dependency | |||
solution by iteratively scanning the dependencies of the containing project and | |||
all transitive dependencies. | |||
.. note:: | |||
Unlike other packaging tools, ``dds`` will find a solution with the | |||
*lowest* possible version that satisfies the given requirements for each | |||
package. | |||
.. _deps.lib-deps: | |||
Library Dependencies | |||
******************** | |||
In ``dds``, library interdependencies are tracked separately from the packages | |||
that contain them. A library must declare its intent to use another library | |||
in the ``library.dds`` at its library root. The minimal content of a | |||
``library.dds`` is the ``Name`` key: | |||
.. code-block:: | |||
Name: gadgets | |||
To announce that a library wishes to *use* another library, use the aptly-named | |||
``Uses`` key: | |||
.. code-block:: | |||
:emphasize-lines: 3-5 | |||
Name: gadgets | |||
Uses: acme/widgets | |||
Uses: acme/gizmos | |||
Uses: acme/utils | |||
Here is where the package's ``Namespace`` key comes into play: A library's | |||
qualified name is specified by joining the ``Namespace`` of the containing | |||
package with the ``Name`` of the library within that package with a ``/`` | |||
between them. | |||
It is the responsibility of package authors to document the ``Namespace`` and | |||
``Name`` of the packages and libraries that they distribute. | |||
.. note:: | |||
The ``Namespace`` of a package is completely arbitrary, and need not relate | |||
to a C++ ``namespace``. | |||
.. note:: | |||
The ``Namespace`` need not be unique to a single package. For example, a | |||
single organization (Like Acme Inc.) can share a single ``Namespace`` for | |||
many of their packages and libraries. | |||
However, it is essential that the ``<Namespace>/<Name>`` pair be | |||
universally unique, so choose wisely! | |||
Once the ``Uses`` key appears in the ``library.dds`` file of a library, ``dds`` | |||
will make available the headers for the library being used, and will | |||
transitively propagate that usage requirement to users of the library. |
@@ -0,0 +1,301 @@ | |||
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. | |||
.. _pkgs.source-root: | |||
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. | |||
.. _pkgs.lib-roots: | |||
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. The | |||
only required key in a ``library.dds`` file is ``Name``: | |||
.. code-block:: yaml | |||
Name: my-library | |||
.. seealso:: More information is discussed on the :ref:`deps.lib-deps` page | |||
.. _pkgs.pkg-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*. | |||
The primary distribution format of packages that is used by ``dds`` is the | |||
*source distribution*. Refer to the page :doc:`source-dists`. | |||
Packages are identified by a name/version pair, joined together by an ``@`` | |||
symbol. The version of a package must be a semantic version string. Together, | |||
the ``name@version`` string forms the *package ID*, and it must be unique | |||
within a repository or package catalog. | |||
In order for a package to be exported by ``dds`` it must have a | |||
``package.dds`` file at its package root. Three keys are required to be | |||
present in the ``package.dds`` file: ``Name``, ``Version``, and ``Namespace``: | |||
.. code-block:: yaml | |||
Name: acme-widgets | |||
Version: 6.7.3 | |||
Namespace: acme | |||
``Version`` must be a valid semantic version string. | |||
.. note:: | |||
The ``Namespace`` key is arbitrary, and not necessarily associated with | |||
and C++ ``namespace``. | |||
.. seealso:: | |||
The purpose of ``Namespace``, as well as additional options in this file, | |||
are described in the :ref:`deps.pkg-deps` page |
@@ -0,0 +1,92 @@ | |||
The Local Package Repository | |||
############################ | |||
``dds`` maintains a local repository of packages that it has obtained at the | |||
request of a user. The packages themselves are stored as | |||
:doc:`source distributions <source-dists>` (``dds`` does not store the binaries | |||
that it builds within the repository). | |||
Reading Repository Contents | |||
*************************** | |||
Most times, ``dds`` will manage the repository content silently, but it may be | |||
useful to see what ``dds`` is currently storing away. | |||
The content of the repostiory can be seen with the ``repo`` subcommand:: | |||
> dds repo ls | |||
This will print the names of packages that ``dds`` has downloaded, as well as | |||
the versions of each. | |||
Obtaining Packages | |||
****************** | |||
.. seealso:: See also: :doc:`catalog` | |||
When ``dds`` builds a package, it will also build the dependency libraries of | |||
that package. In order for the dependency build to succeed, it must have a | |||
local copy of the source distribution of that dependency. | |||
When ``dds`` performs dependency resolution, it will consider both existing | |||
packages in the local repository, as well as packages that are available from | |||
the :doc:`package catalog <catalog>`. If the dependency solution requires any | |||
packages that are not in the local repository, it will use the information in | |||
the catalog to obtain a source distribution for each missing package. These | |||
source distributions will automatically be added to the local repository, and | |||
later dependency resolutions will not need to download that package again. | |||
Manually Downloading a Dependency | |||
================================= | |||
It may be useful to obtain a copy of the source distribution of a package | |||
contained in the catalog. The ``catalog get`` command can be used to do this:: | |||
> dds catalog get <name>@<version> | |||
This will obtain the source distribution of the package matching the named | |||
identifier and place that distribution in current working directory, using the | |||
package ID as the name of the source distribution directory:: | |||
$ dds catalog get spdlog@1.4.2 | |||
[ ... ] | |||
$ ls . | |||
. | |||
.. | |||
spdlog@1.4.2 | |||
$ ls ./spdlog@1.4.2/ | |||
include/ | |||
src/ | |||
library.dds | |||
package.dds | |||
.. _repo.export-local: | |||
Exporting a Project into the Repository | |||
*************************************** | |||
``dds`` can only use packages that are available in the local repository. For | |||
packages that have a listing in the catalog, this is not a problem. But if one | |||
is developing a local package and wants to allow it to be used in another local | |||
package, that can be done by exporting a source distribution from the package | |||
root:: | |||
> dds sdist export | |||
This command will create a source distribution and place it in the local | |||
repository. The package is now available to other projects on the local system. | |||
.. note:: | |||
This doesn't export in "editable" mode: A snapshot of the package root | |||
will be taken and exported to the local repository. | |||
If one tries to export a package root into a repository that already contains | |||
a package with a matching identifier, ``dds`` will issue an error. If the | |||
``--replace`` flag is specified with ``sdist export``, then ``dds`` will | |||
forcibly replace the package in the local repository with a new copy. |
@@ -0,0 +1,43 @@ | |||
Source Distributions | |||
#################### | |||
A *source distribution* is ``dds``'s primary format for consuming and | |||
distributing packages. A source distribution, in essence, is a | |||
:ref:`package root <pkgs.pkg-root>` directory that contains only the files | |||
necessary for ``dds`` to reproduce the full build of all libraries in the | |||
package. The source distribution retains the directory structure of every | |||
:ref:`source root <pkgs.source-root>` of the original package, and thus retains | |||
the header structure thereof. In this way, the ``#include`` directives to use | |||
a library in a source distribution are identical to if the libraries therein | |||
were directly part of the consuming project. | |||
Generating a Source Distribution | |||
******************************** | |||
Generating a source distribution from a project directory is done with the | |||
``sdist`` subcommand:: | |||
> dds sdist create | |||
The above command can be executed within any package root, and the result will | |||
be a new directory that reproduces the package's filesystem structure, but | |||
only maintaining the files that are necessary for ``dds`` to reproduce the | |||
build of that package. | |||
The ``--project=<dir>`` flag can be provided to override the directory that | |||
``dds`` will use as the package root. The default is the working directory of | |||
the project. | |||
The ``--out=<path>`` flag can be provided to override the destination path of | |||
the resulting source distribution. The path should not name an existing file or | |||
directory. By default, ``dds`` will generate a source distribution in the | |||
working directory with the name ``project.dsd/`` (The output is itself a | |||
directory, not an archive). If the ``--replace`` flag is provided, then ``dds`` | |||
will overwrite the destination if it names an existing file or directory. | |||
Exporting a Package to the Local Repository | |||
******************************************* | |||
.. seealso:: :ref:`repo.export-local` |
@@ -0,0 +1,302 @@ | |||
.. highlight:: yaml | |||
Toolchains | |||
########## | |||
One of the core components of ``dds`` is that of the *toolchain*. A toolchain | |||
encompasses the environment used to build and link source code, including, but | |||
not limited to: | |||
#. The executable binaries that constitute the language implementation: | |||
Compilers, linkers, and archive managers. | |||
#. The configuration of those tools, including most options given to those | |||
tools when they are invoked. | |||
#. The set of preprocessor macros and language features that are active during | |||
compilation. | |||
When a build is run, every file in the entire tree (including dependencies) | |||
will be compiled, archived, and linked using the same toolchain. | |||
This page provides an introduction on how one can make use of toolchains most | |||
effectively in your project. | |||
.. note:: | |||
**IMPORTANT**: ``dds`` will *not* automatically load the Visual C++ | |||
environment. To use Visual C++, ``dds`` must be executed from the | |||
appropriate environment in order for the Visual C++ toolchain executables | |||
and files to be available. | |||
Passing a Toolchain | |||
******************* | |||
In ``dds``, the default format of a toolchain is that of a single file that | |||
describes the entire toolchain, and uses the extension ``.tc.dds`` by | |||
convention. When running a build for a project, the ``dds`` executable will | |||
look for a file named ``toolchain.tc.dds`` by default, and will error out if | |||
this file does not exist. A different toolchain can be provided by passing the | |||
toolchain file for the ``--toolchain`` (or ``-t``) option on the command line:: | |||
$ dds build -t my-toolchain.tc.dds | |||
Alternatively, you can pass the name of a built-in toolchain. See below. | |||
Built-in Toolchains | |||
******************* | |||
For convenience, ``dds`` includes several built-in toolchains that can be | |||
accessed in the ``--toolchain`` command-line option using a colon ``:`` | |||
prefix:: | |||
$ dds build -T :gcc | |||
``dds`` will treat the leading colon (``:``) as a name for a built-in | |||
toolchain (this means that a toolchain's filepath may not begin with a colon). | |||
There are several built-in toolchains that may be specified: | |||
``:gcc`` | |||
Uses the default ``gcc`` and ``g++`` executables, linkers, and options | |||
thereof. | |||
``:gcc-N`` (for some integer ``N``) | |||
Equivalent to ``:gcc``, but uses the ``gcc-N`` and ``g++-N`` executables. | |||
``:clang`` | |||
Equivalent to ``:gcc``, but uses the ``clang`` and ``clang++`` executables. | |||
``:clang-N`` (for some integer ``N``) | |||
Equivalent to ``:clang``, but uses the ``clang-N`` and ``clang++-N`` | |||
executables. | |||
``:msvc`` | |||
Compiles and links using the Visual C++ toolchain. | |||
The following pseudo-toolchains are also available: | |||
``:debug:XYZ`` | |||
Uses built-in toolchain ``:XYZ``, but generates debugging information. | |||
``:ccache:XYZ`` | |||
Uses built-in toolchain ``:XYZ``, but prefixes all compile commands with | |||
``ccache``. | |||
``:c++UV:XYZ`` (for two integers ``UV``) | |||
Sets the C++ version to ``C++UV`` and uses the ``:XYZ`` toolchain. | |||
Toolchain Definitions | |||
********************* | |||
Besides using the built-in toolchains, it is likely that you'll soon want to | |||
customize a toolchain further. Further customization must be done with a | |||
file that contains the toolchain definition. The most basic toolchain file is | |||
simply one line: | |||
.. code-block:: | |||
Compiler-ID: <compiler-id> | |||
where ``<compiler-id>`` is one of the known ``Compiler-ID`` options (See the | |||
toolchain option reference). ``dds`` will infer common suitable defaults for | |||
the remaining options based on the value of ``Compiler-ID``. | |||
For example, if you specify ``GNU``, then ``dds`` will assume ``gcc`` to be the | |||
C compiler, ``g++`` to be the C++ compiler, and ``ar`` to be the library | |||
archiving tool. | |||
If you know that your compiler executable has a different name, you can | |||
specify them with additional options: | |||
.. code-block:: | |||
Compiler-ID: GNU | |||
C-Compiler: gcc-9 | |||
C++-Compiler: g++-9 | |||
``dds`` will continue to infer other options based on the ``Compiler-ID``, but | |||
will use the provided executable names when compiling files for the respective | |||
languages. | |||
To specify compilation flags, the ``Flags`` option can be used: | |||
.. code-block:: | |||
Flags: -fsanitize=address -fno-inline | |||
.. note:: | |||
Use ``Warning-Flags`` to specify options regarding compiler warnings. | |||
Flags for linking executables can be specified with ``Link-Flags``: | |||
.. code-block:: | |||
Link-Flags: -fsanitize=address -fPIE | |||
Toolchain Option Reference | |||
************************** | |||
The following options are available to be specified within a toolchain file: | |||
``Compiler-ID`` | |||
--------------- | |||
Specify the identity of the compiler. This option is used to infer many other | |||
facts about the toolchain. If specifying the full toolchain with the command | |||
templates, this option is not required. | |||
Valid values are: | |||
``GNU`` | |||
For GCC | |||
``Clang`` | |||
For LLVM/Clang | |||
``MSVC`` | |||
For Microsoft Visual C++ | |||
``C-Compiler`` and ``C++-Compiler`` | |||
----------------------------------- | |||
Names/paths of the C and C++ compilers, respectively. Defaults will be inferred | |||
from ``Compiler-ID``. | |||
``C-Version`` and ``C++-Version`` | |||
--------------------------------- | |||
Specify the language versions for C and C++, respectively. By default, ``dds`` | |||
will not set any language version. Using this option requires that the | |||
``Compiler-ID`` be specified | |||
Valid ``C-Version`` values are: | |||
- ``C89`` | |||
- ``C99`` | |||
- ``C11`` | |||
- ``C18`` | |||
Valid ``C++-Version`` values are: | |||
- ``C++98`` | |||
- ``C++03`` | |||
- ``C++11`` | |||
- ``C++14`` | |||
- ``C++17`` | |||
- ``C++20`` | |||
.. warning:: | |||
``dds`` will not do any "smarts" to infer the exact option to pass to have | |||
the required effect. If you ask for ``C++20`` from ``gcc 5.3``, ``dds`` | |||
will simply pass ``-std=c++20`` with no questions asked. If you need | |||
finer-grained control, use the ``Flags`` option. | |||
``Warning-Flags`` | |||
----------------- | |||
Override the compiler flags that should be used to enable warnings. This option | |||
is stored separately from ``Flags``, as these options may be enabled/disabled | |||
separately depending on how ``dds`` is invoked. | |||
.. note:: | |||
If ``Compiler-ID`` is provided, a default value will be used that enables | |||
common warning levels. | |||
If you need to tweak warnings further, use this option. | |||
``Flags``, ``C-Flags``, and ``C++-Flags`` | |||
----------------------------------------- | |||
Specify *additional* compiler options, possibly per-language. | |||
``Link-Flags`` | |||
-------------- | |||
Specify *additional* link options to use when linking executables. | |||
``Optimize`` | |||
------------ | |||
Boolean option (``True`` or ``False``) to enable/disable optimizations. Default | |||
is ``False``. | |||
``Debug`` | |||
--------- | |||
Boolean option (``True`` or ``False``) to enable/disable the generation of | |||
debugging information. Default is ``False``. | |||
``Compiler-Launcher`` | |||
--------------------- | |||
Provide a command prefix that should be used on all compiler executions. | |||
e.g. ``ccache``. | |||
Advanced Options Reference | |||
************************** | |||
The options below are probably not good to tweak unless you *really* know what | |||
you are doing. Their values will be inferred from ``Compiler-ID``. | |||
``Deps-Mode`` | |||
------------- | |||
Specify the way in which ``dds`` should track compilation dependencies. One | |||
of ``GNU``, ``MSVC``, or ``None``. | |||
``C-Compile-File`` | |||
------------------ | |||
Override the *command template* that is used to compile C source files. | |||
``C++-Compile-File`` | |||
-------------------- | |||
Override the *command template* that is used to compile C++ source files. | |||
``Create-Archive`` | |||
------------------ | |||
Override the *command template* that is used to generate static library archive | |||
files. | |||
``Link-Executable`` | |||
------------------- | |||
Override the *command template* that is used to link executables. | |||
``Include-Template`` | |||
-------------------- | |||
Override the *command template* for the flags to specify a header search path. | |||
``External-Include-Template`` | |||
----------------------------- | |||
Override the *command template* for the flags to specify a header search path | |||
of an external library. | |||
``Define-Template`` | |||
------------------- | |||
Override the *command template* for the flags to set a preprocessor definition. |
@@ -0,0 +1,27 @@ | |||
DDS | |||
###### | |||
**dds** is the Drop-Dead-Simple build and library management tool. | |||
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 | |||
the :doc:`tut/index` page. | |||
.. toctree:: | |||
:maxdepth: 2 | |||
tut/index | |||
guide/index | |||
design | |||
Indices and tables | |||
================== | |||
* :ref:`genindex` | |||
* :ref:`modindex` | |||
* :ref:`search` |
@@ -0,0 +1,39 @@ | |||
A *Hello, World* Library | |||
######################## | |||
Creating a single application is fun, but what if we want to create code that | |||
we can distribute and reuse across other packages and projects? That's what | |||
*libraries* are for. | |||
You may notice something strange: This page is much shorter than the previous. | |||
What gives? | |||
It turns out that we've already covered all the material to make a library in | |||
the page on creating a :doc:`Hello, world! executable <hello-world>`. As soon | |||
as we created the ``strings.hpp`` file, our project had become a library. When | |||
we added a ``strings.cpp`` file to accompany it, our library became a *compiled* | |||
library. | |||
The ``hello-world.main.cpp`` file is expressly *not* considered to be part of a | |||
library, as it is specifically designated to be an application entry point, | |||
and source files of such kind are not part of a library. | |||
Before continuing on, note the following about creating a library that wasn't | |||
specifically addressed in the prior example: | |||
#. The *source roots* of a library are added to the compiler's ``#include`` | |||
search-path. In our example, this is only the ``src/`` directory of the | |||
project. | |||
#. ``dds`` also supports a top-level directory named ``include/``. Both | |||
``include/`` and ``src/`` may be present in a single library, but there are | |||
some important differences. Refer to :ref:`pkgs.lib-roots` in the layout | |||
guide. | |||
#. A single *library root* may contain any number of applications defined in | |||
``.main`` files, but a *library root* will correspond to exactly *one* | |||
library. Defining additional libraries requires additional packages or | |||
adding multiple library roots to a single package. | |||
.. seealso:: | |||
Like flossing, we all know we *should* be writing tests, but it can be such | |||
a hassle. Fortunately, ``dds`` makes it simple. Read on to: | |||
:doc:`hello-test`. |
@@ -0,0 +1,127 @@ | |||
A *Hello, World* Test | |||
##################### | |||
So far, we have a simple library with a single function: ``get_greeting()`` | |||
and an application that makes use of it. How can we test it? | |||
With ``dds``, similar to generating applications, creating a test requires | |||
adding a suffix to a source filename stem. Instead of ``.main``, simply | |||
add ``.test`` before the file extension. | |||
A New Test Executable | |||
********************* | |||
We'll create a test for our ``strings`` component, in a file named | |||
``strings.test.cpp``. We'll use an ``assert`` to check our ``get_greeting()`` | |||
function: | |||
.. code-block:: c++ | |||
:caption: ``<root>/src/hello/strings.test.cpp`` | |||
:linenos: | |||
#include <hello/strings.hpp> | |||
int main() { | |||
if (hello::get_greeting() != "Hello world!") { | |||
return 1; | |||
} | |||
} | |||
If you run ``dds build`` once again, ``dds`` will generate a test executable | |||
and run it immediately. If the test executable exits with a non-zero exit code, | |||
then it will consider the test to have failed, and ``dds`` itself will exit | |||
with a non-zero exit code. | |||
.. important:: | |||
``dds`` executes tests *in parallel* by default! If the tests need access | |||
to a shared resource, locking must be implemented manually, or the shared | |||
resource should be split. | |||
.. note:: | |||
``dds`` builds and executes tests for *every build* **by default**. The | |||
``*.test.cpp`` tests are meant to be very fast *unit* tests, so consider | |||
their execution time carefully. | |||
If your code matches the examples so far, the above test will *fail*. Keen eyes | |||
will already know the problem, but wouldn't it be better if we had better test | |||
diagnostics? | |||
A ``Test-Driver``: Using *Catch2* | |||
********************************* | |||
``dds`` ships with built-in support for the `Catch2`_ C and C++ testing | |||
framework, a popular | |||
.. _catch2: https://github.com/catchorg/Catch2 | |||
To make use of Catch as our test driver, we simply declare this intent in the | |||
``package.dds`` file at the package root: | |||
.. code-block:: yaml | |||
:caption: ``<root>/package.dds`` | |||
:emphasize-lines: 5 | |||
Name: hello-dds | |||
Version: 0.1.0 | |||
Namespace: tutorial | |||
Test-Driver: Catch-Main | |||
If you now run ``dds build``, we will get a linker error for a multiply-defined | |||
``main`` function. When setting the ``Test-Driver`` to ``Catch-Main``, ``dds`` | |||
will compile an entrypoint separately from any particular test, and the tests | |||
will link against that entrypoint. This means we cannot provide our own | |||
``main`` function, and should instead use Catch's ``TEST_CASE`` macro to | |||
declare our test cases. | |||
In addition to an entrypoint, ``dds`` provides a ``catch.hpp`` header that we | |||
may use in our tests, simply by ``#include``-ing the appropriate path. We'll | |||
modify our test to use the Catch test macros instead of our own logic. We'll | |||
leave the condition the same, though: | |||
.. code-block:: c++ | |||
:caption: ``<root>/src/hello/strings.test.cpp`` | |||
:linenos: | |||
:emphasize-lines: 3, 5-7 | |||
#include <hello/strings.hpp> | |||
#include <catch2/catch.hpp> | |||
TEST_CASE("Check the greeting") { | |||
CHECK(hello::get_greeting() == "Hello world!"); | |||
} | |||
Now running ``dds build`` will print more output that Catch has generated as | |||
part of test execution, and we can see the reason for the failing test:: | |||
[16:41:45] [error] Test <root>/_build/test/hello/strings failed! Output: | |||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
strings is a Catch v2.10.2 host application. | |||
Run with -? for options | |||
------------------------------------------------------------------------------- | |||
Check the greeting | |||
------------------------------------------------------------------------------- | |||
<root>/src/hello/strings.test.cpp:5 | |||
............................................................................... | |||
<root>/src/hello/strings.test.cpp:5: FAILED: | |||
CHECK( hello::get_greeting() == "Hello world!" ) | |||
with expansion: | |||
"Hello, world!" == "Hello world!" | |||
=============================================================================== | |||
test cases: 1 | 1 failed | |||
assertions: 1 | 1 failed | |||
[dds - test output end] | |||
Now that we have the direct results of the offending expression, we can | |||
much more easily diagnose the nature of the test failure. In this case, the | |||
function returns a string containing a comma ``,`` while our expectation lacks | |||
one. If we fix either the ``get_greeting`` or the expected string, we will then | |||
see our tests pass successfully and ``dds`` will exit cleanly. |
@@ -0,0 +1,251 @@ | |||
A *Hello, world!* Application | |||
############################# | |||
Creating a *hello world* application is very simple. | |||
Creating a *Package Root* | |||
************************* | |||
To start, create a new directory for your project. This will be known as the | |||
*package root*, and the entirety of our project will be placed in this | |||
directory the name and location of this directory is not important, but the | |||
contents therein will be significant. | |||
.. note:: | |||
The term *package root* is further described in the :doc:`/guide/packages` page. | |||
From here on, this created directory will simply be noted as ``<root>``. In | |||
the examples, this will refer to the directory package root directory we have | |||
created. | |||
Creating the First *Source Root* | |||
******************************** | |||
Within the package root, we create our first *source root*. Since we are | |||
intending to compile files, we need to use the name that ``dds`` has designated | |||
to be the source root that may contain compilable source files: ``src/``: | |||
.. code-block:: bash | |||
mkdir src | |||
You should now have a single item in the package root, at ``<root>/src/``. This | |||
is the directory from which ``dds`` will search for source files. | |||
Creating an Application Entrypoint | |||
********************************** | |||
To add a source file to our project, we simply create a file within a source | |||
root with the appropriate file extension. Our source root is ``<root>/src/``, | |||
so we'll place a source file in there. In addition, because we want to create | |||
an *application* we need to designate that the source file provides an | |||
application *entry point*, i.e. a ``main()`` function. To do this, we simply | |||
prepend ``.main`` to the file extension. Create a file:: | |||
> <root>/src/hello-world.main.cpp | |||
and open it in your editor of choice. We'll add the classic C++ *hello, world* | |||
program: | |||
.. code-block:: c++ | |||
:linenos: | |||
:caption: ``<root>/src/hello-world.main.cpp`` | |||
#include <iostream> | |||
int main() { | |||
std::cout << "Hello, world!\n"; | |||
} | |||
Building *Hello, World* | |||
*********************** | |||
Now comes the fun part. It is time to actually compile the application! | |||
.. important:: | |||
If you intend to compile with Visual C++, the build must be executed | |||
from within a Visual Studio or Visual C++ development command prompt. These | |||
program shortcuts should be made available with any standard installation | |||
of the Visual C++ toolchain. | |||
``dds`` **will not** automatically load the Visual C++ environment. | |||
To build the program, we must provide ``dds`` with information about our | |||
program toolchain. ``dds`` comes with a few "built in" toolchain options that | |||
can be used out-of-the-box, and they'll be suitable for our purposes. | |||
- If you are compiling with GCC, the toolchain name is ``:gcc`` | |||
- If you are compiling with Clang, the toolchain name is ``:clang`` | |||
- If you are compiling with Visual C++, the toolchain name is ``:msvc`` | |||
.. note:: | |||
The leading colon ``:`` is important: This tells ``dds`` to use its | |||
built-in toolchain information rather than looking for a toolchain file of | |||
that name. | |||
To execute the build, run the ``dds build`` command as in the following | |||
example, providing the appropriate toolchain name in place of ``<toolchain>``:: | |||
> dds build -t <toolchain> | |||
For example, if you are using ``gcc``, you would run the command as:: | |||
> dds build -t :gcc | |||
If all successful, ``dds`` will emit information about the compile and link | |||
process, and then exit without error. | |||
By default, build results will be placed in a subdirectory of the package root | |||
named ``_build``. Within this directory, you will find the generated executable | |||
named ``hello-world`` (with a ``.exe`` suffix if on Windows). | |||
We should not be able to run this executable and see our ``Hello, world!``:: | |||
> ./_build/hello-world | |||
Hello, world! | |||
Obviously this isn't *all* there is to do with ``dds``. Read on to the next | |||
pages to learn more. | |||
.. note:: | |||
You're reading a very early version of these docs. There will be a lot more | |||
here in the future. Watch this space for changes! | |||
More Sources | |||
************ | |||
Modularizing our program is good, right? Let's do that. | |||
Add a Header | |||
************ | |||
Create a new subdirectory of ``src``, and we'll call it ``hello``:: | |||
> mkdir src/hello | |||
Within this directory, create a ``strings.hpp``. Edit the content in your | |||
editor of choice: | |||
.. code-block:: c++ | |||
:caption: ``<root>/src/hello/strings.hpp`` | |||
:linenos: | |||
#ifndef HELLO_STRINGS_HPP_INCLUDED | |||
#define HELLO_STRINGS_HPP_INCLUDED | |||
#include <string> | |||
namespace hello { | |||
std::string get_greeting(); | |||
} | |||
#endif | |||
Change our ``main()`` | |||
********************* | |||
Modify the content of ``<root>/src/hello-world.main.cpp`` to include our new | |||
header and to use our ``get_greeting()`` function: | |||
.. code-block:: c++ | |||
:caption: ``<root>/src/hello-world.main.cpp`` | |||
:linenos: | |||
:emphasize-lines: 1, 6 | |||
#include <hello/strings.hpp> | |||
#include <iostream> | |||
int main() { | |||
std::cout << hello::get_greeting() << '\n'; | |||
} | |||
Compiling Again, and Linking...? | |||
******************************** | |||
If you run the ``dds build`` command again, you will now see an error: | |||
.. code-block:: text | |||
[12:55:25] [info ] [dds-hello] Link: hello-world | |||
[12:55:25] [info ] [dds-hello] Link: hello-world - 57ms | |||
[12:55:25] [error] Failed to link executable '<root>/_build/hello-world'. | |||
... | |||
<additional lines follow> | |||
The problem, of course, is that we've declared ``get_greeting`` to *exist*, but | |||
be haven't *defined it*. | |||
Adding Another Compiled Source | |||
****************************** | |||
We'll add another compilable source file to our project. In the same | |||
directory as ``strings.hpp``, add ``strings.cpp``: | |||
.. code-block:: c++ | |||
:caption: ``<root>/src/hello/strings.cpp`` | |||
:linenos: | |||
#include <hello/strings.hpp> | |||
std::string hello::get_greeting() { | |||
return "Hello, world!"; | |||
} | |||
Compiling and Linking! | |||
********************** | |||
Run the ``dds build`` command again, and you'll find that the application | |||
successfully compiles and links! | |||
If you've used other build systems, you may have noticed a missing step: We | |||
never told ``dds`` about our new source file. Actually, we never told ``dds`` | |||
about *any* of our source files. We never even told it the name of the | |||
executable to generate. What gives? | |||
It turns out, we *did* tell ``dds`` all of this information by simply placing | |||
the files on the filesystem with the appropriate file paths. The name of the | |||
executable, ``hello-world``, was inferred by stripping the trailing ``.main`` | |||
from the stem of the filename which defined the entry point. | |||
Cleaning Up | |||
*********** | |||
There's one final formality that should be taken care of before proceeding: | |||
Creating a package manifest file. | |||
``dds`` will work happily with packages that do not declare themselves, as long | |||
as the filesystem structure is sufficient. However: To use features covered in | |||
later tutorials, we'll need a simple ``package.dds`` file to declare | |||
information about are package. This file should be placed directly in the | |||
package root: | |||
.. code-block:: yaml | |||
:caption: ``<root>/package.dds`` | |||
Name: hello-dds | |||
Version: 0.1.0 | |||
Namespace: tutorial | |||
.. note:: | |||
The ``Namespace`` option will be discussed later. | |||
Rebuilding the project will show no difference at the moment. | |||
.. seealso:: | |||
Creating a single application executable is fine and all, but what if we | |||
want to create libraries? See the next page: :doc:`hello-lib` |
@@ -0,0 +1,19 @@ | |||
Tutorials and Beginner's Guide | |||
############################## | |||
The child pages here contain introductory material for getting started with | |||
using ``dds``. If you don't know where to start, this will be a good | |||
place to begin! | |||
.. note:: | |||
The shell samples in these pages are written with Unix-flavored commands, | |||
but the analogous process will work just as well on Windows systems. | |||
Translate as appropriate. | |||
.. toctree:: | |||
:maxdepth: 2 | |||
hello-world | |||
hello-lib | |||
hello-test |
@@ -0,0 +1,16 @@ | |||
set -eu | |||
THIS_SCRIPT=$(readlink -m $0) | |||
HERE=$(dirname ${THIS_SCRIPT}) | |||
ROOT=$(dirname ${HERE}) | |||
while true; do | |||
echo "Watching for changes..." | |||
inotifywait -r ${ROOT}/docs/ -q \ | |||
-e modify \ | |||
-e close_write \ | |||
-e move \ | |||
-e delete \ | |||
-e create | |||
make docs || : | |||
done |