1# Git helper functions and macros
2
3# git_get_versioned(VERSION <version> FILES <path> ...)
4#
5# Get files by name relative to the current source directory but read it from
6# branch VERSION. Note that the version referenced has to exists as a branch,
7# otherwise you get an error.
8#
9# VERSION <version>
10#
11# Version to read files from. Any object name is accepted as given in
12# gitrevision(7), but typically you should use a tag name.
13#
14# FILES <path> ...
15#
16# Paths to extract. These are relative or absolute paths to files to extract. If
17# relative, the path names are resolved relative the current source directory.
18#
19# RESULT_FILES <var>
20#
21# Variable for the list of the full paths of the retrieved files.
22#
23# IGNORE_ERRORS
24#
25# Ignore errors when fetching file from previous version. This is currently used
26# to ignore missing files, but we should probably be more selective in the
27# following versions.
28function(git_versioned_get)
29  set(options IGNORE_ERRORS)
30  set(oneValueArgs VERSION RESULT_FILES)
31  set(multiValueArgs FILES)
32  cmake_parse_arguments(_git_get "${options}" "${oneValueArgs}"
33                        "${multiValueArgs}" ${ARGN})
34
35  execute_process(
36    COMMAND git show-ref --verify --quiet refs/tags/${_git_get_VERSION}
37    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
38    RESULT_VARIABLE _version_missing)
39  if(_version_missing)
40    message(
41      FATAL_ERROR "Version ${_git_get_VERSION} do not exist in repository.")
42  endif()
43
44  set(_result_files)
45  foreach(_file ${_git_get_FILES})
46    # Build full path, if the path is not absolute.
47    if(IS_ABSOLUTE ${_file})
48      set(_path "${_file}")
49    else()
50      set(_path "${CMAKE_CURRENT_SOURCE_DIR}/${_file}")
51    endif()
52
53    # Remove root source directory to get path relative to source root. This is
54    # necessary for git-show to work correctly and then use that to generate a
55    # full path for the version.  (Yeah, we could use ./ before, but this is
56    # less clear in the log what file is actually fetched.)
57    string(REPLACE ${CMAKE_SOURCE_DIR}/ "" _relpath ${_path})
58    set(_fullpath "${CMAKE_BINARY_DIR}/v${_git_get_VERSION}/${_relpath}")
59    get_filename_component(_dirname ${_fullpath} DIRECTORY)
60    file(MAKE_DIRECTORY ${_dirname})
61
62    # We place the output file next to the final file to avoid cross-device
63    # renames but give it a different name since it is created even if the
64    # command fails.
65    execute_process(
66      COMMAND ${GIT_EXECUTABLE} show ${_git_get_VERSION}:${_relpath}
67      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
68      OUTPUT_FILE "${_fullpath}.part"
69      RESULT_VARIABLE _error
70      ERROR_VARIABLE _message)
71
72    if(_error)
73      if(_git_get_IGNORE_ERRORS)
74        string(STRIP "${_message}" _stripped_message)
75        message(STATUS "${_stripped_message} (ignored).")
76      else()
77        message(FATAL_ERROR "${_message}")
78      endif()
79    else()
80      file(RENAME "${_fullpath}.part" "${_fullpath}")
81      list(APPEND _result_files "${_fullpath}")
82    endif()
83  endforeach()
84  set(${_git_get_RESULT_FILES}
85      ${_result_files}
86      PARENT_SCOPE)
87endfunction()
88