ExternalData.cmake
Go to the documentation of this file.
00001 ############################################################################## 00002 # @file ExternalData.cmake 00003 # @brief Manage data files stored outside the source tree. 00004 # 00005 # 00006 # Copyright 2010-2011 Kitware, Inc. All rights reserved. 00007 # File modified by the SBIA Group. 00008 # 00009 # Contact: SBIA Group <sbia-software at uphs.upenn.edu> 00010 # 00011 # @ingroup CMakeTools 00012 ############################################################################## 00013 00014 # - Manage data files stored outside source tree 00015 # Use this module to unambiguously reference data files stored outside the 00016 # source tree and fetch them at build time from arbitrary local and remote 00017 # content-addressed locations. Functions provided by this module recognize 00018 # arguments with the syntax "DATA{<name>}" as references to external data, 00019 # replace them with full paths to local copies of those data, and create build 00020 # rules to fetch and update the local copies. 00021 # 00022 # The DATA{} syntax is literal and the <name> is a full or relative path 00023 # within the source tree. The source tree must contain either a real data 00024 # file at <name> or a "content link" at <name><ext> containing a hash of the 00025 # real file using a hash algorithm corresponding to <ext>. For example, the 00026 # argument "DATA{img.png}" may be satisfied by either a real "img.png" file in 00027 # the current source directory or a "img.png.md5" file containing its MD5 sum. 00028 # 00029 # The 'ExternalData_Expand_Arguments' function evaluates DATA{} references 00030 # in its arguments and constructs a new list of arguments: 00031 # ExternalData_Expand_Arguments( 00032 # <target> # Name of data management target 00033 # <outVar> # Output variable 00034 # [args...] # Input arguments, DATA{} allowed 00035 # ) 00036 # It replaces each DATA{} reference argument with the full path of a real 00037 # data file on disk that will exist after the <target> builds. 00038 # 00039 # The 'ExternalData_Add_Test' function wraps around the CMake add_test() 00040 # command but supports DATA{} reference arguments: 00041 # ExternalData_Add_Test( 00042 # <target> # Name of data management target 00043 # ... # Arguments of add_test(), DATA{} allowed 00044 # ) 00045 # It passes its arguments through ExternalData_Expand_Arguments and then 00046 # invokes add_test() using the results. 00047 # 00048 # The 'ExternalData_Add_Target' function creates a custom target to manage 00049 # local instances of data files stored externally: 00050 # ExternalData_Add_Target( 00051 # <target> # Name of data management target 00052 # ) 00053 # It creates custom commands in the target as necessary to make data files 00054 # available for each DATA{} reference previously evaluated by other functions 00055 # provided by this module. A list of URL templates must be provided in the 00056 # variable ExternalData_URL_TEMPLATES using the placeholders "%(algo)" and 00057 # "%(hash)" in each template. Data fetch rules try each URL template in order 00058 # by substituting the hash algorithm name for "%(algo)" and the hash value for 00059 # "%(hash)". 00060 # 00061 # The following hash algorithms are supported: 00062 # %(algo) <ext> Description 00063 # ------- ----- ----------- 00064 # MD5 .md5 Message-Digest Algorithm 5, RFC 1321 00065 # Note that the hashes are used only for unique data identification and 00066 # download verification. This is not security software. 00067 # 00068 # Example usage: 00069 # include(ExternalData) 00070 # set(ExternalData_URL_TEMPLATES "file:///local/%(algo)/%(hash)" 00071 # "http://data.org/%(algo)/%(hash)") 00072 # ExternalData_Add_Test(MyData 00073 # NAME MyTest 00074 # COMMAND MyExe DATA{MyInput.png} 00075 # ) 00076 # ExternalData_Add_Target(MyData) 00077 # When test "MyTest" runs the "DATA{MyInput.png}" argument will be replaced by 00078 # the full path to a real instance of the data file "MyInput.png" on disk. If 00079 # the source tree contains a content link such as "MyInput.png.md5" then the 00080 # "MyData" target creates a real "MyInput.png" in the build tree. 00081 # 00082 # The DATA{} syntax can automatically recognize and fetch a file series. If 00083 # the source tree contains a group of files or content links named like a 00084 # series then a DATA{} reference to one member adds rules to fetch all of 00085 # them. Although all members of a series are fetched, only the file 00086 # originally named by the DATA{} argument is substituted for it. Two 00087 # variables configure recognition of a series from DATA{<name>}. First, 00088 # ExternalData_SERIES_PARSE is a regex of the form "^(...)(...)(...)$" to 00089 # parse <prefix>, <number>, and <suffix> parts from <name>. Second, 00090 # ExternalData_SERIES_MATCH is a regex matching the <number> part of series 00091 # members named <prefix><number><suffix>. Note that the <suffix> of a series 00092 # does not include a hash-algorithm extension. Both series configuration 00093 # variables have default values that work well for common cases. 00094 # 00095 # The variable ExternalData_LINK_CONTENT may be set to the name of a supported 00096 # hash algorithm to enable automatic conversion of real data files referenced 00097 # by the DATA{} syntax into content links. For each such <file> a content 00098 # link named "<file><ext>" is created. The original file is renamed to the 00099 # form ".ExternalData_<algo>_<hash>" to stage it for future transmission to 00100 # one of the locations in the list of URL templates (by means outside the 00101 # scope of this module). The data fetch rule created for the content link 00102 # will use the staged object if it cannot be found using any URL template. 00103 # 00104 # The variable ExternalData_SOURCE_ROOT may be set to the highest source 00105 # directory containing any path named by a DATA{} reference. The default is 00106 # CMAKE_SOURCE_DIR. ExternalData_SOURCE_ROOT and CMAKE_SOURCE_DIR must refer 00107 # to directories within a single source distribution (e.g. they come together 00108 # in one tarball). 00109 00110 #============================================================================= 00111 # Copyright 2010-2011 Kitware, Inc. 00112 # All rights reserved. 00113 # 00114 # Redistribution and use in source and binary forms, with or without 00115 # modification, are permitted provided that the following conditions 00116 # are met: 00117 # 00118 # * Redistributions of source code must retain the above copyright 00119 # notice, this list of conditions and the following disclaimer. 00120 # 00121 # * Redistributions in binary form must reproduce the above copyright 00122 # notice, this list of conditions and the following disclaimer in the 00123 # documentation and/or other materials provided with the distribution. 00124 # 00125 # * Neither the names of Kitware, Inc., the Insight Software Consortium, 00126 # nor the names of their contributors may be used to endorse or promote 00127 # products derived from this software without specific prior written 00128 # permission. 00129 # 00130 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00131 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00132 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00133 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00134 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00135 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00136 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00137 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00138 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00139 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00140 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00141 #============================================================================= 00142 00143 ############################################################################## 00144 # @brief @todo Document function. 00145 # 00146 # @param [in] target Name of the test. 00147 00148 function(ExternalData_add_test target) 00149 ExternalData_expand_arguments("${target}" testArgs ${ARGN}) 00150 add_test(${testArgs}) 00151 endfunction() 00152 00153 ############################################################################## 00154 # @brief @todo Document funtion. 00155 # 00156 # @param [in] target Name of the external data target. 00157 00158 function(ExternalData_add_target target) 00159 if(NOT ExternalData_URL_TEMPLATES) 00160 message(FATAL_ERROR "ExternalData_URL_TEMPLATES is not set!") 00161 endif() 00162 set(config ${CMAKE_CURRENT_BINARY_DIR}/${target}_config.cmake) 00163 configure_file(${_ExternalData_SELF_DIR}/ExternalData_config.cmake.in ${config} @ONLY) 00164 00165 set(files "") 00166 00167 # Set "_ExternalData_FILE_${file}" for each output file to avoid duplicate 00168 # rules. Use local data first to prefer real files over content links. 00169 00170 # Custom commands to copy or link local data. 00171 get_property(data_local GLOBAL PROPERTY _ExternalData_${target}_LOCAL) 00172 foreach(entry IN LISTS data_local) 00173 string(REPLACE "|" ";" tuple "${entry}") 00174 list(GET tuple 0 file) 00175 list(GET tuple 1 name) 00176 if(NOT DEFINED "_ExternalData_FILE_${file}") 00177 set("_ExternalData_FILE_${file}" 1) 00178 add_custom_command( 00179 COMMENT "Generating ${file}" 00180 OUTPUT "${file}" 00181 COMMAND ${CMAKE_COMMAND} -Drelative_top=${CMAKE_BINARY_DIR} 00182 -Dfile=${file} -Dname=${name} 00183 -DExternalData_ACTION=local 00184 -DExternalData_CONFIG=${config} 00185 -P ${_ExternalData_SELF} 00186 DEPENDS "${name}" 00187 ) 00188 list(APPEND files "${file}") 00189 endif() 00190 endforeach() 00191 00192 # Custom commands to fetch remote data. 00193 get_property(data_fetch GLOBAL PROPERTY _ExternalData_${target}_FETCH) 00194 foreach(entry IN LISTS data_fetch) 00195 string(REPLACE "|" ";" tuple "${entry}") 00196 list(GET tuple 0 file) 00197 list(GET tuple 1 name) 00198 list(GET tuple 2 ext) 00199 if(NOT DEFINED "_ExternalData_FILE_${file}") 00200 set("_ExternalData_FILE_${file}" 1) 00201 add_custom_command( 00202 # Users care about the data file, so hide the hash/timestamp file. 00203 COMMENT "Generating ${file}" 00204 # The hash/timestamp file is the output from the build perspective. 00205 # List the real file as a second output in case it is a broken link. 00206 # The files must be listed in this order so CMake can hide from the 00207 # make tool that a symlink target may not be newer than the input. 00208 OUTPUT "${file}${ext}" "${file}" 00209 # Run the data fetch/update script. 00210 COMMAND ${CMAKE_COMMAND} -DExternalData_OBJECT_DIR=${CMAKE_BINARY_DIR}/ExternalData/Objects 00211 -Drelative_top=${CMAKE_BINARY_DIR} 00212 -Dfile=${file} -Dname=${name} -Dext=${ext} 00213 -DExternalData_ACTION=fetch 00214 -DExternalData_CONFIG=${config} 00215 -P ${_ExternalData_SELF} 00216 # Update whenever the object hash changes. 00217 DEPENDS "${name}${ext}" 00218 ) 00219 list(APPEND files "${file}${ext}") 00220 endif() 00221 endforeach() 00222 00223 # Custom target to drive all update commands. 00224 add_custom_target(${target} ALL DEPENDS ${files}) 00225 endfunction() 00226 00227 ############################################################################## 00228 # @brief Replace DATA{} references with real arguments. 00229 # 00230 # @param [in] target Name of the external data target. 00231 # @param [out] outArgsVar List of expanded arguments. 00232 00233 function(ExternalData_expand_arguments target outArgsVar) 00234 # Replace DATA{} references with real arguments. 00235 set(data_regex "^xDATA{([^{}\r\n]*)}$") 00236 set(outArgs "") 00237 foreach(arg IN LISTS ARGN) 00238 if("x${arg}" MATCHES "${data_regex}") 00239 string(REGEX REPLACE "${data_regex}" "\\1" data "x${arg}") 00240 _ExternalData_arg("${target}" "${arg}" "${data}" file) 00241 list(APPEND outArgs "${file}") 00242 else() 00243 list(APPEND outArgs "${arg}") 00244 endif() 00245 endforeach() 00246 set("${outArgsVar}" "${outArgs}" PARENT_SCOPE) 00247 endfunction() 00248 00249 #----------------------------------------------------------------------------- 00250 # Private helper interface 00251 00252 set(_ExternalData_SELF "${CMAKE_CURRENT_LIST_FILE}") 00253 get_filename_component(_ExternalData_SELF_DIR "${_ExternalData_SELF}" PATH) 00254 00255 function(_ExternalData_compute_hash var_hash algo file) 00256 if("${algo}" STREQUAL "MD5") 00257 # TODO: Errors 00258 execute_process(COMMAND "${CMAKE_COMMAND}" -E md5sum "${file}" 00259 OUTPUT_VARIABLE output) 00260 string(SUBSTRING ${output} 0 32 hash) 00261 set("${var_hash}" "${hash}" PARENT_SCOPE) 00262 else() 00263 # TODO: Other hashes. 00264 message(FATAL_ERROR "Hash algorithm ${algo} unimplemented.") 00265 endif() 00266 endfunction() 00267 00268 function(_ExternalData_atomic_write file content) 00269 string(RANDOM LENGTH 6 random) 00270 set(tmp "${file}.tmp${random}") 00271 file(WRITE "${tmp}" "${content}") 00272 file(RENAME "${tmp}" "${file}") 00273 endfunction() 00274 00275 function(_ExternalData_link_content name var_ext) 00276 if("${ExternalData_LINK_CONTENT}" MATCHES "^(MD5)$") 00277 set(algo "${ExternalData_LINK_CONTENT}") 00278 else() 00279 message(FATAL_ERROR 00280 "Unknown hash algorithm specified by ExternalData_LINK_CONTENT:\n" 00281 " ${ExternalData_LINK_CONTENT}") 00282 endif() 00283 _ExternalData_compute_hash(hash "${algo}" "${name}") 00284 get_filename_component(dir "${name}" PATH) 00285 set(staged "${dir}/.ExternalData_${algo}_${hash}") 00286 set(ext ".md5") 00287 _ExternalData_atomic_write("${name}${ext}" "${hash}\n") 00288 file(RENAME "${name}" "${staged}") 00289 set("${var_ext}" "${ext}" PARENT_SCOPE) 00290 00291 file(RELATIVE_PATH relname "${ExternalData_SOURCE_ROOT}" "${name}${ext}") 00292 message(STATUS "Linked ${relname} to ExternalData ${algo}/${hash}") 00293 endfunction() 00294 00295 function(_ExternalData_arg target arg data var_file) 00296 # Convert to full path. 00297 if(IS_ABSOLUTE "${data}") 00298 set(absdata "${data}") 00299 else() 00300 # TODO: If ${data} does not start in "./" or "../" then use search path? 00301 get_filename_component(absdata "${CMAKE_CURRENT_SOURCE_DIR}/${data}" ABSOLUTE) 00302 endif() 00303 00304 # Convert to relative path under the source tree. 00305 if(NOT ExternalData_SOURCE_ROOT) 00306 set(ExternalData_SOURCE_ROOT "${CMAKE_SOURCE_DIR}") 00307 endif() 00308 set(top_src "${ExternalData_SOURCE_ROOT}") 00309 file(RELATIVE_PATH reldata "${top_src}" "${absdata}") 00310 if(IS_ABSOLUTE "${reldata}" OR "${reldata}" MATCHES "^\\.\\./") 00311 message(FATAL_ERROR "Data file referenced by argument\n" 00312 " ${arg}\n" 00313 "does not lie under the top-level source directory\n" 00314 " ${top_src}\n") 00315 endif() 00316 set(top_bin "${CMAKE_BINARY_DIR}/ExternalData") # TODO: .../${target} ? 00317 00318 # Configure series parsing and matching. 00319 if(ExternalData_SERIES_PARSE) 00320 if(NOT "${ExternalData_SERIES_PARSE}" MATCHES 00321 "^\\^\\([^()]*\\)\\([^()]*\\)\\([^()]*\\)\\$$") 00322 message(FATAL_ERROR 00323 "ExternalData_SERIES_PARSE is set to\n" 00324 " ${ExternalData_SERIES_PARSE}\n" 00325 "which is not of the form\n" 00326 " ^(...)(...)(...)$\n") 00327 endif() 00328 set(series_parse "${ExternalData_SERIES_PARSE}") 00329 else() 00330 set(series_parse "^(.*[A-Za-z_.-])([0-9]*)(\\.[^.]*)$") 00331 endif() 00332 if(ExternalData_SERIES_MATCH) 00333 set(series_match "${ExternalData_SERIES_MATCH}") 00334 else() 00335 set(series_match "[_.]?[0-9]*") 00336 endif() 00337 00338 # Parse the base, number, and extension components of the series. 00339 string(REGEX REPLACE "${series_parse}" "\\1;\\2;\\3" tuple "${reldata}") 00340 list(LENGTH tuple len) 00341 if(NOT "${len}" EQUAL 3) 00342 message(FATAL_ERROR "Data file referenced by argument\n" 00343 " ${arg}\n" 00344 "corresponds to path\n" 00345 " ${reldata}\n" 00346 "that does not match regular expression\n" 00347 " ${series_parse}") 00348 endif() 00349 00350 # Glob files that might match the series. 00351 list(GET tuple 0 relbase) 00352 list(GET tuple 2 ext) 00353 set(pattern "${relbase}*${ext}*") 00354 file(GLOB globbed RELATIVE "${top_src}" "${top_src}/${pattern}") 00355 00356 # Match base, number, and extension perhaps followed by a hash ext. 00357 string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" series_base "${relbase}") 00358 string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" series_ext "${ext}") 00359 set(series_regex "^(${series_base}${series_match}${series_ext})(\\.[^.]*|)$") 00360 set(external "") # Entries external to the source tree. 00361 set(internal "") # Entries internal to the source tree. 00362 set(have_original 0) 00363 foreach(entry IN LISTS globbed) 00364 string(REGEX REPLACE "${series_regex}" "\\1;\\2" tuple "${entry}") 00365 list(LENGTH tuple len) 00366 if("${len}" EQUAL 2) 00367 list(GET tuple 0 relname) 00368 list(GET tuple 1 alg) 00369 set(name "${top_src}/${relname}") 00370 set(file "${top_bin}/${relname}") 00371 if(alg) 00372 list(APPEND external "${file}|${name}|${alg}") 00373 elseif(ExternalData_LINK_CONTENT) 00374 _ExternalData_link_content("${name}" alg) 00375 list(APPEND external "${file}|${name}|${alg}") 00376 else() 00377 list(APPEND internal "${file}|${name}") 00378 endif() 00379 if("${relname}" STREQUAL "${reldata}") 00380 set(have_original 1) 00381 endif() 00382 endif() 00383 endforeach() 00384 00385 if(NOT have_original) 00386 message(FATAL_ERROR "Data file referenced by argument\n" 00387 " ${arg}\n" 00388 "corresponds to source tree path\n" 00389 " ${reldata}\n" 00390 "that does not exist (with or without an extension)!") 00391 endif() 00392 00393 if(external) 00394 # Make the series available in the build tree. 00395 set_property(GLOBAL APPEND PROPERTY 00396 _ExternalData_${target}_FETCH "${external}") 00397 set_property(GLOBAL APPEND PROPERTY 00398 _ExternalData_${target}_LOCAL "${internal}") 00399 set("${var_file}" "${top_bin}/${reldata}" PARENT_SCOPE) 00400 else() 00401 # The whole series is in the source tree. 00402 set("${var_file}" "${top_src}/${reldata}" PARENT_SCOPE) 00403 endif() 00404 endfunction() 00405 00406 #----------------------------------------------------------------------------- 00407 # Private script mode interface 00408 00409 if(CMAKE_GENERATOR OR NOT ExternalData_ACTION) 00410 return() 00411 endif() 00412 00413 if(ExternalData_CONFIG) 00414 include(${ExternalData_CONFIG}) 00415 endif() 00416 if(NOT ExternalData_URL_TEMPLATES) 00417 message(FATAL_ERROR "No ExternalData_URL_TEMPLATES set!") 00418 endif() 00419 00420 function(_ExternalData_link_or_copy src dst) 00421 # Create a temporary file first. 00422 get_filename_component(dst_dir "${dst}" PATH) 00423 file(MAKE_DIRECTORY "${dst_dir}") 00424 string(RANDOM LENGTH 6 random) 00425 set(tmp "${dst}.tmp${random}") 00426 if(UNIX) 00427 # Create a symbolic link. 00428 set(tgt "${src}") 00429 if(relative_top) 00430 # Use relative path if files are close enough. 00431 file(RELATIVE_PATH relsrc "${relative_top}" "${src}") 00432 file(RELATIVE_PATH relfile "${relative_top}" "${dst}") 00433 if(NOT IS_ABSOLUTE "${relsrc}" AND NOT "${relsrc}" MATCHES "^\\.\\./" AND 00434 NOT IS_ABSOLUTE "${reldst}" AND NOT "${reldst}" MATCHES "^\\.\\./") 00435 file(RELATIVE_PATH tgt "${dst_dir}" "${src}") 00436 endif() 00437 endif() 00438 execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${tgt}" "${tmp}" RESULT_VARIABLE result) 00439 else() 00440 # Create a copy. 00441 execute_process(COMMAND "${CMAKE_COMMAND}" -E copy "${src}" "${tmp}" RESULT_VARIABLE result) 00442 endif() 00443 if(result) 00444 file(REMOVE "${tmp}") 00445 message(FATAL_ERROR "Failed to create\n ${tmp}\nfrom\n ${obj}") 00446 endif() 00447 00448 # Atomically create/replace the real destination. 00449 file(RENAME "${tmp}" "${dst}") 00450 endfunction() 00451 00452 function(_ExternalData_download_object name hash algo var_obj) 00453 set(obj "${ExternalData_OBJECT_DIR}/${algo}/${hash}") 00454 if(EXISTS "${obj}") 00455 message(STATUS "Found object: \"${obj}\"") 00456 set("${var_obj}" "${obj}" PARENT_SCOPE) 00457 return() 00458 endif() 00459 00460 string(RANDOM LENGTH 6 random) 00461 set(tmp "${obj}.tmp${random}") 00462 set(found 0) 00463 set(tried "") 00464 foreach(url_template IN LISTS ExternalData_URL_TEMPLATES) 00465 string(REPLACE "%(hash)" "${hash}" url_tmp "${url_template}") 00466 string(REPLACE "%(algo)" "${algo}" url "${url_tmp}") 00467 message(STATUS "Fetching \"${url}\"") 00468 file(DOWNLOAD "${url}" "${tmp}" STATUS status SHOW_PROGRESS) # TODO: timeout 00469 set(tried "${tried}\n ${url}") 00470 list(GET status 0 err) 00471 if(err) 00472 list(GET status 1 errMsg) 00473 set(tried "${tried} (${errMsg})") 00474 else() 00475 # Verify downloaded object. 00476 _ExternalData_compute_hash(dl_hash "${algo}" "${tmp}") 00477 if("${dl_hash}" STREQUAL "${hash}") 00478 set(found 1) 00479 break() 00480 else() 00481 set(tried "${tried} (wrong hash ${algo}=${dl_hash})") 00482 endif() 00483 endif() 00484 file(REMOVE "${tmp}") 00485 endforeach() 00486 00487 get_filename_component(dir "${name}" PATH) 00488 set(staged "${dir}/.ExternalData_${algo}_${hash}") 00489 00490 if(found) 00491 file(RENAME "${tmp}" "${obj}") 00492 message(STATUS "Downloaded object: \"${obj}\"") 00493 elseif(EXISTS "${staged}") 00494 set(obj "${staged}") 00495 message(STATUS "Staged object: \"${obj}\"") 00496 else() 00497 message(FATAL_ERROR "Object ${algo}=${hash} not found at:${tried}") 00498 endif() 00499 00500 set("${var_obj}" "${obj}" PARENT_SCOPE) 00501 endfunction() 00502 00503 if("${ExternalData_ACTION}" STREQUAL "fetch") 00504 foreach(v ExternalData_OBJECT_DIR file name ext) 00505 if(NOT DEFINED "${v}") 00506 message(FATAL_ERROR "No \"-D${v}=\" value provided!") 00507 endif() 00508 endforeach() 00509 00510 file(READ "${name}${ext}" hash) 00511 string(STRIP "${hash}" hash) 00512 00513 if("${ext}" STREQUAL ".md5") 00514 set(algo "MD5") 00515 else() 00516 message(FATAL_ERROR "Unknown hash algorithm extension \"${ext}\"") 00517 endif() 00518 00519 _ExternalData_download_object("${name}" "${hash}" "${algo}" obj) 00520 00521 # Check if file already corresponds to the object. 00522 set(file_up_to_date 0) 00523 if(EXISTS "${file}" AND EXISTS "${file}${ext}") 00524 file(READ "${file}${ext}" f_hash) 00525 string(STRIP "${f_hash}" f_hash) 00526 if("${f_hash}" STREQUAL "${hash}") 00527 #message(STATUS "File already corresponds to object") 00528 set(file_up_to_date 1) 00529 endif() 00530 endif() 00531 00532 if(file_up_to_date) 00533 # Touch the file to convince the build system it is up to date. 00534 execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${file}") 00535 else() 00536 _ExternalData_link_or_copy("${obj}" "${file}") 00537 endif() 00538 00539 # Atomically update the hash/timestamp file to record the object referenced. 00540 _ExternalData_atomic_write("${file}${ext}" "${hash}\n") 00541 elseif("${ExternalData_ACTION}" STREQUAL "local") 00542 foreach(v file name) 00543 if(NOT DEFINED "${v}") 00544 message(FATAL_ERROR "No \"-D${v}=\" value provided!") 00545 endif() 00546 endforeach() 00547 _ExternalData_link_or_copy("${name}" "${file}") 00548 elseif("${ExternalData_ACTION}" STREQUAL "store") 00549 foreach(v dir file) 00550 if(NOT DEFINED "${v}") 00551 message(FATAL_ERROR "No \"-D${v}=\" value provided!") 00552 endif() 00553 endforeach() 00554 if(NOT DEFINED algo) 00555 set(algo "MD5") 00556 endif() 00557 _ExternalData_compute_hash(hash "${algo}" "${file}") 00558 else() 00559 message(FATAL_ERROR "Unknnown ExternalData_ACTION=[${ExternalData_ACTION}]") 00560 endif()