Discussion:
Please critique my "hello world" CMakeLists.txt and Config.cmake
(too old to reply)
Chris Stankevitz
2013-02-24 04:37:25 UTC
Permalink
Hello,

Would you please critique my CMakeLists.txt and helloConfig.cmake.in
files? I am particularly interested in comments that will help me fix
these problems:

- In helloConfig.cmake.in, I assume static libraries will be built
(".a"). I imaging a better approach would make no assumptions about
this.

- In helloConfig.cmake.in, I assume ${PREFIX} will expand to the
installed prefix, but it does not.

- I type the same expression many times:
"hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR}". I imagine the
better approach would derive this expression from some built-in cmake
variables or from a variable I create.

- INSTALL is looking for helloConfig.cmake in the source directory but
it is in the build directory.

- Any other comments you have

Thank you!

Chris
Jean-Christophe Fillion-Robin
2013-02-24 05:09:04 UTC
Permalink
Hi Chris,

To make the process easier, could you create a small project on Gtihub. We
would then be able to comment on a line-by-line basis.

Hth
Jc


On Sat, Feb 23, 2013 at 11:37 PM, Chris Stankevitz <
Post by Chris Stankevitz
Hello,
Would you please critique my CMakeLists.txt and helloConfig.cmake.in
files? I am particularly interested in comments that will help me fix
- In helloConfig.cmake.in, I assume static libraries will be built
(".a"). I imaging a better approach would make no assumptions about
this.
- In helloConfig.cmake.in, I assume ${PREFIX} will expand to the
installed prefix, but it does not.
"hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR}". I imagine the
better approach would derive this expression from some built-in cmake
variables or from a variable I create.
- INSTALL is looking for helloConfig.cmake in the source directory but
it is in the build directory.
- Any other comments you have
Thank you!
Chris
--
Powered by www.kitware.com
Visit other Kitware open-source projects at
http://www.kitware.com/opensource/opensource.html
http://www.cmake.org/Wiki/CMake_FAQ
http://www.cmake.org/mailman/listinfo/cmake
--
+1 919 869 8849
Ansis Māliņš
2013-02-24 10:23:37 UTC
Permalink
# You don't need the .in file.

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

PROJECT(hello)

set (hello_VERSION_MAJOR 1)
set (hello_VERSION_MINOR 1)

ENABLE_TESTING()

ADD_LIBRARY(hello hello.cpp)

# This works just the same. Unless I'm missing something.
SET(hello_INCLUDE_DIRS

${CMAKE_INSTALL_PREFIX}/include/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})
SET(hello_LIBRARIES

${CMAKE_INSTALL_PREFIX}/lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR}/libhello.a)

INSTALL(TARGETS hello DESTINATION
lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})

INSTALL(FILES helloConfig.cmake DESTINATION
lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})

INSTALL(FILES hello.h DESTINATION
include/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})
J Decker
2013-02-24 10:38:05 UTC
Permalink
Post by Ansis Māliņš
# You don't need the .in file.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(hello)
set (hello_VERSION_MAJOR 1)
set (hello_VERSION_MINOR 1)
ENABLE_TESTING()
ADD_LIBRARY(hello hello.cpp)
# This works just the same. Unless I'm missing something.
SET(hello_INCLUDE_DIRS
${CMAKE_INSTALL_PREFIX}/include/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})
SET(hello_LIBRARIES
${CMAKE_INSTALL_PREFIX}/lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR}/libhello.a)
s/lib/${CMAKE_STATIC_LIBRARY_PREFIX}/
s/.a/${CMAKE_STATIC_LIBRARY_SUFFIX}/

also available ${CMAKE_EXECUTABLE_SUFFIX} would be blank in your case, but
not always.

${CMAKE_SHARED_LIBRARY_PREFX}
${CMAKE_SHARED_LIBRARY_SUFFX}

PREFIX will be blank for windows platforms and SUFFIX appropriate .lib or
.dll (.a, .so )
Post by Ansis Māliņš
INSTALL(TARGETS hello DESTINATION
lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})
INSTALL(FILES helloConfig.cmake DESTINATION
lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})
INSTALL(FILES hello.h DESTINATION
include/hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR})
--
Powered by www.kitware.com
Visit other Kitware open-source projects at
http://www.kitware.com/opensource/opensource.html
http://www.cmake.org/Wiki/CMake_FAQ
http://www.cmake.org/mailman/listinfo/cmake
Alexander Neundorf
2013-02-24 11:28:48 UTC
Permalink
Post by J Decker
Post by Ansis Māliņš
# You don't need the .in file.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(hello)
set (hello_VERSION_MAJOR 1)
set (hello_VERSION_MINOR 1)
ENABLE_TESTING()
ADD_LIBRARY(hello hello.cpp)
# This works just the same. Unless I'm missing something.
SET(hello_INCLUDE_DIRS
${CMAKE_INSTALL_PREFIX}/include/hello-${hello_VERSION_MAJOR}.${hello_VERS
ION_MINOR}) SET(hello_LIBRARIES
${CMAKE_INSTALL_PREFIX}/lib/hello-${hello_VERSION_MAJOR}.${hello_VERSION_
MINOR}/libhello.a)
s/lib/${CMAKE_STATIC_LIBRARY_PREFIX}/
s/.a/${CMAKE_STATIC_LIBRARY_SUFFIX}/
also available ${CMAKE_EXECUTABLE_SUFFIX} would be blank in your case, but
not always.
${CMAKE_SHARED_LIBRARY_PREFX}
${CMAKE_SHARED_LIBRARY_SUFFX}
PREFIX will be blank for windows platforms and SUFFIX appropriate .lib or
.dll (.a, .so )
Please use the target-export feature, this handles all that for you, including
dependent libraries, debug and optimized versions, etc.
http://www.cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets

Alex
Alexander Neundorf
2013-02-24 10:20:36 UTC
Permalink
Hi Chris,
Post by Chris Stankevitz
Hello,
Would you please critique my CMakeLists.txt and helloConfig.cmake.in
files? I am particularly interested in comments that will help me fix
- In helloConfig.cmake.in, I assume static libraries will be built
(".a"). I imaging a better approach would make no assumptions about
this.
- In helloConfig.cmake.in, I assume ${PREFIX} will expand to the
installed prefix, but it does not.
Yes.
You should
* "export" the target (use the EXPORT option in install(TARGETS))
* the install this "export", using install(EXPORT ...), this will install a
cmake script file
* include() this export-file in the Config.cmake file
* use configure_package_config_file() instead of the plain configure_file() to
configure the file. This will help with absolute and relative paths etc.

You may want to have a look at an example for this, e.g. here:
http://quickgit.kde.org/?p=kdelibs.git&a=tree&h=734d0c1887c89dd8260e67f912f026723226ff88&hb=e3f5b30fd287e83f32c2cc756fc3ea4a17358ef7&f=tier1%2Fitemmodels
(you can ignore ecm_setup_version() )
Post by Chris Stankevitz
"hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR}". I imagine the
better approach would derive this expression from some built-in cmake
variables or from a variable I create.
It's ok.
Of course you can do
set(helloSubDir "hello-${hello_VERSION_MAJOR}.${hello_VERSION_MINOR}")
and then use that.
Post by Chris Stankevitz
- INSTALL is looking for helloConfig.cmake in the source directory but
it is in the build directory.
install( ... ${CMAKE_CURRENT_BINARY_DIR}/hellConfig.cmake .... )

and maybe also use the full path in configure_file():

configure_file(helloConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake)

Alex
Chris Stankevitz
2013-02-24 19:39:40 UTC
Permalink
Post by Alexander Neundorf
* "export" the target (use the EXPORT option in install(TARGETS))
* the install this "export", using install(EXPORT ...), this will install a
cmake script file
* include() this export-file in the Config.cmake file
* use configure_package_config_file() instead of the plain configure_file() to
configure the file. This will help with absolute and relative paths etc.
Alexander,

First, thank you for your reply. This was exactly the kind of help I
was looking for.

Second, I attempted to follow your instructions. I am a big proponent
of cmake at work. I love how simple it is to create a "hello world"
project (particularly when compared to autotools). However, my head
is spinning with the new jargon needed to properly install a simple
"hello world" package. Even after reading the docs and wiki I don't
really understand what I am doing, I more or less just tried to copy
it. Most of the EXPORT and CONFIGURE_PACKAGE docs seem to be geared
toward someone who already knows the "old" way of installing and just
needs a refresher to the new style. I hope my "hello" project, once
it meets your approval, can be used to assist other new packagers in
the future.

Third, would you please take a look at my updated "hello" project and
the new "hello-client" project? As you might imagine, hello-client is
an executable that uses the hello library. I have done something
wrong in hello, hello-client, or both as hello-client fails to
compile. I suspect that among other mistakes, I should have some
SET_AND_CHECKs in my Config.cmake.in file. I also suspect that my
hello-client is missing a FIND_PACKAGE reference.

github links:
https://github.com/chrisstankevitz/hello
https://github.com/chrisstankevitz/hello-client

git clone links:
git://github.com/chrisstankevitz/hello.git
git://github.com/chrisstankevitz/hello-client.git

Thank you again for your help,

Chris
Alexander Neundorf
2013-02-24 20:37:58 UTC
Permalink
Hi Chris,
Post by Chris Stankevitz
Post by Alexander Neundorf
* "export" the target (use the EXPORT option in install(TARGETS))
* the install this "export", using install(EXPORT ...), this will install
a cmake script file
* include() this export-file in the Config.cmake file
* use configure_package_config_file() instead of the plain
configure_file() to configure the file. This will help with absolute and
relative paths etc.
Alexander,
First, thank you for your reply. This was exactly the kind of help I
was looking for.
Second, I attempted to follow your instructions. I am a big proponent
of cmake at work. I love how simple it is to create a "hello world"
project (particularly when compared to autotools). However, my head
is spinning with the new jargon needed to properly install a simple
"hello world" package. Even after reading the docs and wiki I don't
really understand what I am doing, I more or less just tried to copy
it. Most of the EXPORT and CONFIGURE_PACKAGE docs seem to be geared
toward someone who already knows the "old" way of installing and just
needs a refresher to the new style. I hope my "hello" project, once
it meets your approval, can be used to assist other new packagers in
the future.
Third, would you please take a look at my updated "hello" project and
the new "hello-client" project? As you might imagine, hello-client is
an executable that uses the hello library. I have done something
wrong in hello, hello-client, or both as hello-client fails to
compile. I suspect that among other mistakes, I should have some
SET_AND_CHECKs in my Config.cmake.in file. I also suspect that my
hello-client is missing a FIND_PACKAGE reference.
https://github.com/chrisstankevitz/hello
https://github.com/chrisstankevitz/hello-client
I had a look, the find_package() is missing indeed.


Maybe we'll start with the old way and get to the new way.

Your client should look like this:

cmake_minimum_required(VERSION 2.8)
project(hello-client)

find_package(hello REQUIRED NO_MODULE)

include_directories( ${hello_INCLUDE_DIRS} )

add_executable(hello-client hello-client.cpp)

target_link_libraries(hello-client ${hello_LIBRARIES} )



I think this is what you probably also expected, but maybe got confused due to
the talking about importing and exporting targets.

Since you want find_package() to search for a Config.cmake file, I recommend
to make this explicit by adding the NO_MODULE keyword. This way people looking
at the CMakeLists.txt can know that a Config.cmake file is searched, not a
Findhello.cmake.
This Config.cmake file should, as has always been the case, set the
<name>_INCLUDE_DIRS and <name>_LIBRARIES variables, which you should use as
always.


Now to the Config.cmake file.
In the most basic case it would look like that:

set(hello_LIBRARIES /some/path/libhello.a)
set(hello_INCLUDE_DIRS /some/path/include/ )


This would already kind of work.
It has the problem that the directories are hardcoded, so e.g. under Windows a
user could not install the package in a directory of his choice, i.e. it is
not "relocatable".
Also, since the variables are simply set, there is not guarantee that the
directories and files actually exist.

So if you use the configure_package_config_file() macro to configure that
file, you put the @PACKAGE_INIT@ "macro" (this time really a macro, cmake will
fill in code there) at the top, and this provides the set_and_check()
function.
Then your file could look like this:


@PACKAGE_INIT@
set(hello_LIBRARIES /some/path/libhello.a)
set_and_check(hello_INCLUDE_DIRS /some/path/include/ )


This would still not be relocatable, but the set_and_check() would produce an
error if the directory /some/path/include/ does not exist. This is good, since
you would get an error at cmake time, and not at build time.
This error case should only happen if something went wrong, the install
process was interrupted, files were moved or deleted manually, or something
similar.

Now how do we get this file relocatable.
If you do this:


@PACKAGE_INIT@
set(hello_LIBRARIES /some/path/libhello.a)
set_and_check(hello_INCLUDE_DIRS @hello_DIRNAME_include@ )


and then call configure_package_config_file(), you'll get the full absolute
hardcoded path again as above.
If you do instead


@PACKAGE_INIT@
set(hello_LIBRARIES /some/path/libhello.a)
set_and_check(hello_INCLUDE_DIR @PACKAGE_hello_DIRNAME_include@ )
set(hello_INCLUDE_DIRS ${hello_INCLUDE_DIR} )


and call
configure_package_config_file(... PATH_VARS hello_DIRNAME_include ...)
then cmake will put in some code so that the Config.cmake file will not have
the absolute path to the include dir, but the relative path to the include,
starting from the installed Config.cmake file itself.
(This is a bit tricky, you can have a look at the generated code in the
Config.cmake file).

In the example above there are the variables hello_INCLUDE_DIR and
hello_INCLUDE_DIRS. The second, plural one is the "public" variable, which is
potentially a list of directories, this one should be used in your client
programs.
But set_and_check() can only set a single variable, because it can only check
a single directory, so the helper variable hello_INCLUDE_DIR is needed.

So, with this you get a relocatable ${hello_INCLUDE_DIRS}.


Now to the importing and exporting of targets (and let's ignore whether it is
relocatable or not for now).

When doing find_library() for some installed project, the author of the Find-
module has to know the details about the library to be found, and he gas to
keep the Find-module up-to-date when new versions of the library are released.
If the library is a static library, he has to search for potentially needed
dependent libraries too.
Especially under Windows the author of the Find-module must take care of
debug- and release-versions of the library, getting the wrong one will make
the executable crash.

The idea is that Config.cmake files are provided by the project itself, so it
can contain "first hand" information.

In a simple case the Config.cmake file you could write a Config.cmake file
look like this (ignoring the include dirs, since we had that already above):


add_library(hello IMPORTED)
set_target_properties(hello PROPERTIES
IMPORTED_LOCATION /some/path/libhello.a)
set(hello_LIBRARIES hello)

set(hello_INCLUDE_DIRS ... )


When this file is loaded by the client project, it will create a library
target, which you can use like a normal target and link against it.
But the "IMPORTED" keyword says that it is not built in the project, but
exists already, and just a virtual reference to that existing library is
created. By setting target properties on this "imported" library the actual
path to the file on disk is attached to this target.

So you could then do

add_executable(whatever main.cpp)
target_link_libraries(whatever hello)

and cmake would see that hello is an imported target, and via the target
property it would see which file this actually is on disk.

By setting the ${hello_LIBRARIES} variable in the Config.cmake file the client
project does not have to care whether this is an imported target or a path to
a file on disk, but can simply use that variable and it will work.

Now writing those

add_library(hello IMPORTED)
set_target_properties(...)

lines manually is not easy and error-prone, so cmake can do this better for
you.

If you want to create that information for a library you want to install, you
put that library into an "export" (a set of targets which will be installed
and exported, so they can be imported again later by clients):
install(TARGETS hello DESTINATION ... EXPORT MyExports)

This just puts the target into a set of targets, which can be referenced by
the given name "MyExports". You can choose any name you want.

This export then also has to be installed:

install(EXPORT MyExport FILE HelloTargets.cmake DESTINATION ... )

This call will actually create a file called HelloTargets.cmake, which
contains these add_library(... IMPORTED ...) calls for all targets which have
been put into this export set.
Actually it does more than that, but this is the basic idea.
You can have a look at the installed files, it additionally adds checks that
the files actually exist on disk, it sets a whole bunch more target
properties, like which dependent libraries have to be linked too, whether it
is a debug or release or some other configurarion build, etc.

But the basic idea is that this creates a file which contains the code which
creates the add_library(... IMPORTED ...) calls.

Now to get these imported targets into your Config.cmake file, you have to
include() the file:

include(${CMAKE_CURRENT_LIST_DIR}/HelloTargets.cmake)

but you may do this only once in a project, otherwise a target with the same
name would be created twice, so a check for that has to be added.

So the full helloConfig.cmake.on file should look like this:

@PACKAGE_INIT@

set_and_check(hello_INCLUDE_DIR @PACKAGE_hello_DIRNAME_include@ )

if(NOT TARGET hello)
include(${CMAKE_CURRENT_LIST_DIR}/HelloTargets.cmake)
endif()

set(hello_INCLUDE_DIRS ${hello_INCLUDE_DIR} )
set(hello_LIBRARIES hello)


I hope this helps.

Alex

P.S. if this is helpful, feel free to put it in the cmake wiki :-)
Chris Stankevitz
2013-02-24 22:05:20 UTC
Permalink
On Sun, Feb 24, 2013 at 12:37 PM, Alexander Neundorf
Post by Alexander Neundorf
Post by Chris Stankevitz
https://github.com/chrisstankevitz/hello
https://github.com/chrisstankevitz/hello-client
Maybe we'll start with the old way and get to the new way.
Alex,

Thank you again. I updated the projects per your teaching. The only
place I had to guess was here:

INSTALL(
FILES
${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake
DESTINATION
${hello_DIRNAME_lib}
)

With respect to this INSTALL directive, I have three questions:

1. Is it needed? [Apparently the answer is yes]

2. Is ${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake the appropriate
way for me to reference this file? I got this from kdelibs.git.

3. Is ${hello_DIRNAME_lib} (aka
/usr/local/lib/hello-1.1/helloConfig.cmake) the correct place to
install helloConfig.cmake? kdelibs.git puts it somewhere else
("${CMAKECONFIG_INSTALL_DIR}" which I guessed corresponds to the weird
location /usr/local/hello/).

Thank you again,

Chris
Alexander Neundorf
2013-02-25 18:36:49 UTC
Permalink
Post by Chris Stankevitz
On Sun, Feb 24, 2013 at 12:37 PM, Alexander Neundorf
Post by Alexander Neundorf
Post by Chris Stankevitz
https://github.com/chrisstankevitz/hello
https://github.com/chrisstankevitz/hello-client
Maybe we'll start with the old way and get to the new way.
Alex,
Thank you again. I updated the projects per your teaching. The only
INSTALL(
FILES
${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake
DESTINATION
${hello_DIRNAME_lib}
)
1. Is it needed? [Apparently the answer is yes]
Yes, this installs the Config.cmake file to a place where it will be found by
find_package().
If you would only install(EXPORT ... FILE helloConfig.cmake) , i.e. name the
exported target file Config.cmake, then you wouldn't have a place where you
can set the include directory variables or other information.
Post by Chris Stankevitz
2. Is ${CMAKE_CURRENT_BINARY_DIR}/helloConfig.cmake the appropriate
way for me to reference this file? I got this from kdelibs.git.
Yes. configure_file() creates the output files in CMAKE_CURRENT_BINARY_DIR if
you don't tell it to put it somewhere else.
Post by Chris Stankevitz
3. Is ${hello_DIRNAME_lib} (aka
/usr/local/lib/hello-1.1/helloConfig.cmake) the correct place to
install helloConfig.cmake? kdelibs.git puts it somewhere else
("${CMAKECONFIG_INSTALL_DIR}" which I guessed corresponds to the weird
location /usr/local/hello/).
See the documentation for find_package(), for the locations where it searches
Config.cmake files.
lib/hello-1.1/helloConfig.cmake sounds good, this should be the
"<prefix>/(lib/<arch>|lib|share)/<name>*/" case from the docs.
kdelibs puts it into lib/cmake/<name>/

Alex

Loading...