| @@ -3,13 +3,14 @@ | |||
| ``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. | |||
| ``dds`` has a strong 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``. | |||
| Indeed, applications *are* essential, and ``dds`` is able to build those as | |||
| well. | |||
| 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 | |||
| @@ -32,8 +33,8 @@ 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. | |||
| realizing it, and we seem to be still a great distance from a unified library | |||
| package distribution and consumption mechanism. | |||
| Tabula Rasa | |||
| @@ -46,7 +47,7 @@ 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 | |||
| 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 | |||
| @@ -69,14 +70,14 @@ 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 | |||
| using ``dds``. Simply running ``dds build`` 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. | |||
| If you play by the rules, you get to play in this space. | |||
| .. _design.rules: | |||
| @@ -91,7 +92,7 @@ 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 | |||
| @@ -105,22 +106,28 @@ violate any of the other existing rules. | |||
| 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. | |||
| ``dds`` *does* contain functionality for building applications, but they must | |||
| also play by the rules. | |||
| If you want to build a complex application with ``dds`` that uses lots of | |||
| platform-specific sources, code generation, and conditional components, a good | |||
| option is to use an external build script that prepares the project tree before | |||
| invoking ``dds``. | |||
| .. _design.rules.change: | |||
| *Your* Code Should Be Changed Before ``dds`` Should Be Changed | |||
| ================================================================= | |||
| *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. | |||
| The wording of this rule means that the onus is on the 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. | |||
| If your project meets all the requirements outlined in this document but you | |||
| still find trouble in making your build work, or if you *cannot* see *any* | |||
| possible way for your project to be built by ``dds`` regardless of what changes | |||
| you make, then it this is grounds for change in ``dds``, either in clarifying | |||
| the rules or tweaking ``dds`` functionality | |||
| .. _design.rules.layout: | |||
| @@ -154,9 +161,8 @@ conditional compilation. | |||
| 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. | |||
| ``dds`` does not provide code-generation functionality. Instead, any generated | |||
| code should be generated by separate build steps before ``dds`` is executed. | |||
| .. _design.rules.one-binary-per-src: | |||
| @@ -176,7 +182,7 @@ 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 | |||
| resolution while building a project, so all ``#include`` directives should be | |||
| relative to those directories. Refer to :ref:`pkg.source-root`. | |||
| @@ -185,7 +191,7 @@ relative to those directories. Refer to :ref:`pkg.source-root`. | |||
| All Files Compile with the Same Options | |||
| ======================================= | |||
| When DDS compiles a library, every source file will be compiled with an | |||
| When DDS compiles a project, 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. | |||
| @@ -12,4 +12,4 @@ as the ``ref`` requires support from the remote Git server, and it is often | |||
| unavailable in most setups). Using a Git tag is strongly recommended. | |||
| .. seealso:: | |||
| Refer to the documentation on :doc:`/guide/catalog`. | |||
| Refer to the documentation on :doc:`/guide/remote-pkgs`. | |||
| @@ -1,9 +0,0 @@ | |||
| Error: Invalid catalog JSON | |||
| ########################### | |||
| This error occurs when the JSON data given to import into the package catalog | |||
| is in some way invalid. Refer to the catalog documentation for a description of | |||
| the proper JSON format. | |||
| .. seealso:: | |||
| :ref:`catalog.adding` | |||
| @@ -1,10 +0,0 @@ | |||
| Error: A repository filesystem transformation is invalid | |||
| ######################################################## | |||
| In ``dds``, a catalog entry can have a list of attached "transforms" that will | |||
| be applies to the root directory of the package before ``dds`` tries to build | |||
| and use it. | |||
| .. seealso:: | |||
| For information on the shape and purpose of transforms, refer to | |||
| :ref:`catalog.fs-transform` on the :doc:`/guide/catalog` page. | |||
| @@ -6,5 +6,3 @@ requires some information regarding how to actually *acquire* that package | |||
| when it is requested. | |||
| If such information is not provided, ``dds`` will issue an error. | |||
| .. seealso:: :ref:`catalog.adding`. | |||
| @@ -8,10 +8,3 @@ in the catalog. | |||
| It is possible that the intended package *does exist* but that the spelling of | |||
| the package name or version number is incorrect. Firstly, check your spelling | |||
| and that the version number you have requested is correct. | |||
| In another case, it is possible that the package *exists somewhere*, but has | |||
| not been loaded into the local catalog. As of this writing, ``dds`` does not | |||
| automatically maintain the catalog against a central package repository, so | |||
| package entries must be loaded and imported manually. If you believe this to be | |||
| the case, refer to the section on the :doc:`/guide/catalog`, especially the | |||
| section :ref:`catalog.adding`. | |||
| @@ -19,7 +19,7 @@ write a source distribution to the named path, it would be required to delete | |||
| whatever exists there before creating the source distribution. | |||
| .. warning:: | |||
| When using ``dds sdist create`` with the ``--out <path>`` parameter, the | |||
| When using ``dds pkg create`` with the ``--out <path>`` parameter, the | |||
| ``<path>`` given **is not the directory in which to place the source | |||
| distribution, but the filepath to the source distribution itself**! | |||
| @@ -27,7 +27,7 @@ whatever exists there before creating the source distribution. | |||
| distribution in that directory, **the following command is incorrect**:: | |||
| # Do not do this: | |||
| dds sdist create --out foo/ | |||
| dds pkg create --out foo/ | |||
| If you pass ``--replace`` to the above command, ``dds`` will **destroy the | |||
| existing directory** and replace it with the source distribution! | |||
| @@ -35,4 +35,4 @@ whatever exists there before creating the source distribution. | |||
| You **must** provide the full path to the source distribution:: | |||
| # Do this: | |||
| dds sdist create --out foo/my-project.dsd | |||
| dds pkg create --out foo/my-project.tar.gz | |||
| @@ -0,0 +1,187 @@ | |||
| Building and Using ``dds`` in Another Build System | |||
| ################################################## | |||
| One of ``dds``'s primary goals is to inter-operate with other build systems | |||
| cleanly. One of ``dds``'s primary outputs is *libman* package indices. These | |||
| package indices can be imported into other build systems that support the | |||
| `libman`_ format. (At the time of writing there is a CMake module which can do | |||
| the import, but other build systems are planned.) | |||
| .. _libman: https://api.csswg.org/bikeshed/?force=1&url=https://raw.githubusercontent.com/vector-of-bool/libman/develop/data/spec.bs | |||
| .. _PMM: https://github.com/vector-of-bool/PMM | |||
| .. _CMakeCM: https://github.com/vector-of-bool/CMakeCM | |||
| .. _lm-cmake: https://raw.githubusercontent.com/vector-of-bool/libman/develop/cmake/libman.cmake | |||
| .. _build-deps.gen-libman: | |||
| Generating a libman Index | |||
| ************************* | |||
| Importing libman packages into a build system requires that we have a libman | |||
| index generated on the filesystem. **This index is not generated globally**: It | |||
| is generated on a per-build basis as part of the build setup. The index will | |||
| describe in build-system-agnostic terms how to include a set of packages and | |||
| libraries as part of a build. | |||
| ``dds`` has first-class support for generating this index. The ``build-deps`` | |||
| subcommand of ``dds`` will download and build a set of dependencies, and places | |||
| an ``INDEX.lmi`` file that can be used to import the built results. | |||
| Declaring Dependencies | |||
| ====================== | |||
| ``dds build-deps`` accepts a list of dependency statements as command line | |||
| arguments, but it may be useful to specify those requirements in a file. | |||
| ``dds build-deps`` accepts a JSON5 file describing the dependencies of a | |||
| project as well. This file is similar to a very stripped-down version of a | |||
| ``dds`` :ref:`package manifest <pkgs.pkgs>`, and only includes the ``depends`` | |||
| key. (The presence of any other key is an error.) | |||
| Here is a simple dependencies file that declares a single requirement: | |||
| .. code-block:: js | |||
| :caption: ``dependencies.json5`` | |||
| { | |||
| depends: [ | |||
| 'neo-sqlite3^0.2.0', | |||
| ] | |||
| } | |||
| Building Dependencies and the Index | |||
| =================================== | |||
| We can invoke ``dds build-deps`` and give it the path to this file: | |||
| .. code-block:: bash | |||
| $ dds build-deps --deps-file dependencies.json5 | |||
| When finished, ``dds`` will write the build results into a subdirectory called | |||
| ``_deps`` and generate a file named ``INDEX.lmi``. This file is ready to be | |||
| imported into any build system that can understand libman files. | |||
| .. note:: | |||
| The output directory and index filepath can be controlled with the | |||
| ``--out`` and ``--lmi-path`` flags, respectively. | |||
| Importing an Index: CMake | |||
| ************************* | |||
| .. highlight:: cmake | |||
| .. note:: | |||
| This section discusses how to import ``INDEX.lmi`` into CMake, but ``dds`` | |||
| also has built-in support for generating a CMake targets file. See | |||
| :doc:`cmake` and :ref:`cmake.pmm` for even simpler integration steps. | |||
| Supposed that we've generated a libman index and set of packages, and we want to | |||
| import them into CMake. CMake doesn't know how to do this natively, but there | |||
| exists a single-file module for CMake that allows CMake to import libraries from | |||
| libman indices without any additional work. | |||
| The module is not shipped with CMake, but is available online as a single | |||
| stand-alone file. The `libman.cmake <lm-cmake_>`_ file can be downloaded and | |||
| added to a project directly, or it can be obtained automatically through a | |||
| CMake tool like `PMM`_ (recommended). | |||
| Getting ``libman.cmake`` via PMM | |||
| ================================ | |||
| Refer to the ``README.md`` file in `the PMM repo <PMM_>`_ for information on how | |||
| to get PMM into your CMake project. In short, download and place the | |||
| ``pmm.cmake`` file in your repository, and ``include()`` the file near the top | |||
| of your ``CMakeLists.txt``:: | |||
| include(pmm.cmake) | |||
| Once it has been included, you can call the ``pmm()`` function. To obtain | |||
| *libman*, we need to start by enabling `CMakeCM`_:: | |||
| pmm(CMakeCM ROLLING) | |||
| .. warning:: | |||
| It is not recommended to use the ``ROLLING`` mode, but it is the easiest to | |||
| use when getting started. For reproducible and reliable builds, you should | |||
| pin your CMakeCM version using the ``FROM <url>`` argument. | |||
| Enabling CMakeCM will make available all of the CMake modules available in `the | |||
| CMakeCM repository <CMakeCM_>`_, which includes `libman.cmake <lm-cmake_>`_. | |||
| After the call to ``pmm()``, simply ``include()`` the ``libman`` module:: | |||
| include(libman) | |||
| That's it! The only function from the module that we will care about for now | |||
| is the ``import_packages()`` function. | |||
| Importing Our Dependencies' Packages | |||
| ==================================== | |||
| To import a package from a libman tree, we need only know the *name* of the | |||
| package we wish to import. In our example case above, we depend on | |||
| ``neo-sqlite3``, so we simply call the libman-CMake function | |||
| ``import_packages()`` with that package name:: | |||
| import_packages("neo-sqlite3") | |||
| You'll note that we don't request any particular version of the package: All | |||
| versioning resolution is handled by ``dds``. You'll also note that we don't | |||
| need to specify our transitive dependencies: This is handled by the libman | |||
| index that was generated by ``dds``: It will automatically ``import_packages()`` | |||
| any of the transitive dependencies required. | |||
| More than one package name can be provided to a single call to | |||
| ``import_packages()``, and ``import_packages()`` may be called multiple times | |||
| within a CMake project. | |||
| Using Our Dependencies' Libraries | |||
| ================================= | |||
| Like with ``dds``, CMake wants us to explicitly declare how our build targets | |||
| *use* other libraries. When we import a package from a libman index, the | |||
| import will generate CMake ``IMPORTED`` targets that can be linked against. | |||
| In ``dds`` and in libman, a library is identified by a combination of | |||
| *namespace* and *name*, joined together with a slash ``/`` character. This | |||
| *qualified name* of a library is decided by the original package author, and | |||
| should be documented. In the case of ``neo-sqlite3``, the only library is | |||
| ``neo/sqlite3``. | |||
| When the libman CMake module imports a library, it creates a qualified name | |||
| using a double-colon "``::``" instead of a slash. As such, our ``neo/sqlite3`` | |||
| is imported in CMake as ``neo::sqlite3``. We can link against it as we would | |||
| with any other target:: | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| Altogether, here is the final CMake file: | |||
| .. code-block:: | |||
| :caption: ``CMakeLists.txt`` | |||
| :linenos: | |||
| cmake_minimum_required(VERSION 3.15) | |||
| project(MyApplication VERSION 1.0.0) | |||
| include(pmm.cmake) | |||
| pmm(CMakeCM ROLLING) | |||
| include(libman) | |||
| import_packages("neo-sqlite3") | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| @@ -1,286 +0,0 @@ | |||
| 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. | |||
| .. _catalog.adding: | |||
| Adding Packages to the Catalog | |||
| ****************************** | |||
| The ``dds catalog import`` supports a ``--json`` flag that specifies a JSON5 | |||
| 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 array of dependency statements for this | |||
| // particular version of the package. (Optional) | |||
| depends: [ | |||
| "acme-widgets^1.4.1" | |||
| ], | |||
| // `description` is an attribute to give a string to describe | |||
| // the package. (Optional) | |||
| description: "A collection of useful gadgets.", | |||
| // 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", | |||
| // List of filesystem transformations to apply to the repository | |||
| // (optional) | |||
| transform: [ | |||
| // ... (see below) ... | |||
| ] | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .. _catalog.fs-transform: | |||
| Filesystem Transformations | |||
| ************************** | |||
| .. note:: | |||
| Filesystem transformations is a transitional feature that is likely to be | |||
| removed in a future release, and replaced with a more robust system when | |||
| ``dds`` has a better way to download packages. Its aim is to allow ``dds`` | |||
| projects to use existing libraries that might not meet the layout | |||
| requirements that ``dds`` imposes, but can otherwise be consumed by ``dds`` | |||
| with a few tweaks. | |||
| A catalog entry can have a set of filesystem transformations attached to its | |||
| remote information (e.g. the ``git`` property). When ``dds`` is obtaining a | |||
| copy of the code for the package, it will apply the associated transformations | |||
| to the filesystem based in the directory of the downloaded/cloned directory. In | |||
| this way, ``dds`` can effectively "patch" the filesystem structure of a project | |||
| arbitrarily. This allows many software projects to be imported into ``dds`` | |||
| without needing to patch/fork the original project to support the required | |||
| filesystem structure. | |||
| .. important:: | |||
| While ``dds`` allows you to patch directories downloaded via the catalog, a | |||
| native ``dds`` project must still follow the layout rules. | |||
| The intention of filesystem transformations is to act as a "bridge" that will allow ``dds`` projects to more easily utilize existing libraries. | |||
| Available Transformations | |||
| ========================= | |||
| At time of writing, there are five transformations available to catalog entries: | |||
| ``copy`` and ``move`` | |||
| Copies or moves a set of files/directories from one location to another. Allows the following options: | |||
| - ``from`` - The path from which to copy/move. **Required** | |||
| - ``to`` - The destination path for the copy/move. **Required** | |||
| - ``include`` - A list of globbing expressions for files to copy/move. If | |||
| omitted, then all files will be included. | |||
| - ``exclude`` - A list of globbing expressions of files to exclude from the | |||
| copy/move. If omitted, then no files will be excluded. **If both** ``include`` and ``exclude`` are provided, ``include`` will be checked *before* ``exclude``. | |||
| - ``strip-components`` - A positive integer (or zero, the default). When the | |||
| ``from`` path identifies a directory, its contents will be copied/moved | |||
| into the destination and maintain their relative path from the source path as their relative path within the destination. If ``strip-components`` is set to an integer ``N``, then the first ``N`` path components of that relative path will be removed when copying/moving the files in a directory. If a file's relative path has less than ``N`` components, then that file will be excluded from the ``copy/move`` operation. | |||
| ``remove`` | |||
| Delete files and directories from the package source. Has the following options: | |||
| - ``path`` - The path of the file/directory to remove. **Required** | |||
| - ``only-matching`` - A list of globbing expressions for files to remove. If omitted and the path is a directory, then the entire directory will be deleted. If at least one pattern is provided, then directories will be left intact and only non-directory files will be removed. If ``path`` names a non-directory file, then this option has no effect. | |||
| ``write`` | |||
| Write the contents of a string to a file in the package source. Has the following options: | |||
| - ``path`` - The path of the file to write. **Required** | |||
| - ``content`` - A string that will be written to the file. **Required** | |||
| If the file exists and is not a directory, the file will be replaced. If the | |||
| path names an existing directory, an error will be generated. | |||
| ``edit`` | |||
| Modifies the contents of the files in the package. | |||
| - ``path`` - Path to the file to edit. **Required** | |||
| - ``edits`` - An array of edit objects, applied in order, with the following | |||
| keys: | |||
| - ``kind`` - One of ``insert`` or ``delete`` to insert/delete lines, | |||
| respectively. **Required** | |||
| - ``line`` - The line at which to perform the insert/delete. The first line | |||
| of the file is line one, *not* line zero. **Required** | |||
| - ``content`` - For ``insert``, the string content to insert into the file. | |||
| A newline will be appended after the content has been inserted. | |||
| Transformations are added as a JSON array to the JSON object that specifies | |||
| the remote information for the package. Each element of the array is an | |||
| object, with one or more of the keys listed above. If an object features more | |||
| than one of the above keys, they are applied in the same order as they have | |||
| been listed. | |||
| Example: Crypto++ | |||
| ================= | |||
| The following catalog entry will build and import `Crypto++`_ for use by a | |||
| ``dds`` project. This uses the unmodified Crypto++ repository, which ``dds`` | |||
| doesn't know how to build immediately. With some simple moving of files, we | |||
| end up with something ``dds`` can build directly: | |||
| .. code-block:: javascript | |||
| "cryptopp": { | |||
| "8.2.0": { | |||
| "git": { | |||
| "url": "https://github.com/weidai11/cryptopp.git", | |||
| "ref": "CRYPTOPP_8_2_0", | |||
| "auto-lib": "cryptopp/cryptopp", | |||
| "transform": [ | |||
| { | |||
| // Crypto++ has no source directories at all, and everything lives | |||
| // at the top level. No good for dds. | |||
| // | |||
| // Clients are expected to #include files with a `cryptopp/` prefix, | |||
| // so we need to move the files around so that they match the | |||
| // expected layout: | |||
| "move": { | |||
| // Move from the root of the repo: | |||
| "from": ".", | |||
| // Move files *into* `src/cryptopp` | |||
| "to": "src/cryptopp", | |||
| // Only move the C++ sources and headers: | |||
| "include": [ | |||
| "*.c", | |||
| "*.cpp", | |||
| "*.h" | |||
| ] | |||
| } | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| } | |||
| Example: libsodium | |||
| ================== | |||
| For example, this catalog entry will build and import `libsodium`_ for use in | |||
| a ``dds`` project. This uses the upstream libsodium repository, which does not | |||
| meet the layout requirements needed by ``dds``. With a few simple | |||
| transformations, we can allow ``dds`` to build and consume libsodium | |||
| successfully: | |||
| .. code-block:: javascript | |||
| "libsodium": { | |||
| "1.0.18": { | |||
| "git": { | |||
| "url": "https://github.com/jedisct1/libsodium.git", | |||
| "ref": "1.0.18", | |||
| "auto-lib": "sodium/sodium", | |||
| /// Make libsodium look as dds expects of a project. | |||
| "transform": [ | |||
| // libsodium has a `src` directory, but it does not look how dds | |||
| // expects it to. The public `#include` root of libsodium lives in | |||
| // a nested subdirectory of `src/` | |||
| { | |||
| "move": { | |||
| // Move the public header root out from that nested subdirectory | |||
| "from": "src/libsodium/include", | |||
| // Put it at `include/` in the top-level | |||
| "to": "include/" | |||
| } | |||
| }, | |||
| // libsodium has some files whose contents are generated by a | |||
| // configure script. For demonstration purposes, we don't need most | |||
| // of them, and we can just swipe an existing pre-configured file | |||
| // that is already in the source repository and put it into the | |||
| // public header root. | |||
| { | |||
| "copy": { | |||
| // Generated version header committed to the repository: | |||
| "from": "builds/msvc/version.h", | |||
| // Put it where the configure script would put it: | |||
| "to": "include/sodium/version.h" | |||
| } | |||
| }, | |||
| // The subdirectory `src/libsodium/` is no good. It now acts as an | |||
| // unnecessary layer of indirection. We want `src/` to be the root. | |||
| // We can just "lift" the subdirectory: | |||
| { | |||
| // Up we go: | |||
| "move": { | |||
| "from": "src/libsodium", | |||
| "to": "src/" | |||
| }, | |||
| // Delete the now-unused subdirectory: | |||
| "remove": { | |||
| "path": "src/libsodium" | |||
| } | |||
| }, | |||
| // Lastly, libsodium's source files expect to resolve their header | |||
| // paths differently than they expect of their clients (Bad!!!). | |||
| // Fortunately, we can do a hack to allow the files in `src/` to | |||
| // resolve its headers. The source files use #include as if the | |||
| // header root was `include/sodium/`, rather than `include/`. | |||
| // To work around this, generate a copy of each header file in the | |||
| // source root, but remove the leading path element. | |||
| // Because we have a separate `include/` and `src/` directory, dds | |||
| // will only expose the `include/` directory to clients, and the | |||
| // header copies in `src/` are not externally visible. | |||
| // | |||
| // For example, the `include/sodium/version.h` file is visible to | |||
| // clients as `sodium/version.h`, but libsodium itself tries to | |||
| // include it as `version.h` within its source files. When we copy | |||
| // from `include/`, we grab the relative path to `sodium/version.h`, | |||
| // strip the leading components to get `version.h`, and then join that | |||
| // path with the `to` path to generate the full destination at | |||
| // `src/version.h` | |||
| { | |||
| "copy": { | |||
| "from": "include/", | |||
| "to": "src/", | |||
| "strip-components": 1 | |||
| } | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| } | |||
| .. _libsodium: https://doc.libsodium.org/ | |||
| .. _Crypto++: https://cryptopp.com/ | |||
| @@ -1,240 +1,134 @@ | |||
| .. highlight:: cmake | |||
| Using ``dds`` Packages in a CMake Project | |||
| ######################################### | |||
| Easy Mode: Using ``dds`` in a CMake Project | |||
| ########################################### | |||
| One of ``dds``'s primary goals is to inter-operate with other build systems | |||
| cleanly. One of ``dds``'s primary outputs is *libman* package indices. These | |||
| package indices can be imported into other build systems that support the | |||
| *libman* format. | |||
| cleanly. Because of CMakes ubiquity, ``dds`` includes built-in support for | |||
| emitting files that can be imported into CMake. | |||
| .. note:: | |||
| ``dds`` doesn't (yet) have a ready-made central repository of packages that | |||
| can be downloaded. You'll need to populate the local package catalog | |||
| appropriately. The default catalog file contains a limited set of useful | |||
| packages, but you may wish to add more for yourself. | |||
| .. seealso:: Refer to :doc:`catalog` for information about remote packages. | |||
| .. _PMM: https://github.com/vector-of-bool/PMM | |||
| .. _CMakeCM: https://github.com/vector-of-bool/CMakeCM | |||
| .. _lm-cmake: https://raw.githubusercontent.com/vector-of-bool/libman/develop/cmake/libman.cmake | |||
| Generating a libman Index | |||
| ************************* | |||
| Importing libman packages into a build system requires that we have a libman | |||
| index generated on the filesystem. **This index is not generated globally**: It | |||
| is generated on a per-build basis as part of the build setup. The index will | |||
| describe in build-system-agnostic terms how to include a set of packages and | |||
| libraries as part of a build. | |||
| ``dds`` has first-class support for generating this index. The ``build-deps`` | |||
| subcommand of ``dds`` will download and build a set of dependencies, and places | |||
| an ``INDEX.lmi`` file that can be used to import the built results. | |||
| Declaring Dependencies | |||
| ====================== | |||
| ``dds build-deps`` accepts a list of dependency statements as commnad line | |||
| arguments, but it may be useful to specify those requirements in a file. | |||
| ``dds build-deps`` accepts a JSON5 file describing the dependencies of a | |||
| project as well. This file is similar to a very stripped-down version of a | |||
| ``dds`` :ref:`package manifest <pkgs.pkgs>`, and only includes the ``depends`` | |||
| key. (The presence of any other key is an error.) | |||
| Here is a simple dependencies file that declares a single requirement: | |||
| .. code-block:: js | |||
| :caption: ``dependencies.json5`` | |||
| { | |||
| depends: [ | |||
| 'neo-sqlite3^0.2.0', | |||
| ] | |||
| } | |||
| Building Dependencies and the Index | |||
| =================================== | |||
| We can invoke ``dds build-deps`` and give it the path to this file: | |||
| .. code-block:: bash | |||
| $ dds build-deps --deps dependencies.json5 | |||
| .. seealso:: | |||
| When finished, ``dds`` will write the build results into a subdirectory called | |||
| ``_deps`` and generate a file named ``INDEX.lmi``. This file is ready to be | |||
| imported into any build system that can understand libman files (in our case, | |||
| CMake). | |||
| Before reading this page, be sure to read the :ref:`build-deps.gen-libman` | |||
| section of the :doc:`build-deps` page, which will discuss how to use the | |||
| ``dds build-deps`` subcommand. | |||
| .. note:: | |||
| The output directory and index filepath can be controlled with the | |||
| ``--out`` and ``--lmi-path`` flags, respectively. | |||
| We'll first look as *easy mode*, but there's also an *easiest mode* for a | |||
| one-line solution: :ref:`see below <cmake.pmm>`. | |||
| Importing into CMake | |||
| ******************** | |||
| We've generated a libman index and set of packages, and we want to import | |||
| them into CMake. CMake doesn't know how to do this natively, but there exists a | |||
| single-file module for CMake that allows CMake to import libraries from libman | |||
| indices without any additional work. | |||
| The module is not shipped with CMake, but is available online as a single | |||
| stand-alone file. The `libman.cmake <lm-cmake_>`_ file can be downloaded and | |||
| added to a project directly, or it can be obtained automatically through a | |||
| CMake tool like `PMM`_ (recommended). | |||
| Enabling *libman* Support in CMake via PMM | |||
| ========================================== | |||
| Refer to the ``README.md`` file in `the PMM repo <PMM_>`_ for information on how | |||
| to get PMM into your CMake project. In short, download and place the | |||
| ``pmm.cmake`` file in your repository, and ``include()`` the file near the top | |||
| of your ``CMakeLists.txt``:: | |||
| include(pmm.cmake) | |||
| Once it has been included, you can call the ``pmm()`` function. To obtain | |||
| *libman*, we need to start by enabling `CMakeCM`_:: | |||
| pmm(CMakeCM ROLLING) | |||
| .. warning:: | |||
| It is not recommended to use the ``ROLLING`` mode, but it is the easiest to | |||
| use when getting started. For reproducible and reliable builds, you should | |||
| pin your CMakeCM version using the ``FROM <url>`` argument. | |||
| Enabling CMakeCM will make available all of the CMake modules available in `the | |||
| CMakeCM repository <CMakeCM_>`_, which includes `libman.cmake <lm-cmake_>`_. | |||
| .. _PMM: https://github.com/vector-of-bool/PMM | |||
| After the call to ``pmm()``, simply ``include()`` the ``libman`` module:: | |||
| include(libman) | |||
| Generating a CMake Import File | |||
| ****************************** | |||
| That's it! The only function from the module that we will care about for now | |||
| is the ``import_packages()`` function. | |||
| ``build-deps`` accepts an ``--lmi-path`` argument, but also accepts a | |||
| ``--cmake=<path>`` argument that serves a similar purpose: It will write a CMake | |||
| file to ``<path>`` that can be ``include()``'d into a CMake project: | |||
| .. code-block:: bash | |||
| Importing Our Dependencies' Packages | |||
| ==================================== | |||
| $ dds build-deps "neo-sqlite3^0.2.0" --cmake=deps.cmake | |||
| To import a package from a libman tree, we need only know the *name* of the | |||
| package we wish to import. In our example case above, we depend on | |||
| ``neo-sqlite3``, so we simply call the libman-CMake function | |||
| ``import_packages()`` with that package name:: | |||
| This will write a file ``./deps.cmake`` that we can ``include()`` from a CMake | |||
| project, which will then expose the ``neo-sqlite3`` package as a set of imported | |||
| targets. | |||
| import_packages("neo-sqlite3") | |||
| You'll note that we don't request any particular version of the package: All | |||
| versioning resolution is handled by ``dds``. You'll also note that we don't | |||
| need to specify our transitive dependencies: This is handled by the libman | |||
| index that was generated by ``dds``: It will automatically ``import_packages()`` | |||
| any of the transitive dependencies required. | |||
| Using the CMake Import File | |||
| =========================== | |||
| Once we have generated the CMake import file using ``dds build-deps``, we can | |||
| simply import it in our ``CMakeLists.txt``:: | |||
| Using Our Dependencies' Libraries | |||
| ================================= | |||
| include(deps.cmake) | |||
| Like with ``dds``, CMake wants us to explicitly declare how our build targets | |||
| *use* other libraries. When we import a package from a libman index, the | |||
| import will generate CMake ``IMPORTED`` targets that can be linked against. | |||
| *use* other libraries. When we ``include()`` the generated CMake file, it will | |||
| generate ``IMPORTED`` targets that can be linked against. | |||
| In ``dds`` and in libman, a library is identified by a combination of | |||
| In ``dds`` (and in libman), a library is identified by a combination of | |||
| *namespace* and *name*, joined together with a slash ``/`` character. This | |||
| *qualified name* of a library is decided by the original package author, and | |||
| should be documented. In the case of ``neo-sqlite3``, the only target is | |||
| should be documented. In the case of ``neo-sqlite3``, the only library is | |||
| ``neo/sqlite3``. | |||
| When the libman CMake module imports a library, it creates a qualified name | |||
| When the generated import file imports a library, it creates a qualified name | |||
| using a double-colon "``::``" instead of a slash. As such, our ``neo/sqlite3`` | |||
| is imported in CMake as ``neo::sqlite3``. We can link against it as we would | |||
| with any other target:: | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| Altogether, here is the final CMake file: | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| .. code-block:: | |||
| :caption: ``CMakeLists.txt`` | |||
| :linenos: | |||
| cmake_minimum_required(VERSION 3.15) | |||
| project(MyApplication VERSION 1.0.0) | |||
| .. _cmake.pmm: | |||
| include(pmm.cmake) | |||
| pmm(CMakeCM ROLLING) | |||
| *Easiest* Mode: PMM Support | |||
| *************************** | |||
| include(libman) | |||
| import_packages("neo-sqlite3") | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| `PMM`_ is the *package package manager*, and can be used to control and access | |||
| package managers from within CMake scripts. This includes controlling ``dds``. | |||
| With PMM, we can automate all of the previous steps into a single line. | |||
| Refer to the ``README.md`` file in `the PMM repo <PMM>`_ for information on how | |||
| to get PMM into your CMake project. In short, download and place the | |||
| ``pmm.cmake`` file in your repository, and ``include()`` the file near the top | |||
| of your ``CMakeLists.txt``:: | |||
| Additional PMM Support | |||
| ********************** | |||
| include(pmm.cmake) | |||
| The ``pmm()`` function also supports ``dds`` directly, similar to ``CMakeCM`` | |||
| mode. This will automatically download a prebuilt ``dds`` for the host platform | |||
| and invoke ``dds build-deps`` in a single pass as part of CMake's configure | |||
| process. This is especially useful for a CI environment where you want to have | |||
| a stable ``dds`` version and always have your dependencies obtained | |||
| just-in-time. | |||
| The ``pmm()`` function also supports ``dds`` directly, and will automatically | |||
| download a prebuilt ``dds`` for the host platform and invoke ``dds build-deps`` | |||
| in a single pass as part of CMake's configure process. This is especially useful | |||
| for a CI environment where you want to have a stable ``dds`` version and always | |||
| have your dependencies obtained just-in-time. | |||
| To start, pass the ``DDS`` argument to ``pmm()`` to use it:: | |||
| pmm(DDS) | |||
| pmm(DDS) | |||
| .. note:: | |||
| The ``_deps`` directory and ``INDEX.lmi`` file will be placed in the CMake | |||
| build directory, out of the way of the rest of the project. | |||
| The ``_deps`` directory and generated CMake imports file will be placed in | |||
| the CMake build directory, out of the way of the rest of the project. | |||
| .. note:: | |||
| The version of ``dds`` that PMM downloads depends on the version of PMM | |||
| that is in use. | |||
| The version of ``dds`` that PMM downloads depends on the version of PMM | |||
| that is in use. | |||
| This alone won't do anything useful, because you'll need to tell it what | |||
| dependencies we want to install:: | |||
| pmm(DDS DEP_FILES dependencies.json5) | |||
| pmm(DDS DEP_FILES dependencies.json5) | |||
| You can also list your dependencies as an inline string in your CMakeLists.txt | |||
| You can also list your dependencies as inline strings in your CMakeLists.txt | |||
| instead of a separate file:: | |||
| pmm(DDS DEPENDS neo-sqlite3^0.2.2) | |||
| pmm(DDS DEPENDS neo-sqlite3^0.2.2) | |||
| Since you'll probably want to be using ``libman.cmake`` at the same time, the | |||
| calls for ``CMakeCM`` and ``DDS`` can simply be combined. This is how our new | |||
| CMake project might look: | |||
| This invocation will run ``build-deps`` with the build options, generate a CMake | |||
| imports file, and immediately ``include()`` it to import the generated CMake | |||
| targets. ``pmm(DDS)`` will also generate a ``dds`` :doc:`toolchain <toolchains>` | |||
| based on the current CMake build environment, ensuring that the generated | |||
| packages have matching build options to the rest of the project. Refer to the | |||
| PMM README for more details. | |||
| .. code-block:: | |||
| :caption: ``CMakeLists.txt`` | |||
| :linenos: | |||
| cmake_minimum_required(VERSION 3.15) | |||
| project(MyApplication VERSION 1.0.0) | |||
| :caption: ``CMakeLists.txt`` | |||
| :linenos: | |||
| :emphasize-lines: 4,5 | |||
| include(pmm.cmake) | |||
| pmm(CMakeCM ROLLING | |||
| DDS DEPENDS neo-sqlite3^0.2.2 | |||
| ) | |||
| cmake_minimum_required(VERSION 3.15) | |||
| project(MyApplication VERSION 1.0.0) | |||
| include(libman) | |||
| import_packages("neo-sqlite3") | |||
| include(pmm.cmake) | |||
| pmm(DDS DEPENDS neo-sqlite3^0.2.2) | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| add_executable(my-application app.cpp) | |||
| target_link_libraries(my-application PRIVATE neo::sqlite3) | |||
| This removes the requirement that we write a separate dependencies file, and we | |||
| no longer need to invoke ``dds build-deps`` externally, as it is all handled | |||
| by ``pmm``. | |||
| by ``pmm()``. | |||
| @@ -9,7 +9,8 @@ User Guide | |||
| packages | |||
| toolchains | |||
| source-dists | |||
| repo | |||
| catalog | |||
| pkg-cache | |||
| remote-pkgs | |||
| interdeps | |||
| build-deps | |||
| cmake | |||
| @@ -85,8 +85,8 @@ versions of the dependency are supported. | |||
| Refer to: :ref:`deps.ranges.why-lowest`. | |||
| ``dds`` compatible-version ranges are similar to the shorthand range specifiers | |||
| supported by ``npm`` and ``npm``-like tools. There are five (and a half) | |||
| version range formats available, listed in order of most-to-least restrictive: | |||
| supported by ``npm`` and ``npm``-like tools. There are four version range kinds | |||
| available, listed in order of most-to-least restrictive: | |||
| Exact: ``@1.2.3`` | |||
| Specifies an *exact* requirement. The dependency must match the named | |||
| @@ -56,7 +56,7 @@ 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*. | |||
| together as part of a *source distribution*. | |||
| .. _pkgs.apps-tests: | |||
| @@ -65,15 +65,42 @@ 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: | |||
| applications and tests, depending on the filenames "stem," which is the part of | |||
| the filename not including the outer-most file extension. If a compilable source | |||
| filename stem ends with ``.main`` or ``.test``, that source file is assumed to | |||
| correspond to an executable to generate. The filename second-inner 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``. | |||
| - Given ``foo.main.cpp`` | |||
| - The stem is ``foo.main``, whose extension is ``.main``, so we will generate | |||
| an application. | |||
| - The stem of ``foo.main`` is ``foo``, so the executable will be named | |||
| ``foo``. | |||
| - Given ``bar.test.cpp`` | |||
| - The stem is ``bar.test``, whose extension is ``.test``, so we will generate | |||
| a test. | |||
| - The stem of ``bar.test`` is ``bar``, so will generate an executable named | |||
| ``bar``. | |||
| - Given ``cat-meow.main.cpp`` | |||
| - The stem is ``cat-meow.main``, which has extension ``.main``, so it is an | |||
| application. | |||
| - The stem of ``cat-meow.main`` is ``cat-meow``, so will generate an | |||
| executable named ``cat-meow``. | |||
| - Given ``cats.musical.test.cpp`` | |||
| - The stem is ``cats.musical.test``, which has extension ``.test``, so this is | |||
| a text executable. | |||
| - The stem of ``cats.musical.test`` is ``cats.musical``, so we will generate | |||
| an executable named ``cats.musical``. | |||
| - Note that the dot in ``cats.musical`` is not significant, as ``dds`` does | |||
| strip any further extensions. | |||
| .. note:: | |||
| ``dds`` will automatically append the appropriate filename extension to the | |||
| @@ -161,7 +188,7 @@ 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 | |||
| the exact 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. | |||
| @@ -285,8 +312,8 @@ The primary distribution format of packages that is used by ``dds`` is the | |||
| 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. | |||
| the ``name@version`` string forms the *package ID*, and it must be unique within | |||
| a repository or local package cache. | |||
| In order for a package to be exported by ``dds`` it must have a | |||
| ``package.json5`` file at its package root. Three keys are required to be | |||
| @@ -0,0 +1,90 @@ | |||
| The Local Package Cache | |||
| ####################### | |||
| ``dds`` maintains a local cache 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 this package cache). | |||
| Reading Repository Contents | |||
| *************************** | |||
| Most times, ``dds`` will manage the cache content silently, but it may be useful | |||
| to see what ``dds`` is currently storing away. | |||
| The content of the cache can be seen with the ``pkg ls`` subcommand:: | |||
| > dds pkg 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:`remote-pkgs` | |||
| 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 locally | |||
| cached packages, as well as packages that are available from any | |||
| :doc:`remote packages <remote-pkgs>`. If the dependency solution requires any | |||
| packages that are not in the local cache, it will use the information in its | |||
| catalog database to obtain a source distribution for each missing package. These | |||
| source distributions will automatically be added to the local cache, and later | |||
| dependency resolutions will not need to download that package again. | |||
| This all happens automatically when a project is built: There is **no** | |||
| "``dds install``" subcommand. | |||
| Manually Downloading a Dependency | |||
| ================================= | |||
| It may be useful to obtain a copy of the source distribution of a package | |||
| from a remote. The ``pkg get`` command can be used to do this:: | |||
| > dds pkg get <name>@<version> | |||
| This will obtain the source distribution of the package matching the given | |||
| package ID and place that distribution in current working directory, using the | |||
| package ID as the name of the source distribution directory:: | |||
| $ dds pkg get spdlog@1.4.2 | |||
| [ ... ] | |||
| $ ls . | |||
| . | |||
| .. | |||
| spdlog@1.4.2 | |||
| $ ls ./spdlog@1.4.2/ | |||
| include/ | |||
| src/ | |||
| library.json5 | |||
| package.json5 | |||
| .. _repo.import-local: | |||
| Exporting a Project into the Repository | |||
| *************************************** | |||
| ``dds`` can only use packages that are available in the local cache. 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 importing that project into the package cache as a | |||
| regular package, as detailed in :ref:`sdist.import`:: | |||
| > dds pkg import /path/to/project | |||
| This command will create a source distribution and place it in the local cache. | |||
| The package is now available to other projects on the local system. | |||
| .. note:: | |||
| This doesn't import in "editable" mode: A snapshot of the package root | |||
| will be taken and imported to the local cache. | |||
| @@ -0,0 +1,231 @@ | |||
| Remote Packages and Repositories | |||
| ################################ | |||
| .. highlight:: bash | |||
| ``dds`` stores a local database of available packages, along with their | |||
| dependency statements and information about how a source distribution thereof | |||
| may be obtained. | |||
| Inside the database are *package repositories*, which are remote servers that | |||
| contain their own database of packages, and may also contain the packages | |||
| themselves. An arbitrary number of package repositories may be added to the | |||
| local database. When ``dds`` updates its package information, it will download | |||
| the package database from each registered remote and import the listings into | |||
| its own local database, making them available for download. | |||
| Viewing Available Packages | |||
| ************************** | |||
| The default catalog database is stored in a user-local location, and the | |||
| available packages can be listed with ``dds pkg search``:: | |||
| $ dds pkg search | |||
| Name: abseil | |||
| Versions: 2018.6.0, 2019.8.8, 2020.2.25 | |||
| From: repo-1.dds.pizza | |||
| Name: asio | |||
| Versions: 1.12.0, 1.12.1, 1.12.2, 1.13.0, 1.14.0, 1.14.1, 1.16.0, 1.16.1 | |||
| From: repo-1.dds.pizza | |||
| Name: boost.leaf | |||
| Versions: 0.1.0, 0.2.0, 0.2.1, 0.2.2, 0.2.3, 0.2.4, 0.2.5, 0.3.0 | |||
| From: repo-1.dds.pizza | |||
| Name: boost.mp11 | |||
| Versions: 1.70.0, 1.71.0, 1.72.0, 1.73.0 | |||
| From: repo-1.dds.pizza | |||
| Optionally, one can search with a glob/fnmatch-style pattern:: | |||
| $ dds pkg search 'neo-*' | |||
| Name: neo-buffer | |||
| Versions: 0.2.1, 0.3.0, 0.4.0, 0.4.1, 0.4.2 | |||
| From: repo-1.dds.pizza | |||
| Name: neo-compress | |||
| Versions: 0.1.0, 0.1.1, 0.2.0 | |||
| From: repo-1.dds.pizza | |||
| Name: neo-concepts | |||
| Versions: 0.2.2, 0.3.0, 0.3.1, 0.3.2, 0.4.0 | |||
| From: repo-1.dds.pizza | |||
| Remote Repositories | |||
| ******************* | |||
| A remote package repository consists of an HTTP(S) server serving the following: | |||
| 1. An accessible directory containing source distributions of various packages, | |||
| and | |||
| 2. An accessible database file that contains a listing of packages and some | |||
| repository metadata. | |||
| The exact details of the directory layout and database are not covered here, and | |||
| are not necessary to make use of a repository. | |||
| When ``dds`` uses a repository, it pulls down the database file and imports its | |||
| contents into its own local database, associating the imported package listings | |||
| with the remote repository which provides them. Pulling the entire database at | |||
| once allows ``dds`` to perform much faster dependency resolution and reduces | |||
| the round-trips associated with using a dynamic package repository. | |||
| Adding a Repository | |||
| =================== | |||
| Adding a remote repository to the local database is a simple single command:: | |||
| $ dds pkg repo add "https://repo-1.dds.pizza" | |||
| [info ] Pulling repository contents for repo-1.dds.pizza [https://repo-1.dds.pizza/] | |||
| This will tell ``dds`` to add ``https://repo-1.dds.pizza`` as a remote | |||
| repository and immediately pull its package listings for later lookup. This | |||
| initial update can be suppressed with the ``--no-update`` flag. | |||
| .. note:: | |||
| After the initial ``pkg repo add``, the repository is *not* identified by its | |||
| URL, but by its *name*, which is provided by the repository itself. The name | |||
| is printed the first time it is added, and can be seen using ``pkg repo ls``. | |||
| Listing Repositories | |||
| ==================== | |||
| A list of package repositories can be seen with the ``pkg repo ls`` subcommand:: | |||
| $ dds pkg repo ls | |||
| Removing Repositories | |||
| ===================== | |||
| A repository can be removed by the ``pkg repo remove`` subcommand:: | |||
| $ dds pkg repo remove <name> | |||
| Where ``<name>`` is given as the *name* (not URL!) of the repository. | |||
| **Note** that removing a repository will make all of its corresponding remote | |||
| packages unavailable, while packages that have been pulled into the local cache | |||
| will remain available even after removing a repository. | |||
| Updating Repository Data | |||
| ======================== | |||
| Repository data and package listings can be updated with the ``pkg repo update`` | |||
| subcommand:: | |||
| $ dds pkg repo update | |||
| This will pull down the databases of all registered remote repositories. If | |||
| ``dds`` can detect that a repository's database is unchanged since a prior | |||
| update, that update will be skipped. | |||
| The Default Repository | |||
| ********************** | |||
| When ``dds`` first initializes its local package database, it will add a single | |||
| remote repository: ``https://repo-1.dds.pizza/``, which has the name | |||
| ``repo-1.dds.pizza``. At the time of writing, this is the only official ``dds`` | |||
| repository, and is populated sparsely with hand-curated and prepared packages. | |||
| In the future, the catalog of packages will grow and be partially automated. | |||
| There is nothing intrinsically special about this repository other than it being | |||
| the default when ``dds`` first creates its package database. It can be removed | |||
| as any other, should one want tighter control over package availability. | |||
| Managing a Repository | |||
| ********************* | |||
| A ``dds`` repository is simply a directory of static files, so any HTTP server | |||
| that can serve from a filesystem can be used as a repository. ``dds`` also | |||
| ships with a subcommand, ``repoman``, that can be used to manage a repository | |||
| directory. | |||
| Initializing a Repository | |||
| ========================= | |||
| Before anything can be done, a directory should be converted to a repository by | |||
| using ``repoman init``:: | |||
| $ dds repoman init ./my-repo-dir --name=my-experimental-repo | |||
| This will add the basic metadata into ``./my-repo-dir`` such that ``dds`` will | |||
| be able to pull package data from it. | |||
| The ``--name`` argument should be used to give the repository a unique name. The | |||
| name should be globally unique to avoid collisions: When ``dds`` pulls a | |||
| repository that declares a given name, it will *replace* the package listings | |||
| associated with any repository of that name. As such, generic names like | |||
| ``main`` or ``packages`` shouldn't be used in production. | |||
| Listing Contents | |||
| ================ | |||
| The packages in a repository can be listed using ``dds repoman ls <repo-dir>``. | |||
| This will simply print each package identifier that is present in the | |||
| repository. | |||
| Importing Source Distributions | |||
| ============================== | |||
| If you have a source distribution archive, it can be imported with the | |||
| appropriately name ``dds repoman import`` command:: | |||
| $ dds repoman import ./my-repo some-pkg@1.2.3.tar.gz | |||
| Multiple archive paths may be provided at once to import them all at once. | |||
| Adding a Package by URL | |||
| ======================= | |||
| A repository can also list packages that it does not host itself. Such a package | |||
| listing can be added "by URL," where the URL tells ``dds`` how to pull the | |||
| source distribution of the package. Beyond basic HTTP(S) URLs, ``dds`` can also | |||
| clone packages via ``git``:: | |||
| $ dds repoman add ./my-repo git+https://github.com/vector-of-bool/neo-fun.git#0.5.2 | |||
| The above URL tells ``dds`` that it can use ``git clone`` against | |||
| ``https://github.com/vector-of-bool/neo-fun.git`` and ask for tag ``0.5.2`` to | |||
| get a source distribution directory that can be imported. Note the fragment on | |||
| ``git`` URLs! The fragment is required to specify the branch or tag to clone. | |||
| If the package is available on GitHub, ``dds`` has a shorthand URL for that:: | |||
| $ dds repoman add ./my-repo github:vector-of-bool/neo-fun/0.6.0 | |||
| The ``github:`` URL scheme tells ``dds`` to clone from GitHub. A ``github:`` URL | |||
| must have exactly three path elements to determine *what* to download: | |||
| ``github:{owner}/{repository}/{branch-or-tag}``. | |||
| .. note:: | |||
| The ``github:`` URL lacks an *authority* element, and as such *does not* use | |||
| the double-slash. | |||
| .. note:: | |||
| ``repoman add`` will immediately attempt to pull a source distribution from | |||
| the given URL so that it may import the package's metadata into its listing | |||
| database. You cannot add a URL that is not already accessible. | |||
| Removing Packages | |||
| ================= | |||
| A package can be removed from a repository with | |||
| ``dds repoman remove <repo-dir> <pkg-id>``, where ``<pkg-id>`` is the | |||
| ``<name>@<version>`` of the package to remove. | |||
| @@ -1,92 +0,0 @@ | |||
| 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.json5 | |||
| package.json5 | |||
| .. _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. | |||
| @@ -1,9 +1,9 @@ | |||
| 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>` archive that contains only the files | |||
| A *source distribution* (often abbreviated as "sdist") is ``dds``'s primary | |||
| format for consuming and distributing packages. A source distribution, in | |||
| essence, is a :ref:`package root <pkgs.pkg-root>` 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 | |||
| @@ -18,7 +18,7 @@ Generating a Source Distribution | |||
| Generating a source distribution from a project directory is done with the | |||
| ``sdist`` subcommand:: | |||
| > dds sdist create | |||
| > dds pkg create | |||
| The above command can be executed within a package root, and the result will be | |||
| a gzip'd tar archive that reproduces the package's filesystem structure, but | |||
| @@ -26,8 +26,8 @@ only maintaining the files that are necessary for ``dds`` to reproduce a 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. | |||
| ``dds`` will use as the package root. The default is the current working | |||
| directory. | |||
| The ``--out=<path>`` flag can be provided to override the destination path of | |||
| the archive. The path should not name an existing file or directory. By default, | |||
| @@ -37,10 +37,32 @@ then ``dds`` will overwrite the destination if it names an existing file or | |||
| directory. | |||
| Importing a Source Ditsribution | |||
| .. _sdist.import: | |||
| Importing a Source Distribution | |||
| ******************************* | |||
| Given a source distribution archive, one can import the package into the local | |||
| repository with a single command:: | |||
| package cache with a single command:: | |||
| > dds pkg import some-package@1.2.3.tar.gz | |||
| You can also specify an HTTP or HTTPS URL to download a source distribution | |||
| archive to import without downloading it separately:: | |||
| > dds pkg import https://example.org/path/to/sdist.tar.gz | |||
| Alternatively, if a directory correctly models a source distribution, then | |||
| that directory can be imported in the same manner:: | |||
| > dds pkg import /path/to/some/project | |||
| Importing a package will allow projects on the system to use the imported | |||
| package as a dependency. | |||
| .. note:: | |||
| > dds repo import some-package@1.2.3.tar.gz | |||
| If one tries to import a package root into the cache that already contains a | |||
| package with a matching identifier, ``dds`` will issue an error. This | |||
| behavior can be overridden by providing ``--if-exists=replace`` on the | |||
| command-line. | |||
| @@ -299,6 +299,16 @@ Specify *additional* compiler options, possibly per-language. | |||
| Specify *additional* link options to use when linking executables. | |||
| .. note:: | |||
| ``dds`` does not invoke the linker directly, but instead invokes the | |||
| compiler with the appropriate flags to perform linking. If you need to pass | |||
| flags directly to the linker, you will need to use the compiler's options to | |||
| direct flags through to the linker. On GNU-style, this is | |||
| ``-Wl,<linker-option>``. With MSVC, a separate flag ``/LINK`` must be | |||
| specified, and all following options are passed to the underlying | |||
| ``link.exe``. | |||
| ``optimize`` | |||
| ------------ | |||
| @@ -98,7 +98,7 @@ leave the condition the same, though: | |||
| 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: | |||
| [error] Test <root>/_build/test/hello/strings failed! Output: | |||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
| strings is a Catch v2.10.2 host application. | |||
| @@ -168,9 +168,9 @@ 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'. | |||
| [info ] [dds-hello] Link: hello-world | |||
| [info ] [dds-hello] Link: hello-world - 57ms | |||
| [error] Failed to link executable '<root>/_build/hello-world'. | |||
| ... | |||
| <additional lines follow> | |||
| @@ -238,6 +238,10 @@ package root: | |||
| Rebuilding the project will show no difference at the moment. | |||
| .. note:: | |||
| You may also use a ``.jsonc`` or ``.json`` file extension. ``dds`` will | |||
| search for all of these files, but they will all be parsed as JSON5. | |||
| .. 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` | |||