From 0dab4aea06b180831ca318f379a00be4102c1d14 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 15 Nov 2017 14:23:04 -0500 Subject: [PATCH] Use CMake to fetch submodules 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. --- CMakeLists.txt | 1 + cmake/modules/CheckSubmodules.cmake | 138 ++++++++++++++++++++++++++++ src/CMakeLists.txt | 10 +- 3 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 cmake/modules/CheckSubmodules.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ee47d120e..99619b9e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ IF(COMMAND CMAKE_POLICY) ENDIF() ENDIF(COMMAND CMAKE_POLICY) +INCLUDE(CheckSubmodules) INCLUDE(AddFileDependencies) INCLUDE(CheckIncludeFiles) INCLUDE(FindPkgConfig) diff --git a/cmake/modules/CheckSubmodules.cmake b/cmake/modules/CheckSubmodules.cmake new file mode 100644 index 000000000..d90cd622b --- /dev/null +++ b/cmake/modules/CheckSubmodules.cmake @@ -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, +# +# 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") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e67c1ec6..28d2455b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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()