SpECTRE Documentation Coverage Report
Current view: top level - __w/spectre/spectre/docs/DevGuide - CompilerLinkerErrors.md Hit Total Coverage
Commit: 37c384043430860f87787999aa7399d01bb3d213 Lines: 0 1 0.0 %
Date: 2024-04-20 02:24:02
Legend: Lines: hit not hit

          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`)

Generated by: LCOV version 1.14