-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGitExternal.cmake
217 lines (196 loc) · 8.44 KB
/
GitExternal.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# Copyright (c) 2014-2015 John Biddiscombe
# Copyright (c) 2014-2015 Daniel Nachbaur
# Copyright (c) 2013-2015 Stefan Eileman
#
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
# configures an external git repository
# Usage:
# * Automatically reads, parses and updates a .gitexternals file if it only
# contains lines in the form "# <directory> <giturl> <gittag>".
# This function parses the file for this pattern and then calls
# git_external on each found entry. Additionally it provides an
# update target to bump the tag to the master revision by
# recreating .gitexternals.
# * Provides function
# git_external(<directory> <giturl> <gittag> [NO_UPDATE, VERBOSE] [RESET <files>])
# git_external_manage(<file>)
#
# [optional] Flags which control behaviour
# NO_UPDATE
# When set, GitExternal will not change a repo that has already been checked out.
# The purpose of this is to allow one to set a default branch to be checked out,
# but stop GitExternal from changing back to that branch if the user has checked
# out and is working on another.
# VERBOSE
# When set, displays information about git commands that are executed
#
find_package(Git)
if(NOT GIT_EXECUTABLE)
return()
endif()
include(CMakeParseArguments)
macro(GIT_EXTERNAL_MESSAGE msg)
if(${GIT_EXTERNAL_VERBOSE})
message(STATUS "${NAME} : ${msg}")
endif()
endmacro(GIT_EXTERNAL_MESSAGE)
function(GIT_EXTERNAL DIR REPO TAG)
cmake_parse_arguments(GIT_EXTERNAL "NO_UPDATE;VERBOSE" "" "RESET" ${ARGN})
get_filename_component(DIR "${DIR}" ABSOLUTE)
get_filename_component(NAME "${DIR}" NAME)
get_filename_component(GIT_EXTERNAL_DIR "${DIR}/.." ABSOLUTE)
if(NOT EXISTS "${DIR}")
message(STATUS "git clone ${REPO} ${DIR}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" clone "${REPO}" "${DIR}"
RESULT_VARIABLE nok ERROR_VARIABLE error
WORKING_DIRECTORY "${GIT_EXTERNAL_DIR}")
if(nok)
message(FATAL_ERROR "${DIR} git clone failed: ${error}\n")
endif()
endif()
if(IS_DIRECTORY "${DIR}/.git")
if (${GIT_EXTERNAL_NO_UPDATE})
GIT_EXTERNAL_MESSAGE("Update branch disabled by user")
else()
GIT_EXTERNAL_MESSAGE("current ref is \"${currentref}\" and tag is \"${TAG}\"")
if(currentref STREQUAL TAG) # nothing to do
return()
endif()
# reset generated files
foreach(GIT_EXTERNAL_RESET_FILE ${GIT_EXTERNAL_RESET})
GIT_EXTERNAL_MESSAGE("git reset -q ${GIT_EXTERNAL_RESET_FILE}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" reset -q "${GIT_EXTERNAL_RESET_FILE}"
RESULT_VARIABLE nok ERROR_VARIABLE error
WORKING_DIRECTORY "${DIR}")
GIT_EXTERNAL_MESSAGE("git checkout -q -- ${GIT_EXTERNAL_RESET_FILE}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" checkout -q -- "${GIT_EXTERNAL_RESET_FILE}"
RESULT_VARIABLE nok ERROR_VARIABLE error
WORKING_DIRECTORY "${DIR}")
endforeach()
# fetch latest update
GIT_EXTERNAL_MESSAGE("git fetch --all -q")
execute_process(COMMAND "${GIT_EXECUTABLE}" fetch --all -q
RESULT_VARIABLE nok ERROR_VARIABLE error
WORKING_DIRECTORY "${DIR}")
if(nok)
message(STATUS "Update of ${DIR} failed:\n ${error}")
endif()
# checkout requested tag
GIT_EXTERNAL_MESSAGE("git checkout -q ${TAG}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" checkout -q "${TAG}"
RESULT_VARIABLE nok ERROR_VARIABLE error
WORKING_DIRECTORY "${DIR}"
)
if(nok)
message(STATUS "${DIR} git checkout ${TAG} failed: ${error}\n")
endif()
# update tag
GIT_EXTERNAL_MESSAGE("git rebase FETCH_HEAD")
execute_process(COMMAND ${GIT_EXECUTABLE} rebase FETCH_HEAD
RESULT_VARIABLE RESULT OUTPUT_VARIABLE OUTPUT ERROR_VARIABLE OUTPUT
WORKING_DIRECTORY "${DIR}")
if(RESULT)
message(STATUS "git rebase failed, aborting ${DIR} merge")
execute_process(COMMAND ${GIT_EXECUTABLE} rebase --abort
WORKING_DIRECTORY "${DIR}")
endif()
endif()
else()
message(STATUS "Can't update git external ${DIR}: Not a git repository")
endif()
endfunction()
set(GIT_EXTERNALS ${GIT_EXTERNALS_FILE})
if(NOT GIT_EXTERNALS)
set(GIT_EXTERNALS "${CMAKE_CURRENT_SOURCE_DIR}/.gitexternals")
endif()
if(EXISTS ${GIT_EXTERNALS})
file(READ ${GIT_EXTERNALS} GIT_EXTERNAL_FILE)
string(REGEX REPLACE "\n" ";" GIT_EXTERNAL_FILE "${GIT_EXTERNAL_FILE}")
foreach(LINE ${GIT_EXTERNAL_FILE})
if(NOT LINE MATCHES "^#.*$")
message(FATAL_ERROR "${GIT_EXTERNALS} contains non-comment line: ${LINE}")
endif()
string(REGEX REPLACE "^#[ ]*(.+[ ]+.+[ ]+.+)$" "\\1" DATA ${LINE})
if(NOT LINE STREQUAL DATA)
string(REGEX REPLACE "[ ]+" ";" DATA "${DATA}")
list(LENGTH DATA DATA_LENGTH)
if(DATA_LENGTH EQUAL 3)
list(GET DATA 0 DIR)
list(GET DATA 1 REPO)
list(GET DATA 2 TAG)
# Create a unique, flat name
string(REPLACE "/" "_" GIT_EXTERNAL_NAME ${DIR}_${PROJECT_NAME})
if(NOT TARGET update_git_external_${GIT_EXTERNAL_NAME}) # not done
# pull in identified external
git_external(${DIR} ${REPO} ${TAG})
# Create update script and target to bump external spec
if(NOT TARGET update)
add_custom_target(update)
endif()
if(NOT TARGET update_git_external)
add_custom_target(update_git_external)
add_custom_target(flatten_git_external)
add_dependencies(update update_git_external)
endif()
# Create a unique, flat name
file(RELATIVE_PATH GIT_EXTERNALS_BASE ${CMAKE_CURRENT_SOURCE_DIR}
${GIT_EXTERNALS})
string(REPLACE "/" "_" GIT_EXTERNAL_TARGET ${GIT_EXTERNALS_BASE})
set(GIT_EXTERNAL_TARGET update_git_external_${GIT_EXTERNAL_TARGET})
if(NOT TARGET ${GIT_EXTERNAL_TARGET})
set(GIT_EXTERNAL_SCRIPT
"${CMAKE_CURRENT_BINARY_DIR}/${GIT_EXTERNAL_TARGET}.cmake")
file(WRITE "${GIT_EXTERNAL_SCRIPT}"
"file(WRITE ${GIT_EXTERNALS} \"# -*- mode: cmake -*-\n\")\n")
add_custom_target(${GIT_EXTERNAL_TARGET}
COMMAND ${CMAKE_COMMAND} -P ${GIT_EXTERNAL_SCRIPT}
COMMENT "Recreate ${GIT_EXTERNALS_BASE}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
set(GIT_EXTERNAL_SCRIPT
"${CMAKE_CURRENT_BINARY_DIR}/gitupdate${GIT_EXTERNAL_NAME}.cmake")
file(WRITE "${GIT_EXTERNAL_SCRIPT}" "
execute_process(COMMAND ${GIT_EXECUTABLE} fetch --all -q
WORKING_DIRECTORY ${DIR})
execute_process(
COMMAND ${GIT_EXECUTABLE} show-ref --hash=7 refs/remotes/origin/master
OUTPUT_VARIABLE newref WORKING_DIRECTORY ${DIR})
if(newref)
file(APPEND ${GIT_EXTERNALS} \"# ${DIR} ${REPO} \${newref}\")
else()
file(APPEND ${GIT_EXTERNALS} \"# ${DIR} ${REPO} ${TAG}\n\")
endif()")
add_custom_target(update_git_external_${GIT_EXTERNAL_NAME}
COMMAND ${CMAKE_COMMAND} -P ${GIT_EXTERNAL_SCRIPT}
COMMENT "Update ${REPO} in ${GIT_EXTERNALS_BASE}"
DEPENDS ${GIT_EXTERNAL_TARGET}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
add_dependencies(update_git_external
update_git_external_${GIT_EXTERNAL_NAME})
# Flattens a git external repository into its parent repo:
# * Clean any changes from external
# * Unlink external from git: Remove external/.git and .gitexternals
# * Add external directory to parent
# * Commit with flattened repo and tag info
# - Depend on release branch checked out
add_custom_target(flatten_git_external_${GIT_EXTERNAL_NAME}
COMMAND ${GIT_EXECUTABLE} clean -dfx
COMMAND ${CMAKE_COMMAND} -E remove_directory .git
COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_SOURCE_DIR}/.gitexternals
COMMAND ${GIT_EXECUTABLE} add -f .
COMMAND ${GIT_EXECUTABLE} commit -m "Flatten ${REPO} into ${DIR} at ${TAG}" . ${CMAKE_CURRENT_SOURCE_DIR}/.gitexternals
COMMENT "Flatten ${REPO} into ${DIR}"
DEPENDS make-branch
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}")
add_dependencies(flatten_git_external
flatten_git_external_${GIT_EXTERNAL_NAME})
endif()
endif()
endif()
endforeach()
endif()