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()