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

Generated by: LCOV version 1.14