CMake not linking library files - c++

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

Related

CMake: Build and install locally stored submodule

There are numerous similar questions on Stack Overflow, but none come near to answering my question.
I have a C++ library built using CMake:
mylib
| - CMakeLists.txt
| - src/
| | - m.h
| | - m.cpp
| - include/
| | - mylib/
| | | - a.h
| | | - something/
| | | | - some.h
| - cmake/
| - mylibConfig.cmake.in
| - mylibConfigVersion.cmake.in
I then create another library or executable which includes the aforementioned library:
myapp
| - CMakeLists.txt
| - src/
| | - main.cpp
| - include/
| | - myapp/
| | | - b.h
| - libs
| | - mylib
And would like to use mylib within myapp like so. Please take notice how mylib headers are included in a directory like format:
#include <mylib/a.h>
#include <mylib/something/some.h>
mylib should be built when building myapp so that the following works without any other build steps:
$ cd myapp/build
$ cmake ..
$ make
Here is a list of some of the Stack Overflow posts I have reviewed. I have attempted each, and they simply do not work:
CMake: How to build external projects and include their targets
CMake ExternalProject_Add() and FindPackage()
Assuming in the project described, only header files in the mylib/include directory are to be considered public:
The top-level CMakeLists.txt for mylib needs to contain:
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(mylib VERSION 0.1 LANGUAGES CXX)
add_library(${MyLibName} ${MyLib_SOURCES})
target_include_directories(${MyLibName}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
This ensures that projects including mylib will only have access to files in the include/ directory. Files in include/ can be used like #include <myfile.h> and files in include/mylib (the general convention) can be used like #include <mylib/myfile.h>.
The top-level CMakeLists.txt for myapp should include:
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(myapp VERSION 0.1 LANGUAGES CXX)
add_subdirectory(libs/mylib)
add_executable(${MyAppName} ${MyApp_SOURCES})
target_link_libraries(${MyAppName} ${MyLibName}
The use of add_subdirectory ensures that mylib will be built before myapp and target_link_libraries adds mylib to the executable.
As mentioned by Tzalumen, make sure you take a look at the CMake tutorials and prefer the use of cmake --build . instead of make.
So, I actually do this in one of my projects.
First, the library needs to be built with ADD_LIBRARY(), but I'm assuming you do that. Something like
ADD_LIBRARY (${MyLibName} ${SOURCES}) in the library's CMakeLists.txt
That adds the library into cmake's list of libraries.
Second, you add the library to your project with
target_link_libraries (${PROJECT_NAME} ${MyLibName}) in the executable's CMakeLists.txt, the same one where you have your add_executable (${PROJECT_NAME} ${SOURCES})
That will set up the dependency chain to force MyLibName to explicitly build before PROJECT_NAME.

Travis CI does not find header files

Basically I wanted to learn CMake and GTest. On top of that I wanted to try out CI's and I have added my simple project to Travis CI and AppVeyor. The project compiles well on my local machine (tested with vs 2017 and g++, not sure about the versions, because I am not near it at the moment) as well as on AppVeyor.
The project consists of a few headers compiled to a static library, executable for "manual testing" the static library and gtest (no tests at the moment, just a template).
The structure looks like that:
.
+-- .appveyor.yml
+-- .travis.yml
+-- CMakeLists.txt
+-- main
| +-- main.cpp [The manual testing exec]
| +-- CMakeLists.txt
+-- include
| +-- Utility.h
| +-- CMakeLists.txt
| +-- Other header files to compile to static library
+-- test
| +-- CMakeLists.txt
| +-- CMakeLists.txt.in
| +-- testutility
| | +-- main.cpp [sample exe file for gtest, not used yet]
| | +-- CMakeLists.txt
During build on Travis CI, it cannot find headers for the static library.
I have tried using
include_directories(".") on main CMakeLists.
Main CMakeLists.txt
cmake_minimum_required (VERSION 3.1)
project (primlibrary)
enable_testing()
include_directories(".")
add_subdirectory(include)
add_subdirectory(main)
add_subdirectory(test)
Travis CI error message
CMake Error at include/CMakeLists.txt:12 (add_library):
Cannot find source file:
Utility.h
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
-- Generating done
-- Build files have been written to: /home/travis/build/SoIAS/PrimLibrary/build
The command "cmake .." exited with 1.
$ make
Scanning dependencies of target prim_library
[ 9%] Linking CXX static library libprim_library.a
[ 9%] Built target prim_library
Scanning dependencies of target manualtestingapp
[ 18%] Building CXX object main/CMakeFiles/manualtestingapp.dir/main.cpp.o
/home/travis/build/SoIAS/PrimLibrary/main/main.cpp:2:29: fatal error: include/Utility.h: No such file or directory
#include "include/Utility.h"
And include/CMakeLists.txt:
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(prim_library_srcs
ForwardList.h
LinkedList.h
Utility.h
tempfix.cpp
)
add_library(prim_library STATIC ${prim_library_srcs})
set_target_properties(prim_library PROPERTIES LINKER_LANGUAGE CXX)
target_include_directories(prim_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
CI and git links:
- Travis CI last build
- Appveyor build
- Github
Do you know what am I doing wrong?
The problem was solved literally a dozen minutes after posting. The Utility.h file locally had capitalized first letter while the one on git was lower case. It was solved by using git command to capitalize the first letter of the file on git:
git mv include/utility.h include/Utility.h

CMake Build a target as part of project and independently

I am attempting to write CMakeLists.txt for a platform. The platform has multiple projects which share some libraries and have their own executables. I wish to be able to build each project as a whole, as well as individual executables from within the project.
out-of-source root build works fine:
mkdir root-build && cd root-build && cmake ../root
out-of-source executable build does not work:
mkdir ui-build && cd ui-build && cmake ../root/project1/ui
My current directory structure is as follows:
root/
+-- CMakeListst.txt
+-- lib/
+-- CMakeLists.txt
+-- sharedLib1/
+-- CMakeLists.txt
+-- sharedLIb2/
+-- CMakeLists.txt
+-- project1/
+-- CMakeLists.txt
+-- ui/
+-- CMakeLists.txt
+-- executable1/
+-- CMakeLists.txt
+-- shared/
+-- CMakeLists.txt
+-- project2/
+-- CMakeLists.txt
+-- executable/
+-- CMakeLists.txt
My current project structure builds all targets and libraries as part of the project great, but I can't figure out how to build an executable in isolation.
The main cmake file for a project is root/project*/CMakeLists.txt. This file INCLUDES root/CMakeLists.txt, which does some setup common to all projects (e.g setting CMAKE_MODULE_PATH and find_package for third-party libraries). The project cmake file also calls add_subdirectory for each of it's executables and add_subdirectory(../lib libs).
This allows executables to link against any of the targets defined in the root/lib subdirectory. If I then try to build the executable independently of the project, CMake will not be able to find the target the executable needs to link against. Is there an elegant solution / pattern that would allow this? One way I thought of was to just add_subdirectory(../../ root) from the ui/CMakeLists.txt, with something resembling header guards to prevent an infinite add_subdirectory loop.
Use make executable1 (for example) to build individual targets.

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

Build and use GTest globally in a project

First, I need to claim that I have been going around Stack Overflow and arrive answers of how to use ExternalProject to build Google Test within a project, for example here.
Now let's say what I have in my project is something like this
+-- CMakeLists.txt (the big CMake File for the entire project)
+-- tests
| +-- CMakeLists.txt (contain of all the small project in the tests)
| +-- Test1
| +-- CMakeLists.txt (test file for Test1 program)
| +-- test_1.cpp
| +-- test_1.h
| +-- Test2
| +-- CMakeLists.txt (test file for Test2 program)
| +-- test_2.cpp
| +-- test_2.h
| +-- Test3
| +-- CMakeLists.txt (test file for Test3 program)
| +-- test_3.cpp
| +-- test_3.h
Now, is there anyway that I can configure and build Google Test using ExternalProject in the big CMakeLists.txt file (at the root folder), and then use those library to build each separate tests in their corresponding folder. At the moment I can only download-built-link an entire new set of GTest libraries in each of the sub folder Test1, Test2, Test3 which is very inefficient. Is there an alternative way to get around this?
It is unclear without seeing your actual CMakeLists.txt files what's happening in your case. That said, used in the normal way, ExternalProject doesn't give you the CMake targets to link to directly, you have to do more manual work to get something you can add to the linked library list of your targets needing to use GTest. So depending on how you've manually defined those things, that could be why you are finding you need to build GTest in each TestX subdirectory.
In the other SO question you linked to, my answer there and the linked article therein mentions how to download and build GTest as part of the CMake stage rather than at build time. This gives you the CMake targets which you can then link against directly without having to define them manually. If you follow that method to download and build GTest in your top level CMakeLists.txt file, then the gtest and gtest main targets will be visible to all subdirectories. Furthermore, you won't have to go figure out the name of the library for each platform, etc. since the CMake target already handles that for you. Thus, in your various Test1, Test2 and Test3 subdirectories, all you'd have to do is to add gtest or gtest_main to the list of libraries each test program linked against.
My approach is to create a directory at the same level of your test projects:
+-- CMakeLists.txt (the big CMake File for the entire project)
+-- tests
| +-- CMakeLists.txt (contain of all the small project in the tests)
| +-- gtest
| +-- CMakeLists.txt (use ExternalProject_Add to import GTest)
| +-- Test1
| +-- CMakeLists.txt (test file for Test1 program)
| +-- test_1.cpp
| +-- test_1.h
| +-- Test2
| ...
In the CMakeLists.txt for gtest I define the gtest target and export the variables for the include dir and library path for the other projects to use.
It works for me and I get gtest built only once so all test projects can link against it. But I'll try to give a shot to Craig Scott's approach to build gtest during Makefile generation instead of at build time.
The CMakeLists.txt file under the gtest folder looks like this:
cmake_minimum_required(VERSION 2.8.11)
include(ExternalProject)
set(GMOCK_VERSION "1.7.0")
set(GMOCK_DIR "${CMAKE_CURRENT_BINARY_DIR}/gmock-${GMOCK_VERSION}")
ExternalProject_Add(project_gmock
SVN_REPOSITORY http://googlemock.googlecode.com/svn/tags/release-${GMOCK_VERSION}
PREFIX ${GMOCK_DIR}
CMAKE_ARGS -DCMAKE_C_COMPILER:PATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:PATH=${CMAKE_CXX_COMPILER}
# Disable update step
UPDATE_COMMAND ""
# Disable install step
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(project_gmock source_dir)
ExternalProject_Get_Property(project_gmock binary_dir)
include_directories(${source_dir}/gtest/include)
add_library(gtest STATIC IMPORTED GLOBAL)
set_target_properties(gtest PROPERTIES IMPORTED_LOCATION ${binary_dir}/gtest/libgtest.a)
set_target_properties(gtest PROPERTIES INCLUDE_DIRECTORIES ${source_dir}/gtest/include)
add_dependencies(gtest project_gmock)
get_property(GTEST_INCLUDE_DIR TARGET gtest PROPERTY INCLUDE_DIRECTORIES)
set(GTEST_INCLUDE_DIR "${GTEST_INCLUDE_DIR}" PARENT_SCOPE)
add_library(gtest_main STATIC IMPORTED GLOBAL)
set_target_properties(gtest_main PROPERTIES IMPORTED_LOCATION ${binary_dir}/gtest/libgtest_main.a)
add_dependencies(gtest_main project_gmock)
include_directories(${source_dir}/include)
add_library(gmock STATIC IMPORTED GLOBAL)
set_target_properties(gmock PROPERTIES IMPORTED_LOCATION ${binary_dir}/libgmock.a)
set_target_properties(gmock PROPERTIES INCLUDE_DIRECTORIES ${source_dir}/include)
add_dependencies(gmock project_gmock)
get_property(GMOCK_INCLUDE_DIR TARGET gmock PROPERTY INCLUDE_DIRECTORIES)
set(GMOCK_INCLUDE_DIR "${GMOCK_INCLUDE_DIR}" PARENT_SCOPE)
add_library(gmock_main STATIC IMPORTED GLOBAL)
set_target_properties(gmock_main PROPERTIES IMPORTED_LOCATION ${binary_dir}/libgmock_main.a)
add_dependencies(gmock_main project_gmock)