Line data Source code
1 0 : \cond NEVER
2 : Distributed under the MIT License.
3 : See LICENSE.txt for details.
4 : \endcond
5 :
6 : # Understanding Compiler and Linker Errors {#compiler_and_linker_errors}
7 :
8 : \tableofcontents
9 :
10 : # Linker Errors {#understanding_linker_errors}
11 :
12 : There are a few common mistakes that can lead to linker problems, specifically
13 : problems where there is an `undefined reference`. These include:
14 : - forgetting to add a `.cpp` file to the list of sources of a library in a
15 : `CMakeLists.txt` file
16 : - missing an explicit instantiation of a function or class template in a `cpp`
17 : file
18 : - not including a `tpp` file inside a `cpp` file
19 : - the template specialization or function overload has been explicitly disabled
20 : via SFINAE (usually through the use of a `Requires`)
21 : - not linking a library (explained below)
22 :
23 : Possibly the most difficult part of fixing linking errors is understanding what
24 : they are trying to tell you. Let's take the following example
25 : ```
26 : error: undefined reference to 'Tensor<DataVector,
27 : brigand::list<brigand::integral_constant<int, 1> >,
28 : brigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)0, Frame::Inertial,
29 : (IndexType)0> > >
30 : random_unit_normal<DataVector>(gsl::not_null<std::mersenne_twister_engine<
31 : unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul,
32 : 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>*>, Tensor<DataVector,
33 : brigand::list<brigand::integral_constant<int, 1>,
34 : brigand::integral_constant<int, 1> >,
35 : brigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,
36 : (IndexType)0>, Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,
37 : (IndexType)0> > > const&)' in
38 : lib/libTest_GeneralizedHarmonic.a(Test_UpwindFlux.cpp.o):
39 : Test_UpwindFlux.cpp:function (anonymous namespace)::test_upwind_flux_random()
40 : ```
41 : We can start by splitting out information about different parts of the
42 : error. First,
43 : ```
44 : error: undefined reference to
45 : ```
46 : tells us that we forgot to add a link dependency for a library or
47 : executable. The next relevant part of information is which library (executable)
48 : and file the missing function/class was in. This is at the end of the error
49 : message (unfortunately, where in the error message can depend on your linker,
50 : these examples used `ld.lld` v9):
51 : ```
52 : lib/libTest_GeneralizedHarmonic.a(Test_UpwindFlux.cpp.o):
53 : Test_UpwindFlux.cpp:function (anonymous namespace)::test_upwind_flux_random()
54 : ```
55 : What this means is that the missing link dependency is used in the library
56 : `Test_GeneralizedHarmonic`, the file `Test_UpwindFlux.cpp`, and the function
57 : `test_upwind_flux_random()`.
58 :
59 : We have now determined what the linker error is (a missing link dependency), and
60 : in which library, file, and function the missing link dependency is used. We now
61 : need to understand what the missing link dependency is. Since SpECTRE uses a lot
62 : of templates, the missing reference (link dependency) can be quite
63 : long. In this case it is:
64 : ```
65 : Tensor<DataVector,
66 : brigand::list<brigand::integral_constant<int, 1> >,
67 : brigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)0, Frame::Inertial,
68 : (IndexType)0> > >
69 : random_unit_normal<DataVector>(gsl::not_null<std::mersenne_twister_engine<
70 : unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul,
71 : 2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>*>, Tensor<DataVector,
72 : brigand::list<brigand::integral_constant<int, 1>,
73 : brigand::integral_constant<int, 1> >,
74 : brigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,
75 : (IndexType)0>, Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,
76 : (IndexType)0> > > const&)
77 : ```
78 : In order to make this easier to read, it is recommended to run the code through
79 : ClangFormat. This can be done by copying the linker output into an empty `cpp`
80 : file and running `clang-format -i EMPTY_FILE_WITH_LINKER_OUTPUT`. Keep in mind
81 : that ClangFormat will only work if you have only copied the part of the linker
82 : output that resembles valid C++. Doing so in this case gives:
83 : \code{.cpp}
84 : Tensor<DataVector, brigand::list<brigand::integral_constant<int, 1> >,
85 : brigand::list<Tensor_detail::TensorIndexType<
86 : 3ul, (UpLo)0, Frame::Inertial, (IndexType)0> > >
87 : random_unit_normal<DataVector>(
88 : gsl::not_null<std::mersenne_twister_engine<
89 : unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul,
90 : 4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul,
91 : 1812433253ul>*>,
92 : Tensor<DataVector,
93 : brigand::list<brigand::integral_constant<int, 1>,
94 : brigand::integral_constant<int, 1> >,
95 : brigand::list<
96 : Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,
97 : (IndexType)0>,
98 : Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,
99 : (IndexType)0> > > const&);
100 : \endcode
101 : We see that it is the function `random_unit_normal` that isn't found. Now we can
102 : search in the code base where than function is defined. Doing a
103 : `git grep "random_unit_normal"` points to
104 : `tests/Unit/Helpers/DataStructures/RandomUnitNormal.?pp`. Looking
105 : at `tests/Unit/Helpers/DataStructures/RandomUnitNormal.hpp` we see that the
106 : function `random_unit_normal` is declared there, and looking in the
107 : corresponding `cpp` we see `random_unit_normal` is instantiated in the source
108 : file. Opening up `tests/Unit/Helpers/DataStructures/CMakeLists.txt` we see that
109 : the library name is `DataStructuresHelpers`, and `RandomUnitNormal.cpp` is in
110 : the list of sources for the library. Thus, linking
111 : `Test_GeneralizedHarmonic` against `DataStructuresHelpers` will resolve our
112 : error. To link against a library, you must add it to the
113 : `target_link_libraries`.
114 :
115 : If `random_unit_normal` had been defined in the header file, then the error
116 : would've indicated that we did not include the header (or `tpp`) file into
117 : `Test_UpwindFlux.cpp`, and so the compiler could not generate an
118 : instantiation.
119 :
120 : In summary:
121 : - Identify target with undefined reference, i.e. the file included in a library
122 : or executable.
123 : - Identify missing source definition (usually a function or static variable).
124 : - Find source declaration and definition in repository.
125 : - If source definition is in a `cpp` file, make sure it is in the list of
126 : sources in the `CMakeLists.txt` in the same directory and that the
127 : corresponding library is linked against by the target.
128 :
129 : If the undefined reference is a template, make sure the required instantiation
130 : exists.
131 : - If the undefined reference's definition is in a `tpp` file, make sure the
132 : `tpp` file is included in the target file.
133 : - If the undefined reference's source definition is in an `hpp` file, make sure
134 : the specific instantiation is possible (e.g. not forbidden by a `Requires`)
|