How to add project local static or dynamic libraries in CMake? - c++

I am trying to learn OpenGL and would like to set up my build environment with cmake. I will use GLEW (OpenGL Extension Wrangler) and freeglut. However there is one problem... Since I am an absolute beginner to "project management" in C++ and cmake I am having hard time pointing to static libraries. I can understand C++ code but compeletly new to C++ based project build systems.
I am just simply trying to generate a Visual Studio 2015 project. My question is: How can show my static libraries to cmake. My project folder structure:
build # Folder where I would like cmake generated files to be generated in this directory.
include
|-- GL
| |-- freeglut.h
| |-- freeglut_ext.h
| |-- freeglut_std.h
| |-- glut.h
|-- glew
| |-- eglew.h
| |-- glew.h
| |-- glxew.h
| |-- wglew.h
lib
|-- freeglut.lib
|-- freeglut_static.lib
|-- glew32.lib
|-- glew32s.lib
src
|-- main.cpp
CMakeLists.txt
My CMakeLists.txt file is as following:
cmake_minimum_required(VERSION 2.8)
project(sb7application)
find_package(OpenGL REQUIRED)
find_library(LINK_LIBS NAMES freeglut freeglut_static glew32 glew32s PATHS lib)
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${LINK_LIBS} ${OPENGL_LIBRARY})
Here, basicly what I want to do is to ask CMake to link static libraries in the lib folder. However this cmake script doesn't add the Linker input configuration to my visual studio project settings. For example, if I open the generated visual studio project and go to Properties > Linker of the project and add this lib directory to additional library directories and specify the libraries (.libs) in Properties > Linker > Input section, it compiles successfuly. Otherwise, it gives a linkage error saying something like '__imp_glewInit()' cannot be found.
Yeah, how can I show this lib directory and its contents as my static libraries to be linked? If you can explain like you explain to a child and explain each step in detail, I would be greatly appriciated.

Related

extract header file exported Target (Shared Library) using CMake

Background
I am trying to use the export/import feature of CMake.
My project structure as like as follows
cmake_export_import
|-- child_project
| |-- CMakeLists.txt
| |-- include
| | `-- child.h
| `-- src
| |-- child.cpp
| `-- main.cpp
`-- parent_project
|-- CMakeLists.txt
|-- include
| `-- parent.h
`-- src
|-- main.cpp
`-- parent.cpp
I want to use the property of parent_project into child_project
I will not use add_subdirectory in child_project to add the parent project Also I will not do make install during building parent_project. This is my limitation or requirements, whatever you say.
Approach
Building parent proj
I have followed this to achieve this.
CMakeLists.txt from parent_project
cmake_minimum_required(VERSION 3.10)
project(parent)
# Set the main target name to the project name
set(MAIN_TARGET_NAME ${PROJECT_NAME})
# executable file path
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
# Create the C++ shared library
add_library(${MAIN_TARGET_NAME}_lib SHARED ${${PROJECT_NAME}_SOURCE_DIR}/src/parent.cpp)
# Targetting header files for the target ${MAIN_TARGET_NAME}_lib
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC ${PROJECT_SOURCE_DIR}/include)
add_executable(${MAIN_TARGET_NAME}_executable ${${PROJECT_NAME}_SOURCE_DIR}/src/main.cpp)
# Set the name of the binary file and the output directory
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES OUTPUT_NAME "parent_executable")
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/bin)
# Set the name of the lib and the output directory
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES RELEASE_OUTPUT_NAME "parent_library")
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/lib)
target_link_libraries(${MAIN_TARGET_NAME}_executable ${MAIN_TARGET_NAME}_lib)
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC
${PROJECT_SOURCE_DIR}/include)
install(TARGETS ${MAIN_TARGET_NAME}_lib
EXPORT parent_Targets
)
# # exporting target from build tree
export(EXPORT parent_Targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/../cmake_export/parent_Targets.cmake"
NAMESPACE cmake_exp_imp_parent::
)
I have build the parent project
cd parent_project
mkdir build && cd build
cmake .. && make
Then I can see in the root of parent project a folder with a generated .cmake file cmake_export/parent_Targets.cmake
Building child proj
CMakeLists.txt from child_project
cmake_minimum_required(VERSION 3.10)
project(child)
# Set the main target name to the project name
set(MAIN_TARGET_NAME ${PROJECT_NAME})
# executable file path
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
# including the generated cmake of parent proj
include(${CMAKE_BINARY_DIR}/../../parent_project/cmake_export/parent_Targets.cmake)
# get the include header property
get_target_property(PARENT_INCLUDES cmake_exp_imp_parent::parent_lib INTERFACE_INCLUDE_DIRECTORIES)
add_library(${MAIN_TARGET_NAME}_lib SHARED ${${PROJECT_NAME}_SOURCE_DIR}/src/child.cpp)
# Targetting header files for the target ${MAIN_TARGET_NAME}_lib
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC ${PROJECT_SOURCE_DIR}/include
${PARENT_INCLUDES})
add_executable(${MAIN_TARGET_NAME}_executable ${${PROJECT_NAME}_SOURCE_DIR}/src/main.cpp)
# Set the name of the binary file and the output directory
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES OUTPUT_NAME "child_executable")
set_target_properties(${MAIN_TARGET_NAME}_executable PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/bin)
# Set the name of the lib and the output directory
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES RELEASE_OUTPUT_NAME "child_library")
set_target_properties(${MAIN_TARGET_NAME}_lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${${PROJECT_NAME}_SOURCE_DIR}/lib)
target_link_libraries(${MAIN_TARGET_NAME}_executable ${MAIN_TARGET_NAME}_lib
cmake_exp_imp_parent::parent_lib)
I have build the child project
cd child_project
mkdir build && cd build
cmake .. && make
Project is perfectly build.
But i am not satisfied here. Maybe I am decoding the CMake documentation in a wrong way.
Want to know
I have used the following lines i child project CMake file to get the header file of the parent projet. But truly I don't want to use this rather I am inclined to fetch automatically the path from the exported target by importing.
# including the generated cmake of parent proj
include(${CMAKE_BINARY_DIR}/../../parent_project/cmake_export/parent_Targets.cmake)
# get the include header property
get_target_property(PARENT_INCLUDES cmake_exp_imp_parent::parent_lib INTERFACE_INCLUDE_DIRECTORIES)
And then I have appended the variable ${PARENT_INCLUDES}
target_include_directories(${MAIN_TARGET_NAME}_lib PUBLIC ${PROJECT_SOURCE_DIR}/include
${PARENT_INCLUDES})
This helped me to find the header file of parent project.
But my query is as parent project's target is exported so is not is also exported it's all dependencies (I mean the header file path)?
One snipet from the generated cmake file
set_target_properties(cmake_exp_imp_parent::parent_lib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "Users/cmake_export_import/parent_project/include"
)
Here, i can see that the header file path of the parent project is added. But, how do I use it without using get_target_property? I have found here abpout INTERFACE_INCLUDE_DIRECTORIES and thinking somehow it is available while I use target_link_libraries to link the exported target.
Eg:
target_link_libraries(${MAIN_TARGET_NAME}_executable ${MAIN_TARGET_NAME}_lib
cmake_exp_imp_parent::parent_lib)
Any idea is highly appreciable.

cmake errors: Cannot find source file

I'm trying to use cmake for the first time and I'm having a hard time getting this to work. There's a source file and a library file (Lab_4.cpp and Trip_4.cpp, respectively) and they're both in the source folder (Lab_4). Here's what's in my cmake file so far:
cmake_minimum_required (VERSION 2.6)
project (Lab_4)
#add executable
add_executable(Lab_4 ${PROJECT_SOURCE DIR}/Lab_4.cxx)
target_link_libraries (Lab_4 ${EXTRA_LISTS})
#add libraries
add_library (${PROJECT_SOURCE_DIR}/Trip.cxx)
ls shows both files are in that folder. I'm really new to cmake so I'm sure I'm making an obvious mistake but I have no idea what it is. Any help would be appreciated!
cmake_minimum_required(VERSION 2.6)
project(Lab_4)
add_library(Trip_4 Lab_4/Trip4.cpp)
add_executable(Lab_4 Lab_4/Lab_4.cpp)
target_link_libraries(Lab_4 Trip_4 ${EXTRA_LISTS})
Your exact intentions are not completely clear, but the above are the simplest possible instructions according to your current question description.
Are you absolutely sure your CMake is version 2.6? You should update the version to whatever CMake you are currently using (type cmake --version to find out).
To add your sources to CMake project, you can use the aux_source_directory command. If your sources are in Lab_4 folder, then you can do the following:
project(Lab_4)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Lab_4 LAB4_SOURCES)
add_executable(Lab_4 ${LAB4_SOURCES})
The CMAKE_CURRENT_SOURCE_DIR is the path of the directory where the current CMakeFiles.txt is. The example above won't build Trip_4.cpp as a library but use it as another source file together with Lab_4.cpp.
If you wish to build the Trip_4.cpp as library fist I recommend to separate it from Lab_4.cpp to make later uses easier. An example directory structure could be:
MyProject/
|-- app/
| |-- src/
| | `-- Lab_4.cpp
| |-- inc/
| `-- CMakeLists.txt
|-- lib/
| |-- src/
| | `-- Trip_4.cpp
| |-- inc/
| `-- CMakeLists.txt
`-- CMakeLists.txt
In this case the MyProject/lib/CMakeLists.txt will contain something like the following:
project(Trip_4)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src TRIP4_SOURCES)
add_library(Trip_4 ${TRIP4_SOURCES})
The MyProject/app/CMakeLists.txt will be almost the same as I showed in my first example, except now it has to maintain its dependency on Trip_4 library:
project(Lab_4)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../lib
${CMAKE_CURRENT_SOURCE_DIR}/inc
)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src LAB4_SOURCES)
add_executable(Lab_4 ${LAB4_SOURCES})
add_dependencies(Lab_4 Trip_4)
target_link_libraries(Lab_4 Trip_4)
Finally, the MyProject/CMakeLists.txt has to tie together the library and the executable subprojects as follows:
project(MyProject)
add_subdirectory(lib)
add_subdirectory(app)

CMake not linking static library

I have a static library (openexr.lib) that was compiled in debug mode. I'm trying to link this library to a project I've created using CMake. Here's my folder structure.
+-- myproject
| +-- Demo
| +-- build
| +-- libraries
| +-- openexr.lib
| +-- cmake
| +-- GenerateVS2015.bat
| +-- CMakeLists.txt
| +-- Demo.cpp
| +-- stdafx.cpp
So, you can see that I have the openexr.lib located in the libraries directory. I have verified that this .lib file has been compiled as a static library and the file size is approximately 64mb. Then, I have a batch file which is found in the cmake directory which tries to build the projects. Here's what my batch script looks like.
#echo off
set startingDir=%CD%
set basepath=%~dp0
set builddir=%basepath%\..\build
if exist %builddir% (#RD /S /Q %builddir%)
mkdir %builddir%
cd %builddir%
cmake -G "Visual Studio 14 2015 Win64" .. %*
cd %startingDir%
So, this basically creates the build directory if it doesn't already exist (otherwise it clears it so it can repopulate everything). Then, it calls the cmake command with the visual studio generator option.
Now, here's what's in my CMakeLists.txt file:
CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
PROJECT (MyProject)
IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
ADD_DEFINITIONS(-DBUILD_DLL)
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} xmllite.lib")
string(REGEX REPLACE "/bin/[^/]*$" "" VCINSTALLDIR "${CMAKE_C_COMPILER}")
message(STATUS "Guessed MSVC directory: ${VCINSTALLDIR}")
ENDIF()
ADD_LIBRARY(openexr STATIC IMPORTED GLOBAL)
SET_TARGET_PROPERTIES(openexr PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libraries/openexr.lib)
ADD_EXECUTABLE(MyDemo ${CMAKE_CURRENT_SOURCE_DIR}/Demo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/stdafx.cpp)
TARGET_LINK_LIBRARIES(MyDemo openexr)
When I run my batch file, it generates the MyDemo.vcxproj file correctly. I can see it's setup to compile with the target extension .exe (which is what I want). But, when I try to build the project, I get the following error message:
C:\myproject\build\Debug\MyDemo.exe : fatal error LNK1120: 78 unresolved externals
If I open up the property pages for my demo project, I can see that under the Linker section that is trying to link to my openexr.lib file.
But, for some reason it continues to fail every time I try to build the executable file. Can anyone provide a reason why?
EDIT
Here is a link to the full error log in case it helps: errorlog.txt

CMake not linking library files

I have a batch script which when run will simply call a cmake command to build a visual studio C++ project. However, that project should have two references to two static libraries... yet, no matter what I try I can't seem to get my project to link the libraries correctly. Here's how my folder structure looks:
.
+-- build
| +-- x64
| +-- DebugStatic
| +-- mylibA_static.lib
| +-- mylibB_static.lib
+-- Include
+-- Source
+-- myproject
| +-- Demo
| +-- build
| +-- cmake
| +-- GenerateVS2015.bat
| +-- CMakeLists.txt
| +-- Demo.cpp
| +-- stdafx.cpp
Now, I'm running the batch script from within the cmake folder inside the Demo project folder structure. My batch script is as follows:
#echo off
set startingDir=%CD%
set basepath=%~dp0
set builddir=%basepath%\..\build
if not exist %builddir% (mkdir %builddir%)
cd %builddir%
cmake -G "Visual Studio 14 2015 Win64" .. %*
cd %startingDir%
Nothing too complicated. Now, let's look at the CMakeLists.txt file:
cmake_minimum_required (VERSION 2.8)
PROJECT (Demo)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
add_definitions(-DBUILD_DLL)
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} xmllite.lib")
string(REGEX REPLACE "/bin/[^/]*$" "" VCINSTALLDIR "${CMAKE_C_COMPILER}")
message(STATUS "Guessed MSVC directory: ${VCINSTALLDIR}")
endif()
SET(SRCS
Demo.cpp
stdafx.cpp
)
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC ../../Include)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibA_static.lib ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibB_static.lib)
Using this logic, wouldn't CMake build a Visual Studio C++ project called Demo.vcproj... and inside that project file would linked two libraries called mylibA_static.lib and mylibB_static.lib inside the references. However, when I try this, I never get any sort of linked library.
First, in your batch script, you only generated the makefiles, but you didn't actually build the library. So I would suggest you add the line make after cmake and before cd.
Second, I would suggest you surround all your strings with double quotes, in order to be specific, and avoid confusion between constants and variables. Also, it is better to add additional slashes. So, change
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibA_static.lib ${CMAKE_CURRENT_SOURCE_DIR}../../build/x64/DebugStatic/mylibB_static.lib)
to
TARGET_LINK_LIBRARIES(${PROJECT_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/../../build/x64/DebugStatic/mylibA_static.lib" "${CMAKE_CURRENT_SOURCE_DIR}/../../build/x64/DebugStatic/mylibB_static.lib)".
As for the relative pathes, I think you are fine. But be careful when you use CMAKE_SOURCE_DIR and CMAKE_CURRENT_SOURCE_DIR. Here are some notes from CMake Wiki:
CMAKE_CURRENT_SOURCE_DIR this is the directory where the currently
processed CMakeLists.txt is located in
CMAKE_SOURCE_DIR this is the directory which contains the top-level
CMakeLists.txt, i.e. the top level source directory

Include header files from static library with CMake

I have a problem building a CMake project with a static library.
My project strucutre looks like that:
Foo/
|-- CMakeLists.txt
|-- lib/
|-- CMakeLists.txt
|-- libA/
|-- CMakeLists.txt
|-- libA.cpp
|-- libA.h
|-- libAB.h
|-- src/
|-- CMakeLists.txt
|-- main.cpp
|-- srcDirA/
|-- CMakeLists.txt
|-- srcA.h
|-- srcDirB/
|-- CMakeLists.txt
|-- srcB.cpp
|-- srcB.h
And the */CMakeLists.txt look like that:
Foo/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5.1)
project(FOO)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(lib)
add_subdirectory(src)
Foo/lib/CMakeLists.txt:
add_subdirectory(libA)
Foo/lib/libA/CMakeLists.txt:
add_library (staticLibA STATIC libA.cpp)
Foo/src/CMakeLists.txt:
add_subdirectory(srcDirA)
add_subdirectory(srcDirB)
include_directories(".")
add_executable(foo main.cpp)
target_link_libraries(foo LINK_PUBLIC libA)
Foo/src/srcDirA/CMakeLists.txt is empty
Foo/src/srcDirB/CMakeLists.txt is empty
Now I am trying to include the header from my static library into my main project like this:
Foo/src/main.cpp:
#include "srcDirB/srcB.h"
#include "libA/libA.h"
int main() {
//...
return 0;
}
If I try to build this with CMake, libA is generated but I am getting a fatal error:
libA/libA.h: No such file or directory.
Does anyone know what I am doing wrong? Do I have to create a ProjectConfig.cmake file?
You don't need to create any Config files; these are for importing 3rd-party projects.
Since you're using CMake >= 3.5.1, you can esaily specify usage requirements for your libraries. Usage requirements are things like flags or include directories which clients need to build with the library.
So in Foo/lib/libA/CMakeLists.txt:, you'll do this:
add_library (staticLibA STATIC libA.cpp)
target_include_directories(staticLibA INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/..)
Or, if you want the same include directory to apply to libA itself (which is likely), use PUBLIC instead of INTERFACE.
That's all you really need to do. However, given the modern CMake you're using, you should replace your use of the legacy keyword LINK_PUBLIC with its modern equivalent PUBLIC.
Also, since you mention both CMakeLists in .../srcDir* are empty, why have them there in the first place? You can easily get rid of both them and the related add_subdirectory calls.