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?
Related
I am developing a module, which depends on another library(wasmtime). I put files into:
modules/mod_wasm/src/include - header files, and
modules/mod_mine/src/lib/libwasmtime.a - the compiled library.
The problem which I faced is that when I compile the acore server with
./acore.sh compiler all
it gives me the error:
[100%] Linking CXX executable worldserver
/usr/bin/ld: ../../../modules/libmodules.a(ModWasm.cpp.o): in function `readWasmFile(char const*)':
ModWasm.cpp:(.text+0x63): undefined reference to `wasm_byte_vec_new_uninitialized'
/usr/bin/ld: ModWasm.cpp:(.text+0xce): undefined reference to `wasm_byte_vec_delete'
The question is it required somehow add to a config that library? If yes, then how to do that?
I was testing my code in simple main.cpp file and it was working with options like "-L${workspaceFolder}/lib" and "-lwasmtime".
Maybe, these options are also required for my module?
Here is a link to azerothcore project which I use.
my module locates in modules/mod-wasm folder
azerothcore-wotlk/modules ‹master*› » tree -L 3 mod-wasm
mod-wasm
├── CMakeLists.txt
├── LICENSE
├── Makefile
├── README.md
├── conf
│ ├── conf.sh.dist
│ └── wasm.conf.dist
├── include.sh
├── mod-wasm.cmake
├── setup_git_commit_template.sh
├── src
│ ├── ModWasm.cpp
│ ├── include
│ │ ├── doc-wasm.h
│ │ ├── wasi.h
│ │ ├── wasm.h
│ │ ├── wasmtime
│ │ ├── wasmtime.h
│ │ └── wasmtime.hh
│ ├── lib
│ │ ├── libwasmtime.a
│ │ └── libwasmtime.so
│ └── wasm_loader.cpp
└── wasm_modules
└── rust_wasm_app.wasm
As I understood from the logs what I see and because CMakeList.txt exists in modules folder, the project considers the folder as module. Which in its turn scans subdirs for *.cmake files and configures the project.
The question now is how to properly configure my module to show that it contains the compiled library wasmtime inside src/lib folder?
As I understood, I could use target_link_libraries, but it requires a target name, and I have no idea what it should be and where I can take it.
At the end, I was able to find an answer with try and catch.
Azerothcore modules supports modname.cmake file to be run when configure libmodules.a which contains all extra modules(if I understood it correctly.
this is part of modules/CMakeFiles.txt
# Enables Devs to Include a cmake file in their module that will get run inline with the config.
foreach(SOURCE_MODULE ${MODULES_MODULE_LIST})
message("SOURCE_MODULE: ${SOURCE_MODULE}")
include("${CMAKE_SOURCE_DIR}/modules/${SOURCE_MODULE}/${SOURCE_MODULE}.cmake" OPTIONAL)
endforeach()
here I have my dirty cmake file which allow me to compile the server
set(WASM_MODULE_DIR ${CMAKE_SOURCE_DIR}/modules/${SOURCE_MODULE})
set(WASM_MODULE_SRC_DIR ${WASM_MODULE_DIR}/src)
message("--------------------->>>>> APPLICATION_NAME : ${APPLICATION_NAME}")
message("--------------------->>>>> APP_PROJECT_NAME : ${APP_PROJECT_NAME}")
message("--------------------->>>>> SOURCE_MODULE : ${SOURCE_MODULE}")
message("--------------------->>>>> WASM_MODULE_DIR : ${WASM_MODULE_DIR}")
message("--------------------->>>>> WASM_MODULE_SRC_DIR : ${WASM_MODULE_SRC_DIR}")
# include wasmtime
target_include_directories(modules PUBLIC ${WASM_MODULE_SRC_DIR}/include)
target_link_directories(modules PUBLIC ${WASM_MODULE_SRC_DIR}/lib)
find_library(LIBWASMTIME_TO_INCLUDE NAMES wasmtime PATHS ${WASM_MODULE_SRC_DIR}/lib REQUIRED)
message("--------------------->>>>>>>>> LIBWASMTIME_TO_INCLUDE: ${LIBWASMTIME_TO_INCLUDE}")
target_link_libraries(modules PUBLIC wasmtime)
So, it compiles now.
But I have next problem, which I am trying to resolve. but this is another story.
Thank you all for the help
my goal is to create libraries like client and generator and use them in src/main.cpp, but sometimes these libraries depend each other.
In this case: client/User.hpp uses generator/IdGenerator.hpp
Project
│
├── CMakeLists.txt
├── libs
│ ├── CMakeLists.txt
│ ├── client
│ │ ├── CMakeLists.txt
│ │ ├── User.cpp
│ │ └── User.hpp
│ └── generator
│ ├── CMakeLists.txt
│ ├── IdGenerator.cpp
│ ├── IdGenerator.hpp
│ └── Types.hpp
└── src
└── main.cpp
Project/CMakeLists.txt:
cmake_minimum_required (VERSION 3.8)
project(game-project VERSION 0.1.0)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_FLAGS "-Wall -Wextra -O0 -std=c++20")
add_executable (game src/main.cpp)
add_subdirectory(libs)
target_link_libraries(game libclient libgenerator)
libs/CMakeLists.txt:
add_subdirectory(generator)
add_subdirectory(client)
libs/client/CMakeLists.txt:
add_library(libclient STATIC
User.cpp
User.hpp
)
include_directories(generator/)
target_link_libraries(libclient libgenerator)
libs/generator/CMakeLists.txt:
add_library(libgenerator STATIC
IdGenerator.cpp
IdGenerator.hpp
Types.hpp
)
C++ files:
main.cpp:
#include <client/User.hpp>
int main(int argc, const char* argv[])
{
User user;
return 0;
}
client/User.hpp:
#pragma once
#include <generator/IdGenerator.hpp>
class User
{
Identifier id = IdGenerator::generateId();
};
When I run make after cmake, I get this error:
In file included from Project/libs/client/User.cpp:1:
Project/libs/client/User.hpp:3:10: fatal error: generator/IdGenerator.hpp: No such file or directory
3 | #include <generator/IdGenerator.hpp>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
Sorry for the verbose summary, I was able to shorten the example this much. What do you think the problem is?
This question might seem like a duplicate of CMake libraries that depend on each other but as you can see I'm already applying the
include_directories(generator/).
I misunderstood what the problem was initially, but now I think this might help.
Try adding to the CMakeLists.txt of your client instead of
include_directories(generator/)
this command
target_include_directories(libclient PUBLIC <Path to your generator file>)
Maybe you need to experiment a little to get the path correct as you might need to either specify it from the root directory or the current one (where this CMakeLists.txt resides), but it might solve your problem.
I have a given project structure
.
├── CMakeLists.txt
├── lib
│ ├── lodepng
│ │ ├── CMakeLists.txt
│ │ └── src
│ │ ├── lodepng.cpp
│ │ └── lodepng.h
│ └── pixel_reader
│ ├── CMakeLists.txt
│ └── src
│ ├── hello.cpp
│ └── hello.h
├── main.cpp
With the following CMakeLists
./CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(pov_system VERSION 1.0)
add_subdirectory(lib/lodepng)
add_subdirectory(lib/pixel_reader)
add_executable(pov_system main.cpp)
target_link_libraries(pixel_reader PRIVATE lodepng)
target_link_libraries(pov_system PRIVATE pixel_reader)
./lodepng/CMakeLists.txt
add_library(
lodepng
src/lodepng.cpp
src/lodepng.h
)
target_include_directories(lodepng PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
./pixel_reader/CMakeLists.txt
add_library(
pixel_reader SHARED
src/hello.cpp
src/hello.h
)
target_include_directories(pixel_reader PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
As one can see, I try to link the 'lodepng' library to the 'pixel_reader' library and include the 'lodepng.h' to the 'hello.h' file.
But at the moment I get the following error while trying to build the project.
[build] <path-to-project>/pov_system/lib/pixel_reader/src/hello.h:2:10: fatal error: lodepng.h: No such file or directory
[build] 2 | #include "lodepng.h"
[build] | ^~~~~~~~~~~
[build] compilation terminated.
Question
Why is my code not finding the 'lodepng.h' file or (and even more important) is it a good practice to link from one library to another?
Maybe two really simple questions, but just started to dive into the world of CMake, Compiling, etc... and I really appreciate your help.
Why is my code not finding the 'lodepng.h' file or (and even more important)
Because you probably didn't give it correct path. One way to fix that would be to give the exact path in hello.h
#include "../../lodepng/src/lodepng.h
Second way is to use target_include_directories:
target_include_directories(pixel_reader PUBLIC "../../lodepng/src/")
is it a good practice to link from one library to another?
It depends on your project. If library A requires library B, then yes, it is okay in my opinion.
More importantly, you are creating the target in the wrong place i.e., in the root CMakeLists file. It must be done in the directory in which target is created.
./pixel_reader/CMakeLists.txt
# create target
add_library(
pixel_reader SHARED
src/hello.cpp
src/hello.h
)
target_link_libraries(pixel_reader PRIVATE lodepng) #link library where target is created
target_include_directories(pixel_reader PUBLIC "../../lodepng/src/")
target_include_directories(pixel_reader PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
Your pixel_reader library target possibly needs lodepng.h header to compile, because it depends on it.
something like
target_include_directories(pixel_reader PUBLIC "PATH_TO_LODE_PNG_HEADER_DIRECTORY")
could solve this problem.
I'm doing Advent Of Code and have each day's project in one Swift package so I can easily share pieces between them. Each day produces its own executable, day01, day02, and so on.
My package is laid out like so:
$ tree
.
├── Package.swift
├── Sources
│ ├── SomethingShared
│ │ └── Shared.swift
│ ├── day01
│ │ └── main.swift
│ └── day02
│ └── main.swift
└── Tests
├── SomethingSharedTests
│ └── SharedTest.swift
├── day01Tests
│ └── day01Test.swift
└── day02Tests
└── day02Test.swift
This builds fine, but the tests won't link with more than one main.swift.
$ swift test
Compile Swift Module 'SomethingShared' (1 sources)
Compile Swift Module 'day02Tests' (1 sources)
Compile Swift Module 'day01Tests' (1 sources)
Linking ./.build/debug/day01
Linking ./.build/debug/day02
Compile Swift Module 'SomethingSharedTests' (1 sources)
Linking ./.build/debug/AdventOfCode2016PackageTests.xctest/Contents/MacOS/AdventOfCode2016PackageTests
duplicate symbol _main in:
/Users/schwern/tmp/foo/.build/debug/day01.build/main.swift.o
/Users/schwern/tmp/foo/.build/debug/day02.build/main.swift.o
ld: 1 duplicate symbol for architecture x86_64
<unknown>:0: error: link command failed with exit code 1 (use -v to see invocation)
<unknown>:0: error: build had 1 command failures
error: exit(1): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f /Users/schwern/tmp/foo/.build/debug.yaml test
It seems like it's trying to link the main functions into one library.
How can I prevent this? Alternatively, is there a better way to accomplish this?
Here is a zip of the test project if you want to look at the contents, there isn't much there. Here is my real project if you'd like to see what I'm doing.
I'm trying to use Libclang to programatically analyse the Opencv library, but when I try to import the main header from Opencv opencv.hpp, libclang won't follow the path.
Previously, it was reading everything quite beautifully, but then I figured it was following my $PATH's headers, and I want it to follow these specifics ones.
opencv.hpp is a file containing lots of #include statements like so:
#include "core/core_c.h"
#include "core/core.hpp"
#include "flann/miniflann.hpp"
// ... and so on
but, when I try to open it with libclang, or either clang ./opencv.hpp, it won't follow:
clang ./Header_Example/opencv.hpp
./Header_Example/opencv.hpp:46:10: fatal error: 'core/core_c.h' file not found
#include "core/core_c.h"
^
1 error generated.
but I'm sure it is on the right directory (a bit of my tree output):
── Header_Example
│ ├── opencv.hpp
│ ├── opencv2
│ │ ├── # more directories
│ │ ├── core
│ │ │ ├── affine.hpp
│ │ │ ├── core.hpp
│ │ │ ├── core_c.h
│ │ │ ├── types_c.h
│ │ │ ├── version.hpp
│ │ │ └── wimage.hpp
maybe I'm not using the right clang parameters?
A bit of context: I want to analyse Opencv types, classes and functions, this info is present on the headers, so I don't think I would need the full library to read the code. When I tried to use the full library I found myself in trouble. I need to use the iOS compilation(?)version(?) of the library, hence I copy/pasted the headers from the compiled version into this working directory (sorry? :) )
EDIT 1: It may seem odd my directories having this opencv2/, but if I remove the headers from inside of it, clang will complain 'bout why aren't they there: fatal error: 'opencv2/core/types_c.h' file not found
I simply had to add -I. before adding -I./opencv2 because I had one file one the same directory as opencv.hpp. Gosh I feel so stupid for missing this.