OS: Windows10
Editor: VScode
Build: CMake
Compiler: GCC(actually MinGW)
ImGui branch: master (I didn't use the release tag branch), GLFW+OpenGL3 implementations
Here is the situation, I'm rewriting my learnOpenGL project with some more features added to it, like using yaml to do the initialization config. Now I want to add a visual config window when I run my opengl demo, so I could adjust colors or shader options or whatever.
I choose ImGui and followed many tutorials from other sites, when I add ImGui cpp files and compile them into a static library, the imgui_gui.cpp(not only this one) can't find any definitions of functions it contains(nearly all).
Below is the structure of my demo's workspace(simplified):
I:.
├───.vscode
├───CMakeLists.txt
├───app
│ ├───CMakeLists.txt
│ └───ImGui_test.cpp
├───bin
├───build
├───config
├───images
├───include
│ ├───glad
│ ├───GLFW
│ ├───glm
│ ├───ImGui
│ │ ├───imconfig.h
│ │ ├───imgui.h
│ │ ├───imgui_impl_glfw.h
│ │ ├───imgui_impl_opengl3.h
│ │ ├───imgui_impl_opengl3_loader.h
│ │ ├───imgui_internal.h
│ │ ├───imstb_rectpack.h
│ │ ├───imstb_textedit.h
│ │ └───imstb_truetype.h
│ ├───KHR
│ ├───myImplement
│ ├───stb_image
│ └───yaml-cpp
├───lib
│ ├───libglfw3.a
│ ├───libimgui.a
│ ├───libmysrc.a
│ └───libyaml-cpp.dll
├───model
├───shader
│ ├───shader_fragment
│ └───shader_vertex
└───src
├───CMakeLists.txt
├───config.cpp
├───glad.c
├───shader.cpp
├───stb_image.cpp
└───ImGui
├───imgui.cpp
├───imgui_demo.cpp
├───imgui_draw.cpp
├───imgui_impl_glfw.cpp
├───imgui_impl_opengl3.cpp
└───imgui_widgets.cpp
The problem is, when I compile other opengl demos, this build system works fine, all static and dynamic libraries can be found and linked properly(glfw, yaml, opengl32, and the lib made of cpp files from the 'src' dir except for the cpp files in the 'ImGui' dir), but adding libimgui.a seems not providing correct definitions for demos that contains any imgui function.
// a piece of error in cmd
// undefined reference of imgui.cpp(part of)
I:/VScode_projects/shader_toy/src/ImGui/imgui.cpp:2557: undefined reference to `ImGui::TableEndRow(ImGuiTable*)'
../../lib/libimgui.a(imgui.cpp.obj): In function `ImGuiListClipper::Begin(int, float)':
I:/VScode_projects/shader_toy/src/ImGui/imgui.cpp:2595: undefined reference to `ImGui::TableEndRow(ImGuiTable*)'
../../lib/libimgui.a(imgui.cpp.obj): In function `ImGuiListClipper::Step()':
I:/VScode_projects/shader_toy/src/ImGui/imgui.cpp:2652: undefined reference to `ImGui::TableEndRow(ImGuiTable*)'
../../lib/libimgui.a(imgui.cpp.obj): In function `ImGui::GcCompactTransientMiscBuffers()':
I:/VScode_projects/shader_toy/src/ImGui/imgui.cpp:3357: undefined reference to `ImGui::TableGcCompactSettings()'
../../lib/libimgui.a(imgui.cpp.obj): In function `ImGui::NewFrame()':
I:/VScode_projects/shader_toy/src/ImGui/imgui.cpp:4516: undefined reference to `ImGui::TableGcCompactTransientBuffers(ImGuiTable*)'
I:/VScode_projects/shader_toy/src/ImGui/imgui.cpp:4519: undefined reference to `ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData*)'
../../lib/libimgui.a(imgui.cpp.obj): In function `ImGui::Initialize()':
// undefined reference in imgui_demo.cpp(part of)
../../lib/libimgui.a(imgui_demo.cpp.obj): In function `ShowPlaceholderObject':
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7096: undefined reference to `ImGui::TableNextRow(int, float)'
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7097: undefined reference to `ImGui::TableSetColumnIndex(int)'
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7100: undefined reference to `ImGui::TableSetColumnIndex(int)'
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7116: undefined reference to `ImGui::TableNextRow(int, float)'
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7117: undefined reference to `ImGui::TableSetColumnIndex(int)'
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7122: undefined reference to `ImGui::TableSetColumnIndex(int)'
I:/VScode_projects/shader_toy/src/ImGui/imgui_demo.cpp:7128: undefined reference to `ImGui::NextColumn()'
Also, three CMakeLists.txt in root directory, src and app:
// CMakeLists.txt in root directory
CMAKE_MINIMUM_REQUIRED(VERSION 3.19)
PROJECT(shader_toy)
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_STANDARD 14)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/include)
LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/lib)
SET(GL_LIB opengl32)
OPTION(DEBUG "debug switch" OFF)
OPTION(GEN_SHARED_LIB "generate shared lib .so" OFF)
OPTION(GEN_STATIC_LIB "generate static lib .a" ON)
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src)
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/app)
//CMakeLists.txt in src directory
AUX_SOURCE_DIRECTORY(. SRC_LIST)
AUX_SOURCE_DIRECTORY(ImGui IMGUI_LIST)
IF(DEBUG)
ADD_DEFINITIONS(-DDEBUG)
ENDIF()
IF(SRC_LIST)
ADD_LIBRARY(mysrc STATIC ${SRC_LIST})
ENDIF()
IF(IMGUI_LIST)
ADD_LIBRARY(imgui STATIC ${IMGUI_LIST})
ENDIF()
MESSAGE("following source files will be linked into library:")
MESSAGE("------------------- SRC LIST -------------------")
FOREACH(src ${SRC_LIST})
MESSAGE("${src}")
ENDFOREACH()
MESSAGE("------------------- LIST END -------------------")
// CMakeLists.txt in app directory
AUX_SOURCE_DIRECTORY(. APP_LIST)
IF (DEBUG)
ADD_DEFINITIONS(-DDEBUG)
MESSAGE("DEBUG set to ON")
ENDIF()
FILE(GLOB LIB_LIST ${PROJECT_SOURCE_DIR}/lib/*.dll ${PROJECT_SOURCE_DIR}/lib/*.a)
IF (LIB_LIST)
FOREACH (lib ${LIB_LIST})
MESSAGE("lib found: ${lib}")
ENDFOREACH ()
ENDIF()
FOREACH (app ${APP_LIST})
GET_FILENAME_COMPONENT(output ${app} NAME_WE)
ADD_EXECUTABLE(${output} ${app})
IF (LIB_LIST)
FOREACH (lib ${LIB_LIST})
GET_FILENAME_COMPONENT(lib_name ${lib} NAME_WE)
GET_FILENAME_COMPONENT(lib_post ${lib} LAST_EXT)
IF (NOT lib_post)
MESSAGE(FATAL_ERROR "lib format not recognized\n")
ENDIF()
IF (NOT(lib_post STREQUAL ".a" OR lib_post STREQUAL ".so" OR lib_post STREQUAL ".lib" OR lib_post STREQUAL ".dll"))
MESSAGE(FATAL_ERROR "lib format not recognized: ${lib_post}\n")
ENDIF()
STRING(SUBSTRING ${lib_name} 3 -1 lib_name_without_prefix)
MESSAGE("${output} will be linked with: ${lib_name_without_prefix}")
TARGET_LINK_LIBRARIES(${output} ${lib_name_without_prefix})
ENDFOREACH()
MESSAGE("${output} will be linked with: ${GL_LIB}")
TARGET_LINK_LIBRARIES(${output} ${GL_LIB})
ENDIF()
ENDFOREACH()
I know 'undefined reference' is a common linking error in cpp projects, I have fixed many similar problems in my previous cpp projects, but I can't figure out what goes wrong in this one, actually, I have used global search(Ctrl+Shift+f) on these undefined reference functions in VScode, they do don't have formal definitions in any file! All references in search results is the usage or declaration, but no definitions! I don't understand, may be definitions will be generated in compiling???
Will it be the problem in:
wrong platform
missed steps in compiling / incorrect compile methods
wrong OpenGL version(as I don't know which version the opengl32.lib from Windows10 implements)
wrong repo, I should download the release tag instead of master branch
missing definition files, I forget to copy them into my workspace
If you have used the ImGui library, any advice would be appreciate! Thank you!
Related
I'm having a little trouble when compiling a project, using Conan.io and CMake.
I'm building a small OpenGL-based project. I use an MVC architecture. I want CMake to produce two distinct .exe :
main.exe being a small GLFW window with a simple OpenGL context. It builds and works totally well, using conan.io to manage the libs used.
pentest.exe being a simple test executable which I want to use to test some basics functions from my model. This one won't be compiled when I call the make command.
Here my simplified project architecture :
├───build
│ ├───.cmake
│ │
│ ├───bin
│ │ └─── // .exe files are here
│ │
│ ├───CMakeFiles
│ │ └─── // Cmake files are here
│ │
│ └─── // ...
│
├───include
│ ├───GL
│ │ └─── GLU.h
│ │
│ └───model
│ ├───Block.hpp
│ └───GameGrid.hpp
│
├───src
│ ├───model
│ │ ├───Block.hpp
│ │ └───GameGrid.hpp
│ │
│ ├───main.cpp
│ └───pentest.cpp
│
├───CMakeLists.txt
└───conanfile.txt
Please note that :
pentest.cpp doesn't rely on any external libs.
Even though my GameGrid class is a template class, I made the header include the implementation file at the end (following this StackOverflow question).
I'm very, very bad with CMake.
CMake command is doing very well, the errors are occuring when the make command is calling the linker for pentest.exe.
Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 2.8.12)
project(TheEndless)
add_definitions("-std=c++17")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/model
)
link_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(main src/main.cpp)
add_executable(pentest src/pentest.cpp)
target_link_libraries(main ${CONAN_LIBS})
target_link_libraries(pentest ${CONAN_LIBS})
Here is my pentest.cpp :
#include <iostream>
#include <string>
#include "model/Block.hpp"
#include "model/GameGrid.hpp"
int main(int argc, char const *argv[]) {
theendless::model::Block b;
theendless::model::GameGrid<1, 1> g;
g(0, 0) = b;
std::string s(g(0, 0).get_name());
std::cout << s << std::endl;
return 0;
}
Here is my model/Block.hpp :
#ifndef THEENDLESS_MODEL_BLOCK_HPP
#define THEENDLESS_MODEL_BLOCK_HPP
#include <string>
namespace theendless::model {
class Block {
private:
std::string name;
public:
Block();
Block(std::string name);
std::string get_name() const;
void set_name(const std::string newName);
};
}
#endif
Here is my model/Block.cpp:
#include "model/Block.hpp"
#include <string>
namespace theendless::model {
Block::Block() : name("default_name") {}
Block::Block(std::string name) : name(name) {}
std::string Block::get_name() const { return this->name; }
void Block::set_name(const std::string newName) { this->name = newName; }
}
Here is the errors that are shown by make :
PS C:\projects\TheEndless\build> make
Scanning dependencies of target pentest
[ 75%] Building CXX object CMakeFiles/pentest.dir/src/pentest.cpp.obj
[100%] Linking CXX executable bin/pentest.exe
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/pentest.dir/objects.a(pentest.cpp.obj): in function `main':
C:/projects/TheEndless/src/pentest.cpp:9: undefined reference to `theendless::model::Block::Block()'
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/projects/TheEndless/src/pentest.cpp:13: undefined reference to `theendless::model::Block::get_name[abi:cxx1c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/pentest.dir/objects.a(pentest.cpp.obj): in function `std::array<theendless::model::Block, 1ull>::array()':
c:/mingw/include/c++/9.2.0/array:94: undefined reference to `theendless::model::Block::Block()'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/pentest.dir/build.make:107: bin/pentest.exe] Error 1
make[1]: *** [CMakeFiles/Makefile2:124: CMakeFiles/pentest.dir/all] Error 2
make: *** [Makefile:103: all] Error 2
Note that including model/Block.cpp to pentest.cpp makes the problem disappear, but I kinda want to make the project be clean, so I would like not to do this.
Additional infos :
I'm on windows 10.
I'm using VSCode to edit my files, and I compile by typing make in the integrated *PowerShell terminal.
I'm using a Mingw distro to compile.
Any help would be greatly appreciated ! :)
I'm not a CMake or compiler expert, but here's how I understand what's going on.
The compiler does not search around for any headers or source files unless it is told that it has to. But when does the compiler have to search for them?
The file (usually headers) are included in a file that the compiler already knows about.
The file has explicitly been made known to the compiler (e.g. inside a CMakeLists.txt).
In your case, the compiler knows about the header file, because it was #included inside the pentest.cpp source file (variant 1. from above). And how did the compiler know about pentest.cpp? It was explicitly stated inside the function add_executable that the specific build target pentest is built from this file.
Now what about Block.cpp? It was not known to the compiler because it neither was included nor stated inside the CMakeLists.txt, that the compiler has to use this file. So the compiler cannot know about it.
As you already mentioned, including the .cpp file is not a good style. One huge advantage (in my opinion) of only including header files is that if the implementation of a function changes (not its declaration, but the body of the function) you don't have to recompile everything where it's used. You just have to recompile that one .cpp file.
So what's the solution? You have to make the compiler be aware of all the source files that should be used to build your target pentest. Therefore, you have to add those source files to the function add_executable. The CMake docs for add_executable tell you the following.
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
Adds an executable target called to be built from the source files listed in the command invocation.
So you have to add all the source files which shall be used for building your target to the same add_executable command invocation. In your case, the CMakeLists.txt would look like this. Note that I had to add target_compile_features and remove add_compile_definitions on my machine in order to enforce the usage of C++17.
cmake_minimum_required(VERSION 2.8.12)
project(TheEndless)
# add_definitions("-std=c++17") # does not work on my Windows 10 machine
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/model
)
link_directories(${CMAKE_SOURCE_DIR}/lib)
add_executable(main src/main.cpp)
# add all source files needed to build to the executable
# GameGrid.cpp is not needed because it is included in the header.
add_executable(pentest src/pentest.cpp src/model/Block.cpp)
target_link_libraries(main ${CONAN_LIBS})
target_link_libraries(pentest ${CONAN_LIBS})
target_compile_features(pentest PRIVATE cxx_std_17) # added to really use C++17, see the docs for explanation
Only "problem" I saw: Visual Studio marked Block.hpp, GameGrid.hpp, and GameGrid.cpp as "external dependencies". If you want them to be shown as part of your target, you also may add them to add_executable. I'm not sure if there is another solution as it seems to be a little bit redundant.
I have a problem building my Qt5 project via cmake.
I run the command cmake .. && make from the directory build and I receive the following error:
/usr/bin/ld: cannot find -lengine-lib
collect2: error: ld returned 1 exit status
make[2]: *** [src/CMakeFiles/blacklist-engine-cli.dir/build.make:102: src/blacklist-engine-cli] Error 1
make[1]: *** [CMakeFiles/Makefile2:117: src/CMakeFiles/blacklist-engine-cli.dir/all] Error 2
make: *** [Makefile:84: all] Error 2
I have searched this topic briefly, however when I ran this project without Qt5Sql, using only Qt5Core I have no problem at all building the project. In order to build the project without Qt5Sql I just have to remove the db folder, and delete lines referring to that in my other CMakeLists.txt files. My question is:
Why does it work if I want to include only Qt5Core, and why does it not work when I also include Qt5Sql? What am I doing wrong including Qt5Sql?
Please do not include answers related to QtCreator, or Qt installation errors. I have checked my Qt installation folder, and I have Qt5Core and Qt5Sql on the same directory level installed.
I am using Ubuntu 20.04, cmake version 3.16.3, Qt version 5.12.8
ls /usr/lib/x86_64-linux-gnu/cmake
Qt5 Qt5Core Qt5Gui Qt5OpenGL Qt5PrintSupport Qt5Test Qt5Xml
Qt5Concurrent Qt5DBus Qt5Network Qt5OpenGLExtensions Qt5Sql Qt5Widgets
I have the following structure in my project:
root
├── CMakeModules
│ └── Qt.cmake
├── build
├── src
│ ├── db
│ │ ├── dbmanager.cpp
│ │ ├── dbmanager.h
│ │ └── CMakeLists.txt
│ ├── engine
│ │ ├── scanner.cpp
│ │ ├── scanner.h
│ │ └── CMakeLists.txt
│ ├── CMakeLists.txt
│ └── main.cpp
└── CMakeLists.txt
CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(blacklist-engine)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules)
add_subdirectory(src)
CMakeModules/Qt.cmake:
set(CMAKE_AUTOMOC ON)
find_package(Qt5 REQUIRED COMPONENTS Core Sql)
src/CMakeLists.txt:
include(Qt)
add_subdirectory(
db
engine
)
add_executable(blacklist-engine-cli main.cpp)
target_link_libraries(
blacklist-engine-cli
Qt5::Core
Qt5::Sql
engine-lib
db-lib
)
src/main.cpp:
#include <QtCore>
#include "db/dbmanager.h"
#include "engine/scanner.h"
...
src/db/CMakeLists.txt (updated):
set (db-lib-source
dbmanager.h
dbmanager.cpp
)
add_library(db-lib ${db-lib-source})
target_link_libraries(
db-lib
Qt5::Sql
)
src/db/dbmanager.h:
#include <QtSql/QSqlDatabase>
...
src/db/dbmanager.cpp:
#include "dbmanager.h"
#include <QtSql/QSqlQuery>
...
src/engine/CMakeLists.txt:
set(engine-lib-source
scanner.h
scanner.cpp
)
add_library(engine-lib ${engine-lib-source})
src/engine/scanner.h:
#include <QtCore>
...
src/engine/scanner.cpp:
#include "scanner.h"
...
The reason for the error is because engine-lib is never built, and its CMake file is never even processed. The offending line in your CMake file is this one:
add_subdirectory(
db
engine
)
When using add_subdirectory in this manner, the second argument becomes the binary directory for the generated content related to db. As a result, you may notice that CMake placed some build artifacts in your src/engine directory, which is probably not what you want.
To fix this, you must call add_subdirectory consecutive times for including multiple sub-directories.
add_subdirectory(db)
add_subdirectory(engine)
I have an imported library that I am trying to link using CMake. However, the library always shows up empty (or not found). I know that imported libraries have different visibility rules (according to this thread) than the standard libraries, but no matter what I try the library always ends up missing. To start, here's my folder structure:
Root
├── build
├── cmake
├── src
│ ├── external
│ │ ├── openvdb
│ │ │ ├── openvdb
│ │ │ ├── cmake
│ │ │ ├── CMakeLists.txt
│ │ ├── tbb
│ │ ├── zlib
│ │ ├── CMakeLists.txt
│ ├── CMakeLists.txt
├── CMakeLists.txt
In my Root CMakeLists.txt file I add the libraries called TBB_LIBS and TBBMALLOC_LIBS like this:
cmake_minimum_required(VERSION 3.2.2 FATAL_ERROR)
include(ExternalProject)
project(openVDBBuild)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (WIN32)
set(CMAKE_CXX_FLAGS "-bigobj /EHsc")
endif ()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
if(NOT CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX debug)
endif()
if(NOT CMAKE_RELEASE_POSTFIX)
set(CMAKE_RELEASE_POSTFIX release)
endif()
set(BUILD_VARIANTS "release debug")
set(TBB_PREFIX "tbb")
set(TBB_LIBS_DIR "${CMAKE_SOURCE_DIR}/src/external/tbb/build/${TBB_PREFIX}_")
set(TBB_ROOT ${CMAKE_SOURCE_DIR}/src/external/tbb)
set(TBB_INCLUDE_DIR ${TBB_ROOT}/include)
#********** Here I create the library and add the Global Flag ***********
add_library(TBBMALLOC_LIBS SHARED IMPORTED GLOBAL)
add_library(TBB_LIBS SHARED IMPORTED GLOBAL)
if (WIN32)
#********** Here I set different paths for debug and release versions of the library ***********
set_property(TARGET TBBMALLOC_LIBS PROPERTY IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/lib/tbbmalloc.dll )
set_property(TARGET TBBMALLOC_LIBS PROPERTY IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/lib/tbbmalloc_${CMAKE_DEBUG_POSTFIX}.dll )
set_property(TARGET TBB_LIBS PROPERTY IMPORTED_LOCATION_RELEASE ${CMAKE_BINARY_DIR}/lib/tbb.dll )
set_property(TARGET TBB_LIBS PROPERTY IMPORTED_LOCATION_DEBUG ${CMAKE_BINARY_DIR}/lib/tbb_${CMAKE_DEBUG_POSTFIX}.dll )
add_custom_command(
TARGET ProjectTbbExternal
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "$<$<CONFIG:debug>:${TBB_LIBS_DIR}${CMAKE_DEBUG_POSTFIX}/tbb_${CMAKE_DEBUG_POSTFIX}.dll>$<$<CONFIG:release>:${TBB_LIBS_DIR}${CMAKE_RELEASE_POSTFIX}/tbb.dll>" "${CMAKE_BINARY_DIR}/lib"
COMMAND ${CMAKE_COMMAND} -E copy "$<$<CONFIG:debug>:${TBB_LIBS_DIR}${CMAKE_DEBUG_POSTFIX}/tbbmalloc_${CMAKE_DEBUG_POSTFIX}.dll>$<$<CONFIG:release>:${TBB_LIBS_DIR}${CMAKE_RELEASE_POSTFIX}/tbbmalloc.dll>" "${CMAKE_BINARY_DIR}/lib"
COMMAND ${CMAKE_COMMAND} -E copy "$<$<CONFIG:debug>:${TBB_LIBS_DIR}${CMAKE_DEBUG_POSTFIX}/tbbmalloc_proxy_${CMAKE_DEBUG_POSTFIX}.dll>$<$<CONFIG:release>:${TBB_LIBS_DIR}${CMAKE_RELEASE_POSTFIX}/tbbmalloc_proxy.dll>" "${CMAKE_BINARY_DIR}/lib"
)
endif ()
add_subdirectory("src")
Now, down inside the CMakeLists.txt in the src/external/openvdb directory I try to link the libraries like this:
project(OpenVDBCore)
#define OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER 4
#define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER 0
#define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER 0
#*********** I'm going to omit some lines here since **********
#*********** they're standard for the OpenVDB build ***********
SET_SOURCE_FILES_PROPERTIES ( ${OPENVDB_LIBRARY_SOURCE_FILES}
PROPERTIES
COMPILE_FLAGS "-DOPENVDB_PRIVATE -DOPENVDB_USE_BLOSC ${OPENVDB_USE_GLFW_FLAG}"
)
ADD_LIBRARY ( openvdb_static STATIC ${OPENVDB_LIBRARY_SOURCE_FILES} )
ADD_LIBRARY ( openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES} )
TARGET_LINK_LIBRARIES ( openvdb_static
zlib
TBB_LIBS
TBBMALLOC_LIBS
)
TARGET_LINK_LIBRARIES ( openvdb_shared
zlib
TBB_LIBS
TBBMALLOC_LIBS
)
MESSAGE(STATUS "TBB_LIBS library location is ${TBB_LIBS}")
MESSAGE(STATUS "TBBMALLOC_LIBS library location is ${TBBMALLOC_LIBS}")
You can see that I put a status message in CMake to see what the ${TBB_LIBS} and ${TBBMALLOC_LIBS} variables are... but that part is always empty. Accordingly, the openvdb_shared and openvdb_static project files that get generated by CMake also do not link the libraries correctly (since those libraries are unknown). Does anyone know why this occurs (and how to fix it)?
First: I'm a total newbie in using cmake especially with Qt5. So my explanations might sound a little weird to you, but I'll try my best.
At the moment I'm about to set up my project using cmake, Qt5 and mingw.
My project structure is the following:
Cmake (my project name)
├── headers
│ ├── Controller
│ │ └── Controller.h
│ └── IUI
│ └── mainwindow.h
└── source
├── Controller
│ └── Controller.cpp
│ └── cmakelists.txt
└── IUI
├── main.cpp
├── mainwindow.cpp
└── mainwindow.ui
└── cmakelists.txt
This is the cmakelists.txt located in source/IUI
cmake_minimum_required(VERSION 2.8.11)
project(CellAnalyser)
find_package (Qt5Widgets)
set (CellAnalyserLib_src ${PROJECT_SOURCE_DIR}/source/IUI/mainwindow.cpp)
set (CellAnalyserLib_hdr ${PROJECT_SOURCE_DIR}/header/IUI/mainwindow.h)
set (CellAnalyserLib_ui ${PROJECT_SOURCE_DIR}/source/IUI/mainwindow.ui)
set (CellAnalyserBin_src ${PROJECT_SOURCE_DIR}/source/IUI/main.cpp)
set (Source ${CellAnalyserLib_src} ${CellAnalyserBin_src})
qt5_wrap_cpp(CellAnalyserLib_hdr_moc ${CellAnalyserLib_hdr})
qt5_wrap_ui (CellAnalyserLib_ui_moc ${CellAnalyserLib_ui})
include_directories (${PROJECT_SOURCE_DIR})
include_directories (${PROJECT_BINARY_DIR})
add_library (CellAnalyserLib SHARED
${CellAnalyserLib_src}
${CellAnalyserLib_hdr_moc}
${CellAnalyserLib_ui_moc}
)
add_executable(CellAnalyser ${Source}
${CellAnalyserLib_hdr}
${CellAnalyserLib_src}
${CMAKE_SOURCE_DIR}/header/Controller/Controller.h
${CMAKE_SOURCE_DIR}/source/Controller/Controller.cpp
)
target_link_libraries (CellAnalyserLib Qt5::Widgets)
target_link_libraries (CellAnalyser ${ITK_LIBRARIES})
target_link_libraries(CellAnalyser ${OPENGL_LIBRARIES})
target_link_libraries (CellAnalyser CellAnalyserLib)
So thats been the important part you need to know. Now the question is: why am I not able to include the Header "controller.h" in mainwindow.cpp? The include in main.cpp works fine and I am able to instantiate the controller-class and call functions. But if I try the same exact thing in mainwindow.cpp I get the following as output:
CMakeFiles\CellAnalyserLib.dir/objects.a(mainwindow.cpp.obj):mainwindow.cpp:(.text+0x820): undefined reference to `_imp___ZN6itksys18SystemToolsManagerD1Ev'
CMakeFiles\CellAnalyserLib.dir/objects.a(mainwindow.cpp.obj):mainwindow.cpp:(.text+0x859): undefined reference to `_imp___ZN6itksys18SystemToolsManagerC1Ev'
CMakeFiles\CellAnalyserLib.dir/objects.a(mainwindow.cpp.obj):mainwindow.cpp:(.data+0xfffff740): undefined reference to `itk::NiftiImageIOFactoryRegister__Private()'
C:/PROGRA~2/MINGW-~1/I686-4~1.2-P/mingw32/bin/../lib/gcc/i686-w64-mingw32/4.9.2/../../../../i686-w64-mingw32/bin/ld.exe: CMakeFiles\CellAnalyserLib.dir/objects.a(mainwindow.cpp.obj): bad reloc address 0x0 in section `.data'
collect2.exe: error: ld returned 1 exit status
source\IUI\CMakeFiles\CellAnalyserLib.dir\build.make:134: recipe for target 'source/IUI/libCellAnalyserLib.dll' failed
mingw32-make[2]: *** [source/IUI/libCellAnalyserLib.dll] Error 1
CMakeFiles\Makefile2:263: recipe for target 'source/IUI/CMakeFiles/CellAnalyserL
ib.dir/all' failed
mingw32-make[1]: *** [source/IUI/CMakeFiles/CellAnalyserLib.dir/all] Error 2
makefile:82: recipe for target 'all' failed
mingw32-make: *** [all] Error 2
So what am I doing wrong?
First of all, including controller.h appears to work fine, if the error messages you provided were complete. Only the linking fails, when CellAnalyserLib is being linked.
The reason for this is that you are using functions obtained from controller.cpp, but you are not including controller.cpp in any way in your linkage (neither directly as source, nor indirectly through another library). In addition, you are not linking ITK with your CellAnalyserLib.
A possible fix would be to ditch the shared library altogether (replace your add_* and target_link_libraries statements):
add_executable(CellAnalyser ${Source}
${CellAnalyserLib_hdr}
${CellAnalyserLib_src}
${CellAnalyserLib_hdr_moc}
${CellAnalyserLib_ui_moc}
${CMAKE_SOURCE_DIR}/header/Controller/Controller.h
${CMAKE_SOURCE_DIR}/source/Controller/Controller.cpp
)
target_link_libraries (CellAnalyser Qt5::Widgets)
target_link_libraries (CellAnalyser ${ITK_LIBRARIES})
target_link_libraries(CellAnalyser ${OPENGL_LIBRARIES})
If ditching the shared library is not an option, you need to clean up your linkage, by not having the same source in two different objects (again, replace your add_* and target_link_libraries statements):
add_library (CellAnalyserLib SHARED
${CellAnalyserLib_src}
${CellAnalyserLib_hdr_moc}
${CellAnalyserLib_ui_moc}
${CMAKE_SOURCE_DIR}/source/Controller/Controller.cpp
)
add_executable(CellAnalyser ${CellAnalyserBin_src}
)
target_link_libraries (CellAnalyserLib ${ITK_LIBRARIES})
target_link_libraries(CellAnalyserLib ${OPENGL_LIBRARIES})
target_link_libraries (CellAnalyserLib Qt5::Widgets)
target_link_libraries (CellAnalyser CellAnalyserLib)
Adding headers to add_* is not neccessary.
The project structure below is a simplified example.
├── CMakeLists.txt
├── debug
├── CommBase
│ ├── Task.h
│ ├── Task.cpp
│ ├── Thread.h
│ ├── Thread.cpp
│ └── CMakeLists.txt
├── NetWork
│ ├── TSocket.h
│ ├── TSocket.cpp
│ ├── Stream_Channel.h
│ ├── Stream_Channel.cpp
│ └── CMakeLists.txt
├── MessageDispatch
│ ├── Channel_Manager.h
│ ├── Channel_Manager.cpp
│ ├── Message_Facade.h
│ ├── Message_Facade.cpp
│ └── CMakeLists.txt
└── DemoServer
├── CMakeLists.txt
└── main.cpp
./CMakeLists.txt
PROJECT(DDLIB)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
MESSAGE(STATUS "This is PROJECT_BINARY_DIR dir "${PROJECT_BINARY_DIR})
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
ADD_SUBDIRECTORY(CommBase)
ADD_SUBDIRECTORY(NetWork)
ADD_SUBDIRECTORY(DemoServer)
CommBase/CMakeLists.txt
AUX_SOURCE_DIRECTORY(. LIB_SRC_LIST)
ADD_LIBRARY(CommBase SHARED STATIC ${LIB_SRC_LIST})
INCLUDE_DIRECTORIES(../boost)
INSTALL(TARGETS CommBase CommBase
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
SET( CMAKE_BUILD_TYPE Debug )
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
NetWork/CMakeLists.txt
AUX_SOURCE_DIRECTORY(. LIB_SRC_LIST)
ADD_LIBRARY(NetWork SHARED STATIC ${LIB_SRC_LIST})
INCLUDE_DIRECTORIES(../boost)
INSTALL(TARGETS NetWork NetWork
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
TARGET_LINK_LIBRARIES(NetWork CommBase)
SET( CMAKE_BUILD_TYPE Debug )
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
MessageDispatch/CMakeLists.txt
AUX_SOURCE_DIRECTORY(. LIB_SRC_LIST)
ADD_LIBRARY(MessageDispatch SHARED STATIC ${LIB_SRC_LIST})
INCLUDE_DIRECTORIES(../boost)
INCLUDE_DIRECTORIES(../CommBase)
INCLUDE_DIRECTORIES(../NetWork)
ADD_DEPENDENCIES(MessageDispatch CommBase NetWork)
LINK_DIRECTORIES(/home/cl/server/ddsvn/debug)
TARGET_LINK_LIBRARIES(MessageDispatch CommBase NetWork)
INSTALL(TARGETS MessageDispatch MessageDispatch
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
DemoServer/CMakeLists.txt
PROJECT(DDLIB)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
MESSAGE(STATUS "This is PROJECT_BINARY_DIR dir "${PROJECT_BINARY_DIR})
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(DemoServer ${SRC_LIST})
INCLUDE_DIRECTORIES(../boost)
INCLUDE_DIRECTORIES(../CommBase)
INCLUDE_DIRECTORIES(../NetWork)
INCLUDE_DIRECTORIES(../MessageDispatch)
ADD_DEPENDENCIES(DemoServer CommBase NetWork MessageDispatch)
LINK_DIRECTORIES(/home/cl/server/ddsvn/debug)
TARGET_LINK_LIBRARIES(DemoServer CommBase NetWork MessageDispatch)
Linking CXX executable DemoServer
/usr/bin/cmake -E cmake_link_script CMakeFiles/DemoServer.dir/link.txt --verbose=1
/usr/bin/c++ -fPIC CMakeFiles/DemoServer.dir/stdafx.cpp.o CMakeFiles/DemoServer.dir/DemoServer.cpp.o -o DemoServer -rdynamic -lCommBase -lNetWork -lMessageDispatch
/usr/local/lib/libMessageDispatch.a(Message_Facade.cpp.o): In function `Message_Facade::stop()':
/home/cl/server/ddsvn/MessageDispatch/Message_Facade.cpp:80: undefined reference to `Timer_Queue::instance()'
/home/cl/server/ddsvn/MessageDispatch/Message_Facade.cpp:80: undefined reference to `Timer_Queue::stop()'
/usr/local/lib/libMessageDispatch.a(Message_Facade.cpp.o): In function `Message_Facade::wait()':
/home/cl/server/ddsvn/MessageDispatch/Message_Facade.cpp:87: undefined reference to `Task::wait()'
/home/cl/server/ddsvn/MessageDispatch/Message_Facade.cpp:88: undefined reference to `Task::wait()'
/usr/local/lib/libMessageDispatch.a(Acceptor_Manager.cpp.o): In function `Stream_Acceptor':
/home/cl/server/ddsvn/MessageDispatch/../NetWork/Stream_Accecptor.h:40: undefined reference to `vtable for Stream_Acceptor'
/usr/local/lib/libMessageDispatch.a(Channel_Manager.cpp.o): In function `Channel_Manager':
/home/cl/server/ddsvn/MessageDispatch/Channel_Manager.cpp:81: undefined reference to `Channel_Handler::~Channel_Handler()'
/usr/local/lib/libMessageDispatch.a(Channel_Manager.cpp.o): In function `~Channel_Manager':
/home/cl/server/ddsvn/MessageDispatch/Channel_Manager.cpp:86: undefined reference to `Channel_Handler::~Channel_Handler()'
/home/cl/server/ddsvn/MessageDispatch/Channel_Manager.cpp:86: undefined reference to `Channel_Handler::~Channel_Handler()'
/usr/local/lib/libMessageDispatch.a(Channel_Manager.cpp.o): In function `Channel_Handler':
/home/cl/server/ddsvn/MessageDispatch/../NetWork/Channel_Handler.h:14: undefined reference to `vtable for Channel_Handler'
/usr/local/lib/libMessageDispatch.a(Channel_Manager.cpp.o):(.rodata._ZTI15Channel_Manager[typeinfo for Channel_Manager]+0x28): undefined reference to `typeinfo for Channel_Handler'
/usr/local/lib/libMessageDispatch.a(Message.cpp.o): In function `Message':
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:49: undefined reference to `Binary_Stream::Binary_Stream(int)'
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:98: undefined reference to `Binary_Stream::~Binary_Stream()'
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:100: undefined reference to `Binary_Stream::Binary_Stream(int)'
/usr/local/lib/libMessageDispatch.a(Message.cpp.o): In function `Message::resize(int)':
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:187: undefined reference to `Stream_Base::resize(int)'
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:196: undefined reference to `Stream_Base::resize(int)'
/usr/local/lib/libMessageDispatch.a(Message.cpp.o): In function `~Message':
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:203: undefined reference to `Binary_Stream::~Binary_Stream()'
/home/cl/server/ddsvn/MessageDispatch/Message.cpp:203: undefined reference to `Binary_Stream::~Binary_Stream()'
/usr/local/lib/libMessageDispatch.a(Message.cpp.o):(.rodata._ZTV7Message[vtable for Message]+0x20): undefined reference to `Stream_Base::clone_stream()'
/usr/local/lib/libMessageDispatch.a(Message.cpp.o):(.rodata._ZTI7Message[typeinfo for Message]+0x10): undefined reference to `typeinfo for Binary_Stream'
/usr/local/lib/libMessageDispatch.a(Connector_Manager.cpp.o): In function `Stream_Connector':
/home/cl/server/ddsvn/MessageDispatch/../NetWork/Stream_Connector.h:42: undefined reference to `vtable for Stream_Connector'
/usr/local/lib/libMessageDispatch.a(Context.cpp.o): In function `Context':
/home/cl/server/ddsvn/MessageDispatch/Context.cpp:15: undefined reference to `Dispatch_Thread::Dispatch_Thread()'
/usr/local/lib/libMessageDispatch.a(Context.cpp.o): In function `Context::initialize()':
/home/cl/server/ddsvn/MessageDispatch/Context.cpp:113: undefined reference to `Timer_Queue::instance()'
/home/cl/server/ddsvn/MessageDispatch/Context.cpp:113: undefined reference to `Timer_Queue::start()'
/home/cl/server/ddsvn/MessageDispatch/Context.cpp:116: undefined reference to `Task::activate(int, int*)'
/home/cl/server/ddsvn/MessageDispatch/Context.cpp:121: undefined reference to `Task::activate(int, int*)'
/usr/local/lib/libMessageDispatch.a(Re_Connect_Handler.cpp.o): In function `Re_Connect_Handler::process_re_connect(Context&, int)':
/home/cl/server/ddsvn/MessageDispatch/Re_Connect_Handler.cpp:23: undefined reference to `Timer_Queue::instance()'
/home/cl/server/ddsvn/MessageDispatch/Re_Connect_Handler.cpp:23: undefined reference to `Timer_Queue::schedule_timer(Smart_Ptr<Timer_Handler>, Time_Value const&, Smart_Ptr<Ref_Object>, EDispatchType, Dispatch_Thread*)'
/usr/local/lib/libMessageDispatch.a(IO_Thread.cpp.o): In function `IO_Thread':
/home/cl/server/ddsvn/MessageDispatch/IO_Thread.cpp:4: undefined reference to `Task::Task()'
/home/cl/server/ddsvn/MessageDispatch/IO_Thread.cpp:7: undefined reference to `Task::~Task()'
/usr/local/lib/libMessageDispatch.a(IO_Thread.cpp.o): In function `~IO_Thread':
/home/cl/server/ddsvn/MessageDispatch/IO_Thread.cpp:12: undefined reference to `Task::~Task()'
/home/cl/server/ddsvn/MessageDispatch/IO_Thread.cpp:12: undefined reference to `Task::~Task()'
/usr/local/lib/libMessageDispatch.a(IO_Thread.cpp.o):(.rodata._ZTI9IO_Thread[typeinfo for IO_Thread]+0x10): undefined reference to `typeinfo for Task'
collect2: ld returned 1 exit status
make[2]: *** [DemoServer] Error 1
make[2]: Leaving directory `/home/cl/server/ddsvn/DemoServer/Debug'
make[1]: *** [CMakeFiles/DemoServer.dir/all] Error 2
make[1]: Leaving directory `/home/cl/server/ddsvn/DemoServer/Debug'
make: *** [all] Error 2
I'm hoping this is a really simple lack of understanding of how CMake handles dependencies. This compiles without error, but fails during linking
NetWork is dependent on CommBase, MessageDispatch dependent on NetWork and CommBase,how can i specify in DemoServer
There are a few issues here, some more critical than others. Roughly in order of importance:
add_library allows you to define the library as either STATIC or SHARED, but not both.
target_link_libraries links the dependencies in the order they appear in the command. You should specify them from most-dependent to least. So, in your DemoServer CMakeLists.txt, it should be
TARGET_LINK_LIBRARIES(DemoServer MessageDispatch NetWork CommBase)
However, from the docs:
Library dependencies are transitive by default. When this target is linked into another target then the libraries linked to this target will appear on the link line for the other target too.
In other words, you should have TARGET_LINK_LIBRARIES(NetWork CommBase) in NetWork (you already do), TARGET_LINK_LIBRARIES(MessageDispatch NetWork) in MessageDispatch, and just TARGET_LINK_LIBRARIES(DemoServer MessageDispatch) for the Demo.
CMAKE_BUILD_TYPE would normally be set by the user via the command line (or CMake GUI). It should probably only be set inside the CMakeLists.txt if the user hasn't already done so, or if it's set to an invalid value. If you set or change the value, it would be good to tell the user via a message. You probably shouldn't be setting it in 2 different places.
aux_source_directory is not recommended as a means to gather lists of source files. The recommended way is to hard-code the file paths into your CMakeLists.txt, so if the source list changes, CMake automatically re-runs.
link_directories is rarely required, and shouldn't contain a hard-coded absolute path.
LIBRARY_OUTPUT_PATH and EXECUTABLE_OUTPUT_PATH are deprecated.
add_dependencies is not required if you have already specified these dependencies via a target_link_libraries call.