| @@ -0,0 +1,18 @@ | |||
| Error: Creating a static library archive failed | |||
| ############################################### | |||
| ``dds`` primarily works with *static libraries*, and the file format of a | |||
| static library is known as an *archive*. This error indicates that ``dds`` | |||
| failed in its attempt to collect one or more compiled *object files* together | |||
| into an archive. | |||
| It is unlikely that regular user action can cause this error, and is more | |||
| likely an error related to the environment and/or filesystem. | |||
| If you are using a custom toolchain and have specified the archiving command | |||
| explicitly, it may also be possible that the argument list you have given to | |||
| the archiving tool is invalid. | |||
| When archiving fails, ``dds`` will print the output and command that tried to | |||
| generate the archive. It should provide more details into the nature of the | |||
| failure. | |||
| @@ -0,0 +1,21 @@ | |||
| Error: The package catalog database is for a later ``dds`` version | |||
| ################################################################## | |||
| If you receive this error, it indicates that the schema version of a catalog | |||
| database is newer than the schema expected by the ``dds`` process that is | |||
| trying to actually make use of the catalog. ``dds`` catalog databases are not | |||
| forwards-compatible and cannot be downgraded. | |||
| If you generated/modified the catalog using a ``dds`` executable that is newer | |||
| than the one you attempted to open it with, it is possible that the newer | |||
| ``dds`` executable performed a schema upgrade that is unsupported by the older | |||
| version of ``dds``. | |||
| If you have not specified a path to a catalog, ``dds`` will use the user-local | |||
| default catalog database. If you receive this error while using the user-local | |||
| database, the solution is to either upgrade ``dds`` to match the newer catalog | |||
| or to delete the user-local catalog database file. | |||
| .. note:: | |||
| Deleting the catalog database will lose any customizations that were | |||
| contained in that catalog, and they will need to be reconstructed. | |||
| @@ -0,0 +1,7 @@ | |||
| Error: Compilation failed | |||
| ######################### | |||
| Receiving this error indicates that the source code compiler exited with an | |||
| error. This usually indicates that there is a syntactic or semantic error in | |||
| the code that was given to the compiler. Refer to the compiler process's output | |||
| for more information. | |||
| @@ -0,0 +1,9 @@ | |||
| Error: The build database is corrupted | |||
| ###################################### | |||
| ``dds`` stores various data about the local project's build in a database file | |||
| kept in the build output directory (default is ``_build``). The file is named | |||
| ``.dds.db``, and can be safely deleted with no ill effects on the project. | |||
| This error is not caused by regular user action, and is probably a bug in | |||
| ``dds``. If you see this error message frequently, please file a bug report. | |||
| @@ -0,0 +1,17 @@ | |||
| Error: The catalog database appears corrupted/invalid | |||
| ##################################################### | |||
| This issue will occur if the schema contained in the catalog database does not | |||
| match the schema that ``dds`` expects. This should never occur during normal | |||
| use of ``dds``. | |||
| .. note:: | |||
| If you suspect that ``dds`` has accidentally corrupted its own catalog | |||
| database, please file a bug report. | |||
| ``dds`` cannot reliably repair a corrupted database, and the only solution will | |||
| be to either manually fix it or to delete the database file and start fresh. | |||
| .. note:: | |||
| Deleting the catalog database will lose any customizations that were | |||
| contained in that catalog, and they will need to be reconstructed. | |||
| @@ -0,0 +1,22 @@ | |||
| Error: A Git ``clone`` operation failed | |||
| ####################################### | |||
| This error indicates that ``dds`` failed to clone a Git repository. | |||
| ``dds`` will invoke ``git clone`` as a subprocess to retrieve a copy of a | |||
| remote repository. There are several reasons this might fail, but it is best | |||
| to refer to the output of the ``git`` subprocess to diagnose the issue. | |||
| A non-exhaustive list of things to check: | |||
| #. Is the ``git`` executable available and on the ``PATH`` environment variable? | |||
| #. Is the URL to the repository correct? | |||
| #. Is the remote server accessible? | |||
| #. Do you have read-access to the repository in question? | |||
| #. Does the named tag/branch exist in the remote? | |||
| #. If cloning a specific Git revision, does the remote server support cloning | |||
| a repository by a specific commit? (Very often Git servers are not | |||
| configured to support this capability). | |||
| Be aware if you are using SSH-style ``git-clone`` that it will require the | |||
| correct SSH keys to be available on the system where ``dds`` is running. | |||
| @@ -0,0 +1,15 @@ | |||
| Error: Git requires both a URL and a ref to clone | |||
| ################################################# | |||
| This error occurs when attempting to add an entry to the package catalog that | |||
| uses the ``Git`` acquisition method. | |||
| When ``dds`` obtains a package from the catalog using the ``Git`` method, it | |||
| needs a URL to clone from, and a Git ref to clone. ``dds`` uses a technique | |||
| known as "shallow cloning," which requires a known Git reference to clone from. | |||
| The reference may be a tag, branch, or an individual commit (Using a Git commit | |||
| as the ``ref`` requires support from the remote Git server, and it is often | |||
| unavailable in most setups). Using a Git tag is strongly recommended. | |||
| .. seealso:: | |||
| Refer to the documentation on :doc:`/guide/catalog`. | |||
| @@ -0,0 +1,18 @@ | |||
| Runtime Error References | |||
| ######################## | |||
| This page enumerates the possible errors that you may encounter while using | |||
| DDS, either from user error or environmental issues. | |||
| .. note:: | |||
| DDS will automatically emit links to the relevant error document when it | |||
| occurs. | |||
| Errors | |||
| ****** | |||
| .. toctree:: | |||
| :maxdepth: 2 | |||
| :glob: | |||
| * | |||
| @@ -0,0 +1,17 @@ | |||
| Error: Invalid built-in toolchain | |||
| ################################# | |||
| ``dds`` requires a toolchain in order to build any packages or projects. It | |||
| ships with several built-in toolchains that do not require the user to write | |||
| any configuration files. | |||
| If you start your toolchain name (The `-t` or `--toolchain` argument) | |||
| with a leading colon, dds will interpret it as a reference to a built-in | |||
| toolchain. (Toolchain file paths cannot begin with a leading colon). | |||
| These toolchain names are encoded into the dds executable and cannot be | |||
| modified. | |||
| .. seealso:: | |||
| Refer to the documentation on :doc:`toolchains </guide/toolchains>` and | |||
| :ref:`toolchains.builtin`. | |||
| @@ -0,0 +1,9 @@ | |||
| Error: Invalid catalog JSON | |||
| ########################### | |||
| This error occurs when the JSON data given to import into the package catalog | |||
| is in some way invalid. Refer to the catalog documentation for a description of | |||
| the proper JSON format. | |||
| .. seealso:: | |||
| :ref:`catalog.adding` | |||
| @@ -0,0 +1,22 @@ | |||
| Error: Invalid version string | |||
| ############################# | |||
| ``dds`` stores version numbers in a type-safe manner, and all version numbers | |||
| are requried to match `Semantic Versioning <https://semver.org>`_. | |||
| If you see this error, it means that a ``dds`` found a version number that does | |||
| not correctly conform to Semantic Versioning's requirements for version numbers | |||
| refer to the ``dds`` output for a description of *where* the bad version string | |||
| was found, and refer to the Semantic Versioning website for information about | |||
| how to properly format a version number. | |||
| .. _range: | |||
| Version Ranges | |||
| ************** | |||
| In addition to requirements on individual versions, providing a version range | |||
| requires a slightly different syntax. | |||
| .. TODO: Write docs on version range strings. | |||
| @@ -0,0 +1,480 @@ | |||
| Error: Linking a runtime binary failed | |||
| ###################################### | |||
| .. contents:: | |||
| .. highlight:: c++ | |||
| This error indicates that the final phases of the build pipeline, the link | |||
| phases, failed. | |||
| The end result of the software development process is to produce applications | |||
| and programs that can be executed by users. In the traditional compilation and | |||
| linking model, which we still use to this day, multiple *translation units* | |||
| (which can be thought of as "source files") are combined together in a process | |||
| known as *linking*. The result of linking is an actual executable. | |||
| .. note:: | |||
| Linking is also used to generate executables that are used as tests. | |||
| Refer: :ref:`pkgs.apps-tests`. | |||
| What is "Linking"? | |||
| ****************** | |||
| The *phases of translation* define the steps taken by a compiler (and linker) | |||
| to map from the input human-readable source code to the code that can be | |||
| executed by a machine. The first few phases are collected bundled together in | |||
| a phase known as "compilation," while the later phases are known as "linking." | |||
| The output of the compilation phases is often generated by a *compiler* and | |||
| then written to the filesystem. The resulting files, known as *object files* | |||
| are then fed into another tool known as a *linker*. | |||
| .. note:: | |||
| "Object files" are just one possible intermediate product. Compilers and | |||
| linkers may deal with different intermediate products depending on compiler | |||
| and linker options. | |||
| Symbol Reference Resolution | |||
| *************************** | |||
| When code within a translation unit uses a function, variable, or member of a | |||
| class that is declared **but not defined** in that translation unit, that | |||
| usage is stored as an "unresolved" reference to an external symbol within the | |||
| resulting object file. | |||
| Each translation unit may also contain the *definitions* of any number of | |||
| symbols. It is possible that other translation units may contain unresolved | |||
| references to the symbol that the translation unit defines. | |||
| It is the job of the linker to fill in these unresolved references when it | |||
| combines translation units together. | |||
| Failure Modes | |||
| ************* | |||
| There are two very common types of linker errors that may be seen: | |||
| Multiple Definitions Found | |||
| ========================== | |||
| When translation units are combined together, the symbols within them are | |||
| combined together into a final binary. If two or more translation units contain | |||
| the *definition* of a single symbol, then the linker must make a decision: | |||
| #. If the symbol is marked properly, then the linker can discard all except one | |||
| of the definitions and choose one to keep in the final binary. For example: | |||
| This is allowed if the associated symbol has been declared with the | |||
| ``inline`` keyword, or is a symbol in any context that is "implicitly | |||
| ``inline``," which includes member functions and static variables which are | |||
| defined within their class's body, and any function template. | |||
| #. **Fail** | |||
| If the linker is not allowed to discard all-but-one of the multiple | |||
| definitions, this is a hard-error. This can happen if multiple translation | |||
| units defined the same variable or function at the same namespace. | |||
| Issue: A non-``inline`` function is defined in a header file | |||
| ------------------------------------------------------------ | |||
| A likely case is that of defining a function in a header file without | |||
| marking it as ``inline``: | |||
| .. code-block:: | |||
| :caption: ``hello.hpp`` | |||
| #ifndef MY_HEADER_INC | |||
| #define MY_HEADER_INC | |||
| #include <cstdio> | |||
| void say_hello() { | |||
| std::puts("Hello!\n"); | |||
| } | |||
| #endif | |||
| and then that header is ``#include``-ed in multiple source files: | |||
| .. code-block:: | |||
| :caption: ``a.cpp`` | |||
| #include "hello.hpp" | |||
| // ... stuff ... | |||
| .. code-block:: | |||
| :caption: ``b.cpp`` | |||
| #include "hello.hpp" | |||
| // .. different stuff ... | |||
| .. note:: | |||
| ``template`` functions and member functions *defined within the class body* | |||
| are implicitly ``inline``, and using the ``inline`` keyword is then | |||
| redundant. | |||
| In the above configuration, the linker will generate an error about multiple | |||
| definitions of the ``say_hello`` function. Possibly confusingly, it will point | |||
| to ``a.cpp`` and ``b.cpp`` as the "definers" of ``say_hello``, even though it | |||
| is actually defined in the header. The issue is that no tools are currently | |||
| able to understand this structure in a way that they can clearly issue | |||
| appropriate instruction on how to fix this. There are two ways to fix this: | |||
| #. Add the ``inline`` keyword to the definition of ``say_hello``:: | |||
| #ifndef MY_HEADER_INC | |||
| #define MY_HEADER_INC | |||
| #include <cstdio> | |||
| inline void say_hello() { | |||
| std::puts("Hello!\n"); | |||
| } | |||
| #endif | |||
| This activates the rule that permits the linker to disregard the multiple | |||
| definitions and choose one to keep arbitrarily. | |||
| .. note:: | |||
| Only use ``inline`` in headers! | |||
| #. Change the definition of ``say_hello`` to be a *declaration*, and move the | |||
| *definition* to a separate source file: | |||
| .. code-block:: | |||
| :caption: ``hello.hpp`` | |||
| #ifndef MY_HEADER_INC | |||
| #define MY_HEADER_INC | |||
| #include <cstdio> | |||
| void say_hello() { | |||
| std::puts("Hello!\n"); | |||
| } | |||
| #endif | |||
| .. code-block:: | |||
| :caption: ``hello.cpp`` | |||
| #include "hello.hpp" | |||
| void say_hello() { | |||
| std::puts("Hello!\n"); | |||
| } | |||
| This will place the sole location of the ``say_hello`` definition within | |||
| ``hello.cpp``. | |||
| Issue: There are two colliding and distinct definitions | |||
| ------------------------------------------------------- | |||
| Suppose you have two different source files: | |||
| .. code-block:: | |||
| :caption: ``a.cpp`` | |||
| #include "a.hpp" | |||
| void error(string message) { | |||
| cerr << "An error occured: " << msg << '\n'; | |||
| } | |||
| void a_func() { | |||
| bool had_error = first_a(); | |||
| if (err) { | |||
| error(*err); | |||
| } | |||
| err = second_a(); | |||
| if (err) { | |||
| error(*err); | |||
| } | |||
| } | |||
| .. code-block:: | |||
| :caption: ``b.cpp`` | |||
| void error(string message) { | |||
| throw runtime_error(msg); | |||
| } | |||
| void b_func() { | |||
| bool had_error = first_b(); | |||
| if (had_error) { | |||
| error("The first step failed!"); | |||
| } | |||
| had_error = second_b(); | |||
| if (had_error) { | |||
| error("The second step failed!"); | |||
| } | |||
| } | |||
| The two functions, ``a_func`` and ``b_func``, despite having a similar | |||
| structure, are *completely different* because of the behavior of ``error``: | |||
| - In ``a.cpp``: | |||
| - ``error()`` will simply log a message but let execution continue. | |||
| - If ``first_a()`` fails, execution will continue into ``second_a()``. | |||
| - In ``b.cpp``: | |||
| - ``error()`` will throw an exception. | |||
| - If ``first_b()`` fails, execution will never reach ``second_b()`` | |||
| Nevertheless, the linker will produce an error that there are multiple visible | |||
| definitions of ``error()``, even though the translation units individually have | |||
| no ambiguity. | |||
| The issue is that both of the definitions have *external linkage* and must be | |||
| visible to all other translation units. | |||
| It may be tempting to fix this issue in the same way that we did in the prior | |||
| example: to declare them ``inline``, and it will *seem* to have worked, but | |||
| **this will not work correctly!!** | |||
| Remember what the linker does in the presence of ``inline`` on multiple | |||
| definitions between different translation units: It will *pick one* and | |||
| *discard the others*. This means that either ``error`` function may replace the | |||
| other across translation units, and the resulting code will have wildly | |||
| different behavior. | |||
| The *correct* solution is to give the ``error`` function *internal linkage*, | |||
| which means that its definition is not visible across translation units. This | |||
| will allow both definitions of ``error`` to live together in the linked binary | |||
| without ambiguity. The classic way of doing this is through the usage of the | |||
| global-scope ``static`` keyword which is present in C:: | |||
| static void error(string s) { | |||
| // ... | |||
| } | |||
| C++ presents another way it can be done: via an *unnamed namespace*:: | |||
| namespace { | |||
| void error(string s) { | |||
| // ... | |||
| } | |||
| } // close namespace | |||
| The benefit of the unnamed namespace is it can be used to mark an entire | |||
| section of declarations to be *internal*, and it can also be used to mark a | |||
| class definition to have *internal linkage* (There is no way to declare a | |||
| "``static class``"). | |||
| Unresolved External Symbol / Undefined Reference | |||
| ================================================ | |||
| Another common error seen while linking is that of the *unresolved external | |||
| symbol* (Visual C++) or *undefined reference* (GCC and Clang). Both have the | |||
| same underlying cause, and both have the same solutions. | |||
| When a translation unit makes use of a symbol which has been declared *but not | |||
| defined within that translation unit*, it is up to the linker to resolve that | |||
| reference to another translation unit that contains the definition. | |||
| If the linker is unable to find the definition of the referenced entity, it | |||
| will emit this error. | |||
| Issue: An external library is not being included in the link | |||
| ------------------------------------------------------------ | |||
| If the unresolved reference is to an entity belonging to an external library, | |||
| you may be missing the linker inputs to actually use that library. | |||
| If your project makes use of a declared entity from a third party (even if that | |||
| usage is transitive through a dependency), it is required that the definitions | |||
| from that third party library are included in the link step. This usually comes | |||
| in the form of a static library, shared library/DLL, or even plain object | |||
| files. | |||
| If the external library containing the definition in question is managed by | |||
| ``dds``, this issue should never occur. If the library exists outside of | |||
| ``dds`` (e.g. a system library), then that library will need to be manually | |||
| added as a linker input using a toolchain file using the ``Link-Flags`` option. | |||
| See: :ref:`toolchains.opt-ref`. | |||
| If the name of the unresolved symbol appears unfamiliar or you do not believe | |||
| that you are making use of it, it is possible that one of your dependencies is | |||
| making use of a system library symbol that needs to be part of the link. The | |||
| link error will refer to the object/source file that is actually making the | |||
| unresolvable reference. Seeing this filepath will be a reliable way to discover | |||
| who would be making the reference, and therefore a good way to track down the | |||
| dependency that needs an additional linker input. Refer to the documentation | |||
| of the dependency in question to see if it requires additional linker inputs | |||
| in order to be used. | |||
| If the library that should contain the unresolved reference is a dependency | |||
| managed by ``dds``, it is possible that the library author has mistakenly | |||
| declared a symbol without providing a definition. If the definition *is* | |||
| present in the ``dds``-provided dependency library, then the failure to resolve | |||
| the reference would be a ``dds`` bug. | |||
| Issue: The definition is simply missing | |||
| --------------------------------------- | |||
| C and C++ allow for an entity to be *declared* and *defined* separately. If you | |||
| *declare* and entity but do not *define* that entity, your code will work as | |||
| long as no one attempts to refer to that entity. | |||
| Ensure that the entity that is "missing" exists. | |||
| Issue: Missing ``virtual`` method implementations | |||
| ------------------------------------------------- | |||
| If the error refers to a missing ``vtable for class``, or if the error refers | |||
| to a missing definition of a ``virtual`` function, it means that one or more | |||
| ``virtual`` functions are not defined. | |||
| Note that ``virtual`` functions are slightly different in this regard: It is | |||
| not required that someone actually make a call to the ``virtual`` function for | |||
| the definition to be required. The metadata that the compiler generates for | |||
| the class containing the ``virtual`` functions will implicitly form a reference | |||
| to every ``virtual`` function, so they must all be defined if someone attempts | |||
| to instantiate the class, as instantiating the class will form a reference to | |||
| that metadata. | |||
| Issue: Mismatched declarations and definitions | |||
| ---------------------------------------------- | |||
| Suppose you have a header file and a corresponding source file: | |||
| .. code-block:: | |||
| :caption: ``a.hpp`` | |||
| namespace foo { | |||
| size_t string_length(const string& str); | |||
| } | |||
| .. code-block:: | |||
| :caption: ``a.cpp`` | |||
| #include "a.hpp" | |||
| using namespace foo; | |||
| size_t string_length(const string& str) { | |||
| // ... implementation goes here ... | |||
| } | |||
| The above code will link correctly, as the definition of ``foo::string_length``, | |||
| is available from ``a.cpp``, while the declaration exists in ``a.hpp``. | |||
| However, if we modify *only the declaration* to use ``string_view`` instead of | |||
| ``const string&``, something different occurs:: | |||
| namespace foo { | |||
| size_t string_length(string_view str); | |||
| } | |||
| It may be tempting to say that "our declaration and definition do not match," | |||
| but that is semantically incorrect: We have declared a function | |||
| ``size_t foo::string_length(string_view)``, but we have defined *and declared* | |||
| a **completely different function** ``size_t string_length(const string&)``! | |||
| The compiler will not warn about this: There is nothing semantically incorrect | |||
| about this code. | |||
| The linker, however, will not find any definition of ``foo::string_length``. | |||
| The function ``::string_length(const string&)`` isn't even in the ``foo`` | |||
| ``namespace``: It was declared and defined at the global scope within | |||
| ``a.cpp``. | |||
| If you are seeing an error about an unresolved reference to a function that is | |||
| declared and defined separately, and you are *sure* is being compiled, check | |||
| that the signature (and name) of the definition and declaration match | |||
| *exactly*. | |||
| .. tip:: | |||
| In essence, the error originates from relying on the | |||
| ``using namespace foo`` directive to cause the definition of | |||
| ``string_length`` to incidentally hit the name lookup of the prior | |||
| declaration. | |||
| In C++, using a *qualified name* at the definition site can prevent this | |||
| error from slipping through:: | |||
| #include "a.hpp" | |||
| using namespace foo; | |||
| size_t foo::string_length(const string& str) { | |||
| // ... implementation goes here ... | |||
| } | |||
| By using the qualified name ``foo::string_length`` at the definition site, | |||
| the compiler will validate that the function being defined has a prior | |||
| declaration that matches *exactly* to the signature of the definition. | |||
| Note that this *is not* the same as defining the function within a | |||
| ``namespace`` block:: | |||
| #include "a.hpp" | |||
| // NOT HELPFUL! | |||
| namespace foo { | |||
| size_t string_length(const string& str) { | |||
| // ... implementation goes here ... | |||
| } | |||
| } | |||
| This will suffer the same potential mistake as defining it with an | |||
| unqualified name. | |||
| Note that within the scope of a function that has been declared within the | |||
| namespace, that namespace is currently within scope even if the definition | |||
| itself is not wrapped in a ``namespace`` block. It may be a good option to | |||
| simply remove the ``using namespace`` directive altogether. | |||
| .. note:: | |||
| This trick cannot be applied to names that are declared at the global | |||
| scope, since you cannot use the global-namespace qualifier at a | |||
| function definition (it is not valid syntax):: | |||
| // Declaration at global scope | |||
| void some_function(); | |||
| // Definition? No: Invalid syntax! | |||
| void ::some_function() { | |||
| // ... stuff ... | |||
| } | |||
| Issue: The source file containing definition is not being included in the link | |||
| ------------------------------------------------------------------------------ | |||
| If the translation unit that contains the definition of an entity is not being | |||
| passed to the linker, the linker will not be able to find it! | |||
| If you are using ``dds`` correctly, and the compiled source file containing the | |||
| definition is placed as a (direct or indirect) descendent of the ``src/`` | |||
| directory, then ``dds`` will always include that source file as part of the | |||
| link for the enclosing library. | |||
| Build systems that require you to enumerate your source files explicitly will | |||
| not automatically see a source file unless it has been added to the source | |||
| list. Even build systems that allow directory-globbing (like CMake) will need | |||
| to have the globbing pattern match the path to the source file. | |||
| @@ -0,0 +1,10 @@ | |||
| Error: Package is missing remote acquisition information | |||
| ######################################################## | |||
| When a package is being added/imported into the package catalog, ``dds`` | |||
| requires some information regarding how to actually *acquire* that package | |||
| when it is requested. | |||
| If such information is not provided, ``dds`` will issue an error. | |||
| .. seealso:: :ref:`catalog.adding`. | |||
| @@ -0,0 +1,17 @@ | |||
| Error: No such package in the catalog | |||
| ##################################### | |||
| This error will occur when one attempts to obtain a package from the package | |||
| catalog by its specific ``name@version`` pair, but no such entry exists | |||
| in the catalog. | |||
| It is possible that the intended package *does exist* but that the spelling of | |||
| the package name or version number is incorrect. Firstly, check your spelling | |||
| and that the version number you have requested is correct. | |||
| In another case, it is possible that the package *exists somewhere*, but has | |||
| not been loaded into the local catalog. As of this writing, ``dds`` does not | |||
| automatically maintain the catalog against a central package repository, so | |||
| package entries must be loaded and imported manually. If you believe this to be | |||
| the case, refer to the section on the :doc:`/guide/catalog`, especially the | |||
| section :ref:`catalog.adding`. | |||
| @@ -0,0 +1,18 @@ | |||
| Error: The generated source distribution's identity is not correct | |||
| ################################################################## | |||
| When ``dds`` attempts to automatically generate a source distribution, | |||
| especially when generating from a remote that was acquired using a catalog | |||
| listing, ``dds`` expects the generated source distribution to have a matching | |||
| package identity to match what was intended. | |||
| This can happen if a catalog listing's package version does not match the | |||
| source of the remote acquisition method. For example, if using ``git`` to clone | |||
| a repository, the ``git-ref`` used to clone must match the package version of | |||
| the listing. If the ``git-ref`` is a branch, it is possible that additional | |||
| changes were pushed into the branch that changed the package version, thus | |||
| invalidating the package. [#f1]_ | |||
| .. [#f1] | |||
| For this reason, it is **highly recommended** to use Git *tags* to | |||
| refer to remote packages *instead of branches*. | |||
| @@ -0,0 +1,17 @@ | |||
| Error: One or more tests failed | |||
| ############################### | |||
| 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 | |||
| ``Test-Driver``. | |||
| If you see this error, it is most likely that you have an issue in the tests of | |||
| your project. | |||
| When a project is built with the ``build`` command, it is the default behavior | |||
| for ``dds`` to compile, link, and execute all tests defined for the project. | |||
| Test execution can be suppressed using the ``--no-tests`` command line option | |||
| with the ``build`` subcommand. | |||
| .. seealso:: | |||
| Refer to the page on :ref:`pkgs.apps-tests`. | |||
| @@ -14,6 +14,8 @@ package IDs available can be listed with ``dds catalog list``. This will only | |||
| list the IDs of the packages, but none of the additional metadata about them. | |||
| .. _catalog.adding: | |||
| Adding Packages to the Catalog | |||
| ****************************** | |||
| @@ -30,8 +32,7 @@ command: | |||
| dds catalog add <package-id> | |||
| [--depends <requirement> [--depends <requirement> [...]]] | |||
| [--git-url <url>] | |||
| [--git-ref <ref>] | |||
| [--git-url <url> --git-ref <ref>] | |||
| [--auto-lib <Namespace>/<Name>] | |||
| The ``<package-id>`` positional arguments is the ``name@version`` package ID | |||
| @@ -58,6 +58,8 @@ If a file's extension is not listed in the table above, ``dds`` will ignore it. | |||
| together as part of *source distribution*. | |||
| .. _pkgs.apps-tests: | |||
| Applications and Tests | |||
| ********************** | |||
| @@ -42,6 +42,8 @@ toolchain file for the ``--toolchain`` (or ``-t``) option on the command line:: | |||
| Alternatively, you can pass the name of a built-in toolchain. See below. | |||
| .. _toolchains.builtin: | |||
| Built-in Toolchains | |||
| ******************* | |||
| @@ -135,6 +137,8 @@ Flags for linking executables can be specified with ``Link-Flags``: | |||
| Link-Flags: -fsanitize=address -fPIE | |||
| .. _toolchains.opt-ref: | |||
| Toolchain Option Reference | |||
| ************************** | |||
| @@ -17,6 +17,14 @@ the :doc:`tut/index` page. | |||
| tut/index | |||
| guide/index | |||
| design | |||
| err/index | |||
| .. Hide the link to the error reference since we don't want it cluttering our | |||
| main toc, but we want Sphinx to not consider it "unreferenced." We'll generate | |||
| our own link | |||
| .. seealso:: | |||
| For in-depth error and troubleshooting information see: :doc:`err/index`. | |||
| Indices and tables | |||
| @@ -52,7 +52,7 @@ A ``Test-Driver``: Using *Catch2* | |||
| ********************************* | |||
| ``dds`` ships with built-in support for the `Catch2`_ C and C++ testing | |||
| framework, a popular | |||
| framework. | |||
| .. _catch2: https://github.com/catchorg/Catch2 | |||
| @@ -9,7 +9,7 @@ Creating a *Package Root* | |||
| To start, create a new directory for your project. This will be known as the | |||
| *package root*, and the entirety of our project will be placed in this | |||
| directory the name and location of this directory is not important, but the | |||
| directory. The name and location of this directory is not important, but the | |||
| contents therein will be significant. | |||
| .. note:: | |||
| @@ -103,19 +103,11 @@ By default, build results will be placed in a subdirectory of the package root | |||
| named ``_build``. Within this directory, you will find the generated executable | |||
| named ``hello-world`` (with a ``.exe`` suffix if on Windows). | |||
| We should not be able to run this executable and see our ``Hello, world!``:: | |||
| We should now be able to run this executable and see our ``Hello, world!``:: | |||
| > ./_build/hello-world | |||
| Hello, world! | |||
| Obviously this isn't *all* there is to do with ``dds``. Read on to the next | |||
| pages to learn more. | |||
| .. note:: | |||
| You're reading a very early version of these docs. There will be a lot more | |||
| here in the future. Watch this space for changes! | |||
| More Sources | |||
| ************ | |||
| @@ -1,6 +1,7 @@ | |||
| #include <dds/build/builder.hpp> | |||
| #include <dds/catalog/catalog.hpp> | |||
| #include <dds/catalog/get.hpp> | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/repo/repo.hpp> | |||
| #include <dds/source/dist.hpp> | |||
| #include <dds/toolchain/from_dds.hpp> | |||
| @@ -38,8 +39,9 @@ struct toolchain_flag : string_flag { | |||
| auto default_tc = tc_path.substr(1); | |||
| auto tc = dds::toolchain::get_builtin(default_tc); | |||
| if (!tc.has_value()) { | |||
| throw std::runtime_error( | |||
| fmt::format("Invalid default toolchain name '{}'", default_tc)); | |||
| dds::throw_user_error< | |||
| dds::errc::invalid_builtin_toolchain>("Invalid built-in toolchain name '{}'", | |||
| default_tc); | |||
| } | |||
| return std::move(*tc); | |||
| } else { | |||
| @@ -190,8 +192,8 @@ struct cli_catalog { | |||
| auto id = dds::package_id::parse(req); | |||
| auto info = cat.get(id); | |||
| if (!info) { | |||
| throw std::runtime_error( | |||
| fmt::format("No package in the catalog matched the ID '{}'", req)); | |||
| dds::throw_user_error<dds::errc::no_such_catalog_package>( | |||
| "No package in the catalog matched the ID '{}'", req); | |||
| } | |||
| auto tsd = dds::get_package_sdist(*info); | |||
| auto out_path = out.Get(); | |||
| @@ -246,14 +248,15 @@ struct cli_catalog { | |||
| if (git_url) { | |||
| if (!git_ref) { | |||
| throw std::runtime_error( | |||
| "`--git-ref` must be specified when using `--git-url`"); | |||
| dds::throw_user_error<dds::errc::git_url_ref_mutual_req>(); | |||
| } | |||
| auto git = dds::git_remote_listing{git_url.Get(), git_ref.Get(), std::nullopt}; | |||
| if (auto_lib) { | |||
| git.auto_lib = lm::split_usage_string(auto_lib.Get()); | |||
| } | |||
| info.remote = std::move(git); | |||
| } else if (git_ref) { | |||
| dds::throw_user_error<dds::errc::git_url_ref_mutual_req>(); | |||
| } | |||
| cat_path.open().store(info); | |||
| @@ -719,6 +722,11 @@ int main(int argc, char** argv) { | |||
| } catch (const dds::user_cancelled&) { | |||
| spdlog::critical("Operation cancelled by user"); | |||
| return 2; | |||
| } catch (const dds::error_base& e) { | |||
| spdlog::error("{}", e.what()); | |||
| spdlog::error("{}", e.explanation()); | |||
| spdlog::error("Refer: {}", e.error_reference()); | |||
| return 1; | |||
| } catch (const std::exception& e) { | |||
| spdlog::critical(e.what()); | |||
| return 2; | |||
| @@ -4,6 +4,7 @@ | |||
| #include <dds/build/plan/full.hpp> | |||
| #include <dds/catch2_embedded.hpp> | |||
| #include <dds/compdb.hpp> | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/usage_reqs.hpp> | |||
| #include <dds/util/time.hpp> | |||
| @@ -234,7 +235,7 @@ void builder::build(const build_params& params) const { | |||
| failures.output); | |||
| } | |||
| if (!test_failures.empty()) { | |||
| throw compile_failure("Test failures during the build!"); | |||
| throw_user_error<errc::test_failure>(); | |||
| } | |||
| if (params.emit_lmi) { | |||
| @@ -1,5 +1,6 @@ | |||
| #include "./archive.hpp" | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <dds/util/time.hpp> | |||
| @@ -45,9 +46,11 @@ void create_archive_plan::archive(const build_env& env) const { | |||
| // Check, log, and throw | |||
| if (!ar_res.okay()) { | |||
| spdlog::error("Creating static library archive failed: {}", out_relpath); | |||
| spdlog::error("Creating static library archive [{}] failed for '{}'", out_relpath, _name); | |||
| spdlog::error("Subcommand FAILED: {}\n{}", quote_command(ar_cmd), ar_res.output); | |||
| throw std::runtime_error( | |||
| fmt::format("Creating archive [{}] failed for '{}'", out_relpath, _name)); | |||
| throw_external_error< | |||
| errc::archive_failure>("Creating static library archive [{}] failed for '{}'", | |||
| out_relpath, | |||
| _name); | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| #include "./compile_exec.hpp" | |||
| #include <dds/build/file_deps.hpp> | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <dds/util/string.hpp> | |||
| #include <dds/util/time.hpp> | |||
| @@ -180,7 +181,7 @@ do_compile(const compile_file_full& cf, build_env_ref env, compile_counter& coun | |||
| if (compile_signal) { | |||
| spdlog::error("Process exited via signal {}", compile_signal); | |||
| } | |||
| throw compile_failure(fmt::format("Compilation failed for {}", source_path.string())); | |||
| throw_user_error<errc::compile_failure>("Compilation failed [{}]", source_path.string()); | |||
| } | |||
| // Print any compiler output, sans whitespace | |||
| @@ -1,6 +1,7 @@ | |||
| #include "./exe.hpp" | |||
| #include <dds/build/plan/library.hpp> | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <dds/util/algo.hpp> | |||
| #include <dds/util/time.hpp> | |||
| @@ -54,12 +55,12 @@ void link_executable_plan::link(build_env_ref env, const library_plan& lib) cons | |||
| // Check and throw if errant | |||
| if (!proc_res.okay()) { | |||
| throw compile_failure( | |||
| fmt::format("Failed to link test executable '{}'. Link command [{}] returned {}:\n{}", | |||
| spec.output.string(), | |||
| quote_command(link_command), | |||
| proc_res.retc, | |||
| proc_res.output)); | |||
| throw_external_error< | |||
| errc::link_failure>("Failed to link executable [{}]. Link command was [{}]", | |||
| spec.output.string(), | |||
| quote_command(link_command), | |||
| proc_res.retc, | |||
| proc_res.output); | |||
| } | |||
| } | |||
| @@ -2,6 +2,7 @@ | |||
| #include <dds/build/iter_compilations.hpp> | |||
| #include <dds/build/plan/compile_exec.hpp> | |||
| #include <dds/error/errors.hpp> | |||
| #include <range/v3/view/concat.hpp> | |||
| #include <range/v3/view/filter.hpp> | |||
| @@ -76,7 +77,7 @@ bool parallel_run(Range&& rng, int n_jobs, Fn&& fn) { | |||
| void build_plan::compile_all(const build_env& env, int njobs) const { | |||
| auto okay = dds::compile_all(iter_compilations(*this), env, njobs); | |||
| if (!okay) { | |||
| throw std::runtime_error("Compilation failed."); | |||
| throw_user_error<errc::compile_failure>(); | |||
| } | |||
| } | |||
| @@ -87,7 +88,7 @@ void build_plan::archive_all(const build_env& env, int njobs) const { | |||
| } | |||
| }); | |||
| if (!okay) { | |||
| throw std::runtime_error("Error creating static library archives"); | |||
| throw_external_error<errc::archive_failure>(); | |||
| } | |||
| } | |||
| @@ -107,7 +108,7 @@ void build_plan::link_all(const build_env& env, int njobs) const { | |||
| exe.get().link(env, lib); | |||
| }); | |||
| if (!okay) { | |||
| throw std::runtime_error("Failure to link executables"); | |||
| throw_user_error<errc::link_failure>(); | |||
| } | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| #include "./catalog.hpp" | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/solve/solve.hpp> | |||
| #include <neo/sqlite3/exec.hpp> | |||
| @@ -75,14 +76,22 @@ void ensure_migrated(sqlite3::database& db) { | |||
| auto meta = nlohmann::json::parse(meta_json); | |||
| if (!meta.is_object()) { | |||
| throw std::runtime_error("Corrupted repository database file."); | |||
| throw_external_error<errc::corrupted_catalog_db>(); | |||
| } | |||
| auto version_ = meta["version"]; | |||
| if (!version_.is_number_integer()) { | |||
| throw std::runtime_error("Corrupted repository database file [bad dds_meta.version]"); | |||
| throw_external_error<errc::corrupted_catalog_db>( | |||
| "The catalog database metadata is invalid [bad dds_meta.version]"); | |||
| } | |||
| constexpr int current_database_version = 1; | |||
| int version = version_; | |||
| if (version > current_database_version) { | |||
| throw_external_error<errc::catalog_too_new>(); | |||
| } | |||
| if (version < 1) { | |||
| migrate_repodb_1(db); | |||
| } | |||
| @@ -101,10 +110,10 @@ catalog catalog::open(const std::string& db_path) { | |||
| ensure_migrated(db); | |||
| } catch (const sqlite3::sqlite3_error& e) { | |||
| spdlog::critical( | |||
| "Failed to load the repository databsae. It appears to be invalid/corrupted. The " | |||
| "Failed to load the repository database. It appears to be invalid/corrupted. The " | |||
| "exception message is: {}", | |||
| e.what()); | |||
| throw; | |||
| throw_external_error<errc::corrupted_catalog_db>(); | |||
| } | |||
| return catalog(std::move(db)); | |||
| } | |||
| @@ -163,10 +172,7 @@ void catalog::store(const package_info& pkg) { | |||
| )"_sql); | |||
| for (const auto& dep : pkg.deps) { | |||
| new_dep_st.reset(); | |||
| if (dep.versions.num_intervals() != 1) { | |||
| throw std::runtime_error( | |||
| "Package dependency may only contain a single version interval"); | |||
| } | |||
| assert(dep.versions.num_intervals() == 1); | |||
| auto iv_1 = *dep.versions.iter_intervals().begin(); | |||
| sqlite3::exec(new_dep_st, | |||
| std::forward_as_tuple(db_pkg_id, | |||
| @@ -274,7 +280,7 @@ namespace { | |||
| void check_json(bool b, std::string_view what) { | |||
| if (!b) { | |||
| throw std::runtime_error("Unable to read repository JSON: " + std::string(what)); | |||
| throw_user_error<errc::invalid_catalog_json>("Catalog JSON is invalid: {}", what); | |||
| } | |||
| } | |||
| @@ -337,8 +343,9 @@ void catalog::import_json_str(std::string_view content) { | |||
| } | |||
| info.remote = git_remote_listing{url, ref, autolib}; | |||
| } else { | |||
| throw std::runtime_error( | |||
| fmt::format("No remote info for /packages/{}/{}", pkg_name, version_)); | |||
| throw_user_error<errc::no_catalog_remote_info>("No remote info for /packages/{}/{}", | |||
| pkg_name, | |||
| version_); | |||
| } | |||
| store(info); | |||
| @@ -1,6 +1,7 @@ | |||
| #include "./get.hpp" | |||
| #include <dds/catalog/catalog.hpp> | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/proc.hpp> | |||
| #include <spdlog/spdlog.h> | |||
| @@ -22,11 +23,11 @@ temporary_sdist do_pull_sdist(const package_info& listing, const git_remote_list | |||
| tmpdir.path().generic_string()}; | |||
| auto git_res = run_proc(command); | |||
| if (!git_res.okay()) { | |||
| throw std::runtime_error( | |||
| fmt::format("Git clone operation failed [Git command: {}] [Exitted {}]:\n{}", | |||
| quote_command(command), | |||
| git_res.retc, | |||
| git_res.output)); | |||
| throw_external_error<errc::git_clone_failure>( | |||
| "Git clone operation failed [Git command: {}] [Exitted {}]:\n{}", | |||
| quote_command(command), | |||
| git_res.retc, | |||
| git_res.output); | |||
| } | |||
| spdlog::info("Create sdist from clone ..."); | |||
| if (git.auto_lib.has_value()) { | |||
| @@ -53,11 +54,11 @@ temporary_sdist do_pull_sdist(const package_info& listing, const git_remote_list | |||
| temporary_sdist dds::get_package_sdist(const package_info& pkg) { | |||
| auto tsd = std::visit([&](auto&& remote) { return do_pull_sdist(pkg, remote); }, pkg.remote); | |||
| if (!(tsd.sdist.manifest.pkg_id == pkg.ident)) { | |||
| throw std::runtime_error(fmt::format( | |||
| "The package name@version in the generated sdist does not match the name listed in " | |||
| "the remote listing file (expected '{}', but got '{}')", | |||
| throw_external_error<errc::sdist_ident_mismatch>( | |||
| "The package name@version in the generated source distribution does not match the name " | |||
| "listed in the remote listing file (expected '{}', but got '{}')", | |||
| pkg.ident.to_string(), | |||
| tsd.sdist.manifest.pkg_id.to_string())); | |||
| tsd.sdist.manifest.pkg_id.to_string()); | |||
| } | |||
| return tsd; | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| #include "./database.hpp" | |||
| #include <dds/error/errors.hpp> | |||
| #include <neo/sqlite3/exec.hpp> | |||
| #include <neo/sqlite3/iter_tuples.hpp> | |||
| #include <neo/sqlite3/single.hpp> | |||
| @@ -62,12 +64,13 @@ void ensure_migrated(sqlite3::database& db) { | |||
| auto meta = nlohmann::json::parse(meta_json); | |||
| if (!meta.is_object()) { | |||
| throw std::runtime_error("Correupted database file."); | |||
| throw_external_error<errc::corrupted_build_db>(); | |||
| } | |||
| auto version_ = meta["version"]; | |||
| if (!version_.is_number_integer()) { | |||
| throw std::runtime_error("Corrupted database file [bad dds_meta.version]"); | |||
| throw_external_error<errc::corrupted_build_db>( | |||
| "The build database file is corrupted [bad dds_meta.version]"); | |||
| } | |||
| int version = version_; | |||
| if (version < 1) { | |||
| @@ -1,5 +1,6 @@ | |||
| #include "./deps.hpp" | |||
| #include <dds/error/errors.hpp> | |||
| #include <dds/repo/repo.hpp> | |||
| #include <dds/source/dist.hpp> | |||
| #include <dds/usage_reqs.hpp> | |||
| @@ -34,11 +35,8 @@ dependency dependency::parse_depends_string(std::string_view str) { | |||
| auto rng = semver::range::parse_restricted(version_str); | |||
| return dependency{std::string(name), {rng.low(), rng.high()}}; | |||
| } catch (const semver::invalid_range&) { | |||
| throw std::runtime_error(fmt::format( | |||
| "Invalid version range string '{}' in dependency declaration '{}' (Should be a " | |||
| "semver range string. See https://semver.org/ for info)", | |||
| version_str, | |||
| str)); | |||
| throw_user_error<errc::invalid_version_range_string>( | |||
| "Invalid version range string '{}' in dependency declaration '{}'", version_str, str); | |||
| } | |||
| } | |||
| @@ -0,0 +1,206 @@ | |||
| #include "./errors.hpp" | |||
| #include <cassert> | |||
| #include <stdexcept> | |||
| using namespace dds; | |||
| namespace { | |||
| std::string error_url_prefix = "http://localhost:3000/err/"; | |||
| std::string error_url_suffix(dds::errc ec) noexcept { | |||
| switch (ec) { | |||
| case errc::invalid_builtin_toolchain: | |||
| return "invalid-builtin-toolchain.html"; | |||
| case errc::no_such_catalog_package: | |||
| return "no-such-catalog-package.html"; | |||
| case errc::git_url_ref_mutual_req: | |||
| return "git-url-ref-mutual-req.html"; | |||
| case errc::test_failure: | |||
| return "test-failure.html"; | |||
| case errc::compile_failure: | |||
| return "compile-failure.html"; | |||
| case errc::archive_failure: | |||
| return "archive-failure.html"; | |||
| case errc::link_failure: | |||
| return "link-failure.html"; | |||
| case errc::catalog_too_new: | |||
| return "catalog-too-new.html"; | |||
| case errc::corrupted_catalog_db: | |||
| return "corrupted-catalog-db.html"; | |||
| case errc::invalid_catalog_json: | |||
| return "invalid-catalog-json.html"; | |||
| case errc::no_catalog_remote_info: | |||
| return "no-catalog-remote-info.html"; | |||
| case errc::git_clone_failure: | |||
| return "git-clone-failure.html"; | |||
| case errc::sdist_ident_mismatch: | |||
| return "sdist-ident-mismatch.html"; | |||
| case errc::corrupted_build_db: | |||
| return "corrupted-build-db.html"; | |||
| case errc::invalid_version_range_string: | |||
| return "invalid-version-string.html#range"; | |||
| case errc::invalid_version_string: | |||
| return "invalid-version-string.html"; | |||
| case errc::none: | |||
| break; | |||
| } | |||
| assert(false && "Unreachable code path generating error explanation URL"); | |||
| std::terminate(); | |||
| } | |||
| } // namespace | |||
| std::string dds::error_reference_of(dds::errc ec) noexcept { | |||
| return error_url_prefix + error_url_suffix(ec); | |||
| } | |||
| std::string_view dds::explanation_of(dds::errc ec) noexcept { | |||
| switch (ec) { | |||
| case errc::invalid_builtin_toolchain: | |||
| return R"( | |||
| If you start your toolchain name (The `-t` or `--toolchain` argument) | |||
| with a leading colon, dds will interpret it as a reference to a built-in | |||
| toolchain. (Toolchain file paths cannot begin with a leading colon). | |||
| These toolchain names are encoded into the dds executable and cannot be | |||
| modified. | |||
| )"; | |||
| case errc::no_such_catalog_package: | |||
| return R"( | |||
| The installation of a package was requested, but the given package ID was not | |||
| able to be found in the package catalog. Check the spelling and version number. | |||
| )"; | |||
| case errc::git_url_ref_mutual_req: | |||
| return R"( | |||
| Creating a Git-based catalog entry requires both a URL to clone from and a Git | |||
| reference (tag, branch, commit) to clone. | |||
| )"; | |||
| case errc::test_failure: | |||
| return R"( | |||
| One or more of the project's tests failed. The failing tests are listed above, | |||
| along with their exit code and output. | |||
| )"; | |||
| case errc::compile_failure: | |||
| return R"( | |||
| Source compilation failed. Refer to the compiler output. | |||
| )"; | |||
| case errc::archive_failure: | |||
| return R"( | |||
| Creating a static library archive failed, which prevents the associated library | |||
| from being used as this archive is the input to the linker for downstream | |||
| build targets. | |||
| It is unlikely that regular user action can cause static library archiving to | |||
| fail. Refer to the output of the archiving tool. | |||
| )"; | |||
| case errc::link_failure: | |||
| return R"( | |||
| Linking a runtime binary file failed. There are a variety of possible causes | |||
| for this error. Refer to the documentation for more information. | |||
| )"; | |||
| case errc::catalog_too_new: | |||
| return R"( | |||
| The catalog database file contains a schema that will automatically be upgraded | |||
| by dds when it is opened/modified. It appears that the given catalog database | |||
| has had a migration beyond a version that we support. Has the catalog been | |||
| modified by a newer version of dds? | |||
| )"; | |||
| case errc::corrupted_catalog_db: | |||
| return R"( | |||
| 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 | |||
| and handle. | |||
| )"; | |||
| case errc::invalid_catalog_json: | |||
| return R"( | |||
| The catalog JSON that was provided does not match the format that was expected. | |||
| Check the JSON schema and try your submission again. | |||
| )"; | |||
| case errc::no_catalog_remote_info: | |||
| return R"( | |||
| The catalog entry requires information regarding the remote acquisition method. | |||
| Refer to the documentation for details. | |||
| )"; | |||
| case errc::git_clone_failure: | |||
| return R"( | |||
| dds tried to clone a repository using Git, but the clone operation failed. | |||
| There are a variety of possible causes. It is best to check the output from | |||
| Git in diagnosing this failure. | |||
| )"; | |||
| case errc::sdist_ident_mismatch: | |||
| return R"( | |||
| We tried to automatically generate a source distribution from a package, but | |||
| the name and/or version of the package that was generated does not match what | |||
| we expected of it. | |||
| )"; | |||
| case errc::corrupted_build_db: | |||
| return R"( | |||
| The local build database file is corrupted. The file is stored in the build | |||
| directory as `.dds.db', and is safe to delete to clear the bad data. This is | |||
| not a likely error, and if you receive this message frequently, please file a | |||
| bug report. | |||
| )"; | |||
| case errc::invalid_version_range_string: | |||
| return R"( | |||
| Parsing of a version range string failed. Refer to the documentation for more | |||
| information. | |||
| )"; | |||
| case errc::invalid_version_string: | |||
| return R"( | |||
| `dds` expects all version numbers to conform to the Semantic Versioning | |||
| specification. Refer to the documentation and https://semver.org/ for more | |||
| information. | |||
| )"; | |||
| case errc::none: | |||
| break; | |||
| } | |||
| assert(false && "Unexpected execution path during error explanation. This is a DDS bug"); | |||
| std::terminate(); | |||
| } | |||
| std::string_view dds::default_error_string(dds::errc ec) noexcept { | |||
| switch (ec) { | |||
| case errc::invalid_builtin_toolchain: | |||
| return "The built-in toolchain name is invalid"; | |||
| case errc::no_such_catalog_package: | |||
| return "The catalog has no entry for the given package ID"; | |||
| case errc::git_url_ref_mutual_req: | |||
| return "Git requires both a URL and a ref to clone"; | |||
| case errc::test_failure: | |||
| return "One or more tests failed"; | |||
| case errc::compile_failure: | |||
| return "Source compilation failed."; | |||
| case errc::archive_failure: | |||
| return "Creating a static library archive failed"; | |||
| case errc::link_failure: | |||
| return "Linking a runtime binary (executable/shared library/DLL) failed"; | |||
| case errc::catalog_too_new: | |||
| return "The catalog appears to be from a newer version of dds."; | |||
| case errc::corrupted_catalog_db: | |||
| return "The catalog database appears to be corrupted or invalid"; | |||
| case errc::invalid_catalog_json: | |||
| return "The given catalog JSON data is not valid"; | |||
| case errc::no_catalog_remote_info: | |||
| return "The catalog JSON is missing remote acquisition information for one or more\n" | |||
| "packages"; | |||
| case errc::git_clone_failure: | |||
| return "A git-clone operation failed."; | |||
| case errc::sdist_ident_mismatch: | |||
| return "The package version of a generated source distribution did not match the version\n" | |||
| "that was expected of it"; | |||
| case errc::corrupted_build_db: | |||
| return "The build database file is corrupted"; | |||
| case errc::invalid_version_range_string: | |||
| return "Attempted to parse an invalid version range string. <- (Seeing this text is a " | |||
| "`dds` bug. Please report it.)"; | |||
| case errc::invalid_version_string: | |||
| return "Attempted to parse an invalid version string. <- (Seeing this text is a `dds` bug. " | |||
| "Please report it.)"; | |||
| case errc::none: | |||
| break; | |||
| } | |||
| assert(false && "Unexpected execution path during error message creation. This is a DDS bug"); | |||
| std::terminate(); | |||
| } | |||
| @@ -0,0 +1,91 @@ | |||
| #pragma once | |||
| #include <spdlog/fmt/fmt.h> | |||
| #include <stdexcept> | |||
| #include <string_view> | |||
| namespace dds { | |||
| enum class errc { | |||
| none = 0, | |||
| invalid_builtin_toolchain, | |||
| no_such_catalog_package, | |||
| git_url_ref_mutual_req, | |||
| test_failure, | |||
| compile_failure, | |||
| archive_failure, | |||
| link_failure, | |||
| catalog_too_new, | |||
| corrupted_catalog_db, | |||
| invalid_catalog_json, | |||
| no_catalog_remote_info, | |||
| git_clone_failure, | |||
| sdist_ident_mismatch, | |||
| corrupted_build_db, | |||
| invalid_version_range_string, | |||
| invalid_version_string, | |||
| }; | |||
| std::string error_reference_of(errc) noexcept; | |||
| std::string_view explanation_of(errc) noexcept; | |||
| std::string_view default_error_string(errc) noexcept; | |||
| struct exception_base : std::runtime_error { | |||
| using runtime_error::runtime_error; | |||
| }; | |||
| struct error_base : exception_base { | |||
| using exception_base::exception_base; | |||
| virtual errc get_errc() const noexcept = 0; | |||
| std::string error_reference() const noexcept { return error_reference_of(get_errc()); } | |||
| std::string_view explanation() const noexcept { return explanation_of(get_errc()); } | |||
| }; | |||
| struct external_error_base : error_base { | |||
| using error_base::error_base; | |||
| }; | |||
| struct user_error_base : error_base { | |||
| using error_base::error_base; | |||
| }; | |||
| template <errc ErrorCode> | |||
| struct user_error : user_error_base { | |||
| using user_error_base::user_error_base; | |||
| errc get_errc() const noexcept override { return ErrorCode; } | |||
| }; | |||
| template <errc ErrorCode> | |||
| struct external_error : external_error_base { | |||
| using external_error_base::external_error_base; | |||
| errc get_errc() const noexcept override { return ErrorCode; } | |||
| }; | |||
| using error_invalid_default_toolchain = user_error<errc::invalid_builtin_toolchain>; | |||
| template <errc ErrorCode, typename... Args> | |||
| [[noreturn]] void throw_user_error(std::string_view fmt_str, Args&&... args) { | |||
| throw user_error<ErrorCode>(fmt::format(fmt_str, std::forward<Args>(args)...)); | |||
| } | |||
| template <errc ErrorCode> | |||
| [[noreturn]] void throw_user_error() { | |||
| throw user_error<ErrorCode>(std::string(default_error_string(ErrorCode))); | |||
| } | |||
| template <errc ErrorCode, typename... Args> | |||
| [[noreturn]] void throw_external_error(std::string_view fmt_str, Args&&... args) { | |||
| throw external_error<ErrorCode>(fmt::format(fmt_str, std::forward<Args>(args)...)); | |||
| } | |||
| template <errc ErrorCode> | |||
| [[noreturn]] void throw_external_error() { | |||
| throw external_error<ErrorCode>(std::string(default_error_string(ErrorCode))); | |||
| } | |||
| } // namespace dds | |||