I want to play around with waLBerla (website and gitlab), which is a C++ library that can be used to simulate certain physical behavior, like fluid dynamics using the Lattice Boltzmann method, but having major difficulties on how to properly set up my CMakeLists.txt, as I don't have much experience with that.
I followed the instructions to install the library, using some of the dependencies like OpenMP, CUDA and OpenMESH, which went fine.
But when trying to compile for example one of the tutorials, I just can't get it to work. So far I have the following Cmake file, where I manually list all the static libraries that have been build when compiling waLBerla:
CMAKE_MINIMUM_REQUIRED (VERSION 3.20)
set (CMAKE_CXX_STANDARD 20)
PROJECT (sugar_dissolving)
add_executable(${PROJECT_NAME} main.cpp)
find_package( waLBerla REQUIRED)
find_package(MPI REQUIRED)
find_package(OpenMP REQUIRED)
target_link_libraries(sugar_dissolving PRIVATE OpenMP::OpenMP_CXX)
target_link_libraries(sugar_dissolving PRIVATE
${MPI_LIBRARIES}
/usr/local/lib/libcore.a
/usr/local/lib/libblockforest.a
/usr/local/lib/liblbm.a
/usr/local/lib/libboundary.a
/usr/local/lib/libdomain_decomposition.a
/usr/local/lib/libblas.a
/usr/local/lib/libfield.a
/usr/local/lib/libexecutiontree.a
/usr/local/lib/liblapack.a
/usr/local/lib/libgeometry.a
/usr/local/lib/libmesh.a
/usr/local/lib/libgather.a
/usr/local/lib/libtimeloop.a
/usr/local/lib/libvtk.a
/usr/local/lib/libcuda.a
/usr/local/lib/libgui.a
/usr/local/lib/liblbm_mesapd_coupling.a
/usr/local/lib/libmesa_pd.a
/usr/local/lib/libpde.a
/usr/local/lib/libpe.a
/usr/local/lib/libpe_coupling.a
/usr/local/lib/libpostprocessing.a
/usr/local/lib/libpython_coupling.a
/usr/local/lib/libsqlite.a
<private>/walberla/build/extern/lodepng/liblodepng.a
)
waLBerla exports a package when building it, so I am using find_package(). At first it couldn't find the waLBerla-config.cmake file, so I manually copied it into the package registery, but I think this is not the way to do it? In general, it feels very weird to have to manually list all the the libraries. In this case I don't know if I can know in advance which libraries I need.
It does manage to compile the tutorial, i.e. the header files are found correctly, but then it fails at the linking stage. It knows where to find most functions, but for some reason some of the math functions, which are in libcore.a, don't get found properly. Similarly for some MPI functions, defined by waLBerla:
/usr/bin/ld: /usr/local/lib/libblockforest.a(Initialization.cpp.o): in function `walberla::blockforest::calculateCellDistribution(walberla::math::Vector3<unsigned long> const&, unsigned long, walberla::math::Vector3<unsigned long>&, walberla::math::Vector3<unsigned long>&)':
Initialization.cpp:(.text+0xbfc): undefined reference to `walberla::math::getFactors(unsigned long, unsigned long, std::vector<double, std::allocator<double> > const&)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(SetupBlockForest.cpp.o): in function `walberla::blockforest::SetupBlockForest::getNextBlock(walberla::blockforest::SetupBlock const*) const':
SetupBlockForest.cpp:(.text+0x117f): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(SetupBlockForest.cpp.o): in function `walberla::blockforest::SetupBlockForest::getNextBlock(walberla::blockforest::SetupBlock const*)':
SetupBlockForest.cpp:(.text+0x1287): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(SetupBlockForest.cpp.o): in function `walberla::blockforest::SetupBlockForest::getBlock(walberla::blockforest::BlockID const&) const':
SetupBlockForest.cpp:(.text+0x132d): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x13b8): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x142c): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(SetupBlockForest.cpp.o):SetupBlockForest.cpp:(.text+0x3e06): more undefined references to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)' follow
/usr/bin/ld: /usr/local/lib/libblockforest.a(SetupBlockForest.cpp.o): in function `walberla::blockforest::SetupBlockForest::toStream(std::ostream&) const':
SetupBlockForest.cpp:(.text+0x9379): undefined reference to `walberla::math::Sample::variance(double) const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x93cd): undefined reference to `walberla::math::Sample::relativeStdDeviation() const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x95d9): undefined reference to `walberla::math::Sample::variance(double) const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x962d): undefined reference to `walberla::math::Sample::relativeStdDeviation() const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x9851): undefined reference to `walberla::math::Sample::variance(double) const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x98a8): undefined reference to `walberla::math::Sample::relativeStdDeviation() const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x9f00): undefined reference to `walberla::math::Sample::variance(double) const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x9f53): undefined reference to `walberla::math::Sample::relativeStdDeviation() const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0xa160): undefined reference to `walberla::math::Sample::variance(double) const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0xa1b3): undefined reference to `walberla::math::Sample::relativeStdDeviation() const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0xa3d0): undefined reference to `walberla::math::Sample::variance(double) const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0xa426): undefined reference to `walberla::math::Sample::relativeStdDeviation() const'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0xa576): undefined reference to `walberla::real_comparison::Epsilon<double>::value'
/usr/bin/ld: /usr/local/lib/libblockforest.a(SetupBlockForest.cpp.o): in function `walberla::blockforest::SetupBlockForest::init(walberla::math::GenericAABB<double> const&, unsigned long, unsigned long, unsigned long, bool, bool, bool, walberla::Set<walberla::uid::UID<walberla::uid::suidgenerator::S> > const&)':
SetupBlockForest.cpp:(.text+0x14267): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x1454a): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: SetupBlockForest.cpp:(.text+0x159c1): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(StructuredBlockForest.cpp.o): in function `walberla::blockforest::StructuredBlockForest::blockExists(walberla::cell::Cell const&, unsigned long) const':
StructuredBlockForest.cpp:(.text+0x24d): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(StructuredBlockForest.cpp.o): in function `walberla::blockforest::StructuredBlockForest::getBlockID(walberla::domain_decomposition::IBlockID&, walberla::cell::Cell const&, unsigned long) const':
StructuredBlockForest.cpp:(.text+0x410): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(BlockForest.cpp.o):BlockForest.cpp:(.text+0x72a0): more undefined references to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)' follow
/usr/bin/ld: /usr/local/lib/libblockforest.a(BlockForest.cpp.o): in function `walberla::blockforest::BlockForest::constructBlockInformation()':
BlockForest.cpp:(.text+0x1c62a): undefined reference to `walberla::mpi::allGathervBuffer(walberla::mpi::GenericSendBuffer<unsigned char, walberla::mpi::OptimalGrowth> const&, walberla::mpi::GenericRecvBuffer<unsigned char>&, ompi_communicator_t*)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(BlockForest.cpp.o): in function `void walberla::blockforest::BlockID::toBuffer<walberla::mpi::GenericSendBuffer<unsigned char, walberla::mpi::OptimalGrowth> >(walberla::mpi::GenericSendBuffer<unsigned char, walberla::mpi::OptimalGrowth>&) const':
BlockForest.cpp:(.text._ZNK8walberla11blockforest7BlockID8toBufferINS_3mpi17GenericSendBufferIhNS3_13OptimalGrowthEEEEEvRT_[_ZNK8walberla11blockforest7BlockID8toBufferINS_3mpi17GenericSendBufferIhNS3_13OptimalGrowthEEEEEvRT_]+0x1a): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(BlockForest.cpp.o): in function `std::vector<walberla::uid::UID<walberla::uid::suidgenerator::S>, std::allocator<walberla::uid::UID<walberla::uid::suidgenerator::S> > > walberla::mpi::allReduceSet<walberla::uid::UID<walberla::uid::suidgenerator::S> >(std::vector<walberla::uid::UID<walberla::uid::suidgenerator::S>, std::allocator<walberla::uid::UID<walberla::uid::suidgenerator::S> > >, walberla::mpi::SetOperation, ompi_communicator_t*, int)':
BlockForest.cpp:(.text._ZN8walberla3mpi12allReduceSetINS_3uid3UIDINS2_13suidgenerator1SEEEEESt6vectorIT_SaIS8_EESA_NS0_12SetOperationEP19ompi_communicator_ti[_ZN8walberla3mpi12allReduceSetINS_3uid3UIDINS2_13suidgenerator1SEEEEESt6vectorIT_SaIS8_EESA_NS0_12SetOperationEP19ompi_communicator_ti]+0x3b2): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(BlockReconstruction.cpp.o): in function `walberla::blockforest::BlockReconstruction::reconstructAABB(walberla::math::GenericAABB<double>&, walberla::blockforest::BlockID const&, walberla::math::GenericAABB<double> const&, unsigned long, unsigned long, unsigned long, unsigned long)':
BlockReconstruction.cpp:(.text+0x8d): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: BlockReconstruction.cpp:(.text+0x129): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: BlockReconstruction.cpp:(.text+0x4eb): undefined reference to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)'
/usr/bin/ld: /usr/local/lib/libblockforest.a(PhantomBlockForest.cpp.o):PhantomBlockForest.cpp:(.text+0x6586): more undefined references to `unsigned long walberla::math::uintMSBPosition<unsigned long>(unsigned long)' follow
/usr/bin/ld: /usr/local/lib/libdomain_decomposition.a(BlockStorage.cpp.o): in function `walberla::domain_decomposition::BlockStorage::loadBlockData(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, walberla::selectable::SetSelectableObject<std::shared_ptr<walberla::domain_decomposition::internal::BlockDataHandlingWrapper>, walberla::uid::UID<walberla::uid::suidgenerator::S> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)':
BlockStorage.cpp:(.text+0x31cf): undefined reference to `walberla::mpi::readMPIIO(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, walberla::mpi::GenericRecvBuffer<unsigned char>&)'
/usr/bin/ld: /usr/local/lib/libdomain_decomposition.a(BlockStorage.cpp.o): in function `walberla::domain_decomposition::BlockStorage::saveBlockData(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, walberla::domain_decomposition::BlockDataID const&)':
BlockStorage.cpp:(.text+0x5f8d): undefined reference to `walberla::mpi::writeMPIIO(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, walberla::mpi::GenericSendBuffer<unsigned char, walberla::mpi::OptimalGrowth>&)'
/usr/bin/ld: /usr/local/lib/libdomain_decomposition.a(IBlock.cpp.o): in function `walberla::domain_decomposition::IBlock::operator==(walberla::domain_decomposition::IBlock const&) const':
IBlock.cpp:(.text+0x1d7): undefined reference to `walberla::real_comparison::Epsilon<double>::value'
/usr/bin/ld: /usr/local/lib/libdomain_decomposition.a(StructuredBlockStorage.cpp.o): in function `walberla::domain_decomposition::StructuredBlockStorage::isCellAlignedAABB(walberla::math::GenericAABB<double> const&, unsigned long) const':
StructuredBlockStorage.cpp:(.text+0x721): undefined reference to `walberla::real_comparison::Epsilon<double>::value'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/sugar_dissolving.dir/build.make:126: sugar_dissolving] Error 1
make[1]: *** [CMakeFiles/Makefile2:2335: CMakeFiles/sugar_dissolving.dir/all] Error 2
make: *** [Makefile:136: all] Error 2
When building waLBerla, it also compiled these tutorials, so I know I have all the necessary packages and libraries, but how do I get it to compile, preferably in a modern Cmake way, in a different directory.
For completeness, here is the output of tree:
.
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ │ ├── 3.20.0
│ │ │ ├── CMakeCCompiler.cmake
│ │ │ ├── CMakeCXXCompiler.cmake
│ │ │ ├── CMakeDetermineCompilerABI_C.bin
│ │ │ ├── CMakeDetermineCompilerABI_CXX.bin
│ │ │ ├── CMakeSystem.cmake
│ │ │ ├── CompilerIdC
│ │ │ │ ├── a.out
│ │ │ │ ├── CMakeCCompilerId.c
│ │ │ │ └── tmp
│ │ │ └── CompilerIdCXX
│ │ │ ├── a.out
│ │ │ ├── CMakeCXXCompilerId.cpp
│ │ │ └── tmp
│ │ ├── cmake.check_cache
│ │ ├── CMakeDirectoryInformation.cmake
│ │ ├── CMakeError.log
│ │ ├── CMakeOutput.log
│ │ ├── CMakeRuleHashes.txt
│ │ ├── CMakeTmp
│ │ ├── FindMPI
│ │ │ ├── test_mpi_C.bin
│ │ │ ├── test_mpi.cpp
│ │ │ └── test_mpi_CXX.bin
│ │ ├── FindOpenMP
│ │ │ ├── ompver_C.bin
│ │ │ ├── ompver_CXX.bin
│ │ │ ├── OpenMPCheckVersion.c
│ │ │ ├── OpenMPCheckVersion.cpp
│ │ │ ├── OpenMPTryFlag.c
│ │ │ └── OpenMPTryFlag.cpp
│ │ ├── Makefile2
│ │ ├── Makefile.cmake
│ │ ├── Progress
│ │ │ └── count.txt
│ │ ├── progress.marks
│ │ ├── sugar_dissolving.dir
│ │ │ ├── build.make
│ │ │ ├── cmake_clean.cmake
│ │ │ ├── compiler_depend.internal
│ │ │ ├── compiler_depend.make
│ │ │ ├── compiler_depend.ts
│ │ │ ├── DependInfo.cmake
│ │ │ ├── depend.make
│ │ │ ├── flags.make
│ │ │ ├── link.txt
│ │ │ ├── main.cpp.o
│ │ │ ├── main.cpp.o.d
│ │ │ └── progress.make
│ │ └── TargetDirectories.txt
│ ├── cmake_install.cmake
│ ├── compile_commands.json
│ └── Makefile
├── CMakeLists.txt
└── main.cpp
Many thanks!
En example project structure can be found here.
For completeness, the file structure looks like:
example_app/
├── apps
│ ├── CMakeLists.txt (1)
│ └── example_app
│ ├── CMakeLists.txt (2)
│ └── ExampleApp.cpp
├── CMakeLists.txt (3)
├── FindwaLBerla.cmake (4)
├── README
├── src
│ └── CMakeLists.txt (5)
└── tests
└── CMakeLists.txt (6)
Going from top to bottom, the content of the CMakeLists.txt is
(1):
add_subdirectory( example_app )
(2):
waLBerla_link_files_to_builddir( *.prm *.py)
waLBerla_add_executable ( NAME ExampleApp
FILES ExampleApp.cpp
DEPENDS blockforest core field lbm geometry timeloop gui mesh)
(3):
CMAKE_MINIMUM_REQUIRED (VERSION 3.0)
PROJECT ( your_project_name )
enable_testing()
include_directories( src )
include_directories ( ${your_project_name_BINARY_DIR}/src )
# Extends cmake module path - so that FindwaLBerla.cmake in the current directory is found
set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${your_project_name_SOURCE_DIR} )
find_package( waLBerla )
add_subdirectory( src )
add_subdirectory( apps )
add_subdirectory( tests )
(4):
set( WALBERLA_DIR WALBERLA_DIR-NOTFOUND CACHE PATH "waLBerla path" )
if ( WALBERLA_DIR )
# WALBERLA_DIR has to point to the waLBerla source directory
# this command builds waLBerla (again) in the current build directory in the subfolder "walberla" (second argument)
add_subdirectory( ${WALBERLA_DIR} walberla )
waLBerla_import()
# Adds the 'src' and 'tests' directory of current app
list( APPEND WALBERLA_MODULE_DIRS "${CMAKE_SOURCE_DIR}/src" "${CMAKE_SOURCE_DIR}/tests" )
list( REMOVE_DUPLICATES WALBERLA_MODULE_DIRS )
set ( WALBERLA_MODULE_DIRS ${WALBERLA_MODULE_DIRS} CACHE INTERNAL "All folders that contain modules or tests" )
else()
message( FATAL_ERROR "waLBerla not found - Use 'cmake -DWALBERLA_DIR=path_to_waLBerla_sources pathToApplicationSources' " )
endif()
(5) + (6):
#add_subdirectory( name_of_your_module_subfolder )
Related
I'm trying to create a CmakeLists.txt that uses ImGUI + SDL + SDLRenderer + OpenCv for my current class.
.
├── CMakeLists.txt
├── imgui
│ ├── imconfig.h
│ ├── imgui.cpp
│ ├── imgui_demo.cpp
│ ├── imgui_draw.cpp
│ ├── imgui.h
│ ├── imgui_impl_sdl.cpp
│ ├── imgui_impl_sdl.h
│ ├── imgui_impl_sdlrenderer.cpp
│ ├── imgui_impl_sdlrenderer.h
│ ├── imgui_internal.h
│ ├── imgui_tables.cpp
│ ├── imgui_widgets.cpp
│ ├── imstb_rectpack.h
│ ├── imstb_textedit.h
│ ├── imstb_truetype.h
│ └── LICENSE.txt
├── main.cpp
├── Makefile
└── testImages
└── test001.png
My current CMakeLists.
project(main)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "")
set(sources
imgui/imconfig.h
imgui/imgui.cpp
imgui/imgui.h
imgui/imgui_demo.cpp
imgui/imgui_draw.cpp
imgui/imgui_internal.h
imgui/imgui_widgets.cpp
imgui/imstb_rectpack.h
imgui/imstb_textedit.h
imgui/imstb_truetype.h
imgui/imgui_impl_sdlrenderer.cpp
imgui/imgui_impl_sdlrenderer.h
imgui/imgui_impl_sdl.cpp
imgui/imgui_impl_sdl.h
)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
find_package( OpenCV REQUIRED )
add_executable( main main.cpp ${sources} )
target_link_libraries( main ${OpenCV_LIBS} ${SDL2_LIBRARIES} )
But i get undefined references for all the ImGUI functions:
[ 12%] Linking CXX executable main
/usr/bin/ld: CMakeFiles/main.dir/imgui/imgui.cpp.o: in function `ImGuiListClipper_SeekCursorAndSetupPrevLine(float, float)':
imgui.cpp:(.text+0x60c6): undefined reference to `ImGui::TableEndRow(ImGuiTable*)'
/usr/bin/ld: CMakeFiles/main.dir/imgui/imgui.cpp.o: in function `ImGuiListClipper::Begin(int, float)':
imgui.cpp:(.text+0x62a3): undefined reference to `ImGui::TableEndRow(ImGuiTable*)'
/usr/bin/ld: CMakeFiles/main.dir/imgui/imgui.cpp.o: in function `ImGuiListClipper_StepInternal(ImGuiListClipper*)':
imgui.cpp:(.text+0x66d3): undefined reference to `ImGui::TableEndRow(ImGuiTable*)'
[...]
Currently i'm only trying to build this in linux, so I can think on the windows/ mac versions later.
What am I doing wrong?
Okay, so as said by drescherjm I missed one file imgui/imgui_tables.cpp. After I add this line to the set on the CMakeLists.txt it worked.
I'm trying to refactor some classes from a large main file into separate header and cpp files and am getting undefined reference errors at link time.
I've got a project that looks like this:
├── CMakeLists.txt
├── data
│ └── ICING BE SI Data.csv
├── gcc
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── lib
│ ├── Makefile
│ ├── src
│ └── tmp
├── include
│ ├── Interpolator.hpp
│ ├── InverseCDFProcess.hpp
│ └── XYParser.hpp
├── lib
│ ├── CMakeLists.txt
│ ├── Interpolator.cpp
│ ├── InverseCDFProcess.cpp
│ └── XYParser.cpp
└── test
└── test_icing.cpp
The project has a few classes, Interpolator and InverseCDFProcess, which I recently moved from the main executable file, test_icing.cpp to their own .cpp and .hpp files, located within the lib and include directories, respectively.
Since the classes do depend on each other (InverseCDFProcess needs Interpolator, which in turn needs a function in XYParser.cpp), I decided to build them as static libraries that then get linked into the main executable at compile time.
They're built like so:
add_library(xyparser STATIC XYParser.cpp)
add_library(interpolator STATIC Interpolator.cpp)
add_library(inversecdf STATIC InverseCDFProcess.cpp)
I then link these libraries into my executable in the normal way:
include_directories(include)
link_directories(lib)
link_directories(include) # Do I need this?
add_executable(test_icing test/test_icing.cpp)
# ... some code adding an external library which works fine
target_link_libraries(test_icing inversecdf interpolator xyparser ${external_library_name})
This produces this link command:
/usr/bin/c++ CMakeFiles/test_icing.dir/test/test_icing.cpp.o -o test_icing -L/mnt/c/Users/foo/projects/chase-icing/lib -L/mnt/c/Users/foo/projects/chase-icing/include -L/mnt/c/Users/foo/projects/chas
e-icing/gcc/src/imtc-build/lib -Wl,-rpath,/mnt/c/Users/foo/projects/chase-icing/lib:/mnt/c/Users/foo/projects/chase-icing/include:/mnt/c/Users/foo/projects/chase-icing/gcc/src/imtc-build/lib lib/libinversecdf
.a lib/libinterpolator.a lib/libxyparser.a -limt
At this point the compilation stop with the error:
/mnt/c/Users/foo/projects/chase-icing/test/test_icing.cpp:(.text+0xcca): undefined reference to `Interpolator<double>::Interpolator(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > co
nst&)'
/mnt/c/Users/foo/projects/chase-icing/test/test_icing.cpp:(.text+0xd4c): undefined reference to `Interpolator<double>::set_bounds(std::pair<double, double> const&)'
/mnt/c/Users/foo/projects/chase-icing/test/test_icing.cpp:(.text+0xd99): undefined reference to `InverseCDFProcess<double>::InverseCDFProcess(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<
char> > const&)'
/mnt/c/Users/foo/projects/chase-icing/test/test_icing.cpp:(.text+0xdd9): undefined reference to `InverseCDFProcess<double>::generate()'
It doesn't matter if the libraries are built STATIC or SHARED. The undefined reference error still happens.
My question is this: am I missing some extern or similar in my class definitions or implementations? Why is this relatively straightforward refactoring resulting in undefined references? Is my link directory incorrect? Should it refer to build directories?
Any help is appreciated.
Gonna go ahead and answer this for future people:
The solution is embedded in the error messages:
/mnt/c/Users/foo/projects/chase-icing/test/test_icing.cpp:(.text+0xdd9): undefined reference to `InverseCDFProcess<double>::generate()'
This error shows that the classes are templated. The problem is that I placed the implementations of these templates in .cpp files, as seen here:
├── include
│ ├── Interpolator.hpp
│ ├── InverseCDFProcess.hpp
│ └── XYParser.hpp
├── lib
│ ├── CMakeLists.txt
│ ├── Interpolator.cpp
│ ├── InverseCDFProcess.cpp
│ └── XYParser.cpp
Templates need to contain the full implementation in the header files. So, the good news is that I don't need those libraries in the first place. Just #include "Interpolator.hpp etc and it should work as expected!
The reason templates need the implementation is seen here: Why can templates only be implemented in the header file?
I'm having a bit of trouble organizing my c++ project as a cmake project, which is something I'm new to. I'm trying to mimic the template given by this github repository. I think I'm almost there, but I'm getting a few linker errors when I try to compile with the following commands:
cd markets
rm -rf build/manual
mkdir build/manual
cd build/manual
cmake -G "Unix Makefiles" -D CMAKE_CXX_COMPILER=/usr/bin/g++-8 ../..
make
There's a lot of output, but here are the first few lines:
[100%] Linking CXX executable markets_tests
CMakeFiles/markets_tests.dir/src/test_data_handler.cpp.o: In function `Testtest_data_handler::RunImpl() const':
test_data_handler.cpp:(.text+0x12a): undefined reference to `Instrument::Instrument(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
test_data_handler.cpp:(.text+0x153): undefined reference to `Instrument::Instrument(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
test_data_handler.cpp:(.text+0x37f): undefined reference to `DataHandler::DataHandler(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, unsigned int const&, unsigned int const&)'
CMakeFiles/markets_tests.dir/src/test_data_reader.cpp.o: In function `Testtest_csv_reader::RunImpl() const':
test_data_reader.cpp:(.text+0xa9): undefined reference to `csv_reader(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned int)'
I guess this must mean that something is wrong with my markets/test/CMakeLists.txt file, which I have now as:
cmake_minimum_required(VERSION 3.10)
project(markets_tests)
find_package(UnitTest++ REQUIRED)
find_package(Eigen3 3.3 REQUIRED NO_MODULE)
SET(GCC_COVERAGE_COMPILE_FLAGS "-no-pie")
SET(CMAKE_CXX_FLAGS "${GCC_COVERAGE_COMPILE_FLAGS}")
include_directories(${MARKETS_HEADERS_DIR})
include_directories(${UTPP_INCLUDE_DIRS})
set(SOURCE_FILES main.cpp
src/test_data_handler.cpp
src/test_data_reader.cpp
src/test_exec_handler.cpp
src/test_fill.cpp
src/test_instrument.cpp
src/test_market_bar.cpp
src/test_market_snapshot.cpp
src/test_order.cpp
src/test_pnl_calculator.cpp
src/test_portfolio.cpp
src/test_position_summary.cpp)
add_executable(markets_tests ${SOURCE_FILES})
target_link_libraries(markets_tests markets Eigen3::Eigen UnitTest++) # added Eigen
install(TARGETS markets_tests DESTINATION bin)
Any ideas? Other questions seem to suggest that I could be linking things in the wrong order, but this project structure isn't that complicated (just an executable, a library, and some unit tests). This was compiling with my old makefile, so I doubt it's because I haven't defined some of the header definitions.
Here is the tree of my markets/ root directory if it helps:
.
├── bin
├── build
│ └── manual
├── CMakeLists.txt
├── docs
├── lib
├── README.md
├── src
│ ├── CMakeLists.txt
│ ├── main.cpp
│ └── markets
│ ├── CMakeLists.txt
│ ├── data_handlers.cpp
│ ├── data_handlers.h
│ ├── data_readers.cpp
│ ├── data_readers.h
│ ├── execution_handler.cpp
│ ├── execution_handler.h
│ ├── fill.cpp
│ ├── fill.h
│ ├── instrument.cpp
│ ├── instrument.h
│ ├── market_bar.cpp
│ ├── market_bar.h
│ ├── market_snapshot.cpp
│ ├── market_snapshot.h
│ ├── order.cpp
│ ├── order.h
│ ├── pnl_calculator.cpp
│ ├── pnl_calculator.h
│ ├── portfolio.cpp
│ ├── portfolio.h
│ ├── position_summary.cpp
│ └── position_summary.h
└── test
├── CMakeLists.txt
├── main.cpp
├── src
│ ├── test_data_handler.cpp
│ ├── test_data_reader.cpp
│ ├── test_exec_handler.cpp
│ ├── test_fill.cpp
│ ├── test_instrument.cpp
│ ├── test_market_bar.cpp
│ ├── test_market_snapshot.cpp
│ ├── test_order.cpp
│ ├── test_pnl_calculator.cpp
│ ├── test_portfolio.cpp
│ └── test_position_summary.cpp
└── test_data
├── QLD.csv
└── SPY.csv
Edit:
Here is src/markets/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(markets)### C CXX)
set(SOURCE_FILES
data_handlers.h
data_handlers.cpp
data_readers.h
data_readers.cpp
execution_handler.h
execution_handler.cpp
fill.h
fill.cpp
instrument.h
instrument.cpp
market_bar.h
market_bar.cpp
market_snapshot.h
market_snapshot.cpp
order.h
order.cpp
pnl_calculator.h
pnl_calculator.cpp
portfolio.h
portfolio.cpp
position_summary.h
position_summary.cpp
)
find_package (Eigen3 3.3 REQUIRED NO_MODULE)
add_library(markets SHARED STATIC ${SOURCE_FILES})
target_link_libraries(markets Eigen3::Eigen stdc++fs) #added this
install(TARGETS markets DESTINATION ${MARKETS_INSTALL_LIB_DIR})
install(FILES markets.h DESTINATION ${MARKETS_INSTALL_INCLUDE_DIR})
I'm trying to compile a project using CMake, but I'm getting undefined references to class functions that I have created. If I create a Makefile by hand, everything compiles fine. But when I use CMake, I'm getting undefined reference errors.
This is the directory structure:
.
├── build
├── CMakeLists.txt
├── info
│ ├── indexer.pdf
│ └── search.pdf
├── Makefile
├── sample
│ ├── 1
│ │ └── b.txt
│ ├── 2
│ │ └── c.txt
│ ├── 3
│ │ └── d.txt
│ └── a.txt
├── src
│ ├── cpp
│ │ ├── index.cpp
│ │ └── record.cpp
│ ├── h
│ │ ├── index.h
│ │ └── record.h
│ └── main.cpp
├── tests
│ └── a
└── todo.txt
CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
# Project Name
PROJECT(indexer CXX)
# Binary dir
# Built in CMake variables:
# CMAKE_SOURCE_DIR: the directory where cmake was executed
# CMAKE_BINARY_DIR: where the output will go
# EXECUTABLE_OUTPUT_PATH: common place to put executables if you don't want it to be CMAKE_BINARY_DIR
# LIBRARY_OUTPUT_PATH: common place to put libraries if you don't want it to be CMAKE_BINARY_DIR
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# Add additional compiler flags
# Built-ins:
# - CMAKE_CXX_FLAGS_DEBUG = -g
# - CMAKE_CXX_FLAGS_RELEASE = -O3 -NDEBUG
set(CMAKE_CXX_FLAGS "-std=c++0x -Wall")
include_directories("${PROJECT_SOURCE_DIR}/src/h")
# Aggregate the sources
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/cpp")
add_executable(indexer "${PROJECT_SOURCE_DIR}/src/main.cpp" ${SOURCES})``
Error:
CMakeFiles/indexer.dir/src/main.cpp.o: In function `parse(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, Index*)':
main.cpp:(.text+0x1b5): undefined reference to `Record::Record(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int)'
main.cpp:(.text+0x1ee): undefined reference to `Index::add(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, Record)'
CMakeFiles/indexer.dir/src/main.cpp.o: In function `main':
main.cpp:(.text+0x77d): undefined reference to `Index::Index()'
main.cpp:(.text+0x84b): undefined reference to `Index::print()'
collect2: error: ld returned 1 exit status
CMakeFiles/indexer.dir/build.make:94: recipe for target 'indexer' failed
make[2]: *** [indexer] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/indexer.dir/all' failed
make[1]: *** [CMakeFiles/indexer.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
Can someone tell me what I'm doing wrong here?
Your issue is with the line
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/cpp")
This will set the SOURCES variable to a file named cpp in the src directory. I think you meant something like
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/cpp/*.cpp")
This will catch all files that end with the extension .cpp, and put them into the SOURCES variable.
Note: I don't think you will need to pass "${PROJECT_SOURCE_DIR}/src/main.cpp" as an argument to create_executable, since your SOURCES variable should already contain the main.cpp.
I can generate my CMake project, but I can't build it.
I have a fairly straightforward project structure:
├── bin
├── build
├── CMakeLists.txt
├── include
│ └── project
│ ├── base_socket.h
│ ├── tcp_client.h
│ ├── tcp_server.h
│ ├── udp_client.h
│ └── udp_server.h
├── LICENSE.txt
├── README.md
└── src
└── project
├── base_socket.cpp
├── CMakeLists.txt
├── main.cpp
├── tcp_client.cpp
├── tcp_server.cpp
├── udp_client.cpp
└── udp_server.cpp
And a fairly straight forward CMakeLists.txt file in the top directory:
# Project initialization
cmake_minimum_required(VERSION 2.7.2 FATAL_ERROR)
project("cpp_sockets")
# Add include directory
include_directories(include)
# Add subdirectories
add_subdirectory(src/project)
And also a fairly simple one in src/project/CmakeLists.txt:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(HEADER_DIR ${CMAKE_SOURCE_DIR}/include/project)
set(HEADER_FILES
${HEADER_DIR}/base_socket.h
${HEADER_DIR}/tcp_client.h
${HEADER_DIR}/tcp_server.h
${HEADER_DIR}/udp_client.h
${HEADER_DIR}/udp_server.h
)
add_executable(cpp_sockets main.cpp ${HEADER_FILES})
When I cd into /build and type cmake .. it works fine.
However, when I follow that with make, I get many terrible errors about undefined reference.
main.cpp:(.text+0x2c6): undefined reference to `UDPClient::UDPClient(int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
main.cpp:(.text+0x2eb): undefined reference to `UDPClient::send_message(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
main.cpp:(.text+0x359): undefined reference to `UDPServer::UDPServer(int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
main.cpp:(.text+0x380): undefined reference to `UDPServer::socket_bind()'
main.cpp:(.text+0x38c): undefined reference to `UDPServer::listen()'
main.cpp:(.text+0x400): undefined reference to `TCPClient::TCPClient(int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
main.cpp:(.text+0x40c): undefined reference to `TCPClient::make_connection()'
main.cpp:(.text+0x42b): undefined reference to `TCPClient::send_message(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
main.cpp:(.text+0x492): undefined reference to `TCPServer::TCPServer(int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
main.cpp:(.text+0x4b9): undefined reference to `TCPServer::socket_bind()'
CMakeFiles/cpp_sockets.dir/main.cpp.o: In function `UDPClient::~UDPClient()':
main.cpp:(.text._ZN9UDPClientD2Ev[_ZN9UDPClientD5Ev]+0x14): undefined reference to `Socket::~Socket()'
CMakeFiles/cpp_sockets.dir/main.cpp.o: In function `UDPServer::~UDPServer()':
main.cpp:(.text._ZN9UDPServerD2Ev[_ZN9UDPServerD5Ev]+0x14): undefined reference to `Socket::~Socket()'
CMakeFiles/cpp_sockets.dir/main.cpp.o: In function `TCPClient::~TCPClient()':
main.cpp:(.text._ZN9TCPClientD2Ev[_ZN9TCPClientD5Ev]+0x14): undefined reference to `Socket::~Socket()'
CMakeFiles/cpp_sockets.dir/main.cpp.o: In function `TCPServer::~TCPServer()':
main.cpp:(.text._ZN9TCPServerD2Ev[_ZN9TCPServerD5Ev]+0x14): undefined reference to `Socket::~Socket()'
collect2: error: ld returned 1 exit status
src/project/CMakeFiles/cpp_sockets.dir/build.make:94: recipe for target '../bin/cpp_sockets' failed
make[2]: *** [../bin/cpp_sockets] Error 1
CMakeFiles/Makefile2:85: recipe for target 'src/project/CMakeFiles/cpp_sockets.dir/all' failed
make[1]: *** [src/project/CMakeFiles/cpp_sockets.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
main.cpp file looks like:
#include "project/udp_server.h"
#include "project/udp_client.h"
#include "project/tcp_client.h"
Why would the linker not be able to find the references like this?
It looks like your src/project/CMakeLists.txt neglects to specify the other cpp files, like udp_server.cpp and udp_client.cpp, as dependencies of the executable cpp_sockets. You need to do this in order for CMake to compile those source files; it won't automatically figure out to compile them just because you have included their corresponding headers. In fact, the add_executable declaration doesn't need to say anything about the headers, because the include_directories command takes care of that.
Given the header files that your main.cpp depends on, its add_executable line should look like this:
add_executable(cpp_sockets main.cpp udp_server.cpp udp_client.cpp tcp_client.cpp)