summaryrefslogtreecommitdiffstats
path: root/Modules/ExternalProject/shared_internal_commands.cmake
blob: ca3cd9fec7c21024cf0b0bf670ad97c9647eb8ce (plain)
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
cmake_policy(VERSION 3.25)

# Determine the remote URL of the project containing the working_directory.
# This will leave output_variable unset if the URL can't be determined.
function(_ep_get_git_remote_url output_variable working_directory)
  set("${output_variable}" "" PARENT_SCOPE)

  find_package(Git QUIET REQUIRED)

  execute_process(
    COMMAND ${GIT_EXECUTABLE} symbolic-ref --short HEAD
    WORKING_DIRECTORY "${working_directory}"
    OUTPUT_VARIABLE git_symbolic_ref
    OUTPUT_STRIP_TRAILING_WHITESPACE
    ERROR_QUIET
  )

  if(NOT git_symbolic_ref STREQUAL "")
    # We are potentially on a branch. See if that branch is associated with
    # an upstream remote (might be just a local one or not a branch at all).
    execute_process(
      COMMAND ${GIT_EXECUTABLE} config branch.${git_symbolic_ref}.remote
      WORKING_DIRECTORY "${working_directory}"
      OUTPUT_VARIABLE git_remote_name
      OUTPUT_STRIP_TRAILING_WHITESPACE
      ERROR_QUIET
    )
  endif()

  if(NOT git_remote_name)
    # Can't select a remote based on a branch. If there's only one remote,
    # or we have multiple remotes but one is called "origin", choose that.
    execute_process(
      COMMAND ${GIT_EXECUTABLE} remote
      WORKING_DIRECTORY "${working_directory}"
      OUTPUT_VARIABLE git_remote_list
      OUTPUT_STRIP_TRAILING_WHITESPACE
      ERROR_QUIET
    )
    string(REPLACE "\n" ";" git_remote_list "${git_remote_list}")
    list(LENGTH git_remote_list git_remote_list_length)

    if(git_remote_list_length EQUAL 0)
      message(FATAL_ERROR "Git remote not found in parent project.")
    elseif(git_remote_list_length EQUAL 1)
      list(GET git_remote_list 0 git_remote_name)
    else()
      set(base_warning_msg "Multiple git remotes found for parent project")
      if("origin" IN_LIST git_remote_list)
        message(WARNING "${base_warning_msg}, defaulting to origin.")
        set(git_remote_name "origin")
      else()
        message(FATAL_ERROR "${base_warning_msg}, none of which are origin.")
      endif()
    endif()
  endif()

  if(GIT_VERSION VERSION_LESS 1.7.5)
    set(_git_remote_url_cmd_args config remote.${git_remote_name}.url)
  elseif(GIT_VERSION VERSION_LESS 2.7)
    set(_git_remote_url_cmd_args ls-remote --get-url ${git_remote_name})
  else()
    set(_git_remote_url_cmd_args remote get-url ${git_remote_name})
  endif()

  execute_process(
    COMMAND ${GIT_EXECUTABLE} ${_git_remote_url_cmd_args}
    WORKING_DIRECTORY "${working_directory}"
    OUTPUT_VARIABLE git_remote_url
    OUTPUT_STRIP_TRAILING_WHITESPACE
    COMMAND_ERROR_IS_FATAL LAST
    ENCODING UTF-8   # Needed to handle non-ascii characters in local paths
  )

  set("${output_variable}" "${git_remote_url}" PARENT_SCOPE)
endfunction()

function(_ep_is_relative_git_remote output_variable remote_url)
  if(remote_url MATCHES "^\\.\\./")
    set("${output_variable}" TRUE PARENT_SCOPE)
  else()
    set("${output_variable}" FALSE PARENT_SCOPE)
  endif()
endfunction()

# Return an absolute remote URL given an existing remote URL and relative path.
# The output_variable will be set to an empty string if an absolute URL
# could not be computed (no error message is output).
function(_ep_resolve_relative_git_remote
  output_variable
  parent_remote_url
  relative_remote_url
)
  set("${output_variable}" "" PARENT_SCOPE)

  if(parent_remote_url STREQUAL "")
    return()
  endif()

  string(REGEX MATCH
    "^(([A-Za-z0-9][A-Za-z0-9+.-]*)://)?(([^/@]+)@)?(\\[[A-Za-z0-9:]+\\]|[^/:]+)?([/:]/?)(.+(\\.git)?/?)$"
    git_remote_url_components
    "${parent_remote_url}"
  )

  set(protocol "${CMAKE_MATCH_1}")
  set(auth "${CMAKE_MATCH_3}")
  set(host "${CMAKE_MATCH_5}")
  set(separator "${CMAKE_MATCH_6}")
  set(path "${CMAKE_MATCH_7}")

  string(REPLACE "/" ";" remote_path_components "${path}")
  string(REPLACE "/" ";" relative_path_components "${relative_remote_url}")

  foreach(relative_path_component IN LISTS relative_path_components)
    if(NOT relative_path_component STREQUAL "..")
      break()
    endif()

    list(LENGTH remote_path_components remote_path_component_count)

    if(remote_path_component_count LESS 1)
      return()
    endif()

    list(POP_BACK remote_path_components)
    list(POP_FRONT relative_path_components)
  endforeach()

  list(APPEND final_path_components ${remote_path_components} ${relative_path_components})
  list(JOIN final_path_components "/" path)

  set("${output_variable}" "${protocol}${auth}${host}${separator}${path}" PARENT_SCOPE)
endfunction()

# The output_variable will be set to the original git_repository if it
# could not be resolved (no error message is output). The original value is
# also returned if it doesn't need to be resolved.
function(_ep_resolve_git_remote
  output_variable
  git_repository
  cmp0150
  cmp0150_old_base_dir
)
  if(git_repository STREQUAL "")
    set("${output_variable}" "" PARENT_SCOPE)
    return()
  endif()

  _ep_is_relative_git_remote(_git_repository_is_relative "${git_repository}")

  if(NOT _git_repository_is_relative)
    set("${output_variable}" "${git_repository}" PARENT_SCOPE)
    return()
  endif()

  if(cmp0150 STREQUAL "NEW")
    _ep_get_git_remote_url(_parent_git_remote_url "${CMAKE_CURRENT_SOURCE_DIR}")
    _ep_resolve_relative_git_remote(_resolved_git_remote_url "${_parent_git_remote_url}" "${git_repository}")

    if(_resolved_git_remote_url STREQUAL "")
      message(FATAL_ERROR
        "Failed to resolve relative git remote URL:\n"
        "  Relative URL: ${git_repository}\n"
        "  Parent URL:   ${_parent_git_remote_url}"
      )
    endif()
    set("${output_variable}" "${_resolved_git_remote_url}" PARENT_SCOPE)
    return()
  elseif(cmp0150 STREQUAL "")
    cmake_policy(GET_WARNING CMP0150 _cmp0150_warning)
    message(AUTHOR_WARNING
      "${_cmp0150_warning}\n"
      "A relative GIT_REPOSITORY path was detected. "
      "This will be interpreted as a local path to where the project is being cloned. "
      "Set GIT_REPOSITORY to an absolute path or set policy CMP0150 to NEW to avoid "
      "this warning."
    )
  endif()

  set("${output_variable}" "${cmp0150_old_base_dir}/${git_repository}" PARENT_SCOPE)
endfunction()