Using constant folder with cmake - cmake

I have a constant MYPROJECT in my ~/.bashrc file:
export MYPROJECT = /home/loom/my_project
I can see the constant is defined in the terminal:
loom#loom$ ls -ld $MYPROJECT
drwxr-xr-x 25 loom loom 4096 Jul 21 22:12 /home/loom/my_project
I tried to use the constant in my CMakeLists.txt:
add_executable(booo src/main.cpp ${MYPROJECT}/foo/trunk/bar/File.h)
However, it produces an error:
CMake Error at CMakeLists.txt:17 (add_executable):
Cannot find source file:
/foo/trunk/bar/File.h
Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
.hxx .in .txx
CMake Error: CMake can not determine linker language for target: booo
CMake Error: Cannot determine link language for target "booo".
It is processed without errors if I have used explicit path without $MYPROJECT:
add_executable(booo src/main.cpp /home/loom/my_project/foo/trunk/bar/File.h)
How to use path defined in .bashrc in cmake?

Instead of
add_executable(booo src/main.cpp ${MYPROJECT}/foo/trunk/bar/File.h)
use
add_executable(booo src/main.cpp $ENV{MYPROJECT}/foo/trunk/bar/File.h)
The CMake documentation states:
Use the syntax $ENV{VAR} to read environment variable VAR. See also
the set() command to set ENV{VAR}.
https://cmake.org/cmake/help/v3.4/variable/ENV.html

Turning my comment into an answer
There are a lot of ways you could do this in CMake. Out of bad experiences with using environment variables directly inside CMake projects (they have to be valid not only during the first call to CMake configuration/generation but also later during all consecutive builds), I would recommend to transfer your constant into a cached CMake variable.
I'm using one of the following ways in my projects:
Injecting MYPROJECT CMake via its -D option, e.g.
cmake -DMYPROJECT:PATH=$MYPROJECT ..
Then CMake would cache this value in its own MYPROJECT internal variable.
When you get values from the "outside" you should think about what to do if no value is provided or it's not a valid directory. Throwing a fatal error and/or retry with a default value?
The following example shows only transformation steps and sanity checks (no default value retry):
if (NOT DEFINED MYPROJECT OR NOT IS_DIRECTORY MYPROJECT)
file(TO_CMAKE_PATH "$ENV{MYPROJECT}" _ENV_MYPROJECT_REL)
get_filename_component(_ENV_MYPROJECT "${_ENV_MYPROJECT_REL}" ABSOLUTE)
if (NOT _ENV_MYPROJECT)
message(FATAL_ERROR "Environment variable MYPROJECT not provided.")
else()
set(MYPROJECT "${_ENV_MYPROJECT}" CACHE INTERNAL "")
endif()
endif()
Using find_path(), which will also cache its result:
find_path(
MYPROJECT_INCLUDE_DIR
NAMES File.h
PATHS ENV MYPROJECT
/home/loom/my_project
PATH_SUFFIXES foo/trunk/bar
)
if (NOT MYPROJECT_INCLUDE_DIR)
...
Or - assuming your external project does not only consists of header files but also libraries like foo.a - following CMake's A Sample Find Module and extending the find_path() code from above by:
find_library(
MYPROJECT_LIBRARY
NAMES foo
PATHS ENV MYPROJECT
PATH_SUFFIXES foo/lib
)
if(MYPROJECT_INCLUDE_DIR AND MYPROJECT_LIBRARY AND NOT TARGET MyProject::MyProject)
add_library(MyProject::MyProject UNKNOWN IMPORTED)
set_target_properties(
MyProject::MyProject
PROPERTIES
IMPORTED_LOCATION "${MYPROJECT_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MYPROJECT_INCLUDE_DIR}"
)
endif()
Now you can use it directly as any other CMake target with
add_executable(booo src/main.cpp)
target_link_libraries(booo MyProject::MyProject)
More References
What's the CMake syntax to set and use variables?
preferred cmake project structure

Related

Adding compiler "#define" flags in sub_directory CMakeLists.txt

My directory structure of project is build with multi CMakeLists.txts.
root
CMakeLists.txt
Src
main.c
CMSIS_lib
calculate.c
calculate.h
CMakeLists.txt
cmake
toolchain.cmake
build
In my CMSIS_lib I build separately my dependency source files calculate.c
and calculate.h with CMSIS_lib/CMakeList.txt:
set(util_source_files
calculate.c
calculate.h
)
add_library(util ${util_source_files})
target_include_directories(util calculate.h)
In my root CMakeLists.txt:
cmake_minimum_required(VERSION 3.4)
project(main_projct)
set(TOOLCHAIN_PREFIX /opt/gcc-arm-none-eabi)
set(CMAKE_TOOLCHAIN_FILE cmake/toolchain.cmake)
add_subdirectory(CMSIS_lib)
add_executable(main_projct main.c)
target_link_libraries(main_projct util)
Problem is that I must tell my compiler to add a #define GUCCI in my calculate.h (In MakeFile I know there is flag to tell a header define with -DGUCCI). I would like to add this flag to my compiler in my CMSIS_lib/CMakeList.txt, because when the first CMSIS_lib/CMakeList.txt is done building, he will skip everything under #ifndef GUCCI in my calculate.h, and when added in root CMakeLists.txt with target_link_libraries() I will not have all defines configuration correctly.
I am using cross-compiler and in my toolchain.cmake I use to define compiler flags with command SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_DEFINITIONS GUCCI}"), but this is to late because this only is seen by my root CMakeLists.txt and not by my sub director CMakeLists.txt.
Your CMSIS_lib/CMakeLists.txt should look like this:
set(util_source_files
calculate.c
calculate.h
)
add_library(util ${util_source_files})
target_include_directories(util ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(util PUBLIC GUCCI)
Note target_compile_definitions line, with PUBLIC parameter: it instructs cmake to use -DGUCCI compiler option while compiling util, and all targets that are linked against util.
Also note change in target_include_directories. You placed header file as parameter, but you should place directory instead.

CMake library not found

I try to run the makefile that using cmake produced. It generate an error
ld: library not found for -lhello
clang: error: linker command failed with exit code 1 (use -v to see invocation)
the file directory is:
the cmakelists.txt is:
the main.c file is:
the ERROR:
I think I set the right directory. How to solve this ERROR?
CMake has a system if you want to link libraries. For many standard libraries we have cmake modules which will allow you to use the find_package command. This will set some variables for include directories and libraries. If there is no such thing for your library you can use find_path for the include files and find_library to search for a library.
Here is what you could do (untested, just out of my head):
add_executable(main main.c)
target_include_directories(
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC ${CMAKE_SOURCE_DIR}/include/hello
)
find_library (
HELLO_LIB
NAMES hello libhello # what to look for
HINTS "${CMAKE_SOURCE_DIR}/lib" # where to look
NO_DEFAULT_PATH # do not search system default paths
)
# check if we found the library
message(STATUS "HELLO_LIB: [${HELLO_LIB}]")
if (NOT HELLO_LIB)
message(SEND_ERROR "Did not find lib hello")
endif
target_link_libraries(main
${HELLO_LIB}
)
Use message to debug your cmake files. If you define the library in cmake as well you can link directly against the cmake target.
When your library isn't in a standard path liking /usr/lib, you should use link_directories() in your CMakeLists.txt to specify a non-standard library path which contains your library. Notice that you MUST put your link_directories() ahead of add_executable() as the following shows:
link_directories(../../lib)
add_executable(newhello main.c)
include_directories(../../include)
target_link_libraries(newhello hello)

cmake include directories issue with cygwin

I want to add include directories to my project, which are defined in some environment variable SOME_DIR. SOME_DIR is set to 'D:\some_dir'.
Here's how I've added it in CMakeLists.txt:
include_directories($ENV{SOME_DIR}/include)
After generating Unix Makefiles via cygwin, compiler can't find header files from this directory. So, I've added some debug outputs to check list of included directories like this:
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs})
message(STATUS "dir='${dir}'")
endforeach()
Output is:
dir='/cygdrive/d/myproject/D:/some_dir/include'
My question is: What should I do so that cmake include correct path, i.e. 'D:/some_dir/include'?

how to use CMake file (GLOB SRCS *. ) with a build directory

this is my current CMakeLists.txt file
cmake_minimum_required(VERSION 3.3)
set(CMAKE_C_FLAGS " -Wall -g ")
project( bmi )
file( GLOB SRCS *.cpp *.h )
add_executable( bmi ${SRCS})
This builds from my source directory, but I have to clean up all the extra files after. My question is how do I build this from a build directory if all my source files are in the same source directory?
thanks
If you really need to use file(GLOB …), this CMakeLists.txt should work :
cmake_minimum_required(VERSION 3.3)
project(bmi)
add_definitions("-Wall" "-g")
include_directories(${PROJECT_SOURCE_DIR})
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/*.cpp)
add_executable(bmi ${SRC_FILES})
In this case you have to launch cmake from your build directory every time you add or delete a source file :
cmake <your_source_dir> -G <your_build_generator>
As Phil reminds, CMake documentation doesn't recommend this use of GLOB. But there are some exceptions. You'll get more information on this post.
If you don't meet those exceptions, you'd rather list your source files than use GLOB :
set(SRC_FILES ${PROJECT_SOURCE_DIR}/main.cpp
${PROJECT_SOURCE_DIR}/bmi.cpp
… )
NB : if you have #include of your .h files in .cpp files, I don't see any reason to put them in add_executable, you just need to specify include directory with include_directories.
Cmake used to only update the list of source files if CMakeLists.txt was changed since the last cmake run or if cmake was used to configure the project again.
In cmake 3.11.0 recursive search and automatic re-configuration on adding or deleting source files was added. Since then you can use the following snippet:
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS *.cpp *.h)
else()
file(GLOB SOURCE_FILES *.cpp *.h */*.h */*.cpp)
endif()
The file() command after the else() provides at least a bit of backwards compatibility: It still searches for source files in the current folder and its direct subfolders. But it doesn't automatically recognize if there are new files or old files have been deleted.
Note that VERSION_GREATER_EQUAL is only available in cmake >= 3.7

Finding directories in CMake

I'm trying to build a static library and use it for a compiling a
fortran file. If I do:
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
add_library(mylib STATIC ${lib_src}/mylib.for)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
add_executable(bin/out ${PROJECT_SOURCE_DIR}/src/program.f)
target_link_libraries(bin/out mylib)
then it all works (note the library is built into the binary directory
root, but the fortran is compiled into a subdirectory); but, if I do
set(archives ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
add_library(mylib STATIC ${lib_src}/mylib.for)
find_library(mylib NAMES mylib PATHS ${archives})
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
add_executable(bin/out ${PROJECT_SOURCE_DIR}/src/program.f)
target_link_libraries(bin/out mylib)
I get an error when I run cmake:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
mylib linked by target "bin/out" in directory /home/chris/project
If I leave out the final 2 lines, then the archive file does get
written to the lib subdirectory, as libmylib.a as expected. If I do
set(archives ${PROJECT_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
add_library(mylib STATIC ${lib_src}/mylib.for)
find_library(mylib NAMES mylib PATHS ${archives})
include_directories(${archives})
set(libs ${libs} ${mylib})
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
add_executable(bin/out ${PROJECT_SOURCE_DIR}/src/program.f)
target_link_libraries(bin/out {LIBS})
then the cmake command runs fine, but running make then generates
compile errors (I know the set command and target_link_libraries
variables are different cases - one of the things I don't understand
is why this only fails when make is run, instead of cmake; if the
variables are the same case, then I get the same error as above).
So, how can I get CMake to recognise my ${PROJECT_BINARY_DIR}/lib
folder that is created during the CMake run? Can someone point out my
(probably obvious) mistake?!
You should not use find_library on one of your target, remove the line:
find_library(mylib NAMES mylib PATHS ${archives})
CMake already knowns about the mylib library as it's one of its target and calling find_library shadows the mylib variable.
You can keep you target_link_libraries call the same, as arguments can be either path to libraries or targets.r