How to specify files that depend on other files - cmake

I am pretty new to cmake and here is my CMakeLists.txt file on my project's root directory
cmake_minimum_required (VERSION 2.6)
project (Tools C)
set(CMAKE_C_FLAGS "-ansi -pedantic -Wall -Werror")
include_directories("include")
SET_SOURCE_FILES_PROPERTIES(lib/xstr.c PROPERTIES
OBJECT_DEPENDS "lib/xalloc.c")
SET_SOURCE_FILES_PROPERTIES(lib/counter.c PROPERTIES
OBJECT_DEPENDS "lib/xstr.c")
SET_SOURCE_FILES_PROPERTIES(lib/dynamic_array.c PROPERTIES
OBJECT_DEPENDS "lib/xalloc.c")
SET_SOURCE_FILES_PROPERTIES(lib/list.c PROPERTIES
OBJECT_DEPENDS "lib/xalloc.c")
add_executable(cat cat.c lib/xalloc.c lib/xfopen.c)
add_executable(counter counter.c lib/counter.c)
add_executable(darr dynamic_array.c lib/dynamic_array.c)
add_executable(linked list.c lib/list.c)
I keep c files that contains a main() function on my root directory. I keep other c files on {project_root}/lib directory.
My problem is that I am getting following error:
[ 33%] Built target cat
mingw32-make.exe[2]: *** No rule to make target 'lib/xstr.c', needed by 'CMakeFi
les/counter.dir/lib/counter.c.obj'. Stop.
CMakeFiles\Makefile2:94: recipe for target 'CMakeFiles/counter.dir/all' failed
mingw32-make.exe[1]: *** [CMakeFiles/counter.dir/all] Error 2
Makefile:75: recipe for target 'all' failed
mingw32-make.exe: *** [all] Error 2
What I want to achieve is to link{project_root}/cat.c with {project_root}/lib/xstr.c and {project_root}/lib/xalloc.c etc.
How can I achieve that?

You are taking cmake far too complicated! You have to set dependencies within targets, not source files. In your specific case, I suggest you add a few intermediate libraries. You can have them static so that the executable linking to them will not have problems in retrieving the linked library at runtime (the executable will integrate all the symbols and definitions he needs from the library).
Your code can be reduced as follow:
cmake_minimum_required (VERSION 2.8) # <<--2.6 is very outdated
project (Tools C)
set(CMAKE_C_FLAGS "-ansi -pedantic -Wall -Werror")
include_directories("include")
add_library(xalloc STATIC lib/xalloc.c)
add_library(xstr STATIC lib/xstr.c)
add_executable(cat cat.c lib/xfopen.c)
add_executable(counter counter.c lib/counter.c)
add_executable(darr dynamic_array.c lib/dynamic_array.c)
add_executable(linked list.c lib/list.c)
target_link_libraries(cat xalloc)
target_link_libraries(counter xstr xalloc) #xstr needs stuff from xalloc
target_link_libraries(darr xalloc)
target_link_libraries(linked xalloc)
Note: Are you sure you need darr and linked as executables? They look very much like a library... Also, having a file called counter.c and one lib/counter.c does not seem very safe (same for dynamic_array and list).
Some useful links:
http://www.cmake.org/cmake/help/v3.0/command/add_library.html
http://www.cmake.org/cmake/help/v3.0/command/target_link_libraries.html

Related

ld: library not found for -lnetcdf

I am new to CMake. I can not resolve the flowing error. Could someone help me?
------------ERROR--------------
ld: library not found for -lnetcdf
collect2: error: ld returned 1 exit status
make[3]: *** [NUP] Error 1
make[2]: *** [CMakeFiles/NUP.dir/all] Error 2
make[1]: *** [CMakeFiles/NUP.dir/rule] Error 2
make: *** [NUP] Error 2
------------------- CMake File------------------
cmake_minimum_required(VERSION 3.10.0)
project(NUP Fortran)
enable_language(Fortran)
set(INCLUDE_FILE /usr/local/Cellar/netcdf/4.7.4/include)
set(lib_FILE /usr/local/Cellar/netcdf/4.7.4/lib)
find_package(netcdf REQUIRED)
if(netcdf_FOUND)
include_directories(${INCLUDE_FILE})
set(
SOURCE_FILES
${PROJECT_BINARY_DIR} unpack.f90
)
add_executable(NUP ${SOURCE_FILES} )
target_link_libraries(NUP netcdf)
endif()
--------------unpack.f90-------------------
PROGRAM unpack_array
IMPLICIT NONE
INCLUDE 'netcdf.inc'
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12,307)
......
I am using MACOS Catalina. Apple clang version 11.0.3 (clang-1103.0.32.59)
Target: x86_64-apple-darwin19.4.0
If you're using find_package() to find NetCDF on your machine, you shouldn't need to manually specify the paths as you have. Let find_package do that for you.
Note: CMake doesn't ship with a Find Module for NetCDF, so you'll have to download one (like this one) from the internet. Then, you need to tell CMake where to locate this FindNetCDF.cmake file on your system using CMAKE_MODULE_PATH. Finally, you can use the NetCDF::NetCDF imported target to link NetCDF to your project's targets.
cmake_minimum_required(VERSION 3.10.0)
project(NUP Fortran)
# Don't need this, you already enabled Fortran above in the 'project' call.
enable_language(Fortran)
set(INCLUDE_FILE /usr/local/Cellar/netcdf/4.7.4/include)
set(lib_FILE /usr/local/Cellar/netcdf/4.7.4/lib)
# Add the location of the 'FindNetCDF.cmake' file to your module path.
list(APPEND CMAKE_MODULE_PATH "/path/to/downloaded/find/module")
# Then, call find package for NetCDF.
find_package(NetCDF REQUIRED)
if(${NetCDF_FOUND})
# Don't need this if you use the imported target below.
include_directories(${INCLUDE_FILE})
# Don't provide directories with source file list.
set(SOURCE_FILES
${PROJECT_BINARY_DIR}
unpack.f90
)
add_executable(NUP ${SOURCE_FILES})
# Use the imported target to link netcdf instead.
target_link_libraries(NUP PRIVATE NetCDF::NetCDF)
endif()
As commented, there are other approaches to adding NetCDF to your CMake project. If you use a different find module, the syntax of the provided NetCDF CMake variables and imported targets may be slightly different. You'll have to examine the find module file itself.
In addition, you may instead use a CMake package configuration file (e.g. netCDFConfig.cmake) downloaded from the internet to add NetCDF to your project. In this case, you would still use find_package(), but you would specify the configuration file's location using CMAKE_PREFIX_PATH, rather than CMAKE_MODULE_PATH.
You can find detailed descriptions for each of these approaches in the CMake find_package() documentation. I highly encourage you spend some time to read it.

CMake finding packages, missing -lGLU

I'm writing my first cmake file from scratch for a project that requires, amongst other things, ODE. If there are other bad practices that I'm doing, comments on that are also very welcome.
The makefile generation step with cmake is working, however in the build step with make, it complains:
Scanning dependencies of target ode_oscillex
[ 50%] Building CXX object CMakeFiles/ode_oscillex.dir/ode_oscillex.cpp.o
[100%] Linking CXX executable ode_oscillex
ld: library not found for -lGLU
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [ode_oscillex] Error 1
make[1]: *** [CMakeFiles/ode_oscillex.dir/all] Error 2
make: *** [all] Error 2
And I can't figure out what library -lGLU is supposed to be. Based on the Eclipse setup this project was previously set up in, I'm already adding X11, OpenGL and GLUT, and there shouldn't be anything else additionally. I'm setting this up on OS X.
My CMakeLists.txt is
cmake_minimum_required (VERSION 3.3)
set(PROJECT_NAME ode_oscillex)
set(TARGET ${PROJECT_NAME})
project (${PROJECT_NAME})
# Add manually built ODE library
set(ODE_PATH /Users/steve/ode-0.13.1)
set(ODE_INC_PATH ${ODE_PATH}/include)
set(ODE_LIB_PATH ${ODE_PATH}/ode/src/.libs)
set(DS_INC_PATH ${ODE_PATH}/include/drawstuff) # not sure why drawstuff needs subfolder to be specified, when ode doesn't...
set(DS_LIB_PATH ${ODE_PATH}/drawstuff/src/.libs)
include_directories(${ODE_INC_PATH} ${DS_INC_PATH})
link_directories(${ODE_LIB_PATH} ${DS_LIB_PATH})
### Add manually built Eigen
SET( EIGEN3_INCLUDE_DIR ../eigen3.1.4) # This is also sucky
include_directories(${EIGEN3_INCLUDE_DIR})
### Add Executable
add_executable(${TARGET} ${PROJECT_NAME}.cpp)
### Adding other required libraries
find_package(X11 REQUIRED)
find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)
include_directories( ${OPENGL_INCLUDE_DIRS} ${GLUT_INCLUDE_DIRS} ${X11_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${X11_LIBRARIES})
target_link_libraries(${TARGET} ode drawstuff GLU GL glut X11)
The problematic instructions for me are:
target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${X11_LIBRARIES})
target_link_libraries(${TARGET} ode drawstuff GLU GL glut X11)
Generally, you shouldn't link "project", you need to link
"project artifacts" (i.e. "${TARGET}").
So try to rewrite this as follows:
target_link_libraries(${TARGET} <other required libraries like "ode"...> {OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${X11_LIBRARIES})
and remove the other line completely.
Generally when you called find_package()s above, those macros filled in corresponding variables which tell how to properly get proper compiler and linker flags (for OpenGL, GLUT etc respectively). So when you use appropriate variables in the target_link_libraries invocation, you can be sure that the linker flags and libraries, specific to a particular system.
On the the other hand, when you simply use target_link_libraries(<target> GLU GL glut...) you forcibly instruct cmake to link your <target> against those GL-related libraries, no matter if they installed in a particular system, nor how they are named there.
So in general you should prefer the first approach for external libraries, it's more reliable and generic.

How to use libxml2 with CMake?

I am using CLion editor (with CMake) for my C project,
I've never used an external library though,
my question is how do I link an external lib (for ex. libxml2) to my project?
I've seen some questions similar to this but none worked for me.
My project is compiled on Windows, and I have libxml .dll, include, and .lib files(binaries for Windows).
Edit: My CMakeLists.txt file after the answer suggested:
cmake_minimum_required(VERSION 3.2)
project(time_table)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(SOURCE_FILES
course.c
course.h
day.h
defines.h
find_tables.c
find_tables.h
item.h
parse_info.c
parse_info.h
table.c
table.h
time_table.c grades.c grades.h)
link_libraries(C:/Users/Gal/Desktop/time_table/libxml2-2.7.8.win32/lib)
add_executable(time_table ${SOURCE_FILES})
target_link_libraries(time_table libxml2)
and this is what I get:
"C:\Program Files (x86)\JetBrains\CLion 1.1.1\bin\cmake\bin\cmake.exe" --build C:\Users\Gal\.clion11\system\cmake\generated\2eda76ff\2eda76ff\Debug --target time_table -- -j 8
[ 14%] Linking C executable time_table.exe
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: cannot find -llibxml2
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[3]: *** [time_table.exe] Error 1
CMakeFiles\time_table.dir\build.make:225: recipe for target 'time_table.exe' failed
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/time_table.dir/all' failed
mingw32-make.exe[2]: *** [CMakeFiles/time_table.dir/all] Error 2
CMakeFiles\Makefile2:78: recipe for target 'CMakeFiles/time_table.dir/rule' failed
mingw32-make.exe[1]: *** [CMakeFiles/time_table.dir/rule] Error 2
What is wrong?
There is a key work for libraries installed on your machine. <lib_name>_INCLUDE_DIR and <lib_name>_LIBRARIES once you have done find_package. This works for me.
find_package(LibXml2 REQUIRED)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/<my_stuff> ${LIBXML2_INCLUDE_DIR})
add_executable(<my_exe> <my_source_files>)
target_link_libraries(<my_exe> ${LIBXML2_LIBRARIES})
---- Just a note for your curiosity ----
If you ever needed to build (1) a (static) library of your calculations, maybe reusable, and (2) an executable that uses that library and LIBXML2, do this.
find_package(LibXml2 REQUIRED)
# add a calculation library: file "lib<mycalc>.a"
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/<your header files folder> ${LIBXML2_INCLUDE_DIR})
add_library(<mycalc> STATIC ${CMAKE_CURRENT_SOURCE_DIR}/<your src files folder>)
target_link_libraries(<mycalc> ${LIBXML2_LIBRARIES})
# add the executable: file <my_exe>
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/<your header files folder> ${LIBXML2_INCLUDE_DIR})
add_executable(<my_exe> <your exe src files>)
target_link_libraries(<my_exe> <mycalc> ${LIBXML2_LIBRARIES})
Assume that full path to the library(.lib) can be expressed as
<library_dir>/<library_name>.lib
For compile executable, which uses this library, you need:
CMakeLists.txt:
link_libraries(<library_dir>)
add_excutable(<my_exe> ..)
target_link_libraries(<my_exe> <library_name>)
For use(run) compiled executable, you need to have <library_name>.dll either under one of the directory, listed in the PATH environment variable, or within same directory with executable.
there are 32bit and 64bit version of libxml2, to be found here.
you can put them below the PATH - or the C:\Windows directory.
With moderns CMake (version 3.2 or newer), it got much easier:
Search for the library. CMake has an according find module, thus it boils down to
find_package(LibXml2 REQUIRED)
Then you can link the library it to your target foo
target_link_libraries(foo PUBLIC LibXml2::LibXml2)
That's it folks! No fiddling around with headers and libraries, all done automatically.
If you have not installed the library, you might want to set LibXml2_DIR to the location where LibXml2 is located, i.e., by passing -DLibXml2_DIR=C:\software\libxml2 to your CMake call. That will help CMake finding LibXml2.

CMake Why cannot use .exe for custom target

Here is the CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project("cmake_oneoneone")
add_executable(main src/main.cpp)
set(library_name liba CACHE string "This variable is the library name")
add_library(${library_name} STATIC src/liba.cpp)
target_link_libraries(main ${library_name})
include_directories(include)
add_custom_target(run_main
COMMAND ./main
DEPENDS main
)
The output is:
~/workspace/cmake_test/build$ make run_main
[ 50%] Built target liba
[100%] Built target main
Hello world!
[100%] Built target run_main
but if I change executable to .exe
cmake_minimum_required(VERSION 2.8)
project("cmake_oneoneone")
add_executable(main.exe src/main.cpp)
set(library_name liba CACHE string "This variable is the library name")
add_library(${library_name} STATIC src/liba.cpp)
target_link_libraries(main.exe ${library_name})
include_directories(include)
add_custom_target(run_main
COMMAND ./main.exe
DEPENDS main.exe
)
~/workspace/cmake_training/build$ make run_main
make[3]: *** No rule to make target `../main.exe', needed by `CMakeFiles/run_main'. Stop.
make[2]: *** [CMakeFiles/run_main.dir/all] Error 2
make[1]: *** [CMakeFiles/run_main.dir/rule] Error 2
make: *** [run_main] Error 2
I tried either Linux and Windows on both the CMake behaves the same.
If you check the documentation of the ADD_EXECUTABLE command, you can figure out that CMake automatically adds the extension .exe to your executable in case that you are generating it in a Windows environment. If you are in Linux, obviously generate a .exe has no sense:
Adds an executable target called to be built from the source files listed in the command invocation. The corresponds to the logical target name and must be globally unique within a project. The actual file name of the executable built is constructed based on conventions of the native platform (such as .exe or just ).
So you do not have to add the .exe at the end of the name of your executable.

CMake adding libraries for Windows/Linux

Visual Studio C++ 2008 / GCC 4.4.2
I have written a program to run on Linux and now I have to port my code to run on Windows. I have decided to use CMake as I want to keep the same build system for both platforms.
However, I need to link with some libraries for both platforms. In my CMakeLists.txt I have the following:
# Compile with gcc c89 standard
IF(CMAKE_COMPILER_IS_GNUCXX)
MESSAGE(STATUS "GCC detected - Adding compiler flags")
SET(CMAKE_C_FLAGS "-pthread -ggdb -Wextra -Wall -pedantic -std=c89")
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
IF(WIN32)
SET(CMAKE_C_FLAGS "ws2_32.lib")
ENDIF(WIN32)
However, when I compile on Visual Studio I get the following error:
fatal error C1083: Cannot open source file: 'ws2_32.lib': No such file or directory
What can I do to resolve this problem?
========= Edit
In the top level directory
# Project Client Server
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
# Name of project
PROJECT(CLIENT_SERVER)
# Compile with gcc c89 standard
IF(CMAKE_COMPILER_IS_GNUCXX)
MESSAGE(STATUS "GCC detected - Adding compiler flags")
SET(CMAKE_C_FLAGS "-pthread -ggdb -Wextra -Wall -pedantic -std=c89")
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
IF(WIN32)
SET(CMAKE_C_FLAGS "ws2_32")
ENDIF(WIN32)
# Includes
INCLUDE_DIRECTORIES(${CLIENT_SERVER_SOURCE_DIR}/cltsvr_ults)
INCLUDE_DIRECTORIES(${CLIENT_SERVER_SOURCE_DIR}/server)
INCLUDE_DIRECTORIES(${CLIENT_SERVER_SOURCE_DIR}/client)
# libraries
LINK_DIRECTORIES($CLIENT_SERVER/cltsvr_ults)
# Add subdirectories
ADD_SUBDIRECTORY(client)
ADD_SUBDIRECTORY(server)
ADD_SUBDIRECTORY(cltsvr_ults)
ADD_SUBDIRECTORY(test_client)
ADD_SUBDIRECTORY(test_server)
In the subdirectory of client I have this CMakeLists.txt
# libray called client from client.c
ADD_LIBRARY(client client)
And in the subdirectory of test_clt where I create and link my executable.
# Test client add executable
INCLUDE_DIRECTORIES($CLIENT_SERVER_SOURCE_DIR/client)
INCLUDE_DIRECTORIES($CLIENT_SERVER_SOURCE_DIR/cltsvr_ults)
# Link the library
LINK_DIRECTORIES($CLIENT_SERVER/client)
# Add the executable
ADD_EXECUTABLE(clt test_clt)
# Link the executable to the client library
IF(WIN32)
TARGET_LINK_LIBRARIES(clt client ws2_32)
ENDIF(WIN32)
Disclaimer: My answer is of philosophical nature which should encourage you to avoid touching CMAKE_C_FLAGS directly. For the direct answer that just solves your problem look what Bill ( the lead architect of the CMake btw. ) wrote.
The thing about CMake is, that it lets you describe what you want to do without referring to a specific compiler or platform. What CMake does is building the compiler and linker flags from your usage of
include_directories
add_definitions
add_library
add_executable
target_link_libraries
If there are no external dependencies, other than the compiler itself, this is all you need. For external dependencies use
find_package
It defines a set of variables, like
find_package(SDL)
defines
SDL_INCLUDE_DIR
SDL_LIBRARY
for usage with respectively include_directories and target_link_libraries. CMake ships with a bunch of so called module files, like FindSDL.cmake and many others can be googled.
The next lower level is to use
find_path
find_library
which are used in the Find???.cmake modules itself.
The CMAKE_C_FLAGS variable is composed by CMake from these commands. Modifying it means you bypass CMake. There are cases, like for special optimization flags, you want to do this, but at this point all power and thus responsibility transfered from CMake to you.
By adding ws2_32.lib to the C_FLAGS, you are using it at compile time, and not link time. If you look at the error message, you can see it it treating the file as if it were a c++ source file: Cannot open source file: 'ws2_32.lib'. target_link_libraries(target ws2_32) should work.
You need to use the Target Link Libraries command. The target would be the executable you're building.
EDIT: You shouldn't specify the libs you're linking against in C_FLAGS. You can do something like TARGET_LINK_LIBRARIES(execName, ws_32, ...). I'm not 100% sure if you need the .lib. Been a while since I used CMake.