Link external library CMakeLists.txt on Windows - c++

I'm trying to realize a CMakeLists.txt to compile my project under Linux and Windows. I use two libraries : SFML and boost. However, I want that my project can be built under Linux or Windows without the installation of libraries from a user.
I explain. Libraries SFML and boost are compiled and placed in my folder project and I want that no matter on which computer I download my project, I can build it.
And here is my problem, I can't link externally from the libraries folder in my folder project, the SFML and boost libraries.
Here is my CMakeLists.txt :
cmake_minimum_required(VERSION 3.0)
project(r-type_client CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
include_directories(./boost/include
./SFML/include
./include
)
if(UNIX)
set(BOOST_LIBRARY
-L./boost/linux
-lboost_regex
)
set(SFML_LIBRARY
-L./SFML/linux
-lsfml-graphics
-lsfml-window
-lsfml-system
)
add_executable(r-type_client
srcs/Main.cpp
)
target_link_libraries(r-type_client
${BOOST_LIBRARY}
${SFML_LIBRARY}
)
elseif(WIN32)
add_library(bar SHARED IMPORTED)
set_property(TARGET bar PROPERTY IMPORTED_LOCATION
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-graphics.dll)
set_property(TARGET bar PROPERTY IMPORTED_IMPLIB
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-graphics.lib)
add_library(bor SHARED IMPORTED)
set_property(TARGET bor PROPERTY IMPORTED_LOCATION
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-window.dll)
set_property(TARGET bor PROPERTY IMPORTED_IMPLIB
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-window.lib)
add_library(bur SHARED IMPORTED)
set_property(TARGET bur PROPERTY IMPORTED_LOCATION
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-system.dll)
set_property(TARGET bur PROPERTY IMPORTED_IMPLIB
C:/Users/Hugo/Downloads/Old/client/SFML/windows/sfml-system.lib)
add_executable(r-type_client srcs/Main.cpp)
target_link_libraries(r-type_client bar bor bur)
endif(UNIX)

As I've mentioned in the comments, you should set up your CMake project in a proper way by using find_package() to properly locate everything.
CMake is a tool to allow people to creation files needed for compilation on the fly no matter the host system. By hardcoding paths and (unneeded) per-platform branches, you're basically negating the whole purpose of CMake.
To include a third party library in CMake, you'll typically want to use find_package(), which includes the necessary logic to locate the library and setup variables properly (it essentially does what you've done by hand so far).
Let's do it for SFML, since it's basically the same for Boost (and I don't really have Boost ready to test everything, because the variable names might differ from project to project).
First, you'll tell CMake that your project wants to use SFML:
find_package(SFML)
Depending on the actual "package", you can extend this. In SFML's case we can even define which minimum version we want and which sub libraries we actually want to use:
find_package(SFML 2.3 COMPONENTS graphics window system REQUIRED)
The REQUIRED flag makes the whole thing mandatory, i.e. CMake will error out, if it's not found.
Once this line succeeded, it will have set a few variables with the proper paths and libraries, which we can then use when defining a target:
include_directories(${SFML_INCLUDE_DIR})
add_executable(myprogram ${MY_SOURCE_FILES})
target_link_libraries(myprogram ${SFML_LIBRARIES} ${SFML_DEPENDENCIES})
And you're done. As you can see, I can setup a CMake project in less than 10 lines without any platform specific paths, code, or knowledge.
However, when running this, you'll most likely run into one error:
CMake by default might not be able to find a FindSFML.cmake file and complain.
You'll find this file in your SFML directory under the cmake path. Copy it to your project and tell CMake where to look for it, for example:
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
Once this is done as well, CMake might not be able to locate the actual SFML files, especially on Windows. To tell it where to look for it, you'd define SFML_ROOT when invoking CMake:
cmake -DSFML_ROOT=C:/Users/Hugo/Downloads/Old/client/SFML path/to/source
This gives you the big advantage that you won't have to hardcode the path to SFML (or any other library such as Boost). On your linux machine, you typically won't even have to provide any path and everything should just work out of the box.

Related

Using CMake to create an executable that can run independently on other machines

I’m using LibTorch and OpenCV for a program in Cpp. The compilation and building is done on Linux using CMake. The program builds and runs as expected.
I want to use the executable that CMake created on another Linux machine.
The problem is that I don’t want to install either LibTorch nor OpenCV on the other machine. I’d rather supply the user with a single executable if possible.
How can CMake create a single independent executable?
If making just a single file is irrelevant, how can CMake copy all needed libraries to a single directory?
The current CMake file:
cmake_minimum_required(VERSION 2.8)
project(prediction)
list(APPEND CMAKE_PREFIX_PATH “libtorch”) # the folder where libtorch in found
set(CMAKE_BUILD_TYPE Release)
find_package( OpenCV REQUIRED )
find_package( Torch REQUIRED )
if(NOT Torch_FOUND)
message(FATAL_ERROR “Pytorch Not Found!”)
endif(NOT Torch_FOUND)
message(STATUS “Pytorch status:”)
message(STATUS " libraries: ${TORCH_LIBRARIES}")
message(STATUS “OpenCV library status:”)
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
file(GLOB SOURCES ".h" ".cpp") # Link all headers and sources in root dir
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable(entrypoint ${SOURCES})
target_link_libraries(entrypoint ${TORCH_LIBRARIES} ${OpenCV_LIBS})
set_property(TARGET entrypoint PROPERTY CXX_STANDARD 14)
####### EDIT
Thanks for the answers.
Following Phesho_T answer bellow, I got the static compilation of LibTorch, but it won't compile with the set() instruction. It throws C10_LIBRARY NOTFOUND.
I think I'll try to use the shared libraries. How can CMake be instructed to copy the releveant shared libraries to the "build" folder, so I can pack everything in a .zip file and send it to the user.
Like another answer said, you need to link the static libs of Torch and OpenCV in your executable.
There are a few pre-requisites for this:
The two libraries need to have static (.a) libraries installed on your system. If they don't, you may have to manually build them. Steps for this differ between different packages.
You need to tell CMake to search for the static libraries ONLY. This is done via the CMAKE_FIND_LIBRARY_SUFFIXES variable. The chances are the default for this is .so;.a, meaning it will find the shared library first.
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
The fact that you're using variables in your target_link_libraries command, instead of imported libraries (the modern CMake way) makes me think that this should be enough - these variables should expand to full paths to the static libraries, which should then be added to your linker command.
Things are a bit more complicated to explain if imported targets were used, but this might be out-of-scope for this question.
Try it out and let us know how you get on.
To create a single executable you need to statically link the dependencies into your executable. Check your libraries to see if they provide static-libs else you need to recompile libtorch or opencv to make static libraries.

CMakeLists C++ Beginner

I've started playing a little bit with C++ and to make it happen I decided to write a simple game engine.
For this purpose, I'm using CLion as my IDE and it works all good but adding libraries is just a nightmare. First I've installed all required libraries like glew, glfw or glm using brew, all went fine. Then I spent almost 2 hours to get it to work on my project.
My biggest mystery is the reason why it works, I've worked with build systems in java, python or golang and everything was always clear to me. However, I have no idea why it works the way it works and I'd love to know!
Here is my CMakeLists file.
cmake_minimum_required(VERSION 3.10)
project(untitled2)
find_package(GLEW REQUIRED)
find_package(GLFW3 REQUIRED)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled2 main.cpp)
target_link_libraries(untitled2 ${GLEW_LIBRARIES})
target_link_libraries(untitled2 glfw)
Now I have a few questions:
1. Why am I able to use GLM library without including it in the CMakeLists?
2. Why do I need to include glfw and glew but not glm?
3. Why do I need to use ${GLEW_LIBRARIES} and not some name like glew? (I tried different names, but nothing worked.)
btw. I'm using macOS.
The first thing to remember is that C++ doesn't (yet) have a real module system like newer languages. It just has a list of directories that it searches for header files, a list of directories that it searches for libraries, and a list of libraries to search for symbols when linking. The target_link_libraries directive just adds compiler flags that add to those three lists.
Now, on to this specific scenario. Most of the magic happens in the find_package directive. That directive really just ends up running cmake scripts. Sometimes those are packaged with cmake, sometimes they're installed along with the package you're finding. In the end, those scripts can do basically whatever they want. They all have the same objective, to give you a way to add the appropriate compiler flags to use the package, but there are a couple common ways they do that.
The older way is to set variables that you can use to tell the compiler what directories to search for headers and libraries and what libraries to link to. That's the approach GLEW seems to have taken. It sets the variables GLEW_LIBRARIES and GLEW_INCLUDE_DIRS and you then have to use link_libraries and include_directories to tell the compiler what to do. This was the only approach available in older versions of cmake (pre 2.8 IIRC), so while it's not as nice to use, it is still how many libraries' find_package scripts work.
The newer way is to create imported targets. Those targets have appropriate properties set so that any targets that link to the imported target inherit the appropriate include directories and library flags. This is the approach GLFW took. It creates an imported target named glfw that has the INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES properties set. When you pass that to target_link_libraries, your untitled2 target will inherit those include directories and libraries.
Finally, GLM is a header-only library. There are no library files to link to, so as long as the appropriate directory is added to the compilers header search path you'll be able to include and use GLM.
Since you used homebrew to install your libraries, all of their headers are likely under the same base directory; most likely "/usr/local/include". All of their library files are similarly likely under the same directory; probably something "/usr/local/lib". That means that your compiler will be able to find any of their headers and libraries is you tell it to search "/usr/local/include" for headers and "/usr/local/lib" for libraries.
So, to finally answer the question: Thing's work because the glfw target told cmake that it should set the compiler flags to add "/usr/local/include" to its list of include directories. Since that's the same directory it needs to search for GLM and GLEW, the compiler is able to find the headers for all of your libraries. The compiler is also able to find the library files it need to link to because cmake told it to look for them explicitly via the list GLEW_LIBRARIES and the inherited properties from the glfw target. GLM doesn't have any library files to link to, so there's nothing to tell it about.
You really shouldn't rely on everything being in the same place though. You should be able to tell the compiler about everything like this (note that I haven't actually tested this):
cmake_minimum_required(VERSION 3.10)
project(untitled2)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled2 main.cpp)
# This will fill the variables GLEW_INCLUDE_DIRES and GLEW_LIBRARIES
# that you can use to add the appropriate compiler flags
find_package(GLEW REQUIRED)
# This will create an imported target named glfw that you can link to
# to inherit the appropriate include directories and libraries
find_package(GLFW3 REQUIRED)
# This also creates an imported target named glm that you can "link to"
# to inherit the appropriate include directories
find_package(glm REQUIRED)
# GLEW uses an old-style find_package script, so you have to
# explicitly tell cmake about GLEW's include directories
target_include_directories(untitled2 PUBLIC ${GLEW_INCLUDE_DIRS})
# And the library files to link to
target_link_libraries(untitled ${GLEW_LIBRARIES})
# cmake will automatically add the appropriate include directories
# and library files that the imported glfw target tells it about
target_link_libraries(untitled2 glfw)
# You use the target_link_libraries directive with the glm imported target
# even though you're not actually linking to any libraries. It's just how
# you tell cmake you want your untitled2 target to inherit the appropriate
# include directories from the imported glm target
target_link_libraries(untitled2 glm)

How to statically link to antlr-runtime.lib without the dll

I'm trying to statically link ANTLR 4's C++ runtime library to a program in Windows. The build is done with CMake. However when I run the program it says that antlr-runtime.dll is missing (I don't want to have to provide antlr-runtime.dll as a separate DLL).
Here are the relevant lines from CMakeLists.txt:
add_library(antlr_runtime_lib STATIC IMPORTED)
if(MSVC)
set_target_properties(antlr_runtime_lib PROPERTIES
IMPORTED_LOCATION "$ENV{ANTLR4_LIB_DIR}/antlr4-runtime.lib")
elseif(UNIX)
set_target_properties(antlr_runtime_lib PROPERTIES
IMPORTED_LOCATION "$ENV{ANTLR4_LIB_DIR}/libantlr4-runtime.a")
endif()
target_link_libraries(parser_lib_test antlr_runtime_lib)
On Mac and Linux it works fine. Also, static libraries that I have built myself also link fine on Windows, so I'm not sure what the problem is.

Linking both third party precompiled dynamic and static libaries in Windows

Lately, I have been using cmake as a generator for my projects. I have successfully generated many vtk and other application projects. However, I now face a problem when trying to link both dynamic and static precompiled libraries. In particular, I have been given some dynamic precompiled third party dlls along with their respective .lib files. Furthermore, I am trying to link some static precompiled libraries (only .lib files) to my project so as to check the software licences.
Let say that my project is called test_example and I have some precompiled dynamic libraries in libs directory. The structure of my project directory is:
Test_example
-/include
-/libs
-/build
-CMakeLists.txt
The CMakeLists.txt for linking the dynamic libaries has the following content:
cmake_minimum_required(VERSION 2.8.9)
project (test_example)
set(CMAKE_BUILD_TYPE Release)
#For the shared libraries:
set (PROJECT_LINK_LIBS dynamic_1.dll dynamic_2.dll )
set (PROJECT_LINK_DIR ${test_example_SOURCE_DIR}/libs/)
set (PROJECT_INCLUDE_DIR ${test_example_SOURCE_DIR}/include/)
link_directories(${PROJECT_LINK_DIR})
include_directories(${PROJECT_INCLUDE_DIR})
add_executable(test_example test_example.cpp)
target_link_libraries(test_example ${PROJECT_LINK_LIBS})
When I generate the project with this cmake lists, I can successfully use methods from the precompiled dlls. However, I have not found a way to link against my static libraries, as well. Let say I have one static library which is called test_licence.lib. Should I drop it in the libs folder as well and simply refer to it like I do with the dynamic? When I do so and when opening my project solution in Visual Studio, I can see that both dynamic and static libraries have been added to Linker-->Input-->Additional DEpendencies. However, when I am trying to build the project, I have unresolved external dependencies which are methods from the static lib.
Does any of you have any idea what would be the most efficient way to accomplish that? Many thanks in advance!
There is a couple of issues here.
Unlike to what you may be used to from VS, CMake prefers absolute paths for linking, instead of setting a link directory and giving the relative path.
Also, you do not link against .dll files. Dlls are loaded at runtime, not at link time. Many dlls are shipped with import libraries (with a .lib ending), that handle the runtime loading automatically for you. These are the ones you should link against.
Also try not to hardcode libraries in CMake code. The problem here is that if something goes wrong, you end up with a cryptic linker error. You should use find_library instead, which will usually make CMake complain early if something is off.
A cleaner version of your CMake script would be something like
cmake_minimum_required(VERSION 2.8.9)
project (test_example)
# note setting the build type does nothing on a visual studio build
# and should probably be left to the user for other generators
set(CMAKE_BUILD_TYPE Release)
#For the shared libraries:
# this call will succeed if it finds a dynamic_1.lib file
find_library(DYNAMIC_LIB1 dynamic_1 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT DYNAMIC_LIB1)
message(FATAL_ERROR "Library dynamic_1 was not found!")
endif()
find_library(DYNAMIC_LIB2 dynamic_2 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT DYNAMIC_LIB2)
message(FATAL_ERROR "Library dynamic_2 was not found!")
endif()
# for the static libraries:
# basically the same; again this looks for a static_1.lib file
find_library(STATIC_LIB1 static1 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT STATIC_LIB1)
message(FATAL_ERROR "Library static_1 was not found!")
endif()
set (PROJECT_INCLUDE_DIR ${test_example_SOURCE_DIR}/include/)
include_directories(${PROJECT_INCLUDE_DIR})
add_executable(test_example test_example.cpp)
target_link_libraries(test_example ${DYNAMIC_LIB1} ${DYNAMIC_LIB2} ${STATIC_LIB1})
Double check in Visual Studio that all libraries were added as linker inputs, as expected. If you still get linker errors, it means that something is wrong with your third-party .lib file. Open a new question with the exact linker error that you get.
Here is how we are doing it:
Firstly, use add_library with final arguments STATIC IMPORTED. Then subsequently use set_property to set the IMPORTED_LOCATION property, which is a path to the built library. For example, we pull in gtest like so:
add_library(gtest UNKNOWN IMPORTED)
set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${binary_dir}/googlemock/gtest/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a)
Then, gtest is a known library in your build system and you can link with it normally later on by just doing
target_link_libraries(target-name gtest)
See also: Cmake imported libraries documenation

Using Cmake, how do I prevent library sources from being included in my IDE?

Using a bunch of different libraries in my project (from GitHub sources, not precompiled), I add them to my target like this in my root CMakeLists.txt file:
add_subdirectory(lib/glew-1.13.0/build/cmake)
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/lib/glew-1.13.0/include/)
...
target_link_libraries(MyApp glew ${GLEW_LIBRARIES} ... )
However, you can see from the screenshot below that Xcode includes all of the sources for those libraries in my project, which makes an insanely long list that I have to scroll through to find my code.
I have tried the EXCLUDE_FROM_ALL flag in the add_subdirectory command, which removes the library sources from my Xcode project, but then I cannot compile my project because Xcode doesn't compile the library at all.
Additionally, Xcode gives me tons of warnings from the libraries that I don't really care about. Using the SYSTEM flag with the include_directories command doesn't fix it.
What's the best way to solve this? Should I be compiling my libraries as a completely separate part of my build process rather than compiling them with my executable?
I'm not sure how it will work, but try this:
turn on the USE_FOLDERS in your root CMakeLists.txt
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
And then after you've added all the projects, set the FOLDER target property on all of the third party libraries:
set_property(TARGET target1 target2 ...
PROPERTY FOLDER "ThirdPartyLibs")
Being unfamiliar with C++, I thought that all of my libraries should be compiled along with my project every time. I ended up solving this by writing a shell script that precompiles all of my libraries once as static libraries, and now I don't have to worry about their sources in my IDE, plus I get faster compile times.