Skip to content

Commit

Permalink
Use CMake to fetch submodules
Browse files Browse the repository at this point in the history
Custom CMake module which attempts to automatically clone submodules when they're missing.  Uses the `--depth` option if supported, which should be faster than running `--recursive` on initial clone.
  • Loading branch information
tresf authored Nov 15, 2017
1 parent 0f7d192 commit 0dab4ae
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 4 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ IF(COMMAND CMAKE_POLICY)
ENDIF()
ENDIF(COMMAND CMAKE_POLICY)

INCLUDE(CheckSubmodules)
INCLUDE(AddFileDependencies)
INCLUDE(CheckIncludeFiles)
INCLUDE(FindPkgConfig)
Expand Down
138 changes: 138 additions & 0 deletions cmake/modules/CheckSubmodules.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Utility for validating and, if needed, cloning all submodules
#
# Looks for a .gitmodules in the root project folder
# Loops over all modules looking well-known configure/build scripts
#
# Usage:
# INCLUDE(CheckSubmodules)
#
# Options:
# SET(SKIP_SUBMODULES "foo;bar")
#
# Or via command line:
# cmake -DSKIP_SUBMODULES=foo;bar
#
# Copyright (c) 2017, Tres Finocchiaro, <tres.finocchiaro@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.

# Files which confirm a successful clone
SET(VALID_CRUMBS "CMakeLists.txt;Makefile;Makefile.in;Makefile.am;configure.ac;configure.py;autogen.sh")

# Try and use the specified shallow clone on submodules, if supported
SET(DEPTH_VALUE 100)

# Number of times git commands will retry before failing
SET(MAX_ATTEMPTS 2)

MESSAGE("\nValidating submodules...")
FILE(READ "${CMAKE_SOURCE_DIR}/.gitmodules" SUBMODULE_DATA)

# Assume alpha-numeric paths
STRING(REGEX MATCHALL "path = [-0-9A-Za-z/]+" SUBMODULE_LIST ${SUBMODULE_DATA})
FOREACH(_part ${SUBMODULE_LIST})
STRING(REPLACE "path = " "" SUBMODULE_PATH ${_part})

# Remove submodules from validation as specified in -DSKIP_SUBMODULES=foo;bar
SET(SKIP false)
IF(SKIP_SUBMODULES)
FOREACH(_skip ${SKIP_SUBMODULES})
IF(${SUBMODULE_PATH} MATCHES ${_skip})
MESSAGE("-- Skipping ${SUBMODULE_PATH} matches \"${_skip}\"")
SET(SKIP true)
ENDIF()
ENDFOREACH()
ENDIF()
LIST(REMOVE_ITEM SUBMODULE_LIST ${_part})
IF(NOT SKIP)
LIST(APPEND SUBMODULE_LIST ${SUBMODULE_PATH})
ENDIF()
ENDFOREACH()

LIST(SORT SUBMODULE_LIST)

# Once called, status is stored in GIT_RESULT respectively.
# Note: Git likes to write to stderr. Don't assume stderr is error; Check GIT_RESULT instead.
MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT)
FIND_PACKAGE(Git REQUIRED)
IF(${FORCE_DEINIT})
MESSAGE("-- Resetting ${SUBMODULE_PATH}")
EXECUTE_PROCESS(
COMMAND ${GIT_EXECUTABLE} submodule deinit -f ${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_QUIET
)
# Recurse
GIT_SUBMODULE(${SUBMODULE_PATH} false)
ELSE()
# Try to use the depth switch
SET(DEPTH_CMD "")
MESSAGE("-- Fetching ${SUBMODULE_PATH}")
IF(DEPTH_VALUE)
SET(DEPTH_CMD "--depth" )
MESSAGE("-- Fetching ${SUBMODULE_PATH} @ --depth ${DEPTH_VALUE}")
ENDIF()

EXECUTE_PROCESS(
COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive ${DEPTH_CMD} ${DEPTH_VALUE} ${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_RESULT
OUTPUT_VARIABLE GIT_STDOUT
ERROR_VARIABLE GIT_STDERR
)

SET(GIT_MESSAGE "${GIT_STDOUT}${GIT_STDERR}")
MESSAGE(${GIT_MESSAGE})
ENDIF()
ENDMACRO()

SET(RETRY_PHRASES "Failed to recurse;unadvertised object;cannot create directory")

# Attempt to do lazy clone
FOREACH(_submodule ${SUBMODULE_LIST})
STRING(REPLACE "/" ";" PATH_PARTS ${_submodule})
LIST(REVERSE PATH_PARTS)
LIST(GET PATH_PARTS 0 SUBMODULE_NAME)

MESSAGE("-- Checking ${SUBMODULE_NAME}...")
SET(CRUMB_FOUND false)
FOREACH(_crumb ${VALID_CRUMBS})
IF(EXISTS "${CMAKE_SOURCE_DIR}/${_submodule}/${_crumb}")
SET(CRUMB_FOUND true)
MESSAGE("-- Found ${_submodule}/${_crumb}")
ENDIF()
ENDFOREACH()
IF(NOT CRUMB_FOUND)
GIT_SUBMODULE(${_submodule} false)

SET(COUNTED 0)
SET(COUNTING "")
# Handle edge-cases where submodule didn't clone properly or re-uses a non-empty directory
WHILE(NOT GIT_RESULT EQUAL 0 AND COUNTED LESS MAX_ATTEMPTS)
LIST(APPEND COUNTING "x")
LIST(LENGTH COUNTING COUNTED)

FOREACH(_phrase ${RETRY_PHRASES})
IF("${GIT_MESSAGE}" MATCHES "${_phrase}")
MESSAGE("-- Retrying ${_submodule} using 'deinit' (attempt ${COUNTED} of ${MAX_ATTEMPTS})...")

# Shallow submodules were introduced in 1.8.4
# Shallow commits can fail to clone from non-default branches, only try once
IF(GIT_VERSION_STRING VERSION_GREATER "1.8.3" AND COUNTED LESS 2)
# Try a shallow submodule clone
ELSE()
UNSET(DEPTH_VALUE)
ENDIF()

GIT_SUBMODULE(${_submodule} true)
ENDIF()
ENDFOREACH()
ENDWHILE()

IF(NOT GIT_RESULT EQUAL 0)
MESSAGE(FATAL_ERROR "${GIT_EXECUTABLE} exited with status of ${GIT_RESULT}")
ENDIF()
ENDIF()
ENDFOREACH()
MESSAGE("-- Done validating submodules.\n")
10 changes: 6 additions & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,12 @@ TARGET_LINK_LIBRARIES(lmms
)

FOREACH(LIB ${LMMS_REQUIRED_LIBS})
GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES)
if (INCLUDE_DIRS)
TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS})
ENDIF()
IF(TARGET ${LIB})
GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES)
IF(INCLUDE_DIRS)
TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS})
ENDIF()
ENDIF()
ENDFOREACH()


Expand Down

0 comments on commit 0dab4ae

Please sign in to comment.