CMake: Static libraries not linking correctly with second project - c++

Let me preface this by saying I'm quite new to CMake, so please let me know if I'm just misunderstanding something here.
First I'm compiling a static library that acts as a wrapper to an external program (Paraview). The CMakeLists file I use to create this library is like so:
cmake_minimum_required(VERSION 3.3)
project(POP)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY [LIBRARY HOME]/lib/)
set(ParaView_DIR [PARAVIEW HOME]/ParaView-v5.4.1/)
find_package(ParaView REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/pop_API.h ${CMAKE_CURRENT_SOURCE_DIR}/include/pop_API_vars.h ${CMAKE_CURRENT_SOURCE_DIR}/include/pop_Structures.h ${CMAKE_CURRENT_SOURCE_DIR}/include/wrapper_pop_API.h)
add_library(POP STATIC src/pop_API.cpp src/wrapper_pop_API.cpp src/pop_Adaptor.cpp ${HEADER_FILES})
target_include_directories(POP PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(POP LINK_PUBLIC vtkPVPythonCatalyst)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(POP)
This links in a specific Paraview module I need, and some VTK datastructures that are required for that as well.
This CMake file runs just fine, and I can compile the libraries no problem. If I add a Main function to my C++ code and compile it as an executable, that runs no problem as well. So everything seems fine here.
Now, I ultimately want this static library to be called by a second piece of code. To test this I've written a toy C++ project that just calls the main functions of this library
#include "pop_Structures.h"
#include "pop_API.h"
int main()
{
PopGrid* grid;
PopSolution* solution;
a = 1;
b = 2;
int x = POP_INITIALIZE(a, b);
int y = COPROCESS(PopGrid, PopSolution, a, b, a, b);
int z = POP_FINALIZE();
}
The included header files just define the PopSolution and PopGrid structures, as well as the prototypes for the three functions being called. I create the executable for this program using a CMakeLists file that looks like
cmake_minimum_required(VERSION 3.3)
project(CPP_Test)
find_package(MPI REQUIRED)
include_directories(${MPI_CXX_INCLUDE_PATH})
set(HEADER_FILES [LIBRARY HOME]/include/pop_API.h [LIBRARY HOME]/include/pop_Structures.h)
ADD_EXECUTABLE(CPP_TEST CPP_Test.cpp ${HEADER_FILES})
target_include_directories(CPP_TEST PUBLIC [LIBRARY HOME]/include)
target_link_libraries(CPP_TEST[LIBRARY HOME]/lib/libPOP.a)
This CMake also runs just fine, but when I compile it I get a number of undefined reference errors pointing to functions in libPOP.a where I refer to VTK/Paraview data structures and functions. These are not directly defined in the toy code above, but they should be defined in the library, correct? If these structures are all defined when I compile the initial library, why are they not defined when I call that library externally?
Thanks.

Related

How to link CUDA dynamic libraries using CMake?

I want to know how to use CMake to dynamically link CUDA libraries, I know it seems to require some extra restrictions, but don't know exactly how to do it. Here is a simple example I wrote to illustrate my problem.
Directory structure:
Dir/
├── CMakeLists.txt
├── header.cuh
├── kernel.cu
└── main.cpp
Environment:
OS: Windows 11
GPU: RTX 3060 laptop
CUDA Toolkit: 11.6
Platform: Visual Studio 2022
header.cuh:
#include "stdio.h"
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
extern "C" void f();
kernel.cu:
#include "header.cuh"
void __global__ print()
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
printf("%d\n", idx);
}
void f()
{
print<<<1, 10>>>();
}
main.cpp:
#include "header.cuh"
extern "C" void f();
int main()
{
f();
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.17)
project(test)
set(CMAKE_CXX_STANDARD 17)
find_package(CUDA REQUIRED)
enable_language("CUDA")
set(CMAKE_CUDA_STANDARD 14)
set(CUDA_SEPARABLE_COMPILATION ON)
string(APPEND CMAKE_CUDA_FLAGS " -rdc=true --cudart shared")
add_library(CUDA_COMP SHARED header.cuh kernel.cu)
set_property(TARGET CUDA_COMP PROPERTY CUDA_ARCHITECTURES 86-real 86-virtual)
add_executable(main main.cpp)
target_link_libraries(main CUDA_COMP)
The project can be configured successfully, but a problem with unresolved external symbols f referenced in function main occurs when building it.
I also looked up the corresponding solution on Stackoverflow, but it didn't work. For example, an answer on Stackoverflow mentioned adding " -rdc=true --cudart shared" to cmake, and I did the same (see line 10 of CMakeLists.txt).
This problem has been bothering me for a long time. I hope you can tell me the cause of the problem and how to solve it. Thank you so much!
There is a lot of issues with OP's code.
CUDA/C++ issues:
CUDA nowadays is a C++ dialect, not C. Therefore I would not declare f() to be extern "C".
There seems to be no reason for the header to include anything. When something is needed by the implementation, but not by the interface, the include should not be part of the interface.
If you want to use this header to interface with non-CUDA C++ code (a .cpp file) it should be named .h (or .hpp). I would expect .cuh files to only ever be included by .cu files. One also usually puts the same name on interface and implementation, so I renamed the header to kernel.h.
Header guards are missing.
Why would you redeclare f() in main.cpp? That is what the header is for.
You forgot to synchronize after the kernel launch, so the app will most probably print nothing at all.
CMake issues:
Setting properties etc. in CMakeLists.txt globally is discouraged. One should use the target specific APIs whenever possible. Some of these global properties have to be set before project() to work at all.
CMake sets -rdc=true automatically when CUDA_SEPARABLE_COMPILATION is set.
CUDA separable compilation is only needed when e.g. a kernel uses a device function that is defined in a different translation unit. I am not sure if OP thinks he needs it, or if the part that he needs it for was removed when creating a minimal example.
There is the CUDA_RUNTIME_LIBRARY property for dynamically linking the CUDA runtime.
Using find_package(CUDA) is deprecated in favor of using CUDA as a language. In the rare case where one does not want to use the CUDA language, but still needs to find the CUDA toolkit, there is also FindCUDAToolkit. OP only needs the language. But the linked documentation also lists all the CUDA libraries that come with the toolkit. Here we can see that most libraries provide a _static version to differentiate between static and dynamic linking. When using the CUDA language, these are also available but without the CUDA:: "namespace". Do not use target_link_libraries(... cudart) when using the language, as this will not influence the CUDA_RUNTIME_LIBRARY property, i.e. you might get some kind of undefined CMake behavior. But when you want to dynamically link against e.g. CUBLAS, you can use target_link_libraries(... cublas).
The CUDA architecture should not be hardcoded into the CMakeLists.txt as long as the project does not use some kind of architecture-specific intrinsics. Instead one should specify the architecture at configuration time. CMake 3.18 added CMAKE_CUDA_ARCHITECTURES in version so I would recommend setting the minimum to 3.18 instead of 3.17. Then you can call e.g. cmake -DCMAKE_CUDA_ARCHITECTURES=86 (not sure how this is handled in Visual Studio). I added a default for 86 that can still be overwritten at configuration time when needed.
Fixed Example
The following files work for me under Linux with CMake 3.23.1, CUDA 11.8.0 and GCC 11.3.0:
kernel.h:
#pragma once
void f();
kernel.cu:
#include "kernel.h"
#include <cstdio>
void __global__ print()
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
std::printf("%d\n", idx);
}
void f()
{
print<<<1, 10>>>();
cudaDeviceSynchronize();
}
main.cpp:
#include "kernel.h"
int main()
{
f();
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.18)
# this has to be set before project()
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES)
set(CMAKE_CUDA_ARCHITECTURES 86)
endif()
project(test LANGUAGES CXX CUDA)
add_library(cuda_comp SHARED kernel.h kernel.cu)
# this command would only set the minimum standard,
# i.e. CMake can still decide to use -std=c++17 instead
# if the given compilers support C++17
target_compile_features(cuda_comp PRIVATE cuda_std_14)
set_target_properties(cuda_comp
PROPERTIES
CUDA_RUNTIME_LIBRARY Shared
# CUDA_STANDARD 14 # this one cannot be changed by CMake
# CUDA_SEPARABLE_COMPILATION ON # not needed for this example
)
add_executable(main main.cpp)
target_compile_features(main PRIVATE cxx_std_17)
# set_target_properties(main CXX_STANDARD 17)
target_link_libraries(main cuda_comp)

glfwInit() causes segmentation fault with exit code -1073741515 (0xc0000135)

I have been trying to get one my older projects to work which uses some OpenGL code. I am unable to produce a working executable. All that happens is, just by calling glfwInit(), is a segmentation fault:
My best guess is that it somehow doesnt use/find the glfw dll i am trying to use.
Let me explain my current setup:
I have installed glfw using msys2:
pacman -S mingw-w64-x86_64-glfw
I have created a glad header and source file
I wrote a simple cmake-file (which also used to work 2 years ago)
cmake_minimum_required(VERSION 3.23)
project(2DGameEngine)
find_package(glfw3 3.3 REQUIRED)
find_package(OpenGL REQUIRED)
set(CMAKE_CXX_STANDARD 23)
file(GLOB_RECURSE SRCS src/*.cpp src/*.c)
add_executable(2DGameEngine ${SRCS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wshadow")
target_link_libraries(2DGameEngine glfw)
target_link_libraries(2DGameEngine OpenGL::GL)
I used the simplest example I could find:
#include "glad.h"
#include <GLFW/glfw3.h>
#include <iostream>
int main()
{
// glfw: initialize and configure
// ------------------------------
if(!glfwInit()){
std::cout << "error" << std::endl;
exit(1);
}
return 0;
}
Yet I am unable to get rid of the segmentation fault when calling glfwInit(). I assume it has to do with some .dll missing but I have no idea how I could check this. I am very happy for any help.
0xc0000135 is the error you get when your Windows OS could not find a required dll when executing your program. There is a handy site that decodes these types of errors here: https://james.darpinian.com/decoder/?q=0xc0000135
Use this program: https://github.com/lucasg/Dependencies to figure out what dll can not be found and then put that dll in the same folder as the executable or edit your OS PATH environment variable to contain the folder which has that dll.
Here is a good article on how to set the OS PATH environment variable: https://www.computerhope.com/issues/ch000549.htm
There are several other options contained in this Microsoft document which explains how and where your OS searches for dlls: https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#search-order-for-desktop-applications

Building antlr4 project in cpp

I'm using antlr4-4.9.2 on Windows 10. I want to use antlr4 in c++, and here's part of my CMakeLists.txt
file(GLOB antlr4-cpp-src
third_party/antlr4-cpp-runtime-4.9.2/src/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/atn/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/dfa/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/misc/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/support/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/tree/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/tree/pattern/*.cpp
third_party/antlr4-cpp-runtime-4.9.2/src/tree/xpath/*.cpp
)
add_library (antlr4-cpp-runtime ${antlr4-cpp-src})
add_executable(MiniSql ${src_dir} src/main.cpp)
target_link_libraries(MiniSql antlr4-cpp-runtime)
But I got 171 errors and warnings. For example, in BufferedTokenStream,
TokenSource *BufferedTokenStream::getTokenSource() const
{
return _tokenSource;
}
got reference to TokenSource is ambiguous.
And many more like this.
What's my problem in the code?

CMake linking error with Eigen "CMake can not determine linker language for target" [duplicate]

I know there are already a few threads on this topic, however, after reading through many of them I have been unable to find a solution to my problem. I am working on a file loader/parser and am using CMake for the first time. My CMakeList.txt file is being used to import an XML parser (xerces) and currently looks like:
cmake_minimum_required(VERSION 2.8)
project(fileloader)
set(SRCS
Source.cpp
)
include_directories(./
${SPF_ROOT}/xerces/win64/include/xercesc/dom
)
add_executables(${PROJECT_NAME} ${SRCS})
add_library(HEADER_ONLY_TARGET STATIC XMLString.hpp XercesDOMParser.hpp DOM.hpp HandlerBase.hpp PlatformUtils.hpp)
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)
When running my solution the error I am recieving is "CMake can not determine linker language for target: fileloader"
I am relatively new to c++ and completely new to CMake so hopefully I am missing something simple, but any and all help is greatly appreciated!
EDIT:
The code I am writing is on a non-internet enabled machine so I cannot copy and paste the entire code, however this is the except causing the issue:
...
#include "XMLString.hpp"
#include "XercesDOMParser.hpp"
#include "DOM.hpp"
#include "HandlerBase.hpp"
#include "PlatformUtils.hpp"
class XMLReader : public IFileReader {
public:
XMLReader(){};
void read(std::ifstream& file) {
xerces::XMLPlatformUtils::Initialize();
xercesc::XercesDOMParser* parser = new xercesc::XercesDOMParser();
parser->setValidationScheme(xercesc::XercesDOMParser::Val_Always);
parser->setDoNamespaces(true);
xercesc::ErrorHandler* errHandler = (xercesc::ErrorHandler*) new xercesc::HandlerBase();
parser->setErrorHandler(errHandler);
std::getline(file, line);
newFile = line.c_str();
parser->parse(newFile);
}
}
...
HEADER_ONLY_TARGET is not a keyword argument.
If you want a header-only library, use an interface library:
add_library(<name> INTERFACE [IMPORTED [GLOBAL]])
Added:
SET_TARGET_PROPERTIES([some name] PROPERTIES LINKER_LANGUAGE C11)
to the end of my program and the error went away. After reading a million web pages I found https://kuniganotas.wordpress.com/2011/05/25/error-cmake-can-not-determine-linker-language-for-target/ and the solution was literally that simple! Hopefully this can help others with this error!

spdlog crash on factory methods

Yesterday I have started including spdlog into a personal project of mine to use for logging. So far I have had some problems with getting library inclusion to work but those are now solved completely.
Now everything compiles just fine, with all headers found however when I try to create loggers or simply set the pattern for logging the code crashes with a segmentation fault. More specifically no matter which function I call from the spdlog namespace for the very first time in the program causes the crash.
I have a class abstracting some parts from spdlog (based on this repo) as follows:
//Logger.hpp
#ifndef TE_LOGGER_HPP
#define TE_LOGGER_HPP
#include <spdlog/spdlog.h>
namespace te {
class Logger {
public:
static void Init();
inline static std::shared_ptr<spdlog::logger> &getCoreLogger() {
return sCoreLogger;
}
inline static std::shared_ptr<spdlog::logger> &getClientLogger() {
return sClientLogger;
}
private:
static std::shared_ptr<spdlog::logger> sCoreLogger;
static std::shared_ptr<spdlog::logger> sClientLogger;
};
}
#endif //TE_LOGGER_HPP
//Logger.cpp
#include "Logger.hpp"
#include <spdlog/sinks/stdout_color_sinks.h>
std::shared_ptr<spdlog::logger> te::Logger::sCoreLogger;
std::shared_ptr<spdlog::logger> te::Logger::sClientLogger;
void te::Logger::Init() {
//The first of any of the following three lines cause a crash
//no matter the order, regardless of the pattern used in set_pattern
spdlog::set_pattern("%v");
sCoreLogger = spdlog::stdout_color_mt("CORE");
sClientLogger = spdlog::stdout_color_mt("CORE");
sCoreLogger->set_level(spdlog::level::trace);
sClientLogger->set_level(spdlog::level::trace);
}
From the stack traces it seems that the issue is with the formatter class in spdlog being set null for some reason somewhere within the library. I am using the latest CLion, C++14 (I am aware that spdlog is C++11, but I need features from 14 later down the line, also setting -std=c++11 doesn't solve the issue) and the latest version of spdlog as of yesterday (pulled straight from their GitHub repo) on Ubuntu 18.04.
EDIT: As per the request in the comments I have created a small project (single cpp file, include spdlog the way I do in the real project, or the same code and library setup as in the real project referenced from the main.cpp file and linked to accordingly) that aims to reproduce the issue and here are my findings:
* The issue is not present when I use spdlog directly in the executable
* The issue is present if the Logger class is moved into a shared library and linked to from there
Here is the error message I am getting:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
And the CMakeLists.txt files I am using (I nest the library's one into the project since as of now CLion does not support "multiple projects in the same solution" like for example VS does):
#CMakeLists.txt for Library
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(TokenEngine VERSION 0.0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(SOURCE_FILES src/Application.cpp src/Application.hpp src/EntryPoint.hpp src/Logger.cpp src/Logger.hpp)
#include_directories("${CMAKE_CURRENT_SOURCE_DIR}/libs/")
add_library(TokenEngine SHARED ${SOURCE_FILES})
target_include_directories(TokenEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/libs/spdlog-1.x/include")
#Expose the public API of the engine to any project that might use it
target_include_directories(TokenEngine PUBLIC include)
#CMakeLists.txt for top level project
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
add_definitions(-DTE_PLATFORM_LINUX)
project(Build CXX)
add_subdirectory(TokenEngine)
add_subdirectory(Sandbox)
You are using the same name for both loggers, when you run it you'll get:
$ ./logger
libc++abi.dylib: terminating with uncaught exception of type spdlog::spdlog_ex: logger with name 'CORE' already exists
Abort trap: 6
If you change the name of the client logger to something else it works fine:
sCoreLogger = spdlog::stdout_color_mt("CORE");
sClientLogger = spdlog::stdout_color_mt("CLIENT");
The problem is probably that spdlog’s static objects are defined twice - from inside the shared library and from client code that includes your logger header (which incldes spdlog.h).
Try to remove the include to spdlog.h from the header file and (and use forward declaration of spdlog::logger instead), and include spdlog.h only from your Logger.cpp file.
Edit:
spdlog::logger cannot be forward declared across compilation unit boundries.
The solution is to wrap the logger with some simple class defined in logger.cpp and only export it in logger.h