Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ament_cmake_gen_version_h package #198

Merged
merged 8 commits into from
Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ament_cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ament_export_dependencies(
"ament_cmake_export_libraries"
"ament_cmake_export_link_flags"
"ament_cmake_export_targets"
"ament_cmake_gen_version_h"
"ament_cmake_libraries"
"ament_cmake_python"
"ament_cmake_target_dependencies"
Expand Down
1 change: 1 addition & 0 deletions ament_cmake/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<build_export_depend>ament_cmake_export_libraries</build_export_depend>
<build_export_depend>ament_cmake_export_link_flags</build_export_depend>
<build_export_depend>ament_cmake_export_targets</build_export_depend>
<build_export_depend>ament_cmake_gen_version_h</build_export_depend>
<build_export_depend>ament_cmake_libraries</build_export_depend>
<build_export_depend>ament_cmake_python</build_export_depend>
<build_export_depend>ament_cmake_target_dependencies</build_export_depend>
Expand Down
12 changes: 12 additions & 0 deletions ament_cmake_gen_version_h/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.5)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge-nikulin nit: extra blank line

project(ament_cmake_gen_version_h NONE)

find_package(ament_cmake_core REQUIRED)

ament_package(CONFIG_EXTRAS "ament_cmake_gen_version_h-extras.cmake")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge-nikulin this line should be (conventionally) the last.


install(
DIRECTORY cmake
DESTINATION share/${PROJECT_NAME}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# copied from
# ament_cmake_version/ament_cmake_version-extras.cmake

include("${ament_cmake_gen_version_h_DIR}/ament_cmake_gen_version_h.cmake")
84 changes: 84 additions & 0 deletions ament_cmake_gen_version_h/cmake/ament_cmake_gen_version_h.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

################################################################################
# `ament_cmake_gen_version_h` function creates and installs a version header file.
# The version is taken from `package.xml` file's `<version>` tag
# The function uses a provided "version.h.in" template file to generate
# the destination `version.h` file and installs that generated file into `DESTINATION include`.
# The generated file is being (re-)created if:
# - the file does not exist
# - the file does exists but contains a version that differs from the version in `package.xml` file
################################################################################
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge-nikulin nit: follow the docblock format that is used for other functions and macros in this repository.

function(ament_cmake_gen_version_h)
if (NOT ${ARGC} EQUAL 0)
message(FATAL_ERROR "ament_cmake_gen_version_h does not accept parameters but ${ARGC} parameters provided")
endif()

# Find myself so I can use my own cmake instalation folder `ament_cmake_gen_version_h_DIR`
# I can't rely on a previous find_package call because it could be a direct call
find_package(ament_cmake_gen_version_h REQUIRED)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge-nikulin direct call? If this .cmake file is simply included, there may not even be an installed config module to look for in the CMAKE_PREFIX_PATH.

I'm inclined to drop this and instead document how to make proper use of this function.

Copy link
Contributor Author

@serge-nikulin serge-nikulin Sep 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hidmic, The find_package call provides me with $ament_cmake_gen_version_h_DIR variable so I can have access to my template file ${ament_cmake_gen_version_h_DIR}/version.h.in. That's the only reliable way to do it, AFAIK.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. My point is, the only scenario I can think of where a downstream package wouldn't have had to call find_package(ament_cmake_gen_version_h) first is when ament_cmake_gen_version_h is included into another, outer project. In that case, find_package(ament_cmake_gen_version_h) will fail anyways. The outer project has to set that directory for you.

set(TEMPLATE_FILE "${ament_cmake_gen_version_h_DIR}/version.h.in")
if(NOT EXISTS "${TEMPLATE_FILE}")
message(FATAL_ERROR "Can't find ${TEMPLATE_FILE}. Reinstall ament_cmake_gen_version_h package.")
endif()

include_directories(${CMAKE_CURRENT_BINARY_DIR}/ament_cmake_gen_version_h/include)
set(TMP_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/ament_cmake_gen_version_h/include/${PROJECT_NAME})

set(VERSION_FILE_NAME ${TMP_INCLUDE_DIR}/version.h)
set(NEED_TO_CREATE_VERSION_FILE TRUE)

# retrieve version information from <package>.xml file
# call ament_package_xml() if it has not been called before
if(NOT _AMENT_PACKAGE_NAME)
ament_package_xml()
endif()

string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER)
set(VERSION_STR ${${PROJECT_NAME}_VERSION})

# parse version information from the version string
string(REGEX MATCH "([0-9]+)\.([0-9]+)\.([0-9]+)" "" dummy ${VERSION_STR})
set(VERSION_MAJOR ${CMAKE_MATCH_1})
set(VERSION_MINOR ${CMAKE_MATCH_2})
set(VERSION_PATCH ${CMAKE_MATCH_3})

# Check if the version file exist
if(EXISTS "${VERSION_FILE_NAME}")
# The file exists
# Check if it contains the same version
set(LINE_PATTERN "#define[ \t]+${PROJECT_NAME_UPPER}_VERSION_STR")
file(STRINGS ${VERSION_FILE_NAME} VERSION_FILE_STRINGS REGEX ${LINE_PATTERN})
set(VERSION_PATTERN "^#define[ \t]+${PROJECT_NAME_UPPER}_VERSION_STR[ \t]+\"([0-9]+\.[0-9]+\.[0-9]+)\"")
string(REGEX MATCH ${VERSION_PATTERN} dummy ${VERSION_FILE_STRINGS})
if("${CMAKE_MATCH_1}" STREQUAL "${VERSION_STR}")
message(STATUS "File and project versions match: \"${CMAKE_MATCH_1}\" == \"${VERSION_STR}\"")
set(NEED_TO_CREATE_VERSION_FILE FALSE)
endif()
endif()

if(${NEED_TO_CREATE_VERSION_FILE})
message(STATUS "Create new version file for version ${${PROJECT_NAME}_VERSION}")
file(MAKE_DIRECTORY ${TMP_INCLUDE_DIR})
# create the version.h file
configure_file(${TEMPLATE_FILE} ${VERSION_FILE_NAME})
else()
message(STATUS "Skip version file creation")
endif()
hidmic marked this conversation as resolved.
Show resolved Hide resolved

install(
DIRECTORY ${TMP_INCLUDE_DIR}
DESTINATION include)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge-nikulin I'm somewhat inclined towards generating the file and letting the caller decide if and where to install it (as if it was an OUTPUT of add_custom_command).

I'd be OK with optionally delegating the installation though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hidmic, I decided to parametrize pretty much everything. How do the following optional parameters look to you?
If you are OK, I'll proceed with them and also create some unit tests for different scenarios.

# :param DO_NOT_INSTALL: whether to autmatically install the generated version file into DESTINATION include
# :type DO_NOT_INSTALL: BOOL
# :default value DO_NOT_INSTALL: FALSE

# :param PRJ_NAME: project name to use
# :type PRJ_NAME: string
# :default value PRJ_NAME: ${PROJECT_NAME}

# :param INCLUDE_DIR: path to the include folder where the file will be generated
#     ${INCLUDE_DIR} folder will be added to the include paths
#     the file will be placed into ${INCLUDE_DIR}/${PRJ_NAME} folder according to ROS2 standard
# :type INCLUDE_DIR: string
# :default value INCLUDE_DIR: ${CMAKE_CURRENT_BINARY_DIR}/ament_cmake_gen_version_h/include

# :param VERSION_FILE_NAME: file name of the generated header file
# :type VERSION_FILE_NAME: string
# :default value VERSION_FILE_NAME: version.h

Copy link
Contributor

@hidmic hidmic Sep 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@serge-nikulin may I suggest a NO_INSTALL option instead of a DO_NOT_INSTALL one-value argument? Also, what use case do you have in mind for PRJ_NAME? Why would you want to change it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with both points. Please review again.

endfunction()
46 changes: 46 additions & 0 deletions ament_cmake_gen_version_h/cmake/version.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ${PROJECT_NAME_UPPER}__VERSION_H_
#define ${PROJECT_NAME_UPPER}__VERSION_H_

/// \def ${PROJECT_NAME_UPPER}_VERSION_MAJOR
/// Defines ${PROJECT_NAME_UPPER} major version number
#define ${PROJECT_NAME_UPPER}_VERSION_MAJOR (${VERSION_MAJOR})

/// \def ${PROJECT_NAME_UPPER}_VERSION_MINOR
/// Defines ${PROJECT_NAME_UPPER} minor version number
#define ${PROJECT_NAME_UPPER}_VERSION_MINOR (${VERSION_MINOR})

/// \def ${PROJECT_NAME_UPPER}_VERSION_PATCH
/// Defines ${PROJECT_NAME_UPPER} version patch number
#define ${PROJECT_NAME_UPPER}_VERSION_PATCH (${VERSION_PATCH})

/// \def ${PROJECT_NAME_UPPER}_VERSION_STR
/// Defines ${PROJECT_NAME_UPPER} version string
#define ${PROJECT_NAME_UPPER}_VERSION_STR "${VERSION_STR}"

/// \def ${PROJECT_NAME_UPPER}_VERSION_GTE
/// Defines a macro to check whether the version of ${PROJECT_NAME_UPPER} is greater than or equal to
/// the given version triple.
#define ${PROJECT_NAME_UPPER}_VERSION_GTE(major, minor, patch) ( \
(major < ${PROJECT_NAME_UPPER}_VERSION_MAJOR) ? true \
: (major > ${PROJECT_NAME_UPPER}_VERSION_MAJOR) ? false \
: (minor < ${PROJECT_NAME_UPPER}_VERSION_MINOR) ? true \
: (minor > ${PROJECT_NAME_UPPER}_VERSION_MINOR) ? false \
: (patch < ${PROJECT_NAME_UPPER}_VERSION_PATCH) ? true \
: (patch > ${PROJECT_NAME_UPPER}_VERSION_PATCH) ? false \
: true)

#endif // ${PROJECT_NAME_UPPER}__VERSION_H_
19 changes: 19 additions & 0 deletions ament_cmake_gen_version_h/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>ament_cmake_gen_version_h</name>
<version>1.1.4</version>
<description>Generate a C header containing the version number of the package</description>
<maintainer email="serge@safeai.ai">Serge Nikulin</maintainer>
<license>Apache License 2.0</license>

<author>Serge Nikulin</author>

<buildtool_depend>ament_cmake_core</buildtool_depend>

<buildtool_export_depend>ament_cmake_core</buildtool_export_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>