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