How to idiomatically set the version number in a CMAKE/CPACK project? - cmake

I'm trying to edit the CPack settings so that the outputted package file has the correct version number in it. But that's not all. I'd like CMake/CPack to have all the places where the version number is updated also be set. The .so file should also be set. And whatever else needs it.
From where I sit, the CPack documentation appears to be telling us to repeat ourselves.
If I do nothing, the output file is like so:
mystuff-0.1.1-Linux.tar.gz
Let's say the version number should be 1.2.3.
I would think that this is the minimalistic CPACK settings to cause all the version numbers to be changed -- and I placed these lines in my CMakeLists.txt file:
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My app is great")
SET(CPACK_PACKAGE_VENDOR "My Name")
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
SET(CPACK_PACKAGE_VERSION_MAJOR "1")
SET(CPACK_PACKAGE_VERSION_MINOR "3")
SET(CPACK_PACKAGE_VERSION_PATCH "2")
When I build, the output file is still mystuff-0.1.1-Linux.tar.gz
If I repeat myself and do this:
SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
Now that variable is set, but the file is still mystuff-0.1.1-Linux.tar.gz
It seems that I must also change CPACK_PACKAGE_FILE_NAME
What other places do I have to re-state the version number?
This doesn't feel idiomatic. I must be missing something to make this automatic. Am I supposed to specify the variable at an earlier point in time? I also see some projects on the internet containing a CPackConfig.cmake file -- the file contains overrides, but I still see some repeating oneself going on in those as well.
This bug also mentions the same thing. Apparently, they do want us to repeat ourselves. (as of 2015) However, even if that is the case, I was wondering if anyone has found a work-around?
If no work around, how about a way to re-state all the variables so that they end up automatically set correctly? For instance, doing the following is a way to compose the version number by using the variables
SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
According to the documentation, the file name is composed like this:
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}")
There are probably other variables that need re-setting. If I have to restate everything, then exactly what do I need to restate to be complete?

Make sure that you have the following line after setting the CPACK_... variables:
include (CPack)
This is the spot where CPACK_PACKAGE_FILE_NAME and others are automatically set, so the version variables (such as CPACK_PACKAGE_VERSION_MAJOR) must be already set at this point.

If you set your project's version via https://cmake.org/cmake/help/v3.0/command/project.html, you can set the cpack version like
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
include(CPack)

You'll probably want to look at the documentation for the PROJECT() command, which has a VERSION field. I'm not sure if it is wired through to CPack, but at least that is the idiomatic place for setting the project version.
https://cmake.org/cmake/help/v3.0/command/project.html

Related

change cpack generated name for component RPMs

I am trying to create an RPM installer using cpack (via cmake) that creates multiple RPM files. I've almost got it working but the last remaining stumbling block is controlling the package names.
Specifically I want to control where the component name appears in the RPM file name.
Here is my experimental CMakeLists.txt file which installs foo.txt and bar.txt to two different packages "myproject-foo" & "myproject-bar".
I want the RPMs to follow my own naming conventions (actually I want conventions that are more FSH friendly) but for some reason cmake insists on adding the component name at the end. So I get:
myproject-comp-1.0.i686-foo.rpm
myproject-comp-1.0.i686-bar.rpm
rather than:
myproject-compfoo-1.0.i686.rpm
myproject-compbar-1.0.i686.rpm
I notice it names the source RPMs "more correctly":
E.g.
>rpm -qip <package> | grep source
Source RPM : myproject-foo-1.0-1.src.rpm
Though I do not actually want a source RPM at all (is there a way to blank this field?)
cmake_minimum_required(VERSION 2.8)
message("CMAKE_VERSION=${CMAKE_VERSION}")
SET(DOCDIR "doc")
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "core")
install(FILES foo.txt COMPONENT foo DESTINATION "${RPMBUILDROOT}${DOCDIR}")
install(FILES bar.txt COMPONENT bar DESTINATION "${RPMBUILDROOT}${DOCDIR}")
project(myproject CXX)
set(CPACK_PACKAGE_NAME "myproject")
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_PACKAGE_VERSION 1.0)
set(CPACK_PACKAGE_VENDOR "some org")
set(CPACK_COMPONENT_foo_DESCRIPTION "the foo component")
set(CPACK_COMPONENT_bar_DESCRIPTION "the bar component")
# mentioned in cpack docs but doesn't seem to do anything here
set(CPACK_COMPONENT_foo_DISPLAY_NAME "foo display name?")
set(CPACK_COMPONENT_bar_DISPLAY_NAME "foo display name?")
set(CPACK_COMPONENT_bar_DEPENDS foo)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "describe the complete suite of stuff")
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README.txt)
# added in cmake 3.6?
set(CPACK_RPM_foo_PACKAGE_SUMMARY "describe the foo package")
set(CPACK_RPM_bar_PACKAGE_SUMMARY "describe the bar package")
set(CPACK_RPM_foo_PACKAGE_NAME "${CPACK_PACKAGE_NAME}-foo")
set(CPACK_RPM_bar_PACKAGE_NAME "${CPACK_PACKAGE_NAME}-bar")
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README.txt)
#set(CPACK_RPM_foo_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-foo-${CPACK_PACKAGE_VERSION}.i686")
#set(CPACK_RPM_bar_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-bar-${CPACK_PACKAGE_VERSION}.i686")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-comp${CPACK_COMPONENT_DISPLAY_NAME}-${CPACK_PACKAGE_VERSION}.i686")
set(CPACK_RPM_PACKAGE_ARCHITECTURE "i686")
set(CPACK_RPM_PACKAGE_RELOCATABLE TRUE)
include(CPack)
cpack_add_component(foo
DISPLAY_NAME foo comp
DESCRIPTION this is the foocomp)
cpack_add_component(bar
DISPLAY_NAME bar comp
DESCRIPTION this is the bar comp
DEPENDS foo)
It seems like CPACK_PACKAGE_<component>_FILE_NAME or CPACK_PACKAGE_FILE_NAME is not quite working.
I've tried with both cmake 3.4.3 and the latest (3.9.0 at the time of writing) as there is some suggestion of fixes to component based installs in 3.6.0
There is no such variable as the CPACK_COMPONENT_DISPLAY_NAME I've used in CPACK_PACKAGE_FILE_NAME. Also something in cmake adds the component name to the end automatically.
There is a similar question for .deb packages from long ago here but I would rather avoid such a complex solution if possible.
It would be easier to rename the packages after generation (but still feel hacky).
I also note that the package dependency set(CPACK_COMPONENT_bar_DEPENDS foo) is not recorded in the bar package.
Update: 01-Aug-2017
I've added a bug report for cmake here as to my mind there is a bug in the implementation of CPACK_RPM_<component>_PACKAGE_FILE_NAME. At the very least the documentation is insufficiently helpful.
First of there is a large note at the top of the documentation:
Note
part of variables is preferred to be in upper case (for e.g. if component is named foo then use CPACK_RPM_FOO_XXXX variable name format) as is with other CPACK__XXXX variables. For the purposes of back compatibility (CMake/CPack version 3.5 and lower) support for same cased component (e.g. fOo would be used as CPACK_RPM_fOo_XXXX) is still supported for variables defined in older versions of CMake/CPack but is not guaranteed for variables that will be added in the future. For the sake of back compatibility same cased component variables also override upper cased versions where both are present.
So if you have a component named foo, FOO, fOo... the variables are CPACK_RPM_FOO_... otherwise the variables will be ignored - Note that this is the case since the beginning of CPack's existance for the common component variables e.g. CPACK_COMPONENT_foo_DESCRIPTION <- foo part must be in upper case.
Next to the file names - before CPack 3.6 it was not possible to change file names without getting an error and having to copy the generated package manually. Since that version you can set the file name as you like or request the platform specific package name (defined by rpm installation) by setting CPACK_RPM_FILE_NAME or CPACK_RPM_<component>_FILE_NAME to RPM-DEFAULT. See https://cmake.org/cmake/help/v3.6/module/CPackRPM.html?highlight=cpack_rpm_file_name#variable:CPACK_RPM_FILE_NAME
Also set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-comp${CPACK_COMPONENT_DISPLAY_NAME}-${CPACK_PACKAGE_VERSION}.i686") won't work as ${CPACK_COMPONENT_DISPLAY_NAME} is not an anchor and is automatically replaced on that location - not set by you so replacement text is an empty string... So if you really want to set the name manually you should set the RPM packager equivalent of that variable CPACK_RPM_<component>_FILE_NAME for each and every component (so you'll need a for loop or some more copy pasting...) - note that as described in the documentation the file name must end with .rpm and the default is (copied from the documentation) <CPACK_PACKAGE_FILE_NAME>[-<component>].rpm so this is the explanation why component part is always placed at the end.
There are quite a few tests for different CPack packagers here: https://github.com/Kitware/CMake/tree/master/Tests/RunCMake/CPack/tests
So you can explore those snippets as well.
P.S. contributions to the documentation are always welcome :)

How can a CMake variable be hidden?

I have a CMake project which lets a globally set variable (set with -DARDUINO_SDK_PATH=/a/b/c on command line) disappear i.e. suddenly the given value is gone which leads to a fatal error.
I know there are different ways to "hide" a variable (e.g. inside functions or external projects)
In my case:
the variable is not being set explicitly anywhere in the code (e.g. via set() or find_path())
the access which leads to the error is on top level (i.e. not inside a function)
there are instructions (i.e. same file/line) where in one case the variable has the value it's been given and the next time it's gone
Tracing the variable with variable_watch(ARDUINO_SDK_PATH) I can see that everything works fine before the compiler is being checked:
cmake -DARDUINO_SDK_PATH=/a/b/c <path>
...
... everything fine, ${DARDUINO_SDK_PATH} == '/a/b/c' everywhere
...
-- Check for working C compiler: /usr/bin/avr-gcc
...
... here the variable is empty and not being traced any more
...
Here is my suggestion:
Does the compiler check (indicated by check for working C compiler .. on the terminal) have it's own variable space and does not know variables provided on command line?
Note: This question is a generalization of this question, which has become way too specialized but might offer some useful background information.
That any modification to variable is not traced after the variable_watch() command seems like a bug somewhere in CMake to me.
Generally speaking a "cached CMake variable" can be hidden by a "normal CMake variable" with the same name. But e.g. find_path() won't run again or modify a variable if already set.
Here is an example:
cmake_minimum_required(VERSION 2.4)
project(VariableWatchTest NONE)
variable_watch(MY_TEST_VAR)
set(MY_TEST_VAR "something" CACHE INTERNAL "")
message("${MY_TEST_VAR}")
set(MY_TEST_VAR "hiding something")
message("${MY_TEST_VAR}")
unset(MY_TEST_VAR)
message("${MY_TEST_VAR}")
find_path(MY_TEST_VAR NAMES "CMakeLists.txt" HINTS "${CMAKE_CURRENT_LIST_DIR}")
message("${MY_TEST_VAR}")
Would give (without the variable_watch() messages:
-- something
-- hiding something
-- something
-- something
References
What's the CMake syntax to set and use variables?
I'm not sure whether this is a bug or a feature but (at least some) CMake variables are not available in certain steps of the CMake configuration procedure.
You can check this by adding something like this to your toolchain file:
MESSAGE("FOO: ${FOO}")
and run CMake like this
cd build-dir
cmake -DFOO=TEST ..
You will likely see FOO printed with value TEST once in the beginning of the configuration process and later printed again but being empty.
Just don't access variables from the global space inside a toolchain file (doesn't belong there anyway).

CMake to find path to a file which its name contains a certain string

So I am writing a CMake module to find some libraries which is being used a lot in our company. These libraries all have different versions and are such a mess here. In a certain library, even the name of header files and binary files don't match. So I am writing a CMake script to handle all the problems of finding each library once and for all. What I have in my mind is to write something like how we find boost packages but include the version of each component as well. Something like this:
find_package(OrgName COMPONENTS librarya-1.4.3 libraryb-2.3.1 libraryc-3.6.0)
So I created a FindOrgName.cmake file and iterated on the requested components, then I processed the string which is passed and gained the library name along with its version information something like this (never mind the difference between include and binary files):
IF(OrgName_FIND_COMPONENTS)
FOREACH(comp ${OrgName_FIND_COMPONENTS})
SET(OrgName_${comp}_FOUND 0)
STRING(FIND ${comp} "-" dashind REVERSE)
STRING(LENGTH ${comp} length)
STRING(SUBSTRING ${comp} 0 ${dashind} name)
MATH(EXPR s "${dashind}+1")
MATH(EXPR l "${length}-${dashind}-1")
STRING(SUBSTRING ${comp} ${s} ${l} version)
SET(OrgName_${name}_INCLUDE_DIR "/usr/local/include/OrgName/${comp}/")
find_library(OrgName_${comp}_LIBRARIES NAMES "${comp}" HINTS "/usr/lib")
IF(OrgName_${comp}_INCLUDE_DIR AND OrgName_${comp}_LIBRARIES)
SET(OrgName_${comp}_FOUND 1)
ENDIF()
IF(NOT OrgName_${comp}_FOUND AND OrgName_FIND_REQUIRED_${comp})
MESSAGE(FATAL_ERROR "OrgName ${comp} not available.")
ENDIF()
SET (OrgName_INCLUDE_DIR ${OrgName_INCLUDE_DIR} ${OrgName_${comp}_INCLUDE_DIR})
SET (OrgName_LIBRARIES ${OrgName_LIBRARIES} ${OrgName_${comp}_LIBRARIES})
ENDFOREACH()
ENDIF()
Now here is the problem, imagine someone didn't enter the version part in components names. I want to search for the versions which he has installed (assume the path to library is always the same) and use the last version it can find, so I have to search for the files which their name contains the library name (${name}). First of all how can I do this? Second, Am I doing things right? I mean is there an easier way to do this task?
OK, after a lot of searching and getting exhausted I finally found a solution. I just need to use the following command:
file(GLOB files "Mylib*")
Which will create a list named files and adds each file that its name matches the pattern "Mylib*" to it. I really don't know why they didn't implement it in find_file command but never mind, I'll put it here in case anyone got confused like me.

library versioning with cmake

I have a project with 4 different sub projects. To specify the versions, I use the
SET(parent_VERSION_MAJOR 1)
SET(parent_VERSION_MINOR 0)
set(parent_VERSION_PATCH 0)
set(parent_VERSION 1.0.0)
and then I can use this in the sub projects if the add_subdirectory is used.
Q1. I could not set parent_VERSION based on MAJOR, MINOR and PATCH. According to the documentation is should be set automatically but whenever I try printing it, it is empty without using the last line in the code.
Q2. In case I want to build from sub directory only, I get an error shouting :
CMake Error at CMakeLists.txt:28 (set_target_properties):
set_target_properties called with incorrect number of arguments.
which is because I am using parent_VERSION there.
So I understand that it isn't able to get the parent_VERSION without running cmake from the top directory but how do I change the code such that it can build even without running from the top level.
I read about SET with INHERITED but I don't think that is what I need.
Here is how I solved it. If someone could tell me a better/more elegant way I'd be happy.
if(NOT parent_VERSION)
SET(parent_VERSION_MAJOR 1)
SET(parent_VERSION_MINOR 0)
SET(parent_VERSION_PATCH 0)
SET(parent_VERSION 1.0.0)
endif(NOT parent_VERSION)

How to disable CPACK_PACKAGE_VERSION_PATCH in CMakeLists.txt?

I am still a CMake newbie (started learning 3 days ago). In my current CMakeLists.txt, I have the following set directives:
[...]
SET(CPACK_GENERATOR "RPM")
SET(CPACK_PACKAGE_VERSION_MAJOR "3")
SET(CPACK_PACKAGE_VERSION_MINOR "3")
SET(CPACK_PACKAGE_VERSION_PATCH "svn")
SET(CPACK_SYSTEM_NAME "0.el6.x86_64")
[...]
Once I run make package, I got a libcxx-3.3.svn-0.el6.x86_64.rpm.
But IMHO this is "cheating".
According to http://fedoraproject.org/wiki/Packaging:NamingGuidelines#Package_Naming_and_Versioning_Guidelines, ideally I should be able to generate a libcxx-3.3-0.el6.x86_64.rpm instead. But this demands that CPack not to show the CPACK_PACKAGE_VERSION_PATCH.
Nevertheless, according to my trial results, it doesn't seem to be feasible. I would appreciate a hint as to how.
Did you try setting CPACK_PACKAGE_FILE_NAME (without the extension) to the desired name? This is provided so that you can define your own package naming scheme if the default does not suit your needs. See http://www.cmake.org/Wiki/CMake:CPackConfiguration for more details on key CPack variables and what they do.