Просмотр исходного кода

Merge branch 'feature/docs' into develop

default_compile_flags
vector-of-bool 5 лет назад
Родитель
Сommit
0c26a59280
17 измененных файлов: 1716 добавлений и 0 удалений
  1. +27
    -0
      Makefile
  2. +0
    -0
      docs/_static/tweaks.css
  3. +31
    -0
      docs/conf.py
  4. +195
    -0
      docs/design.rst
  5. +116
    -0
      docs/guide/catalog.rst
  6. +14
    -0
      docs/guide/index.rst
  7. +116
    -0
      docs/guide/interdeps.rst
  8. +301
    -0
      docs/guide/packages.rst
  9. +92
    -0
      docs/guide/repo.rst
  10. +43
    -0
      docs/guide/source-dists.rst
  11. +302
    -0
      docs/guide/toolchains.rst
  12. +27
    -0
      docs/index.rst
  13. +39
    -0
      docs/tut/hello-lib.rst
  14. +127
    -0
      docs/tut/hello-test.rst
  15. +251
    -0
      docs/tut/hello-world.rst
  16. +19
    -0
      docs/tut/index.rst
  17. +16
    -0
      tools/docs-watch.sh

+ 27
- 0
Makefile Просмотреть файл

@@ -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
docs/_static/tweaks.css Просмотреть файл


+ 31
- 0
docs/conf.py Просмотреть файл

@@ -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')

+ 195
- 0
docs/design.rst Просмотреть файл

@@ -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.

+ 116
- 0
docs/guide/catalog.rst Просмотреть файл

@@ -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"
}
}
}
}
}

+ 14
- 0
docs/guide/index.rst Просмотреть файл

@@ -0,0 +1,14 @@
.. _guide:

User Guide
##########

.. toctree::
:maxdepth: 2

packages
toolchains
source-dists
repo
catalog
interdeps

+ 116
- 0
docs/guide/interdeps.rst Просмотреть файл

@@ -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.

+ 301
- 0
docs/guide/packages.rst Просмотреть файл

@@ -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

+ 92
- 0
docs/guide/repo.rst Просмотреть файл

@@ -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.

+ 43
- 0
docs/guide/source-dists.rst Просмотреть файл

@@ -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`

+ 302
- 0
docs/guide/toolchains.rst Просмотреть файл

@@ -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.

+ 27
- 0
docs/index.rst Просмотреть файл

@@ -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`

+ 39
- 0
docs/tut/hello-lib.rst Просмотреть файл

@@ -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`.

+ 127
- 0
docs/tut/hello-test.rst Просмотреть файл

@@ -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.

+ 251
- 0
docs/tut/hello-world.rst Просмотреть файл

@@ -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`

+ 19
- 0
docs/tut/index.rst Просмотреть файл

@@ -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

+ 16
- 0
tools/docs-watch.sh Просмотреть файл

@@ -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

Загрузка…
Отмена
Сохранить