[PATCH] ExternalProject: Preserve source folder ourside the build tree

Daniele E. Domenichelli daniele.domenichelli at iit.it
Tue Sep 10 11:23:31 EDT 2013


If the source tree of the project is outside the build tree, do not
delete it automatically. This will ensure that if the user keeps the
sources outside the build repository, and has some valuable work in it,
it won't be lost if he deletes the build directory.
Also it should avoid multiple useless clones with different build
directories.

A new directory property EP_SOURCE_DIR_PRESERVE and a new parameter
SOURCE_DIR_PRESERVE of the ExternalProject_Add function will allow one
to explicitly set or unset this behaviour for a directory and its
subdirectories or just for one project.

Source preservation is always disabled when the source directory is
inside the build tree.
In order to keep the same behaviour, source preservation is enabled
by default if CMAKE_MINIMUM_REQUIRED_VERSION is greater than 2.8.13, and
disabled otherwise.
---
 Modules/ExternalProject.cmake | 137 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 129 insertions(+), 8 deletions(-)

diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 0781ea1..7370009 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -30,6 +30,7 @@
 #    [TLS_VERIFY bool]           # Should certificate for https be checked
 #    [TLS_CAINFO file]           # Path to a certificate authority file
 #    [TIMEOUT seconds]           # Time allowed for file download operations
+#    [SOURCE_DIR_PRESERVE 1]     # FIXME docs
 #   #--Update/Patch step----------
 #    [UPDATE_COMMAND cmd...]     # Source work-tree update command
 #    [PATCH_COMMAND cmd...]      # Command to patch downloaded source
@@ -158,6 +159,8 @@
 # any ExternalProject_Add calls in your CMakeLists file:
 #
 #   set_property(DIRECTORY PROPERTY EP_STEP_TARGETS configure build test)
+#
+# FIXME: Add documentation here
 
 #=============================================================================
 # Copyright 2008-2012 Kitware, Inc.
@@ -270,8 +273,16 @@ define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED
   "ExternalProject module."
   )
 
+define_property(DIRECTORY PROPERTY "EP_SOURCE_DIR_PRESERVE" INHERITED
+  BRIEF_DOCS "Whether source dir stored outside the build directory should be preserved."
+  FULL_DOCS
+  "See documentation of the ExternalProject_Add() function in the "
+  "ExternalProject module."
+  )
+
+
 
-function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag src_name work_dir gitclone_infofile gitclone_stampfile)
+function(_ep_write_gitclone_script script_filename source_dir git_EXECUTABLE git_repository git_tag src_name work_dir gitclone_infofile gitclone_stampfile source_dir_preserve)
   file(WRITE ${script_filename}
 "if(\"${git_tag}\" STREQUAL \"\")
   message(FATAL_ERROR \"Tag for git checkout should not be empty.\")
@@ -288,12 +299,70 @@ if(NOT run)
   return()
 endif()
 
-execute_process(
-  COMMAND \${CMAKE_COMMAND} -E remove_directory \"${source_dir}\"
-  RESULT_VARIABLE error_code
-  )
-if(error_code)
-  message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\")
+if(EXISTS \"${source_dir}\" AND ${source_dir_preserve})
+  if(NOT IS_DIRECTORY \"${source_dir}\")
+    # FIXME Perhaps support symbolic links?
+    message(FATAL_ERROR \"\\\"${source_dir}\\\" exists and is not a git repository. Remove it and try again\")
+  elseif(NOT IS_DIRECTORY \"${source_dir}/.git\")
+    file(GLOB files \"${source_dir}/*\")
+    list(LENGTH files nfiles)
+    if(nfiles)
+      message(FATAL_ERROR \"\\\"${source_dir}\\\" folder exists and is not a git repository. Remove it and try again\")
+    endif()
+  else()
+    # Already initialized git repository: no need to clone again
+    execute_process(
+      COMMAND \"${git_EXECUTABLE}\" config --local --get remote.origin.url
+      WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+      OUTPUT_VARIABLE origin_url
+      RESULT_VARIABLE error_code
+      OUTPUT_STRIP_TRAILING_WHITESPACE
+      )
+    if(error_code)
+      message(FATAL_ERROR \"Failed to get origin remote url in: '${work_dir}/${src_name}'\")
+    endif()
+    if(\"\${origin_url}\" STREQUAL \"${git_repository}\")
+      message(STATUS \"Avoiding repeated git clone, repository already exists\")
+      return()
+    else()
+      string(TIMESTAMP now \"%Y%m%d%H%M%S\")
+      message(WARNING \"Repository URL is different. Renaming origin remote to origin.\${now}\")
+      execute_process(
+        COMMAND \"${git_EXECUTABLE}\" remote rename origin origin.\${now}
+        WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+        RESULT_VARIABLE error_code
+        )
+      if(error_code)
+        message(FATAL_ERROR \"Failed to rename remote in: '${work_dir}/${src_name}'\")
+      endif()
+
+      execute_process(
+        COMMAND \"${git_EXECUTABLE}\" remote add origin \"${git_repository}\"
+        WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+        RESULT_VARIABLE error_code
+        )
+      if(error_code)
+        message(FATAL_ERROR \"Failed to add origin remote in: '${work_dir}/${src_name}'\")
+      endif()
+
+      execute_process(
+        COMMAND \"${git_EXECUTABLE}\" fetch origin
+        WORKING_DIRECTORY \"${work_dir}/${src_name}\"
+        RESULT_VARIABLE error_code
+        )
+      if(error_code)
+        message(FATAL_ERROR \"Failed to fetch in: '${work_dir}/${src_name}'\")
+      endif()
+    endif()
+  endif()
+else()
+  execute_process(
+    COMMAND \${CMAKE_COMMAND} -E remove_directory \"${source_dir}\"
+    RESULT_VARIABLE error_code
+    )
+  if(error_code)
+    message(FATAL_ERROR \"Failed to remove directory: '${source_dir}'\")
+  endif()
 endif()
 
 # try the clone 3 times incase there is an odd git clone issue
@@ -1199,6 +1268,58 @@ function(_ep_add_download_command name)
   get_property(url TARGET ${name} PROPERTY _EP_URL)
   get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME)
 
+  # Check if the external project source directory is inside the build directory
+  set(source_in_binary_dir 0)
+  get_filename_component(abs_src_dir ${source_dir} ABSOLUTE)
+  get_filename_component(abs_bin_dir ${CMAKE_BINARY_DIR} ABSOLUTE)
+  if(\"${abs_src_dir}\" STREQUAL \"${abs_bin_dir}\")
+    set(source_in_binary_dir 1)
+  else()
+    string(LENGTH \"${abs_src_dir}\" src_len)
+    string(LENGTH \"${abs_bin_dir}\" bin_len)
+    if(src_len GREATER bin_len)
+      math(EXPR substr_len \"${bin_len}-1\")
+      string(SUBSTRING "${abs_src_dir}" 0 ${substr_len} src_dir)
+      if(src_dir STREQUAL "${abs_bin_dir}/")
+        set(source_in_binary_dir 1)
+      endif()
+    endif()
+  endif()
+
+  # If source is not in build directory it should be optionally saved (since CMake 2.8.13)
+  set(source_dir_preserve 0)
+  if(NOT source_in_binary_dir)
+    get_property(source_dir_preserve_set TARGET ${name} PROPERTY _EP_SOURCE_DIR_PRESERVE SET)
+    if(source_dir_preserve_set)
+      get_property(source_dir_preserve TARGET ${name} PROPERTY _EP_SOURCE_DIR_PRESERVE)
+    else()
+      get_property(ep_source_dir_preserve_set DIRECTORY PROPERTY EP_SOURCE_DIR_PRESERVE SET)
+      if(source_dir_preserve_set)
+        get_property(source_dir_preserve DIRECTORY PROPERTY EP_SOURCE_DIR_PRESERVE)
+      elseif(NOT CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.13)
+        set(source_dir_preserve 1)
+      endif()
+    endif()
+  endif()
+  # FIXME is there a better way to avoid this CMP0012 warning? (see line 295)
+  #
+  #  given arguments:
+  #
+  #    "EXISTS" "xxx" "AND" "TRUE"
+  #
+  #  An argument named "TRUE" appears in a conditional statement.  Policy
+  #  CMP0012 is not set: if() recognizes numbers and boolean constants.  Run
+  #  "cmake --help-policy CMP0012" for policy details.  Use the cmake_policy
+  #  command to set the policy and suppress this warning.
+  cmake_policy(PUSH)
+  cmake_policy(SET CMP0012 NEW)
+  if(${source_dir_preserve})
+    set(source_dir_preserve 1)
+  else()
+    set(source_dir_preserve 0)
+  endif()
+  cmake_policy(POP)
+
   # TODO: Perhaps file:// should be copied to download dir before extraction.
   string(REGEX REPLACE "^file://" "" url "${url}")
 
@@ -1314,7 +1435,7 @@ function(_ep_add_download_command name)
     _ep_write_gitclone_script(${tmp_dir}/${name}-gitclone.cmake ${source_dir}
       ${GIT_EXECUTABLE} ${git_repository} ${git_tag} ${src_name} ${work_dir}
       ${stamp_dir}/${name}-gitinfo.txt ${stamp_dir}/${name}-gitclone-lastrun.txt
-      )
+      ${source_dir_preserve})
     set(comment "Performing download step (git clone) for '${name}'")
     set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake)
     list(APPEND depends ${stamp_dir}/${name}-gitinfo.txt)
-- 
1.8.4.rc3


--------------050006060809080202070001--


More information about the cmake-developers mailing list