| echo Executing Build and Tests | echo Executing Build and Tests | ||||
| reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f || exit 1 | reg add HKLM\SYSTEM\CurrentControlSet\Control\FileSystem /v LongPathsEnabled /t REG_DWORD /d 1 /f || exit 1 | ||||
| python -m pip install pytest pytest-xdist || exit 1 | python -m pip install pytest pytest-xdist || exit 1 | ||||
| python -u tools/ci.py -B download -T tools\msvc.dds || exit 1 | |||||
| python -u tools/ci.py -B download -T tools\msvc.dds -T2 tools\msvc.jsonc || exit 1 | |||||
| displayName: Full CI | displayName: Full CI | ||||
| - publish: _build/dds.exe | - publish: _build/dds.exe | ||||
| artifact: DDS Executable - Windows VS2019 | artifact: DDS Executable - Windows VS2019 | ||||
| sudo apt install -y python3-minimal g++-9 ccache | sudo apt install -y python3-minimal g++-9 ccache | ||||
| python3 -m pip install pytest pytest-xdist | python3 -m pip install pytest pytest-xdist | ||||
| displayName: Prepare System | displayName: Prepare System | ||||
| - script: python3 -u tools/ci.py -B download -T tools/gcc-9.dds | |||||
| - script: python3 -u tools/ci.py -B download -T tools/gcc-9.dds -T2 tools/gcc-9.jsonc | |||||
| displayName: Full CI | displayName: Full CI | ||||
| - publish: _build/dds | - publish: _build/dds | ||||
| artifact: DDS Executable - Linux | artifact: DDS Executable - Linux | ||||
| - script: | | - script: | | ||||
| set -eu | set -eu | ||||
| python3 -m pip install pytest pytest-xdist | python3 -m pip install pytest pytest-xdist | ||||
| python3 -u tools/ci.py -B download -T tools/gcc-9.dds | |||||
| python3 -u tools/ci.py -B download -T tools/gcc-9.dds -T2 tools/gcc-9.jsonc | |||||
| displayName: Build and Run Unit Tests | displayName: Build and Run Unit Tests | ||||
| - publish: _build/dds | - publish: _build/dds | ||||
| artifact: DDS Executable - macOS | artifact: DDS Executable - macOS |
| "url": "https://github.com/gabime/spdlog.git" | "url": "https://github.com/gabime/spdlog.git" | ||||
| } | } | ||||
| } | } | ||||
| }, | |||||
| "vob-json5": { | |||||
| "0.1.5": { | |||||
| "depends": {}, | |||||
| "description": "A C++ implementation of a JSON5 parser", | |||||
| "git": { | |||||
| "auto-lib": null, | |||||
| "ref": "0.1.5", | |||||
| "url": "https://github.com/vector-of-bool/json5.git" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "vob-semester": { | |||||
| "0.1.0": { | |||||
| "depends": { | |||||
| "neo-concepts": "^0.2.1", | |||||
| "neo-fun": "^0.1.0" | |||||
| }, | |||||
| "description": "A C++ library to process recursive dynamic data", | |||||
| "git": { | |||||
| "auto-lib": null, | |||||
| "ref": "0.1.0", | |||||
| "url": "https://github.com/vector-of-bool/semester.git" | |||||
| } | |||||
| } | |||||
| } | } | ||||
| }, | }, | ||||
| "version": 1 | "version": 1 |
| The "namespace" of a library in this case is arbitrary and not necessarily | The "namespace" of a library in this case is arbitrary and not necessarily | ||||
| associated with any C++ ``namespace``. | associated with any C++ ``namespace``. | ||||
| If more than one library declares itself to have the same ``Name`` and lives in | |||||
| the same ``Namespace``, ``dds`` will issue an error. | |||||
| If more than one library declares itself to have the same ``name`` and lives in | |||||
| the same ``namespace``, ``dds`` will issue an error. | |||||
| To avoid this error in your own project and to avoid causing this error in your | To avoid this error in your own project and to avoid causing this error in your | ||||
| downstream consumers, the ``Namespace`` of your package should be considered | |||||
| carefully and be unique. Do not use a ``Namespace`` that is likely to be used | |||||
| downstream consumers, the ``namespace`` of your package should be considered | |||||
| carefully and be unique. Do not use a ``namespace`` that is likely to be used | |||||
| by another developer or organization, especially generic names. | by another developer or organization, especially generic names. | ||||
| If you are seeing this issue and it names a library that you do not own, it | If you are seeing this issue and it names a library that you do not own, it | ||||
| means that two or more of your dependencies are attempting to declare a library | means that two or more of your dependencies are attempting to declare a library | ||||
| of the same ``Name`` in the same ``Namespace``. This issue should be raised | |||||
| of the same ``name`` in the same ``namespace``. This issue should be raised | |||||
| with the maintainers of the packages in question. | with the maintainers of the packages in question. | ||||
| .. seealso:: | .. seealso:: |
| Error: Invalid library manifest | |||||
| ############################### | |||||
| Every ``dds`` package must contain a valid library manifest for each library | |||||
| that it exports. These manifests are stored in the library root to which they | |||||
| correspond, and are stored in JSON5 format in ``library.json5`` (or similarly | |||||
| named file). | |||||
| The contents of this file must follow a prescribed content, or ``dds`` will | |||||
| reject the manifest. Refer to the :ref:`pkgs.libs` documentation page for more | |||||
| information about how to declare libraries. |
| and is thoroughly explained on the :doc:`/guide/packages` page. | and is thoroughly explained on the :doc:`/guide/packages` page. | ||||
| For exporting/generating a source distribution from a package, the *package | For exporting/generating a source distribution from a package, the *package | ||||
| root* requires a ``package.dds`` file and each *library root* requires a | |||||
| ``library.dds`` file. | |||||
| root* requires a ``package.json5`` file and each *library root* requires a | |||||
| ``library.json5`` file. | |||||
| .. . | .. . | ||||
| TODO: Create are more detailed reference page for package and library layout, | TODO: Create are more detailed reference page for package and library layout, |
| Error: Invalid package manifest | |||||
| ############################### | |||||
| Every ``dds`` package must contain a valid package manifest, which is stored in | |||||
| JSON5 format in ``package.json5`` (or similarly named file). | |||||
| The contents of this file must follow a prescribed content, or ``dds`` will | |||||
| reject the manifest. Refer to the :ref:`pkgs.pkgs` documentation page for more | |||||
| information about how to declare packages. |
| This error message is printed when a project's tests encounter a failure | This error message is printed when a project's tests encounter a failure | ||||
| condition. The exact behavior of tests is determined by a project's | condition. The exact behavior of tests is determined by a project's | ||||
| ``Test-Driver``. | |||||
| ``test_driver``. | |||||
| If you see this error, it is most likely that you have an issue in the tests of | If you see this error, it is most likely that you have an issue in the tests of | ||||
| your project. | your project. |
| Error: Unknown ``Test-Driver`` | |||||
| Error: Unknown ``test_driver`` | |||||
| ############################## | ############################## | ||||
| ``dds`` has a set of known ``Test-Driver``s built-in, and they may be specified | |||||
| with the ``Test-Driver`` key. Receiving this error indicates that the specified | |||||
| ``Test-Driver`` was not recognized by ``dds``. Check your spelling, and check | |||||
| ``dds`` has a set of known ``test_driver``s built-in, and they may be specified | |||||
| with the ``test_driver`` key. Receiving this error indicates that the specified | |||||
| ``test_driver`` was not recognized by ``dds``. Check your spelling, and check | |||||
| that the driver you want to use is supported by ``dds``. Refer to the | that the driver you want to use is supported by ``dds``. Refer to the | ||||
| :doc:`/guide/packages` page. | :doc:`/guide/packages` page. |
| ######################################### | ######################################### | ||||
| A library can declare that it *uses* or *links* to another library by using the | A library can declare that it *uses* or *links* to another library by using the | ||||
| ``Uses`` and ``Links`` keys in ``library.dds``, respectively. | |||||
| ``uses`` and ``links`` keys in ``library.json5``, respectively. | |||||
| These requirements are specified by using the ``Namespace/Name`` pair that | |||||
| These requirements are specified by using the ``namespace/name`` pair that | |||||
| identifies a library. These are defined by both the project's dependencies and | identifies a library. These are defined by both the project's dependencies and | ||||
| the project itself. If a ``Uses`` or ``Links`` key does not correspond to a | |||||
| the project itself. If a ``uses`` or ``links`` key does not correspond to a | |||||
| known library, ``dds`` will not be able to resolve the usage requirements, and | known library, ``dds`` will not be able to resolve the usage requirements, and | ||||
| will generate an error. | will generate an error. | ||||
| dds catalog add <package-id> | dds catalog add <package-id> | ||||
| [--depends <requirement> [--depends <requirement> [...]]] | [--depends <requirement> [--depends <requirement> [...]]] | ||||
| [--git-url <url> --git-ref <ref>] | [--git-url <url> --git-ref <ref>] | ||||
| [--auto-lib <Namespace>/<Name>] | |||||
| [--auto-lib <namespace>/<name>] | |||||
| The ``<package-id>`` positional arguments is the ``name@version`` package ID | The ``<package-id>`` positional arguments is the ``name@version`` package ID | ||||
| that will be added to the catalog. The following options are supported: | that will be added to the catalog. The following options are supported: | ||||
| ``--depends <requirement>`` | ``--depends <requirement>`` | ||||
| This argument, which can be specified multiple times to represent multiple | This argument, which can be specified multiple times to represent multiple | ||||
| dependencies, sets the dependencies of the package within the catalog. If | 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. | |||||
| the obtained package root contains a ``package.json5``, then the | |||||
| dependencies listed here must be identical to those listed in | |||||
| ``package.json5``, or dependency resolution may yield unexpected results. | |||||
| ``--git-url <url>`` | ``--git-url <url>`` | ||||
| Specify a Git URL to clone from to obtain the package. The root of the | 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 | 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 | |||||
| have the ``package.json5`` and ``library.json5`` files if relying on the | |||||
| ``--auto-lib`` parameter. | ``--auto-lib`` parameter. | ||||
| ``--git-ref`` **must** be passed with ``--git-url``. | ``--git-ref`` **must** be passed with ``--git-url``. | ||||
| can only be specified for packages that contain a single library root at | can only be specified for packages that contain a single library root at | ||||
| the package root. | 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 | |||||
| The form of the argument is that of ``<namespace>/<name>``, where | |||||
| ``namespace`` and ``name`` are the usage requirement keys that should be | |||||
| generated for the library. | generated for the library. | ||||
| ******************** | ******************** | ||||
| Consider that we are creating a package ``acme-gadgets@4.3.6``. We declare the | 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: | |||||
| name and version in the ``package.json5`` in the package root: | |||||
| .. code-block:: | |||||
| .. code-block:: js | |||||
| Name: acme-gadgets | |||||
| Version: 4.3.6 | |||||
| Namespace: acme | |||||
| { | |||||
| name: 'acme-widgets', | |||||
| version: '4.3.6', | |||||
| namespace: 'acme', | |||||
| } | |||||
| .. note:: | .. note:: | ||||
| The ``Namespace`` field is required, but will be addressed in the | |||||
| The ``namespace`` field is required, but will be addressed in the | |||||
| :ref:`deps.lib-deps` section. | :ref:`deps.lib-deps` section. | ||||
| Suppose that our package's libraries build upon the libraries in the | Suppose that our package's libraries build upon the libraries in the | ||||
| not as new as ``2.0.0``. Such a dependency can be declared with the ``Depends`` | not as new as ``2.0.0``. Such a dependency can be declared with the ``Depends`` | ||||
| key: | key: | ||||
| .. code-block:: | |||||
| :emphasize-lines: 5 | |||||
| Name: acme-gadgets | |||||
| Version: 4.3.6 | |||||
| Namespace: acme | |||||
| .. code-block:: js | |||||
| :emphasize-lines: 5-7 | |||||
| Depends: acme-widgets ^1.4.3 | |||||
| { | |||||
| name: 'acme-gadgets', | |||||
| version: '4.3.6', | |||||
| namespace: 'acme', | |||||
| depends: { | |||||
| 'acme-widgets': '^1.4.3', | |||||
| }, | |||||
| } | |||||
| .. seealso:: :ref:`deps.ranges`. | .. seealso:: :ref:`deps.ranges`. | ||||
| additional ``Depends`` keys | additional ``Depends`` keys | ||||
| .. code-block:: | .. 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 | |||||
| :emphasize-lines: 7-8 | |||||
| { | |||||
| name: 'acme-gadgets', | |||||
| version: '4.3.6', | |||||
| namespace: 'acme', | |||||
| depends: { | |||||
| 'acme-widgets': '^1.4.3', | |||||
| 'acme-gizmos': '~5.6.5', | |||||
| 'acme-utils': '^3.3.0', | |||||
| }, | |||||
| } | |||||
| When ``dds`` attempts to build a project, it will first build the dependency | When ``dds`` attempts to build a project, it will first build the dependency | ||||
| solution by iteratively scanning the dependencies of the containing project and | solution by iteratively scanning the dependencies of the containing project and | ||||
| In ``dds``, library interdependencies are tracked separately from the packages | In ``dds``, library interdependencies are tracked separately from the packages | ||||
| that contain them. A library must declare its intent to use another library | 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: | |||||
| in the ``library.json5`` at its library root. The minimal content of a | |||||
| ``library.json5`` is the ``name`` key: | |||||
| .. code-block:: | |||||
| .. code-block:: js | |||||
| Name: gadgets | |||||
| { | |||||
| name: 'gadgets' | |||||
| } | |||||
| To announce that a library wishes to *use* another library, use the aptly-named | 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 ``/`` | |||||
| ``uses`` key: | |||||
| .. code-block:: js | |||||
| :emphasize-lines: 3-7 | |||||
| { | |||||
| name: 'gadgets', | |||||
| uses: [ | |||||
| 'acme/widgets', | |||||
| 'acme/gizmos', | |||||
| '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. | between them. | ||||
| It is the responsibility of package authors to document the ``Namespace`` and | |||||
| ``Name`` of the packages and libraries that they distribute. | |||||
| It is the responsibility of package authors to document the ``namespace`` and | |||||
| ``name`` of the packages and libraries that they distribute. | |||||
| .. note:: | .. note:: | ||||
| The ``Namespace`` of a package is completely arbitrary, and need not relate | |||||
| The ``namespace`` of a package is completely arbitrary, and need not relate | |||||
| to a C++ ``namespace``. | to a C++ ``namespace``. | ||||
| .. note:: | .. 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 | |||||
| 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. | many of their packages and libraries. | ||||
| However, it is essential that the ``<Namespace>/<Name>`` pair be | |||||
| However, it is essential that the ``<namespace>/<name>`` pair be | |||||
| universally unique, so choose wisely! | universally unique, so choose wisely! | ||||
| Once the ``Uses`` key appears in the ``library.dds`` file of a library, ``dds`` | |||||
| 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 | will make available the headers for the library being used, and will | ||||
| transitively propagate that usage requirement to users of the library. | transitively propagate that usage requirement to users of the library. |
| A *test* source file is a source file whose file stem ends with ``.test``. Like | 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 | 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 | 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 | |||||
| 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`` | source file will generate a single test executable that is executed by ``dds`` | ||||
| when running unit tests. | when running unit tests. | ||||
| dependencies of a header-only library. | dependencies of a header-only library. | ||||
| In order for ``dds`` to be able to distribute and interlink libraries, a | 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``: | |||||
| ``library.json5`` file must be present at the corresponding library root. The | |||||
| only required key in a ``library.json5`` file is ``name``: | |||||
| .. code-block:: yaml | |||||
| Name: my-library | |||||
| .. code-block:: js | |||||
| { | |||||
| name: 'my-library' | |||||
| } | |||||
| .. seealso:: More information is discussed on the :ref:`deps.lib-deps` page | .. seealso:: More information is discussed on the :ref:`deps.lib-deps` page | ||||
| within a repository or package catalog. | within a repository or package catalog. | ||||
| In order for a package to be exported by ``dds`` it must have a | 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``: | |||||
| ``package.json5`` file at its package root. Three keys are required to be | |||||
| present in the ``package.json5`` file: ``name``, ``version``, and ``namespace``: | |||||
| .. code-block:: yaml | |||||
| .. code-block:: js | |||||
| Name: acme-widgets | |||||
| Version: 6.7.3 | |||||
| Namespace: acme | |||||
| { | |||||
| name: 'acme-widgets', | |||||
| version: '6.7.3', | |||||
| namespace: 'acme', | |||||
| } | |||||
| ``Version`` must be a valid semantic version string. | |||||
| ``version`` must be a valid semantic version string. | |||||
| .. note:: | .. note:: | ||||
| The ``Namespace`` key is arbitrary, and not necessarily associated with | |||||
| and C++ ``namespace``. | |||||
| The ``namespace`` key is arbitrary, and not necessarily associated with | |||||
| any C++ ``namespace``. | |||||
| .. seealso:: | .. seealso:: | ||||
| The purpose of ``Namespace``, as well as additional options in this file, | |||||
| The purpose of ``namespace``, as well as additional options in this file, | |||||
| are described in the :ref:`deps.pkg-deps` page | are described in the :ref:`deps.pkg-deps` page | ||||
| Package names aren't a complete free-for-all. Package names must follow a set | Package names aren't a complete free-for-all. Package names must follow a set | ||||
| of specific rules: | of specific rules: | ||||
| - Package names may consist of ASCII, lowercase characters, digits, | |||||
| underscores (``_``), hyphens (``-``), and periods (``.``). | |||||
| - Package names may consist of a subset of ASCII including lowercase | |||||
| characters, digits, underscores (``_``), hyphens (``-``), and periods | |||||
| (``.``). | |||||
| .. note:: | .. note:: | ||||
| Different filesystems differ in their handling of filenames. Some platforms | Different filesystems differ in their handling of filenames. Some platforms |
| $ ls ./spdlog@1.4.2/ | $ ls ./spdlog@1.4.2/ | ||||
| include/ | include/ | ||||
| src/ | src/ | ||||
| library.dds | |||||
| package.dds | |||||
| library.json5 | |||||
| package.json5 | |||||
| .. _repo.export-local: | .. _repo.export-local: |
| diagnostics? | diagnostics? | ||||
| A ``Test-Driver``: Using *Catch2* | |||||
| A ``test_driver``: Using *Catch2* | |||||
| ********************************* | ********************************* | ||||
| ``dds`` ships with built-in support for the `Catch2`_ C and C++ testing | ``dds`` ships with built-in support for the `Catch2`_ C and C++ testing | ||||
| .. _catch2: https://github.com/catchorg/Catch2 | .. _catch2: https://github.com/catchorg/Catch2 | ||||
| To make use of Catch as our test driver, we simply declare this intent in the | To make use of Catch as our test driver, we simply declare this intent in the | ||||
| ``package.dds`` file at the package root: | |||||
| ``package.json5`` file at the package root: | |||||
| .. code-block:: yaml | |||||
| :caption: ``<root>/package.dds`` | |||||
| .. code-block:: js | |||||
| :caption: ``<root>/package.json5`` | |||||
| :emphasize-lines: 5 | :emphasize-lines: 5 | ||||
| Name: hello-dds | |||||
| Version: 0.1.0 | |||||
| Namespace: tutorial | |||||
| Test-Driver: Catch-Main | |||||
| { | |||||
| 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 | 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`` | |||||
| ``main`` function. When setting the ``test_driver`` to ``Catch-Main``, ``dds`` | |||||
| will compile an entrypoint separately from any particular test, and the tests | will compile an entrypoint separately from any particular test, and the tests | ||||
| will link against that entrypoint. This means we cannot provide our own | will link against that entrypoint. This means we cannot provide our own | ||||
| ``main`` function, and should instead use Catch's ``TEST_CASE`` macro to | ``main`` function, and should instead use Catch's ``TEST_CASE`` macro to |
| ``dds`` will work happily with packages that do not declare themselves, as long | ``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 | as the filesystem structure is sufficient. However: To use features covered in | ||||
| later tutorials, we'll need a simple ``package.dds`` file to declare | |||||
| later tutorials, we'll need a simple ``package.json5`` file to declare | |||||
| information about are package. This file should be placed directly in the | information about are package. This file should be placed directly in the | ||||
| package root: | package root: | ||||
| .. code-block:: yaml | |||||
| :caption: ``<root>/package.dds`` | |||||
| Name: hello-dds | |||||
| Version: 0.1.0 | |||||
| Namespace: tutorial | |||||
| .. code-block:: js | |||||
| :caption: ``<root>/package.json5`` | |||||
| { | |||||
| name: 'hello-dds', | |||||
| version: '0.1.0', | |||||
| namespace: 'tutorial', | |||||
| } | |||||
| .. note:: | .. note:: | ||||
| The ``Namespace`` option will be discussed later. | |||||
| The ``namespace`` option will be discussed later. | |||||
| Rebuilding the project will show no difference at the moment. | Rebuilding the project will show no difference at the moment. | ||||
| Uses: neo/sqlite3 | Uses: neo/sqlite3 | ||||
| Uses: neo/fun | Uses: neo/fun | ||||
| Uses: semver/semver | Uses: semver/semver | ||||
| Uses: vob/semester | |||||
| Uses: pubgrub/pubgrub | Uses: pubgrub/pubgrub | ||||
| Uses: vob/json5 |
| { | |||||
| "$schema": "./res/library-schema.json", | |||||
| "name": "dds", | |||||
| "uses": [ | |||||
| "spdlog/spdlog", | |||||
| "Microsoft/wil", | |||||
| "range-v3/range-v3", | |||||
| "nlohmann/json", | |||||
| "neo/sqlite3", | |||||
| "neo/fun", | |||||
| "semver/semver", | |||||
| "pubgrub/pubgrub", | |||||
| "vob/json5", | |||||
| "vob/semester", | |||||
| ] | |||||
| } |
| Depends: neo-fun 0.1.0 | Depends: neo-fun 0.1.0 | ||||
| Depends: semver 0.2.1 | Depends: semver 0.2.1 | ||||
| Depends: pubgrub 0.2.0 | Depends: pubgrub 0.2.0 | ||||
| Depends: vob-json5 0.1.5 | |||||
| Depends: vob-semester 0.1.0 | |||||
| Test-Driver: Catch-Main | |||||
| Test-Driver: Catch-Main |
| { | |||||
| "$schema": "./res/package-schema.json", | |||||
| "name": "dds", | |||||
| "version": "0.1.0", | |||||
| "namespace": "dds", | |||||
| "depends": { | |||||
| "spdlog": "1.4.2", | |||||
| "ms-wil": "2019.11.10", | |||||
| "range-v3": "0.10.0", | |||||
| "nlohmann-json": "3.7.1", | |||||
| "neo-sqlite3": "0.2.2", | |||||
| "neo-fun": "0.1.0", | |||||
| "semver": "0.2.1", | |||||
| "pubgrub": "0.2.0", | |||||
| "vob-json5": "0.1.5", | |||||
| "vob-semester": "0.1.0" | |||||
| }, | |||||
| "test_driver": "Catch-Main" | |||||
| } |
| { | |||||
| "type": "object", | |||||
| "description": "DDS Library Manifest", | |||||
| "additionalProperties": false, | |||||
| "patternProperties": { | |||||
| "^\\$": {} | |||||
| }, | |||||
| "required": [ | |||||
| "name" | |||||
| ], | |||||
| "properties": { | |||||
| "name": { | |||||
| "type": "string", | |||||
| "description": "The name of the library within the package.", | |||||
| "pattern": "^[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*$" | |||||
| }, | |||||
| "uses": { | |||||
| "type": "array", | |||||
| "items": { | |||||
| "type": "string", | |||||
| "description": "A library that is used by this library. Should be of the form `namespace/name`.", | |||||
| "pattern": "^[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*/[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*$" | |||||
| } | |||||
| }, | |||||
| "links": { | |||||
| "type": "array", | |||||
| "items": { | |||||
| "type": "string", | |||||
| "description": "A library that is linked to this library. Should be of the form `namespace/name`.", | |||||
| "pattern": "^[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*/[A-z][A-z0-9_]*((\\.|-)[A-z0-9_]+)*$" | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| { | |||||
| "type": "object", | |||||
| "description": "DDS Package Manifest", | |||||
| "additionalProperties": false, | |||||
| "patternProperties": { | |||||
| "^\\$": {} | |||||
| }, | |||||
| "required": [ | |||||
| "name", | |||||
| "version", | |||||
| "namespace" | |||||
| ], | |||||
| "properties": { | |||||
| "name": { | |||||
| "type": "string", | |||||
| "description": "The name of the package", | |||||
| "pattern": "^[a-z][a-z0-9_]*((\\.|-)[a-z0-9_]+)*$" | |||||
| }, | |||||
| "version": { | |||||
| "type": "string", | |||||
| "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", | |||||
| "description": "The version of the package. Must be a valid Semantic Version string.", | |||||
| "default": "0.1.0" | |||||
| }, | |||||
| "namespace": { | |||||
| "type": "string", | |||||
| "description": "The package's namespace. Must be a valid string.", | |||||
| "pattern": "^[a-z][a-z0-9_]*((\\.|-)[a-z0-9_]+)*$" | |||||
| }, | |||||
| "$schema": { | |||||
| "type": "string", | |||||
| "description": "JSON schema tag. Ignored by dds." | |||||
| }, | |||||
| "depends": { | |||||
| "type": "object", | |||||
| "patternProperties": { | |||||
| "^[a-z][a-z0-9_]*((\\.|-)[a-z0-9_]+)*$": { | |||||
| "type": "string", | |||||
| "description": "The version of the dependency. Must be a valid Semantic Version string", | |||||
| "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" | |||||
| } | |||||
| } | |||||
| }, | |||||
| "test_driver": { | |||||
| "type": "string", | |||||
| "default": "Catch-Main", | |||||
| "enum": [ | |||||
| "Catch-Main", | |||||
| "Catch" | |||||
| ] | |||||
| } | |||||
| } | |||||
| } |
| { | |||||
| "type": "object", | |||||
| "description": "DDS Toolchain Description File", | |||||
| "additionalProperties": false, | |||||
| "patternProperties": { | |||||
| "^\\$": {} | |||||
| }, | |||||
| "definitions": { | |||||
| "command_line_flags": { | |||||
| "anyOf": [ | |||||
| { | |||||
| "type": "string", | |||||
| "description": "Shell-style string of command-line arguments" | |||||
| }, | |||||
| { | |||||
| "type": "array", | |||||
| "description": "An array of command-line arguments. Will be passed verbatim.", | |||||
| "items": { | |||||
| "type": "string", | |||||
| "description": "A single command-line argument. Will be passed verbatim." | |||||
| } | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| "properties": { | |||||
| "compiler_id": { | |||||
| "type": "string", | |||||
| "description": "The general compiler identification. This is one of a fixed set of values that DDS will use to infer most toolchain attributes.", | |||||
| "enum": [ | |||||
| "msvc", | |||||
| "gnu", | |||||
| "clang" | |||||
| ] | |||||
| }, | |||||
| "c_compiler": { | |||||
| "type": "string", | |||||
| "description": "Executable name or filepath for a C compiler", | |||||
| "examples": [ | |||||
| "gcc", | |||||
| "clang-9", | |||||
| "cl.exe" | |||||
| ] | |||||
| }, | |||||
| "cxx_compiler": { | |||||
| "type": "string", | |||||
| "description": "Executable name or filepath for a C++ compiler", | |||||
| "examples": [ | |||||
| "g++", | |||||
| "clang++-9", | |||||
| "cl.exe" | |||||
| ] | |||||
| }, | |||||
| "flags": { | |||||
| "description": "Pass additional compile flags, regardless of the source language", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "c_flags": { | |||||
| "description": "Pass additional flags to the C compiler.", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "cxx_flags": { | |||||
| "description": "Pass additional flags to the C++ compiler.", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "c_version": { | |||||
| "description": "The C language version", | |||||
| "type": "string", | |||||
| "enum": [ | |||||
| "c89", | |||||
| "c99", | |||||
| "c11", | |||||
| "c18" | |||||
| ] | |||||
| }, | |||||
| "cxx_version": { | |||||
| "description": "The C++ language version", | |||||
| "type": "string", | |||||
| "enum": [ | |||||
| "c++98", | |||||
| "c++03", | |||||
| "c++11", | |||||
| "c++14", | |||||
| "c++17", | |||||
| "c++20" | |||||
| ] | |||||
| }, | |||||
| "warning_flags": { | |||||
| "description": "Set the flags that will be passed to the compiler to enable/disable warnings", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "link_flags": { | |||||
| "description": "Pass additional flags to the compiler when it is linking runtime binaries (executables)", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "compiler_launcher": { | |||||
| "description": "Set a command-line prefix that will be prepended to all compiler invocations", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "debug": { | |||||
| "description": "Enable the generation of debug information", | |||||
| "type": "boolean", | |||||
| "default": true | |||||
| }, | |||||
| "optimize": { | |||||
| "description": "Optimize generated code", | |||||
| "type": "boolean", | |||||
| "default": true | |||||
| }, | |||||
| "advanced": { | |||||
| "type": "object", | |||||
| "additionalProperties": false, | |||||
| "description": "Advanced toolchain options. All of these options will be inferred from `compiler_id` by default. Handle with care.", | |||||
| "properties": { | |||||
| "deps_mode": { | |||||
| "type": "string", | |||||
| "description": "Dependency tracking mode.", | |||||
| "enum": [ | |||||
| "msvc", | |||||
| "gnu", | |||||
| "none" | |||||
| ] | |||||
| }, | |||||
| "include_template": { | |||||
| "description": "Set the include-directory flags template", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "external_include_template": { | |||||
| "description": "Set the external include-directory flags template", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "define_template": { | |||||
| "description": "Set the preprocessor-definition flags template", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "base_warning_flags": { | |||||
| "description": "Set the base warning flags for the toolchain. These are always prepended to `warning_flags`.", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "c_compile_file": { | |||||
| "description": "Set the command template for compiling C source files", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "cxx_compile_file": { | |||||
| "description": "Set the command template for compiling C++ source files", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "create_archive": { | |||||
| "description": "Set the command template for generating static library archives", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "link_executable": { | |||||
| "description": "Set the command template for linking executable binaries", | |||||
| "$ref": "#/definitions/command_line_flags" | |||||
| }, | |||||
| "obj_prefix": { | |||||
| "description": "Set the filename prefix for object files", | |||||
| "type": "string" | |||||
| }, | |||||
| "obj_suffix": { | |||||
| "description": "Set the filename suffix for object files", | |||||
| "type": "string" | |||||
| }, | |||||
| "archive_prefix": { | |||||
| "description": "Set the filename prefix for library archive files", | |||||
| "type": "string" | |||||
| }, | |||||
| "archive_suffix": { | |||||
| "description": "Set the filename suffix for library archive files", | |||||
| "type": "string" | |||||
| }, | |||||
| "exe_prefix": { | |||||
| "description": "Set the filename prefix for executable files", | |||||
| "type": "string" | |||||
| }, | |||||
| "exe_suffix": { | |||||
| "description": "Set the filename suffix for executable files", | |||||
| "type": "string" | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } |
| #include <dds/error/errors.hpp> | #include <dds/error/errors.hpp> | ||||
| #include <dds/repo/repo.hpp> | #include <dds/repo/repo.hpp> | ||||
| #include <dds/source/dist.hpp> | #include <dds/source/dist.hpp> | ||||
| #include <dds/toolchain/from_dds.hpp> | |||||
| #include <dds/toolchain/from_json.hpp> | |||||
| #include <dds/util/fs.hpp> | #include <dds/util/fs.hpp> | ||||
| #include <dds/util/paths.hpp> | #include <dds/util/paths.hpp> | ||||
| #include <dds/util/signal.hpp> | #include <dds/util/signal.hpp> | ||||
| } | } | ||||
| return std::move(*tc); | return std::move(*tc); | ||||
| } else { | } else { | ||||
| return dds::parse_toolchain_dds(dds::slurp_file(tc_path)); | |||||
| return dds::parse_toolchain_json5(dds::slurp_file(tc_path)); | |||||
| // return dds::parse_toolchain_dds(dds::slurp_file(tc_path)); | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| params.out_root = out.Get(); | params.out_root = out.Get(); | ||||
| params.toolchain = tc_filepath.get_toolchain(); | params.toolchain = tc_filepath.get_toolchain(); | ||||
| params.parallel_jobs = n_jobs.Get(); | params.parallel_jobs = n_jobs.Get(); | ||||
| dds::package_manifest man; | |||||
| const auto man_filepath = project.root.Get() / "package.dds"; | |||||
| if (exists(man_filepath)) { | |||||
| man = dds::package_manifest::load_from_file(man_filepath); | |||||
| } | |||||
| auto man = dds::package_manifest::load_from_directory(project.root.Get()) | |||||
| .value_or(dds::package_manifest{}); | |||||
| dds::builder bd; | dds::builder bd; | ||||
| dds::sdist_build_params main_params; | dds::sdist_build_params main_params; |
| #include <dds/error/errors.hpp> | #include <dds/error/errors.hpp> | ||||
| #include <dds/proc.hpp> | #include <dds/proc.hpp> | ||||
| #include <nlohmann/json.hpp> | |||||
| #include <spdlog/spdlog.h> | #include <spdlog/spdlog.h> | ||||
| using namespace dds; | using namespace dds; | ||||
| spdlog::info("Create sdist from clone ..."); | spdlog::info("Create sdist from clone ..."); | ||||
| if (git.auto_lib.has_value()) { | if (git.auto_lib.has_value()) { | ||||
| spdlog::info("Generating library data automatically"); | spdlog::info("Generating library data automatically"); | ||||
| auto pkg_strm = dds::open(tmpdir.path() / "package.dds", std::ios::binary | std::ios::out); | |||||
| pkg_strm << "Name: " << listing.ident.name << '\n' // | |||||
| << "Version: " << listing.ident.version.to_string() << '\n' // | |||||
| << "Namespace: " << git.auto_lib->namespace_; | |||||
| auto lib_strm = dds::open(tmpdir.path() / "library.dds", std::ios::binary | std::ios::out); | |||||
| lib_strm << "Name: " << git.auto_lib->name; | |||||
| auto pkg_strm | |||||
| = dds::open(tmpdir.path() / "package.json5", std::ios::binary | std::ios::out); | |||||
| auto man_json = nlohmann::json::object(); | |||||
| man_json["name"] = listing.ident.name; | |||||
| man_json["version"] = listing.ident.version.to_string(); | |||||
| man_json["namespace"] = git.auto_lib->namespace_; | |||||
| pkg_strm << nlohmann::to_string(man_json); | |||||
| auto lib_strm | |||||
| = dds::open(tmpdir.path() / "library.json5", std::ios::binary | std::ios::out); | |||||
| auto lib_json = nlohmann::json::object(); | |||||
| lib_json["name"] = git.auto_lib->name; | |||||
| lib_strm << nlohmann::to_string(lib_json); | |||||
| } | } | ||||
| sdist_params params; | sdist_params params; |
| return "sdist-ident-mismatch.html"; | return "sdist-ident-mismatch.html"; | ||||
| case errc::corrupted_build_db: | case errc::corrupted_build_db: | ||||
| return "corrupted-build-db.html"; | return "corrupted-build-db.html"; | ||||
| case errc::invalid_lib_manifest: | |||||
| return "invalid-lib-manifest.html"; | |||||
| case errc::invalid_pkg_manifest: | |||||
| return "invalid-pkg-manifest.html"; | |||||
| case errc::invalid_version_range_string: | case errc::invalid_version_range_string: | ||||
| return "invalid-version-string.html#range"; | return "invalid-version-string.html#range"; | ||||
| case errc::invalid_version_string: | case errc::invalid_version_string: | ||||
| The catalog database schema doesn't match what dds expects. This indicates that | The catalog database schema doesn't match what dds expects. This indicates that | ||||
| the database file has been modified in a way that dds cannot automatically fix | the database file has been modified in a way that dds cannot automatically fix | ||||
| and handle. | and handle. | ||||
| )"; | |||||
| case errc::invalid_lib_manifest: | |||||
| return R"( | |||||
| A library manifest is malformed Refer to the documentation and above error | |||||
| message for more details. | |||||
| )"; | |||||
| case errc::invalid_pkg_manifest: | |||||
| return R"( | |||||
| The package manifest is malformed. Refer to the documentation and above error | |||||
| message for more details. | |||||
| )"; | )"; | ||||
| case errc::invalid_catalog_json: | case errc::invalid_catalog_json: | ||||
| return R"( | return R"( | ||||
| )"; | )"; | ||||
| case errc::unknown_usage_name: | case errc::unknown_usage_name: | ||||
| return R"( | return R"( | ||||
| A `Uses` or `Links` field for a library specifies a library of an unknown name. | |||||
| A `uses` or `links` field for a library specifies a library of an unknown name. | |||||
| Check your spelling, and check that the package containing the library is | Check your spelling, and check that the package containing the library is | ||||
| available, either from the `package.dds` or from the `INDEX.lmi` that was used | |||||
| available, either from the `package.json5` or from the `INDEX.lmi` that was used | |||||
| for the build. | for the build. | ||||
| )"; | )"; | ||||
| case errc::none: | case errc::none: | ||||
| "that was expected of it"; | "that was expected of it"; | ||||
| case errc::corrupted_build_db: | case errc::corrupted_build_db: | ||||
| return "The build database file is corrupted"; | return "The build database file is corrupted"; | ||||
| case errc::invalid_lib_manifest: | |||||
| return "The library manifest is invalid"; | |||||
| case errc::invalid_pkg_manifest: | |||||
| return "The package manifest is invalid"; | |||||
| case errc::invalid_version_range_string: | case errc::invalid_version_range_string: | ||||
| return "Attempted to parse an invalid version range string. <- (Seeing this text is a " | return "Attempted to parse an invalid version range string. <- (Seeing this text is a " | ||||
| "`dds` bug. Please report it.)"; | "`dds` bug. Please report it.)"; | ||||
| case errc::invalid_version_string: | case errc::invalid_version_string: | ||||
| return "Attempted to parse an invalid version string. <- (Seeing this text is a `dds` bug. " | |||||
| "Please report it.)"; | |||||
| return "Attempted to parse an invalid version string. <- (Seeing this text is a `dds` " | |||||
| "bug. Please report it.)"; | |||||
| case errc::invalid_config_key: | case errc::invalid_config_key: | ||||
| return "Found an invalid configuration key. <- (Seeing this text is a `dds` bug. Please " | |||||
| "report it.)"; | |||||
| return "Found an invalid configuration key. <- (Seeing this text is a `dds` bug. " | |||||
| "Please report it.)"; | |||||
| case errc::invalid_lib_filesystem: | case errc::invalid_lib_filesystem: | ||||
| case errc::invalid_pkg_filesystem: | case errc::invalid_pkg_filesystem: | ||||
| return "The filesystem structure of the package/library is invalid. <- (Seeing this text " | |||||
| "is a `dds` bug. Please report it.)"; | |||||
| return "The filesystem structure of the package/library is invalid. <- (Seeing this " | |||||
| "text is a `dds` bug. Please report it.)"; | |||||
| case errc::invalid_pkg_id: | case errc::invalid_pkg_id: | ||||
| return "A package identifier is invalid <- (Seeing this text is a `dds` bug. Please " | return "A package identifier is invalid <- (Seeing this text is a `dds` bug. Please " | ||||
| "report it.)"; | "report it.)"; | ||||
| case errc::invalid_pkg_name: | case errc::invalid_pkg_name: | ||||
| return "A package name is invalid <- (Seeing this text is a `dds` bug. Please report it.)"; | |||||
| return "A package name is invalid <- (Seeing this text is a `dds` bug. Please report " | |||||
| "it.)"; | |||||
| case errc::sdist_exists: | case errc::sdist_exists: | ||||
| return "The source ditsribution already exists at the destination <- (Seeing this text is " | |||||
| "a `dds` bug. Please report it.)"; | |||||
| return "The source ditsribution already exists at the destination <- (Seeing this " | |||||
| "text is a `dds` bug. Please report it.)"; | |||||
| case errc::unknown_test_driver: | case errc::unknown_test_driver: | ||||
| return "The specified Test-Driver is not known to `dds`"; | |||||
| return "The specified test_driver is not known to `dds`"; | |||||
| case errc::dependency_resolve_failure: | case errc::dependency_resolve_failure: | ||||
| return "`dds` was unable to find a solution for the package dependencies given."; | return "`dds` was unable to find a solution for the package dependencies given."; | ||||
| case errc::dup_lib_name: | case errc::dup_lib_name: | ||||
| return "More than one library has claimed the same name."; | return "More than one library has claimed the same name."; | ||||
| case errc::unknown_usage_name: | case errc::unknown_usage_name: | ||||
| return "A `Uses` or `Links` field names a library that isn't recognized."; | |||||
| return "A `uses` or `links` field names a library that isn't recognized."; | |||||
| case errc::none: | case errc::none: | ||||
| break; | break; | ||||
| } | } |
| corrupted_build_db, | corrupted_build_db, | ||||
| invalid_lib_manifest, | |||||
| invalid_pkg_manifest, | |||||
| invalid_version_range_string, | invalid_version_range_string, | ||||
| invalid_version_string, | invalid_version_string, | ||||
| invalid_pkg_id, | invalid_pkg_id, |
| #include "./manifest.hpp" | #include "./manifest.hpp" | ||||
| #include <dds/dym.hpp> | #include <dds/dym.hpp> | ||||
| #include <dds/error/errors.hpp> | |||||
| #include <dds/util/algo.hpp> | #include <dds/util/algo.hpp> | ||||
| #include <range/v3/view/transform.hpp> | |||||
| #include <libman/parse.hpp> | #include <libman/parse.hpp> | ||||
| #include <spdlog/fmt/fmt.h> | |||||
| #include <json5/parse_data.hpp> | |||||
| #include <range/v3/view/transform.hpp> | |||||
| #include <semester/decomp.hpp> | |||||
| #include <spdlog/spdlog.h> | |||||
| using namespace dds; | using namespace dds; | ||||
| library_manifest library_manifest::load_from_file(const fs::path& fpath) { | |||||
| library_manifest library_manifest::load_from_dds_file(path_ref fpath) { | |||||
| spdlog::warn( | |||||
| "Using deprecated library.dds parsing (on file {}). This will be removed soon. Migrate!", | |||||
| fpath.string()); | |||||
| auto kvs = lm::parse_file(fpath); | auto kvs = lm::parse_file(fpath); | ||||
| library_manifest ret; | library_manifest ret; | ||||
| ret.name = fpath.parent_path().filename().string(); | ret.name = fpath.parent_path().filename().string(); | ||||
| extend(ret.links, ranges::views::transform(links_strings, lm::split_usage_string)); | extend(ret.links, ranges::views::transform(links_strings, lm::split_usage_string)); | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| library_manifest library_manifest::load_from_file(path_ref fpath) { | |||||
| auto content = slurp_file(fpath); | |||||
| auto data = json5::parse_data(content); | |||||
| if (!data.is_object()) { | |||||
| throw_user_error<errc::invalid_lib_manifest>("Root value must be an object"); | |||||
| } | |||||
| library_manifest lib; | |||||
| using namespace semester::decompose_ops; | |||||
| auto res = semester::decompose( // | |||||
| data, | |||||
| try_seq{require_type<json5::data::mapping_type>{ | |||||
| "The root of the library manifest must be an object (mapping)"}, | |||||
| mapping{ | |||||
| if_key{"name", | |||||
| require_type<std::string>{"`name` must be a string"}, | |||||
| put_into{lib.name}}, | |||||
| if_key{"uses", | |||||
| require_type<json5::data::array_type>{ | |||||
| "`uses` must be an array of usage requirements"}, | |||||
| for_each{ | |||||
| require_type<std::string>{"`uses` elements must be strings"}, | |||||
| [&](auto&& uses) { | |||||
| lib.uses.push_back(lm::split_usage_string(uses.as_string())); | |||||
| return semester::dc_accept; | |||||
| }, | |||||
| }}, | |||||
| if_key{"links", | |||||
| require_type<json5::data::array_type>{ | |||||
| "`links` must be an array of usage requirements"}, | |||||
| for_each{ | |||||
| require_type<std::string>{"`links` elements must be strings"}, | |||||
| [&](auto&& links) { | |||||
| lib.links.push_back(lm::split_usage_string(links.as_string())); | |||||
| return semester::dc_accept; | |||||
| }, | |||||
| }}, | |||||
| }}); | |||||
| auto rej = std::get_if<semester::dc_reject_t>(&res); | |||||
| if (rej) { | |||||
| throw_user_error<errc::invalid_lib_manifest>(rej->message); | |||||
| } | |||||
| // using namespace json_read::ops; | |||||
| // json_read::decompose( // | |||||
| // data.as_object(), | |||||
| // object(key("name", require_string(put_into{lib.name}, "`name` must be a string")), | |||||
| // key("uses", | |||||
| // array_each{require_string( | |||||
| // [&](auto&& uses) { | |||||
| // lib.uses.push_back(lm::split_usage_string(uses.as_string())); | |||||
| // return json_read::accept_t{}; | |||||
| // }, | |||||
| // "All `uses` items must be strings")}), | |||||
| // key("links", | |||||
| // array_each{require_string( | |||||
| // [&](auto&& links) { | |||||
| // lib.links.push_back(lm::split_usage_string(links.as_string())); | |||||
| // return json_read::accept_t{}; | |||||
| // }, | |||||
| // "All `links` items must be strings")}))); | |||||
| if (lib.name.empty()) { | |||||
| throw_user_error<errc::invalid_lib_manifest>( | |||||
| "The 'name' field is required (Reading library manifest [{}])", fpath.string()); | |||||
| } | |||||
| return lib; | |||||
| } | |||||
| std::optional<fs::path> library_manifest::find_in_directory(path_ref dirpath) { | |||||
| auto fnames = { | |||||
| "library.json5", | |||||
| "library.jsonc", | |||||
| "library.json", | |||||
| }; | |||||
| for (auto c : fnames) { | |||||
| auto cand = dirpath / c; | |||||
| if (fs::is_regular_file(cand)) { | |||||
| return cand; | |||||
| } | |||||
| } | |||||
| auto dds_file = dirpath / "library.dds"; | |||||
| if (fs::is_regular_file(dds_file)) { | |||||
| return dds_file; | |||||
| } | |||||
| return std::nullopt; | |||||
| } | |||||
| std::optional<library_manifest> library_manifest::load_from_directory(path_ref dirpath) { | |||||
| auto found = find_in_directory(dirpath); | |||||
| if (!found.has_value()) { | |||||
| return std::nullopt; | |||||
| } | |||||
| if (found->extension() == ".dds") { | |||||
| return load_from_dds_file(*found); | |||||
| } else { | |||||
| return load_from_file(*found); | |||||
| } | |||||
| } |
| namespace dds { | namespace dds { | ||||
| /** | /** | ||||
| * Represents the contents of a `library.dds`. This is somewhat a stripped-down | |||||
| * Represents the contents of a `library.json5`. This is somewhat a stripped-down | |||||
| * version of lm::library, to only represent exactly the parts that we want to | * version of lm::library, to only represent exactly the parts that we want to | ||||
| * offer via `library.dds`. | |||||
| * offer via `library.json5`. | |||||
| */ | */ | ||||
| struct library_manifest { | struct library_manifest { | ||||
| /// The name of the library | /// The name of the library | ||||
| /** | /** | ||||
| * Load the library manifest from an existing file | * Load the library manifest from an existing file | ||||
| */ | */ | ||||
| static library_manifest load_from_file(const fs::path&); | |||||
| static library_manifest load_from_file(path_ref); | |||||
| static library_manifest load_from_dds_file(path_ref); | |||||
| /** | |||||
| * Find a library manifest within a directory. This will search for a few | |||||
| * file candidates and return the result from the first matching. If none | |||||
| * match, it will return nullopt. | |||||
| */ | |||||
| static std::optional<fs::path> find_in_directory(path_ref); | |||||
| static std::optional<library_manifest> load_from_directory(path_ref); | |||||
| }; | }; | ||||
| } // namespace dds | } // namespace dds |
| auto sources = collect_pf_sources(lib_dir); | auto sources = collect_pf_sources(lib_dir); | ||||
| library_manifest man; | library_manifest man; | ||||
| man.name = lib_dir.filename().string(); | |||||
| auto man_path = lib_dir / "library.dds"; | |||||
| if (fs::is_regular_file(man_path)) { | |||||
| man = library_manifest::load_from_file(man_path); | |||||
| man.name = lib_dir.filename().string(); | |||||
| auto found = library_manifest::find_in_directory(lib_dir); | |||||
| if (found) { | |||||
| if (found->extension() == ".dds") { | |||||
| man = library_manifest::load_from_dds_file(*found); | |||||
| } else { | |||||
| man = library_manifest::load_from_file(*found); | |||||
| } | |||||
| } | } | ||||
| auto lib = library_root(lib_dir, std::move(sources), std::move(man)); | auto lib = library_root(lib_dir, std::move(sources), std::move(man)); |
| #include <range/v3/view/split.hpp> | #include <range/v3/view/split.hpp> | ||||
| #include <range/v3/view/split_when.hpp> | #include <range/v3/view/split_when.hpp> | ||||
| #include <range/v3/view/transform.hpp> | #include <range/v3/view/transform.hpp> | ||||
| #include <spdlog/fmt/fmt.h> | |||||
| #include <semester/decomp.hpp> | |||||
| #include <spdlog/spdlog.h> | |||||
| #include <json5/parse_data.hpp> | |||||
| using namespace dds; | using namespace dds; | ||||
| package_manifest package_manifest::load_from_file(const fs::path& fpath) { | |||||
| package_manifest package_manifest::load_from_dds_file(const fs::path& fpath) { | |||||
| spdlog::warn( | |||||
| "Using deprecated package.dds parsing (on file {}). This will be removed soon. Migrate!", | |||||
| fpath.string()); | |||||
| auto kvs = lm::parse_file(fpath); | auto kvs = lm::parse_file(fpath); | ||||
| package_manifest ret; | package_manifest ret; | ||||
| std::string version_str; | std::string version_str; | ||||
| } else { | } else { | ||||
| auto dym = *did_you_mean(test_driver_str, {"Catch-Main", "Catch"}); | auto dym = *did_you_mean(test_driver_str, {"Catch-Main", "Catch"}); | ||||
| throw_user_error< | throw_user_error< | ||||
| errc::unknown_test_driver>("Unknown 'Test-Driver' '{}' (Did you mean '{}'?)", | |||||
| errc::unknown_test_driver>("Unknown 'test_driver' '{}' (Did you mean '{}'?)", | |||||
| test_driver_str, | test_driver_str, | ||||
| dym); | dym); | ||||
| } | } | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| package_manifest package_manifest::load_from_file(const fs::path& fpath) { | |||||
| auto content = slurp_file(fpath); | |||||
| auto data = json5::parse_data(content); | |||||
| if (!data.is_object()) { | |||||
| throw_user_error<errc::invalid_pkg_manifest>("Root value must be an object"); | |||||
| } | |||||
| package_manifest ret; | |||||
| using namespace semester::decompose_ops; | |||||
| auto res = semester::decompose( // | |||||
| data, | |||||
| try_seq{ | |||||
| require_type<json5::data::mapping_type>{ | |||||
| "The root of a package manifest must be an object (mapping)"}, | |||||
| mapping{ | |||||
| if_key{"$schema", just_accept}, | |||||
| if_key{ | |||||
| "name", | |||||
| require_type<std::string>{"`name` must be a string"}, | |||||
| put_into{ret.pkg_id.name}, | |||||
| }, | |||||
| if_key{ | |||||
| "namespace", | |||||
| require_type<std::string>{"`namespace` must be a string"}, | |||||
| put_into{ret.namespace_}, | |||||
| }, | |||||
| if_key{ | |||||
| "version", | |||||
| require_type<std::string>{"`version` must be a string"}, | |||||
| [&](auto&& version_str_) { | |||||
| auto& version = version_str_.as_string(); | |||||
| ret.pkg_id.version = semver::version::parse(version); | |||||
| return semester::dc_accept; | |||||
| }, | |||||
| }, | |||||
| if_key{ | |||||
| "depends", | |||||
| require_type<json5::data::mapping_type>{ | |||||
| "`depends` must be a mapping between package names and version ranges"}, | |||||
| mapping{[&](auto pkg_name, auto&& range_str_) { | |||||
| if (!range_str_.is_string()) { | |||||
| throw_user_error<errc::invalid_pkg_manifest>( | |||||
| "Dependency for '{}' must be a range string", pkg_name); | |||||
| } | |||||
| try { | |||||
| auto rng = semver::range::parse_restricted(range_str_.as_string()); | |||||
| dependency dep{std::string(pkg_name), {rng.low(), rng.high()}}; | |||||
| ret.dependencies.push_back(std::move(dep)); | |||||
| } catch (const semver::invalid_range&) { | |||||
| throw_user_error<errc::invalid_version_range_string>( | |||||
| "Invalid version range string '{}' in dependency declaration for " | |||||
| "'{}'", | |||||
| range_str_.as_string(), | |||||
| pkg_name); | |||||
| } | |||||
| return semester::dc_accept; | |||||
| }}, | |||||
| }, | |||||
| if_key{"test_driver", | |||||
| require_type<std::string>{"`test_driver` must be a string"}, | |||||
| [&](auto&& test_driver_str_) { | |||||
| auto& test_driver = test_driver_str_.as_string(); | |||||
| if (test_driver == "Catch-Main") { | |||||
| ret.test_driver = test_lib::catch_main; | |||||
| } else if (test_driver == "Catch") { | |||||
| ret.test_driver = test_lib::catch_; | |||||
| } else { | |||||
| auto dym = *did_you_mean(test_driver, {"Catch-Main", "Catch"}); | |||||
| throw_user_error<errc::unknown_test_driver>( | |||||
| "Unknown 'test_driver' '{}' (Did you mean '{}'?)", | |||||
| test_driver, | |||||
| dym); | |||||
| } | |||||
| return semester::dc_accept; | |||||
| }}, | |||||
| [&](auto key, auto&&) { | |||||
| return semester::dc_reject_t{ | |||||
| fmt::format("Unknown key `{}` in package manifest", key)}; | |||||
| }}}); | |||||
| auto rej = std::get_if<semester::dc_reject_t>(&res); | |||||
| if (rej) { | |||||
| throw_user_error<errc::invalid_pkg_manifest>(rej->message); | |||||
| } | |||||
| if (ret.pkg_id.name.empty()) { | |||||
| throw_user_error<errc::invalid_pkg_manifest>("The 'name' field is required."); | |||||
| } | |||||
| if (ret.namespace_.empty()) { | |||||
| throw_user_error<errc::invalid_pkg_manifest>("The 'namespace'` field is required."); | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| std::optional<fs::path> package_manifest::find_in_directory(path_ref dirpath) { | |||||
| auto cands = { | |||||
| "package.json5", | |||||
| "package.jsonc", | |||||
| "package.json", | |||||
| }; | |||||
| for (auto c : cands) { | |||||
| auto cand = dirpath / c; | |||||
| if (fs::is_regular_file(cand)) { | |||||
| return cand; | |||||
| } | |||||
| } | |||||
| auto dds_fname = dirpath / "package.dds"; | |||||
| if (fs::is_regular_file(dds_fname)) { | |||||
| return dds_fname; | |||||
| } | |||||
| return std::nullopt; | |||||
| } | |||||
| std::optional<package_manifest> package_manifest::load_from_directory(path_ref dirpath) { | |||||
| auto found = find_in_directory(dirpath); | |||||
| if (!found.has_value()) { | |||||
| return std::nullopt; | |||||
| } | |||||
| if (found->extension() == ".dds") { | |||||
| return load_from_dds_file(*found); | |||||
| } else { | |||||
| return load_from_file(*found); | |||||
| } | |||||
| } |
| namespace dds { | namespace dds { | ||||
| /** | /** | ||||
| * Possible values for Test-Driver in a package.dds | |||||
| * Possible values for test_driver in a package.json5 | |||||
| */ | */ | ||||
| enum class test_lib { | enum class test_lib { | ||||
| catch_, | catch_, | ||||
| package_id pkg_id; | package_id pkg_id; | ||||
| /// The declared `Namespace` of the package. This directly corresponds with the libman Namespace | /// The declared `Namespace` of the package. This directly corresponds with the libman Namespace | ||||
| std::string namespace_; | std::string namespace_; | ||||
| /// The `Test-Driver` that this package declares, or `nullopt` if absent. | |||||
| /// The `test_driver` that this package declares, or `nullopt` if absent. | |||||
| std::optional<test_lib> test_driver; | std::optional<test_lib> test_driver; | ||||
| /// The dependencies declared with the `Depends` fields, if any. | /// The dependencies declared with the `Depends` fields, if any. | ||||
| std::vector<dependency> dependencies; | std::vector<dependency> dependencies; | ||||
| * Load a package manifest from a file on disk. | * Load a package manifest from a file on disk. | ||||
| */ | */ | ||||
| static package_manifest load_from_file(path_ref); | static package_manifest load_from_file(path_ref); | ||||
| static package_manifest load_from_dds_file(path_ref); | |||||
| /** | |||||
| * Find a package manifest contained within a directory. This will search | |||||
| * for a few file candidates and return the result from the first matching. | |||||
| * If none match, it will return nullopt. | |||||
| */ | |||||
| static std::optional<fs::path> find_in_directory(path_ref); | |||||
| static std::optional<package_manifest> load_from_directory(path_ref); | |||||
| }; | }; | ||||
| } // namespace dds | } // namespace dds |
| ranges::sort(sources_to_keep, std::less<>(), [](auto&& s) { return s.path; }); | ranges::sort(sources_to_keep, std::less<>(), [](auto&& s) { return s.path; }); | ||||
| auto lib_dds_path = lib.path() / "library.dds"; | |||||
| if (!fs::is_regular_file(lib_dds_path)) { | |||||
| auto lib_man_path = library_manifest::find_in_directory(lib.path()); | |||||
| if (!lib_man_path) { | |||||
| throw_user_error<errc::invalid_lib_filesystem>( | throw_user_error<errc::invalid_lib_filesystem>( | ||||
| "Each library root in a source distribution requires a library manifest (Expected " | |||||
| "[{}])", | |||||
| lib_dds_path.string()); | |||||
| "Each library root in a source distribution requires a library manifest (Expected a " | |||||
| "library manifest in [{}])", | |||||
| lib.path().string()); | |||||
| } | } | ||||
| sdist_export_file(out_root, params.project_dir, lib_dds_path); | |||||
| sdist_export_file(out_root, params.project_dir, *lib_man_path); | |||||
| spdlog::info("sdist: Export library from {}", lib.path().string()); | spdlog::info("sdist: Export library from {}", lib.path().string()); | ||||
| fs::create_directories(out_root); | fs::create_directories(out_root); | ||||
| sdist_copy_library(out, lib, params); | sdist_copy_library(out, lib, params); | ||||
| } | } | ||||
| auto man_path = params.project_dir / "package.dds"; | |||||
| if (!fs::is_regular_file(man_path)) { | |||||
| auto man_path = package_manifest::find_in_directory(params.project_dir); | |||||
| if (!man_path) { | |||||
| throw_user_error<errc::invalid_pkg_filesystem>( | throw_user_error<errc::invalid_pkg_filesystem>( | ||||
| "Creating a source distribution requires a package.dds file for the project (Expected " | |||||
| "[{}])", | |||||
| man_path.string()); | |||||
| "Creating a source distribution requires a package.json5 file for the project " | |||||
| "(Expected manifest in [{}])", | |||||
| params.project_dir.string()); | |||||
| } | } | ||||
| sdist_export_file(out, params.project_dir, man_path); | |||||
| auto pkg_man = package_manifest::load_from_file(man_path); | |||||
| auto pkg_man = man_path->extension() == ".dds" ? package_manifest::load_from_dds_file(*man_path) | |||||
| : package_manifest::load_from_file(*man_path); | |||||
| sdist_export_file(out, params.project_dir, *man_path); | |||||
| spdlog::info("Generated export as {}", pkg_man.pkg_id.to_string()); | spdlog::info("Generated export as {}", pkg_man.pkg_id.to_string()); | ||||
| return sdist::from_directory(out); | return sdist::from_directory(out); | ||||
| } | } | ||||
| sdist sdist::from_directory(path_ref where) { | sdist sdist::from_directory(path_ref where) { | ||||
| auto pkg_man = package_manifest::load_from_file(where / "package.dds"); | |||||
| return sdist{std::move(pkg_man), where}; | |||||
| auto pkg_man = package_manifest::load_from_directory(where); | |||||
| // Code paths should only call here if they *know* that the sdist is valid | |||||
| assert(pkg_man.has_value()); | |||||
| return sdist{pkg_man.value(), where}; | |||||
| } | } |
| #pragma once | |||||
| #include <dds/toolchain/toolchain.hpp> | |||||
| #include <libman/parse_fwd.hpp> | |||||
| #include <string_view> | |||||
| namespace dds { | |||||
| class toolchain; | |||||
| toolchain parse_toolchain_dds(std::string_view str, | |||||
| std::string_view context = "Loading toolchain file"); | |||||
| toolchain parse_toolchain_dds(const lm::pair_list&, | |||||
| std::string_view context = "Loading toolchain file"); | |||||
| } // namespace dds |
| #include "./from_dds.hpp" | |||||
| #include "./from_json.hpp" | |||||
| #include <dds/dym.hpp> | #include <dds/dym.hpp> | ||||
| #include <dds/error/errors.hpp> | |||||
| #include <dds/toolchain/prep.hpp> | #include <dds/toolchain/prep.hpp> | ||||
| #include <dds/toolchain/toolchain.hpp> | |||||
| #include <dds/util/algo.hpp> | #include <dds/util/algo.hpp> | ||||
| #include <dds/util/shlex.hpp> | #include <dds/util/shlex.hpp> | ||||
| #include <libman/parse.hpp> | |||||
| #include <json5/parse_data.hpp> | |||||
| #include <semester/decomp.hpp> | |||||
| #include <spdlog/fmt/fmt.h> | #include <spdlog/fmt/fmt.h> | ||||
| #include <map> | |||||
| #include <optional> | |||||
| #include <tuple> | |||||
| #include <vector> | |||||
| #include <string> | |||||
| using namespace dds; | using namespace dds; | ||||
| using fmt::format; | |||||
| using std::optional; | using std::optional; | ||||
| using std::string; | using std::string; | ||||
| using std::vector; | using std::vector; | ||||
| using string_seq = vector<string>; | |||||
| using opt_string = optional<string>; | |||||
| using opt_str_seq = optional<string_seq>; | |||||
| using strv = std::string_view; | |||||
| toolchain dds::parse_toolchain_dds(strv str, strv context) { | |||||
| auto kvs = lm::parse_string(str); | |||||
| return parse_toolchain_dds(kvs, context); | |||||
| } | |||||
| using string_seq = vector<string>; | |||||
| using opt_string = optional<string>; | |||||
| using opt_string_seq = optional<string_seq>; | |||||
| using strv = std::string_view; | |||||
| namespace { | namespace { | ||||
| struct read_argv_acc { | |||||
| strv my_key; | |||||
| opt_str_seq& out; | |||||
| bool operator()(strv, strv key, strv value) const { | |||||
| if (key != my_key) { | |||||
| return false; | |||||
| } | |||||
| if (!out) { | |||||
| out.emplace(); | |||||
| } | |||||
| auto cmd = split_shell_string(value); | |||||
| extend(*out, cmd); | |||||
| return true; | |||||
| } | |||||
| }; | |||||
| struct read_argv { | |||||
| strv my_key; | |||||
| opt_str_seq& out; | |||||
| bool operator()(strv ctx, strv key, strv value) const { | |||||
| if (key != my_key) { | |||||
| return false; | |||||
| } | |||||
| if (out.has_value()) { | |||||
| throw std::runtime_error( | |||||
| format("{}: More than one value provided for key '{}'", ctx, key)); | |||||
| } | |||||
| out.emplace(split_shell_string(value)); | |||||
| return true; | |||||
| } | |||||
| }; | |||||
| template <typename T, typename Func> | template <typename T, typename Func> | ||||
| T read_opt(const std::optional<T>& what, Func&& fn) { | T read_opt(const std::optional<T>& what, Func&& fn) { | ||||
| template <typename... Args> | template <typename... Args> | ||||
| [[noreturn]] void fail(strv context, strv message, Args&&... args) { | [[noreturn]] void fail(strv context, strv message, Args&&... args) { | ||||
| auto fmtd = format(message, args...); | |||||
| throw std::runtime_error(format("{} - Failed to read toolchain file: {}", context, fmtd)); | |||||
| auto fmtd = fmt::format(message, args...); | |||||
| throw std::runtime_error(fmt::format("{} - Failed to read toolchain file: {}", context, fmtd)); | |||||
| } | } | ||||
| } // namespace | } // namespace | ||||
| toolchain dds::parse_toolchain_dds(const lm::pair_list& pairs, strv context) { | |||||
| toolchain dds::parse_toolchain_json5(std::string_view j5_str, std::string_view context) { | |||||
| auto dat = json5::parse_data(j5_str); | |||||
| return parse_toolchain_json_data(dat, context); | |||||
| } | |||||
| toolchain dds::parse_toolchain_json_data(const json5::data& dat, std::string_view context) { | |||||
| using namespace semester; | |||||
| opt_string compiler_id; | opt_string compiler_id; | ||||
| opt_string c_compiler_fpath; | |||||
| opt_string cxx_compiler_fpath; | |||||
| opt_string c_compiler; | |||||
| opt_string cxx_compiler; | |||||
| opt_string c_version; | opt_string c_version; | ||||
| opt_string cxx_version; | opt_string cxx_version; | ||||
| opt_string_seq compiler_launcher; | |||||
| opt_string_seq common_flags; | |||||
| opt_string_seq c_flags; | |||||
| opt_string_seq cxx_flags; | |||||
| opt_string_seq link_flags; | |||||
| opt_string_seq warning_flags; | |||||
| optional<bool> do_debug; | |||||
| optional<bool> do_optimize; | |||||
| // Advanced-mode: | |||||
| opt_string deps_mode_str; | |||||
| opt_string archive_prefix; | opt_string archive_prefix; | ||||
| opt_string archive_suffix; | opt_string archive_suffix; | ||||
| opt_string obj_prefix; | opt_string obj_prefix; | ||||
| opt_string obj_suffix; | opt_string obj_suffix; | ||||
| opt_string exe_prefix; | opt_string exe_prefix; | ||||
| opt_string exe_suffix; | opt_string exe_suffix; | ||||
| opt_string deps_mode_str; | |||||
| optional<bool> do_debug; | |||||
| optional<bool> do_optimize; | |||||
| opt_str_seq include_template; | |||||
| opt_str_seq external_include_template; | |||||
| opt_str_seq define_template; | |||||
| opt_str_seq warning_flags; | |||||
| opt_str_seq flags; | |||||
| opt_str_seq c_flags; | |||||
| opt_str_seq cxx_flags; | |||||
| opt_str_seq link_flags; | |||||
| opt_str_seq c_compile_file; | |||||
| opt_str_seq cxx_compile_file; | |||||
| opt_str_seq create_archive; | |||||
| opt_str_seq link_executable; | |||||
| opt_str_seq compile_launcher; | |||||
| lm::read(context, | |||||
| pairs, | |||||
| // Base compile info: | |||||
| lm::read_opt("Compiler-ID", compiler_id), | |||||
| lm::read_opt("C-Compiler", c_compiler_fpath), | |||||
| lm::read_opt("C++-Compiler", cxx_compiler_fpath), | |||||
| // Language options | |||||
| lm::read_opt("C-Version", c_version), | |||||
| lm::read_opt("C++-Version", cxx_version), | |||||
| // Flag templates | |||||
| read_argv{"Include-Template", include_template}, | |||||
| read_argv{"External-Include-Template", include_template}, | |||||
| read_argv{"Define-Template", define_template}, | |||||
| // Flags | |||||
| read_argv_acc{"Warning-Flags", warning_flags}, | |||||
| read_argv_acc{"Flags", flags}, | |||||
| read_argv_acc{"C-Flags", c_flags}, | |||||
| read_argv_acc{"C++-Flags", cxx_flags}, | |||||
| read_argv_acc{"Link-Flags", link_flags}, | |||||
| // Options for flags | |||||
| lm::read_bool("Optimize", do_optimize), | |||||
| lm::read_bool("Debug", do_debug), | |||||
| // Miscellaneous | |||||
| read_argv{"Compiler-Launcher", compile_launcher}, | |||||
| lm::read_opt("Deps-Mode", deps_mode_str), | |||||
| // Command templates | |||||
| read_argv{"C-Compile-File", c_compile_file}, | |||||
| read_argv{"C++-Compile-File", cxx_compile_file}, | |||||
| read_argv{"Create-Archive", create_archive}, | |||||
| read_argv{"Link-Executable", link_executable}, | |||||
| // Filename affixes | |||||
| lm::read_opt("Archive-Prefix", archive_prefix), | |||||
| lm::read_opt("Archive-Suffix", archive_suffix), | |||||
| lm::read_opt("Object-Prefix", obj_prefix), | |||||
| lm::read_opt("Object-Suffix", obj_suffix), | |||||
| lm::read_opt("Executable-Prefix", exe_prefix), | |||||
| lm::read_opt("Executable-Suffix", exe_suffix), | |||||
| // Die: | |||||
| lm_reject_dym{{ | |||||
| "Compiler-ID", | |||||
| "C-Compiler", | |||||
| "C++-Compiler", | |||||
| "C-Version", | |||||
| "C++-Version", | |||||
| "Include-Template", | |||||
| "External-Include-Template", | |||||
| "Define-Template", | |||||
| "Warning-Flags", | |||||
| "Flags", | |||||
| "C-Flags", | |||||
| "C++-Flags", | |||||
| "Link-Flags", | |||||
| "Optimize", | |||||
| "Debug", | |||||
| "Compiler-Launcher", | |||||
| "Deps-Mode", | |||||
| "C-Compile-File", | |||||
| "C++-Compile-File", | |||||
| "Create-Archive", | |||||
| "Link-Executable", | |||||
| "Archive-Prefix", | |||||
| "Archive-Suffix", | |||||
| "Object-Prefix", | |||||
| "Object-Suffix", | |||||
| "Executable-Prefix", | |||||
| "Executable-Suffix", | |||||
| }}); | |||||
| opt_string_seq base_warning_flags; | |||||
| opt_string_seq include_template; | |||||
| opt_string_seq external_include_template; | |||||
| opt_string_seq define_template; | |||||
| opt_string_seq c_compile_file; | |||||
| opt_string_seq cxx_compile_file; | |||||
| opt_string_seq create_archive; | |||||
| opt_string_seq link_executable; | |||||
| // For copy-pasting convenience: ‘{}’ | |||||
| auto extend_flags = [&](string key, auto& opt_flags) { | |||||
| return [&opt_flags, key](const json5::data& dat) { | |||||
| if (!opt_flags) { | |||||
| opt_flags.emplace(); | |||||
| } | |||||
| return decompose( // | |||||
| dat, | |||||
| try_seq{ | |||||
| if_type<string>([&](auto& str_) { | |||||
| auto more_flags = split_shell_string(str_.as_string()); | |||||
| extend(*opt_flags, more_flags); | |||||
| return dc_accept; | |||||
| }), | |||||
| if_array{for_each{ | |||||
| require_type<string>{ | |||||
| fmt::format("Elements of `{}` array must be strings", key)}, | |||||
| write_to{std::back_inserter(*opt_flags)}, | |||||
| }}, | |||||
| reject_with{fmt::format("`{}` must be an array or a shell-like string", key)}, | |||||
| }); | |||||
| }; | |||||
| }; | |||||
| toolchain_prep tc; | |||||
| #define KEY_EXTEND_FLAGS(Name) \ | |||||
| if_key { #Name, extend_flags(#Name, Name) } | |||||
| #define KEY_STRING(Name) \ | |||||
| if_key { #Name, require_type < string>("`" #Name "` must be a string"), put_into{Name }, } | |||||
| auto result = semester::decompose( // | |||||
| dat, | |||||
| try_seq{ | |||||
| require_type<json5::data::mapping_type>("Root of toolchain data must be a mapping"), | |||||
| mapping{ | |||||
| if_key{"$schema", just_accept}, | |||||
| KEY_STRING(compiler_id), | |||||
| KEY_STRING(c_compiler), | |||||
| KEY_STRING(cxx_compiler), | |||||
| KEY_STRING(c_version), | |||||
| KEY_STRING(cxx_version), | |||||
| KEY_EXTEND_FLAGS(c_flags), | |||||
| KEY_EXTEND_FLAGS(cxx_flags), | |||||
| KEY_EXTEND_FLAGS(warning_flags), | |||||
| KEY_EXTEND_FLAGS(link_flags), | |||||
| KEY_EXTEND_FLAGS(compiler_launcher), | |||||
| if_key{"debug", | |||||
| require_type<bool>("`debug` must be a boolean value"), | |||||
| put_into{do_debug}}, | |||||
| if_key{"optimize", | |||||
| require_type<bool>("`optimize` must be a boolean value"), | |||||
| put_into{do_optimize}}, | |||||
| if_key{"flags", extend_flags("flags", common_flags)}, | |||||
| if_key{ | |||||
| "advanced", | |||||
| require_type<json5::data::mapping_type>("`advanced` must be a mapping"), | |||||
| mapping{ | |||||
| if_key{"deps_mode", | |||||
| require_type<string>("`deps_mode` must be a string"), | |||||
| put_into{deps_mode_str}}, | |||||
| KEY_EXTEND_FLAGS(include_template), | |||||
| KEY_EXTEND_FLAGS(external_include_template), | |||||
| KEY_EXTEND_FLAGS(define_template), | |||||
| KEY_EXTEND_FLAGS(base_warning_flags), | |||||
| KEY_EXTEND_FLAGS(c_compile_file), | |||||
| KEY_EXTEND_FLAGS(cxx_compile_file), | |||||
| KEY_EXTEND_FLAGS(create_archive), | |||||
| KEY_EXTEND_FLAGS(link_executable), | |||||
| KEY_STRING(obj_prefix), | |||||
| KEY_STRING(obj_suffix), | |||||
| KEY_STRING(archive_prefix), | |||||
| KEY_STRING(archive_suffix), | |||||
| KEY_STRING(exe_prefix), | |||||
| KEY_STRING(exe_suffix), | |||||
| [&](auto key, auto) -> dc_reject_t { | |||||
| auto dym = did_you_mean(key, | |||||
| { | |||||
| "deps_mode", | |||||
| "include_template", | |||||
| "external_include_template", | |||||
| "define_template", | |||||
| "base_warning_flags", | |||||
| "c_compile_file", | |||||
| "cxx_compile_file", | |||||
| "create_archive", | |||||
| "link_executable", | |||||
| "obj_prefix", | |||||
| "obj_suffix", | |||||
| "archive_prefix", | |||||
| "archive_suffix", | |||||
| "exe_prefix", | |||||
| "exe_suffix", | |||||
| }); | |||||
| fail(context, | |||||
| "Unknown toolchain advanced-config key ‘{}’ (Did you mean ‘{}’?)", | |||||
| key, | |||||
| *dym); | |||||
| std::terminate(); | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| [&](auto key, auto &&) -> dc_reject_t { | |||||
| // They've given an unknown key. Ouch. | |||||
| auto dym = did_you_mean(key, | |||||
| { | |||||
| "compiler_id", | |||||
| "c_compiler", | |||||
| "cxx_compiler", | |||||
| "c_version", | |||||
| "cxx_version", | |||||
| "c_flags", | |||||
| "cxx_flags", | |||||
| "warning_flags", | |||||
| "link_flags", | |||||
| "flags", | |||||
| "debug", | |||||
| "optimize", | |||||
| }); | |||||
| fail(context, | |||||
| "Unknown toolchain config key ‘{}’ (Did you mean ‘{}’?)", | |||||
| key, | |||||
| *dym); | |||||
| std::terminate(); | |||||
| }, | |||||
| }, | |||||
| }); | |||||
| auto rej_opt = std::get_if<dc_reject_t>(&result); | |||||
| if (rej_opt) { | |||||
| fail(context, rej_opt->message); | |||||
| } | |||||
| enum compiler_id_e_t { | enum compiler_id_e_t { | ||||
| no_comp_id, | no_comp_id, | ||||
| = [&] { | = [&] { | ||||
| if (!compiler_id) { | if (!compiler_id) { | ||||
| return no_comp_id; | return no_comp_id; | ||||
| } else if (compiler_id == "MSVC") { | |||||
| } else if (compiler_id == "msvc") { | |||||
| return msvc; | return msvc; | ||||
| } else if (compiler_id == "GNU") { | |||||
| } else if (compiler_id == "gnu") { | |||||
| return gnu; | return gnu; | ||||
| } else if (compiler_id == "Clang") { | |||||
| } else if (compiler_id == "clang") { | |||||
| return clang; | return clang; | ||||
| } else { | } else { | ||||
| fail(context, "Unknown Compiler-ID '{}'", *compiler_id); | |||||
| fail(context, "Invalid `compiler_id` value ‘{}’", *compiler_id); | |||||
| } | } | ||||
| }(); | }(); | ||||
| } else { | } else { | ||||
| return file_deps_mode::none; | return file_deps_mode::none; | ||||
| } | } | ||||
| } else if (deps_mode_str == "GNU") { | |||||
| } else if (deps_mode_str == "gnu") { | |||||
| return file_deps_mode::gnu; | return file_deps_mode::gnu; | ||||
| } else if (deps_mode_str == "MSVC") { | |||||
| } else if (deps_mode_str == "msvc") { | |||||
| return file_deps_mode::msvc; | return file_deps_mode::msvc; | ||||
| } else if (deps_mode_str == "None") { | |||||
| } else if (deps_mode_str == "none") { | |||||
| return file_deps_mode::none; | return file_deps_mode::none; | ||||
| } else { | } else { | ||||
| fail(context, "Unknown Deps-Mode '{}'", *deps_mode_str); | |||||
| fail(context, "Invalid `deps_mode` value ‘{}’", *deps_mode_str); | |||||
| } | } | ||||
| }(); | }(); | ||||
| // Now convert the flags we've been given into a real toolchain | // Now convert the flags we've been given into a real toolchain | ||||
| auto get_compiler = [&](language lang) -> string { | |||||
| if (lang == language::cxx && cxx_compiler_fpath) { | |||||
| return *cxx_compiler_fpath; | |||||
| auto get_compiler_executable_path = [&](language lang) -> string { | |||||
| if (lang == language::cxx && cxx_compiler) { | |||||
| return *cxx_compiler; | |||||
| } | } | ||||
| if (lang == language::c && c_compiler_fpath) { | |||||
| return *c_compiler_fpath; | |||||
| if (lang == language::c && c_compiler) { | |||||
| return *c_compiler; | |||||
| } | } | ||||
| if (!compiler_id.has_value()) { | if (!compiler_id.has_value()) { | ||||
| fail(context, "Unable to determine what compiler to use."); | |||||
| if (lang == language::c) { | |||||
| fail(context, "Unable to determine the executable for a C compiler"); | |||||
| } | |||||
| if (lang == language::cxx) { | |||||
| fail(context, "Unable to determine the executable for a C++ compiler"); | |||||
| } | |||||
| } | } | ||||
| if (is_gnu) { | if (is_gnu) { | ||||
| return (lang == language::cxx) ? "g++" : "gcc"; | return (lang == language::cxx) ? "g++" : "gcc"; | ||||
| std::terminate(); | std::terminate(); | ||||
| }; | }; | ||||
| // Determine the C language version | |||||
| enum c_version_e_t { | enum c_version_e_t { | ||||
| c_none, | c_none, | ||||
| c89, | c89, | ||||
| = [&] { | = [&] { | ||||
| if (!c_version) { | if (!c_version) { | ||||
| return c_none; | return c_none; | ||||
| } else if (c_version == "C89") { | |||||
| } else if (c_version == "c89") { | |||||
| return c89; | return c89; | ||||
| } else if (c_version == "C99") { | |||||
| } else if (c_version == "c99") { | |||||
| return c99; | return c99; | ||||
| } else if (c_version == "C11") { | |||||
| } else if (c_version == "c11") { | |||||
| return c11; | return c11; | ||||
| } else if (c_version == "C18") { | |||||
| } else if (c_version == "c18") { | |||||
| return c18; | return c18; | ||||
| } else { | } else { | ||||
| fail(context, "Unknown C-Version '{}'", *c_version); | |||||
| fail(context, "Unknown `c_version` ‘{}’", *c_version); | |||||
| } | } | ||||
| }(); | }(); | ||||
| = [&] { | = [&] { | ||||
| if (!cxx_version) { | if (!cxx_version) { | ||||
| return cxx_none; | return cxx_none; | ||||
| } else if (cxx_version == "C++98") { | |||||
| } else if (cxx_version == "c++98") { | |||||
| return cxx98; | return cxx98; | ||||
| } else if (cxx_version == "C++03") { | |||||
| } else if (cxx_version == "c++03") { | |||||
| return cxx03; | return cxx03; | ||||
| } else if (cxx_version == "C++11") { | |||||
| } else if (cxx_version == "c++11") { | |||||
| return cxx11; | return cxx11; | ||||
| } else if (cxx_version == "C++14") { | |||||
| } else if (cxx_version == "c++14") { | |||||
| return cxx14; | return cxx14; | ||||
| } else if (cxx_version == "C++17") { | |||||
| } else if (cxx_version == "c++17") { | |||||
| return cxx17; | return cxx17; | ||||
| } else if (cxx_version == "C++20") { | |||||
| } else if (cxx_version == "c++20") { | |||||
| return cxx20; | return cxx20; | ||||
| } else { | } else { | ||||
| fail(context, "Unknown C++-Version '{}'", *cxx_version); | |||||
| fail(context, "Unknown `cxx_version` ‘{}’", *cxx_version); | |||||
| } | } | ||||
| }(); | }(); | ||||
| auto get_c_version_flags = [&]() -> string_seq { | auto get_c_version_flags = [&]() -> string_seq { | ||||
| if (!compiler_id.has_value()) { | if (!compiler_id.has_value()) { | ||||
| fail(context, "Unable to deduce flags for 'C-Version' without setting 'Compiler-ID'"); | |||||
| fail(context, "Unable to deduce flags for 'c_version' without setting 'compiler_id'"); | |||||
| } | } | ||||
| auto c_ver_iter = c_version_flag_table.find({compiler_id_e, c_version_e}); | auto c_ver_iter = c_version_flag_table.find({compiler_id_e, c_version_e}); | ||||
| assert(c_ver_iter != c_version_flag_table.end()); | assert(c_ver_iter != c_version_flag_table.end()); | ||||
| auto get_cxx_version_flags = [&]() -> string_seq { | auto get_cxx_version_flags = [&]() -> string_seq { | ||||
| if (!compiler_id.has_value()) { | if (!compiler_id.has_value()) { | ||||
| fail(context, "Unable to deduce flags for 'C++-Version' without setting 'Compiler-ID'"); | |||||
| fail(context, "Unable to deduce flags for 'cxx_version' without setting 'compiler_id'"); | |||||
| } | } | ||||
| auto cxx_ver_iter = cxx_version_flag_table.find({compiler_id_e, cxx_version_e}); | auto cxx_ver_iter = cxx_version_flag_table.find({compiler_id_e, cxx_version_e}); | ||||
| assert(cxx_ver_iter != cxx_version_flag_table.end()); | assert(cxx_ver_iter != cxx_version_flag_table.end()); | ||||
| auto get_flags = [&](language lang) -> string_seq { | auto get_flags = [&](language lang) -> string_seq { | ||||
| string_seq ret; | string_seq ret; | ||||
| if (lang == language::cxx && cxx_flags) { | |||||
| extend(ret, *cxx_flags); | |||||
| } | |||||
| if (lang == language::cxx && cxx_version) { | |||||
| extend(ret, get_cxx_version_flags()); | |||||
| } | |||||
| if (lang == language::c && c_flags) { | |||||
| extend(ret, *c_flags); | |||||
| } | |||||
| if (lang == language::c && c_version) { | |||||
| extend(ret, get_c_version_flags()); | |||||
| } | |||||
| if (is_msvc) { | if (is_msvc) { | ||||
| strv rt_lib = "/MT"; | strv rt_lib = "/MT"; | ||||
| if (do_optimize.has_value() && *do_optimize) { | if (do_optimize.has_value() && *do_optimize) { | ||||
| "<IN>", | "<IN>", | ||||
| "-o<OUT>"}); | "-o<OUT>"}); | ||||
| } | } | ||||
| if (flags) { | |||||
| extend(ret, *flags); | |||||
| if (common_flags) { | |||||
| extend(ret, *common_flags); | |||||
| } | |||||
| if (lang == language::cxx && cxx_flags) { | |||||
| extend(ret, *cxx_flags); | |||||
| } | |||||
| if (lang == language::cxx && cxx_version) { | |||||
| extend(ret, get_cxx_version_flags()); | |||||
| } | |||||
| if (lang == language::c && c_flags) { | |||||
| extend(ret, *c_flags); | |||||
| } | |||||
| if (lang == language::c && c_version) { | |||||
| extend(ret, get_c_version_flags()); | |||||
| } | } | ||||
| return ret; | return ret; | ||||
| }; | }; | ||||
| toolchain_prep tc; | |||||
| tc.deps_mode = deps_mode; | tc.deps_mode = deps_mode; | ||||
| tc.c_compile = read_opt(c_compile_file, [&] { | tc.c_compile = read_opt(c_compile_file, [&] { | ||||
| string_seq c; | string_seq c; | ||||
| if (compile_launcher) { | |||||
| extend(c, *compile_launcher); | |||||
| if (compiler_launcher) { | |||||
| extend(c, *compiler_launcher); | |||||
| } | } | ||||
| c.push_back(get_compiler(language::c)); | |||||
| c.push_back(get_compiler_executable_path(language::c)); | |||||
| extend(c, get_flags(language::c)); | extend(c, get_flags(language::c)); | ||||
| return c; | return c; | ||||
| }); | }); | ||||
| tc.cxx_compile = read_opt(cxx_compile_file, [&] { | tc.cxx_compile = read_opt(cxx_compile_file, [&] { | ||||
| string_seq cxx; | string_seq cxx; | ||||
| if (compile_launcher) { | |||||
| extend(cxx, *compile_launcher); | |||||
| if (compiler_launcher) { | |||||
| extend(cxx, *compiler_launcher); | |||||
| } | } | ||||
| cxx.push_back(get_compiler(language::cxx)); | |||||
| cxx.push_back(get_compiler_executable_path(language::cxx)); | |||||
| extend(cxx, get_flags(language::cxx)); | extend(cxx, get_flags(language::cxx)); | ||||
| return cxx; | return cxx; | ||||
| }); | }); | ||||
| tc.include_template = read_opt(include_template, [&]() -> string_seq { | tc.include_template = read_opt(include_template, [&]() -> string_seq { | ||||
| if (!compiler_id) { | if (!compiler_id) { | ||||
| fail(context, "Cannot deduce 'Include-Template' without 'Compiler-ID'"); | |||||
| fail(context, "Cannot deduce 'include_template' without 'compiler_id'"); | |||||
| } | } | ||||
| if (is_gnu_like) { | if (is_gnu_like) { | ||||
| return {"-I", "<PATH>"}; | return {"-I", "<PATH>"}; | ||||
| } else if (is_msvc) { | } else if (is_msvc) { | ||||
| return {"/I", "<PATH>"}; | return {"/I", "<PATH>"}; | ||||
| } | } | ||||
| assert(false && "Include-Template deduction failed"); | |||||
| assert(false && "'include_template' deduction failed"); | |||||
| std::terminate(); | std::terminate(); | ||||
| }); | }); | ||||
| // MSVC has external-header support inbound, but it is not fully ready yet | // MSVC has external-header support inbound, but it is not fully ready yet | ||||
| return {"/I", "<PATH>"}; | return {"/I", "<PATH>"}; | ||||
| } | } | ||||
| assert(false && "External-Include-Template deduction failed"); | |||||
| assert(false && "external_include_template deduction failed"); | |||||
| std::terminate(); | std::terminate(); | ||||
| }); | }); | ||||
| tc.define_template = read_opt(define_template, [&]() -> string_seq { | tc.define_template = read_opt(define_template, [&]() -> string_seq { | ||||
| if (!compiler_id) { | if (!compiler_id) { | ||||
| fail(context, "Cannot deduce 'Define-Template' without 'Compiler-ID'"); | |||||
| fail(context, "Cannot deduce 'define_template' without 'compiler_id'"); | |||||
| } | } | ||||
| if (is_gnu_like) { | if (is_gnu_like) { | ||||
| return {"-D", "<DEF>"}; | return {"-D", "<DEF>"}; | ||||
| } else if (is_msvc) { | } else if (is_msvc) { | ||||
| return {"/D", "<DEF>"}; | return {"/D", "<DEF>"}; | ||||
| } | } | ||||
| assert(false && "Define-Template deduction failed"); | |||||
| assert(false && "define_template deduction failed"); | |||||
| std::terminate(); | std::terminate(); | ||||
| }); | }); | ||||
| #endif | #endif | ||||
| }); | }); | ||||
| /// TODO: Handle base_warning_flags: | |||||
| tc.warning_flags = read_opt(warning_flags, [&]() -> string_seq { | tc.warning_flags = read_opt(warning_flags, [&]() -> string_seq { | ||||
| if (!compiler_id) { | if (!compiler_id) { | ||||
| // No error. Just no warning flags | // No error. Just no warning flags | ||||
| tc.link_archive = read_opt(create_archive, [&]() -> string_seq { | tc.link_archive = read_opt(create_archive, [&]() -> string_seq { | ||||
| if (!compiler_id) { | if (!compiler_id) { | ||||
| fail(context, "Unable to deduce archive creation rules without a Compiler-ID"); | |||||
| fail(context, "Unable to deduce archive creation rules without a 'compiler_id'"); | |||||
| } | } | ||||
| if (is_msvc) { | if (is_msvc) { | ||||
| return {"lib", "/nologo", "/OUT:<OUT>", "<IN>"}; | return {"lib", "/nologo", "/OUT:<OUT>", "<IN>"}; | ||||
| tc.link_exe = read_opt(link_executable, [&]() -> string_seq { | tc.link_exe = read_opt(link_executable, [&]() -> string_seq { | ||||
| if (!compiler_id) { | if (!compiler_id) { | ||||
| fail(context, "Unable to deduce how to link executables without a Compiler-ID"); | |||||
| fail(context, "Unable to deduce how to link executables without a 'compiler_id'"); | |||||
| } | } | ||||
| string_seq ret; | string_seq ret; | ||||
| if (is_msvc) { | if (is_msvc) { | ||||
| ret = {get_compiler(language::cxx), "/nologo", "/EHsc", "<IN>", "/Fe<OUT>"}; | |||||
| ret = {get_compiler_executable_path(language::cxx), | |||||
| "/nologo", | |||||
| "/EHsc", | |||||
| "<IN>", | |||||
| "/Fe<OUT>"}; | |||||
| } else if (is_gnu_like) { | } else if (is_gnu_like) { | ||||
| ret = {get_compiler(language::cxx), | |||||
| ret = {get_compiler_executable_path(language::cxx), | |||||
| "-fPIC", | "-fPIC", | ||||
| "-fdiagnostics-color", | "-fdiagnostics-color", | ||||
| "<IN>", | "<IN>", | ||||
| }); | }); | ||||
| return tc.realize(); | return tc.realize(); | ||||
| } | |||||
| } |
| #pragma once | |||||
| #include <dds/toolchain/toolchain.hpp> | |||||
| #include <json5/data.hpp> | |||||
| #include <string_view> | |||||
| namespace dds { | |||||
| toolchain parse_toolchain_json5(std::string_view json5, | |||||
| std::string_view context = "Loading toolchain JSON"); | |||||
| toolchain parse_toolchain_json_data(const json5::data& data, | |||||
| std::string_view context = "Loading toolchain JSON"); | |||||
| } // namespace dds |
| #include <dds/toolchain/from_dds.hpp> | |||||
| #include <dds/toolchain/from_json.hpp> | |||||
| #include <dds/proc.hpp> | #include <dds/proc.hpp> | ||||
| // #include <dds/util.test.hpp> | |||||
| #include <catch2/catch.hpp> | #include <catch2/catch.hpp> | ||||
| namespace { | namespace { | ||||
| void check_tc_compile(std::string_view tc_content, | void check_tc_compile(std::string_view tc_content, | ||||
| std::string_view expected_compile, | std::string_view expected_compile, | ||||
| std::string_view expected_compile_warnings, | std::string_view expected_compile_warnings, | ||||
| std::string_view expected_ar, | std::string_view expected_ar, | ||||
| std::string_view expected_exe) { | std::string_view expected_exe) { | ||||
| auto tc = dds::parse_toolchain_dds(tc_content); | |||||
| auto tc = dds::parse_toolchain_json5(tc_content); | |||||
| dds::compile_file_spec cf; | dds::compile_file_spec cf; | ||||
| cf.source_path = "foo.cpp"; | cf.source_path = "foo.cpp"; | ||||
| CHECK(exe_cmd_str == expected_exe); | CHECK(exe_cmd_str == expected_exe); | ||||
| } | } | ||||
| } // namespace | |||||
| TEST_CASE("Generating toolchain commands") { | TEST_CASE("Generating toolchain commands") { | ||||
| check_tc_compile( | check_tc_compile( | ||||
| "Compiler-ID: GNU", | |||||
| "{compiler_id: 'gnu'}", | |||||
| "g++ -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | "g++ -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | ||||
| "g++ -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | "g++ -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | ||||
| "-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | "-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | ||||
| "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe"); | "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe"); | ||||
| check_tc_compile( | check_tc_compile( | ||||
| "Compiler-ID: GNU\nDebug: True", | |||||
| "{compiler_id: 'gnu', debug: true}", | |||||
| "g++ -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | "g++ -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | ||||
| "g++ -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | "g++ -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | ||||
| "-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | "-MD -MF foo.o.d -MT foo.o -c foo.cpp -ofoo.o", | ||||
| "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe -g"); | "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe -g"); | ||||
| check_tc_compile( | check_tc_compile( | ||||
| "Compiler-ID: GNU\nDebug: True\nOptimize: True", | |||||
| "{compiler_id: 'gnu', debug: true, optimize: true}", | |||||
| "g++ -O2 -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp " | "g++ -O2 -g -fPIC -fdiagnostics-color -pthread -MD -MF foo.o.d -MT foo.o -c foo.cpp " | ||||
| "-ofoo.o", | "-ofoo.o", | ||||
| "g++ -O2 -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | "g++ -O2 -g -fPIC -fdiagnostics-color -pthread -Wall -Wextra -Wpedantic -Wconversion " | ||||
| "ar rcs stuff.a foo.o bar.o", | "ar rcs stuff.a foo.o bar.o", | ||||
| "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe -O2 -g"); | "g++ -fPIC -fdiagnostics-color foo.o bar.a -pthread -omeow.exe -O2 -g"); | ||||
| check_tc_compile("Compiler-ID: MSVC", | |||||
| check_tc_compile("{compiler_id: 'msvc'}", | |||||
| "cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | "cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | ||||
| "cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | "cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | ||||
| "lib /nologo /OUT:stuff.a foo.o bar.o", | "lib /nologo /OUT:stuff.a foo.o bar.o", | ||||
| "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /MT"); | "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /MT"); | ||||
| check_tc_compile( | check_tc_compile( | ||||
| "Compiler-ID: MSVC\nDebug: True", | |||||
| "{compiler_id: 'msvc', debug: true}", | |||||
| "cl.exe /Z7 /DEBUG /MTd /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | "cl.exe /Z7 /DEBUG /MTd /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o", | ||||
| "cl.exe /Z7 /DEBUG /MTd /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | "cl.exe /Z7 /DEBUG /MTd /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o", | ||||
| "lib /nologo /OUT:stuff.a foo.o bar.o", | "lib /nologo /OUT:stuff.a foo.o bar.o", | ||||
| "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /Z7 /DEBUG /MTd"); | "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /Z7 /DEBUG /MTd"); | ||||
| auto tc = dds::parse_toolchain_dds(R"( | |||||
| Compiler-ID: GNU | |||||
| )"); | |||||
| check_tc_compile( | |||||
| "{compiler_id: 'msvc', flags: '-DFOO'}", | |||||
| "cl.exe /MT /EHsc /nologo /permissive- /showIncludes /c foo.cpp /Fofoo.o -DFOO", | |||||
| "cl.exe /MT /EHsc /nologo /permissive- /W4 /showIncludes /c foo.cpp /Fofoo.o -DFOO", | |||||
| "lib /nologo /OUT:stuff.a foo.o bar.o", | |||||
| "cl.exe /nologo /EHsc foo.o bar.a /Femeow.exe /MT"); | |||||
| } | |||||
| TEST_CASE("Manipulate a toolchain and file compilation") { | |||||
| auto tc = dds::parse_toolchain_json5("{compiler_id: 'gnu'}"); | |||||
| dds::compile_file_spec cfs; | dds::compile_file_spec cfs; | ||||
| cfs.source_path = "foo.cpp"; | cfs.source_path = "foo.cpp"; | ||||
| "-c", | "-c", | ||||
| "foo.cpp", | "foo.cpp", | ||||
| "-ofoo.o"}); | "-ofoo.o"}); | ||||
| } | |||||
| } // namespace | |||||
| } |
| #include "./toolchain.hpp" | #include "./toolchain.hpp" | ||||
| #include <dds/toolchain/from_dds.hpp> | |||||
| #include <dds/toolchain/from_json.hpp> | |||||
| #include <dds/toolchain/prep.hpp> | #include <dds/toolchain/prep.hpp> | ||||
| #include <dds/util/algo.hpp> | #include <dds/util/algo.hpp> | ||||
| #include <dds/util/paths.hpp> | #include <dds/util/paths.hpp> | ||||
| std::optional<toolchain> toolchain::get_builtin(std::string_view tc_id) noexcept { | std::optional<toolchain> toolchain::get_builtin(std::string_view tc_id) noexcept { | ||||
| using namespace std::literals; | using namespace std::literals; | ||||
| std::string tc_content; | |||||
| json5::data tc_data = json5::data::mapping_type(); | |||||
| auto& root_map = tc_data.as_object(); | |||||
| if (starts_with(tc_id, "debug:")) { | if (starts_with(tc_id, "debug:")) { | ||||
| tc_id = tc_id.substr("debug:"sv.length()); | tc_id = tc_id.substr("debug:"sv.length()); | ||||
| tc_content += "Debug: True\n"; | |||||
| root_map.emplace("debug", true); | |||||
| } | } | ||||
| if (starts_with(tc_id, "ccache:")) { | if (starts_with(tc_id, "ccache:")) { | ||||
| tc_id = tc_id.substr("ccache:"sv.length()); | tc_id = tc_id.substr("ccache:"sv.length()); | ||||
| tc_content += "Compiler-Launcher: ccache\n"; | |||||
| root_map.emplace("compiler_launcher", "ccache"); | |||||
| } | } | ||||
| #define CXX_VER_TAG(str, version) \ | #define CXX_VER_TAG(str, version) \ | ||||
| if (starts_with(tc_id, str)) { \ | if (starts_with(tc_id, str)) { \ | ||||
| tc_id = tc_id.substr(std::string_view(str).length()); \ | tc_id = tc_id.substr(std::string_view(str).length()); \ | ||||
| tc_content += "C++-Version: "s + version + "\n"; \ | |||||
| root_map.emplace("cxx_version", version); \ | |||||
| } \ | } \ | ||||
| static_assert(true) | static_assert(true) | ||||
| CXX_VER_TAG("c++98:", "C++98"); | |||||
| CXX_VER_TAG("c++03:", "C++03"); | |||||
| CXX_VER_TAG("c++11:", "C++11"); | |||||
| CXX_VER_TAG("c++14:", "C++14"); | |||||
| CXX_VER_TAG("c++17:", "C++17"); | |||||
| CXX_VER_TAG("c++20:", "C++20"); | |||||
| CXX_VER_TAG("c++98:", "c++98"); | |||||
| CXX_VER_TAG("c++03:", "c++03"); | |||||
| CXX_VER_TAG("c++11:", "c++11"); | |||||
| CXX_VER_TAG("c++14:", "c++14"); | |||||
| CXX_VER_TAG("c++17:", "c++17"); | |||||
| CXX_VER_TAG("c++20:", "c++20"); | |||||
| struct compiler_info { | struct compiler_info { | ||||
| string c; | string c; | ||||
| const auto [c_compiler_base, cxx_compiler_base, compiler_id] = [&]() -> compiler_info { | const auto [c_compiler_base, cxx_compiler_base, compiler_id] = [&]() -> compiler_info { | ||||
| if (is_gcc) { | if (is_gcc) { | ||||
| return {"gcc", "g++", "GNU"}; | |||||
| return {"gcc", "g++", "gnu"}; | |||||
| } else if (is_clang) { | } else if (is_clang) { | ||||
| return {"clang", "clang++", "Clang"}; | |||||
| return {"clang", "clang++", "clang"}; | |||||
| } | } | ||||
| assert(false && "Unreachable"); | assert(false && "Unreachable"); | ||||
| std::terminate(); | std::terminate(); | ||||
| auto cxx_compiler_name = cxx_compiler_base + compiler_suffix; | auto cxx_compiler_name = cxx_compiler_base + compiler_suffix; | ||||
| return compiler_info{c_compiler_name, cxx_compiler_name, compiler_id}; | return compiler_info{c_compiler_name, cxx_compiler_name, compiler_id}; | ||||
| } else if (tc_id == "msvc") { | } else if (tc_id == "msvc") { | ||||
| return compiler_info{"cl.exe", "cl.exe", "MSVC"}; | |||||
| return compiler_info{"cl.exe", "cl.exe", "msvc"}; | |||||
| } else { | } else { | ||||
| return std::nullopt; | return std::nullopt; | ||||
| } | } | ||||
| return std::nullopt; | return std::nullopt; | ||||
| } | } | ||||
| tc_content += "C-Compiler: "s + opt_triple->c + "\n"; | |||||
| tc_content += "C++-Compiler: "s + opt_triple->cxx + "\n"; | |||||
| tc_content += "Compiler-ID: " + opt_triple->id + "\n"; | |||||
| return parse_toolchain_dds(tc_content); | |||||
| root_map.emplace("c_compiler", opt_triple->c); | |||||
| root_map.emplace("cxx_compiler", opt_triple->cxx); | |||||
| root_map.emplace("compiler_id", opt_triple->id); | |||||
| return parse_toolchain_json_data(tc_data); | |||||
| } | } | ||||
| std::optional<dds::toolchain> dds::toolchain::get_default() { | std::optional<dds::toolchain> dds::toolchain::get_default() { | ||||
| auto candidates = { | auto candidates = { | ||||
| fs::current_path() / "toolchain.dds", | |||||
| dds_config_dir() / "toolchain.dds", | |||||
| user_home_dir() / "toolchain.dds", | |||||
| fs::current_path() / "toolchain.json5", | |||||
| fs::current_path() / "toolchain.jsonc", | |||||
| fs::current_path() / "toolchain.json", | |||||
| dds_config_dir() / "toolchain.json5", | |||||
| dds_config_dir() / "toolchain.jsonc", | |||||
| dds_config_dir() / "toolchain.json", | |||||
| user_home_dir() / "toolchain.json5", | |||||
| user_home_dir() / "toolchain.jsonc", | |||||
| user_home_dir() / "toolchain.json", | |||||
| }; | }; | ||||
| for (auto&& cand : candidates) { | for (auto&& cand : candidates) { | ||||
| if (fs::exists(cand)) { | if (fs::exists(cand)) { | ||||
| return parse_toolchain_dds(slurp_file(cand)); | |||||
| return parse_toolchain_json5(slurp_file(cand)); | |||||
| } | } | ||||
| } | } | ||||
| return std::nullopt; | return std::nullopt; | ||||
| } | |||||
| } |
| def basic_pkg_dds(dds: DDS): | def basic_pkg_dds(dds: DDS): | ||||
| return set_contents( | return set_contents( | ||||
| dds.source_root / 'package.dds', b''' | |||||
| Name: test-pkg | |||||
| Version: 0.2.2 | |||||
| dds.source_root / 'package.json5', b''' | |||||
| { | |||||
| name: 'test-pkg', | |||||
| version: '0.2.2', | |||||
| namespace: 'test', | |||||
| } | |||||
| ''') | ''') | ||||
| from tests import dds, DDS | from tests import dds, DDS | ||||
| from tests.fileutil import ensure_dir | from tests.fileutil import ensure_dir | ||||
| import pytest | |||||
| def test_get(dds: DDS): | def test_get(dds: DDS): | ||||
| dds.scope.enter_context(ensure_dir(dds.build_dir)) | dds.scope.enter_context(ensure_dir(dds.build_dir)) | ||||
| '0.2.2': { | '0.2.2': { | ||||
| 'depends': {}, | 'depends': {}, | ||||
| 'git': { | 'git': { | ||||
| 'url': 'https://github.com/vector-of-bool/neo-sqlite3.git', | |||||
| 'ref': '0.2.2', | |||||
| 'url': | |||||
| 'https://github.com/vector-of-bool/neo-sqlite3.git', | |||||
| 'ref': | |||||
| '0.2.2', | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, |
| "0.1.0": { | "0.1.0": { | ||||
| "git": { | "git": { | ||||
| "url": "https://github.com/vector-of-bool/neo-buffer.git", | "url": "https://github.com/vector-of-bool/neo-buffer.git", | ||||
| "ref": "develop" | |||||
| "ref": "0.1.0" | |||||
| }, | }, | ||||
| "depends": {} | "depends": {} | ||||
| } | } |
| Name: deps-test | |||||
| Version: 0.0.0 | |||||
| Depends: neo-buffer 0.1.0 | |||||
| Depends: range-v3 0.9.1 |
| { | |||||
| name: 'deps-test', | |||||
| "namespace": "test", | |||||
| version: '0.0.0', | |||||
| depends: { | |||||
| 'neo-buffer': '0.1.0', | |||||
| 'range-v3': '0.9.1', | |||||
| } | |||||
| } |
| Name: deps-test | |||||
| Version: 0.0.0 |
| { | |||||
| name: 'deps-test', | |||||
| version: '0.0.0', | |||||
| "namespace": "test", | |||||
| } |
| Name: dummy | |||||
| Uses: nlohmann/json |
| { | |||||
| name: "dummy", | |||||
| uses: [ | |||||
| 'nlohmann/json', | |||||
| ] | |||||
| } |
| Name: json-test | |||||
| Version: 0.0.0 | |||||
| Depends: nlohmann-json 3.7.1 |
| { | |||||
| "name": "json-test", | |||||
| "version": "0.0.0", | |||||
| "namespace": "test", | |||||
| "depends": { | |||||
| "nlohmann-json": "3.7.1" | |||||
| } | |||||
| } |
| { | |||||
| "compiler_id": 'gnu', | |||||
| "cxx_version": 'c++17', | |||||
| "cxx_compiler": 'g++-9', | |||||
| "flags": '-DSPDLOG_COMPILED_LIB', | |||||
| } |
| { | |||||
| "compiler_id": 'msvc', | |||||
| "flags": '-DSPDLOG_COMPILED_LIB', | |||||
| } |
| Name: spdlog-user | |||||
| Uses: spdlog/spdlog |
| { | |||||
| name: 'spdlog-user', | |||||
| uses: [ | |||||
| 'spdlog/spdlog', | |||||
| ] | |||||
| } |
| Name: test | |||||
| Version: 0.0.0 | |||||
| Depends: spdlog 1.4.2 |
| { | |||||
| name: 'test', | |||||
| version: '0.0.0', | |||||
| "namespace": "test", | |||||
| depends: { | |||||
| 'spdlog': '1.4.2', | |||||
| }, | |||||
| } |
| def test_get_build_use_spdlog(dds: DDS): | def test_get_build_use_spdlog(dds: DDS): | ||||
| dds.catalog_import(dds.source_root / 'catalog.json') | dds.catalog_import(dds.source_root / 'catalog.json') | ||||
| tc_fname = 'gcc.tc.dds' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.dds' | |||||
| tc_fname = 'gcc.tc.jsonc' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.jsonc' | |||||
| tc = str(dds.test_dir / tc_fname) | tc = str(dds.test_dir / tc_fname) | ||||
| dds.build(toolchain=tc, apps=True) | dds.build(toolchain=tc, apps=True) | ||||
| proc.check_run((dds.build_dir / 'use-spdlog').with_suffix(dds.exe_suffix)) | proc.check_run((dds.build_dir / 'use-spdlog').with_suffix(dds.exe_suffix)) |
| Name: foo |
| { | |||||
| "name": "foo" | |||||
| } |
| Name: foo | |||||
| Version: 1.2.3 |
| { | |||||
| name: 'foo', | |||||
| version: '1.2.3', | |||||
| "namespace": "test", | |||||
| } |
| Name: Test | |||||
| Version: 0.0.0 | |||||
| Test-Driver: Catch |
| { | |||||
| "name": "test", | |||||
| "version": "0.0.0", | |||||
| "namespace": "test", | |||||
| "test_driver": "Catch", | |||||
| } |
| Name: Test | |||||
| Version: 0.0.0 | |||||
| Test-Driver: Catch-Main |
| { | |||||
| "name": "test", | |||||
| "version": "0.0.0", | |||||
| "namespace": "test", | |||||
| "test_driver": "Catch-Main", | |||||
| } |
| class CIOptions(NamedTuple): | class CIOptions(NamedTuple): | ||||
| toolchain: str | toolchain: str | ||||
| toolchain_json5: str | |||||
| def _do_bootstrap_build(opts: CIOptions) -> None: | def _do_bootstrap_build(opts: CIOptions) -> None: | ||||
| '-T', | '-T', | ||||
| help='The toolchain to use for the CI process', | help='The toolchain to use for the CI process', | ||||
| required=True) | required=True) | ||||
| parser.add_argument( | |||||
| '--toolchain-json5', | |||||
| '-T2', | |||||
| help='The toolchain JSON to use with the bootstrapped executable', | |||||
| required=True, | |||||
| ) | |||||
| args = parser.parse_args(argv) | args = parser.parse_args(argv) | ||||
| opts = CIOptions(toolchain=args.toolchain) | |||||
| opts = CIOptions( | |||||
| toolchain=args.toolchain, toolchain_json5=args.toolchain_json5) | |||||
| if args.bootstrap_with == 'build': | if args.bootstrap_with == 'build': | ||||
| _do_bootstrap_build(opts) | _do_bootstrap_build(opts) | ||||
| ]) | ]) | ||||
| self_build( | self_build( | ||||
| paths.CUR_BUILT_DDS, | paths.CUR_BUILT_DDS, | ||||
| toolchain=opts.toolchain, | |||||
| toolchain=opts.toolchain_json5, | |||||
| dds_flags=[f'--repo-dir={ci_repo_dir}', f'--catalog={cat_path}']) | dds_flags=[f'--repo-dir={ci_repo_dir}', f'--catalog={cat_path}']) | ||||
| print('Bootstrap test PASSED!') | print('Bootstrap test PASSED!') | ||||
| # Range-v3 0.10.0 contains an accidental conversion warning | # Range-v3 0.10.0 contains an accidental conversion warning | ||||
| Flags: -D SPDLOG_COMPILED_LIB -Werror=return-type -Wno-conversion | Flags: -D SPDLOG_COMPILED_LIB -Werror=return-type -Wno-conversion | ||||
| C++-Flags: -fconcepts | C++-Flags: -fconcepts | ||||
| Link-Flags: -static-libgcc -static-libstdc++ | |||||
| # Debug: True | |||||
| Optimize: True | |||||
| # Link-Flags: -static-libgcc -static-libstdc++ | |||||
| Debug: True | |||||
| #Optimize: True | |||||
| Compiler-Launcher: ccache | Compiler-Launcher: ccache |
| { | |||||
| "$schema": "../res/toolchain-schema.json", | |||||
| "compiler_id": "gnu", | |||||
| "c_compiler": "gcc-9", | |||||
| "cxx_compiler": "g++-9", | |||||
| "cxx_version": "c++17", | |||||
| "flags": [ | |||||
| "-DSPDLOG_COMPILED_LIB", // Required to use a compiled spdlog | |||||
| "-Werror=return-type" | |||||
| ], | |||||
| "cxx_flags": [ | |||||
| "-fconcepts" | |||||
| ], | |||||
| // "debug": true, | |||||
| "optimize": true, | |||||
| "compiler_launcher": "ccache" | |||||
| } |
| ret: dict = { | ret: dict = { | ||||
| 'description': self.description, | 'description': self.description, | ||||
| } | } | ||||
| ret['depends'] = {} | |||||
| ret['depends'] = self.depends | |||||
| if isinstance(self.remote, Git): | if isinstance(self.remote, Git): | ||||
| ret['git'] = self.remote.to_dict() | ret['git'] = self.remote.to_dict() | ||||
| return ret | return ret | ||||
| 'A C++ implementation of the Pubgrub version solving algorithm', | 'A C++ implementation of the Pubgrub version solving algorithm', | ||||
| git_url='https://github.com/vector-of-bool/pubgrub.git', | git_url='https://github.com/vector-of-bool/pubgrub.git', | ||||
| ), | ), | ||||
| many_versions( | |||||
| 'vob-json5', | |||||
| ('0.1.5', ), | |||||
| description='A C++ implementation of a JSON5 parser', | |||||
| git_url='https://github.com/vector-of-bool/json5.git', | |||||
| ), | |||||
| Package('vob-semester', [ | |||||
| Version( | |||||
| '0.1.0', | |||||
| description='A C++ library to process recursive dynamic data', | |||||
| remote=Git('https://github.com/vector-of-bool/semester.git', | |||||
| '0.1.0'), | |||||
| depends={ | |||||
| 'neo-fun': '^0.1.0', | |||||
| 'neo-concepts': '^0.2.1', | |||||
| }), | |||||
| ]), | |||||
| many_versions( | many_versions( | ||||
| 'spdlog', | 'spdlog', | ||||
| ( | ( |
| { | |||||
| "$schema": "../res/toolchain-schema.json", | |||||
| "compiler_id": "msvc", | |||||
| "flags": [ | |||||
| "/experimental:preprocessor", // Required for range-v3 | |||||
| "/DSPDLOG_COMPILED_LIB", // Required to use spdlog as a compiled lib | |||||
| "/std:c++latest", | |||||
| ], | |||||
| "link_flags": [ | |||||
| "rpcrt4.lib", | |||||
| ], | |||||
| // "debug": true, | |||||
| "optimize": true | |||||
| } |