Merge topic 'add-ExternalData-module'
5484c60
Merge branch 'vs6-rule-files' into add-ExternalData-module1fd8d01
ExternalData: Attach download rules to content links in IDEsa6d3ffc
Fix Module.ExternalData test on VS 6aed590a
Fix Module.ExternalData test on Cygwin06e8ded
Merge branch 'fix-atomic-rename-on-Windows' into add-ExternalData-modulee2e0d2e
ExternalData: Collapse ../ components in DATA{} pathsee2abfd
ExternalData: Add support for SHA 1 and 2 hash algorithmsaa8b228
ExternalData: Generalize hash algo/ext handling9e518a8
ExternalData: Allow DATA{} syntax to reference directories175ed02
ExternalData: Allow ()-groups in series match regex4befecc
ExternalData: Add tests covering interfaces and errorsbcd2580
ExternalData: Improve series matching using an explicit syntaxc0cebcb
ExternalData: Remove unused private interface5275993
ExternalData: Cleanup stray TODO and typo in comments7bb8344
ExternalData: Do not match directory names when resolving DATA{}00d801f
ExternalData: Remove compatibility with CMake < 2.8.5 ...
This commit is contained in:
commit
020525845a
|
@ -0,0 +1,761 @@
|
||||||
|
# - Manage data files stored outside source tree
|
||||||
|
# Use this module to unambiguously reference data files stored outside the
|
||||||
|
# source tree and fetch them at build time from arbitrary local and remote
|
||||||
|
# content-addressed locations. Functions provided by this module recognize
|
||||||
|
# arguments with the syntax "DATA{<name>}" as references to external data,
|
||||||
|
# replace them with full paths to local copies of those data, and create build
|
||||||
|
# rules to fetch and update the local copies.
|
||||||
|
#
|
||||||
|
# The DATA{} syntax is literal and the <name> is a full or relative path
|
||||||
|
# within the source tree. The source tree must contain either a real data
|
||||||
|
# file at <name> or a "content link" at <name><ext> containing a hash of the
|
||||||
|
# real file using a hash algorithm corresponding to <ext>. For example, the
|
||||||
|
# argument "DATA{img.png}" may be satisfied by either a real "img.png" file in
|
||||||
|
# the current source directory or a "img.png.md5" file containing its MD5 sum.
|
||||||
|
#
|
||||||
|
# The 'ExternalData_Expand_Arguments' function evaluates DATA{} references
|
||||||
|
# in its arguments and constructs a new list of arguments:
|
||||||
|
# ExternalData_Expand_Arguments(
|
||||||
|
# <target> # Name of data management target
|
||||||
|
# <outVar> # Output variable
|
||||||
|
# [args...] # Input arguments, DATA{} allowed
|
||||||
|
# )
|
||||||
|
# It replaces each DATA{} reference in an argument with the full path of a
|
||||||
|
# real data file on disk that will exist after the <target> builds.
|
||||||
|
#
|
||||||
|
# The 'ExternalData_Add_Test' function wraps around the CMake add_test()
|
||||||
|
# command but supports DATA{} references in its arguments:
|
||||||
|
# ExternalData_Add_Test(
|
||||||
|
# <target> # Name of data management target
|
||||||
|
# ... # Arguments of add_test(), DATA{} allowed
|
||||||
|
# )
|
||||||
|
# It passes its arguments through ExternalData_Expand_Arguments and then
|
||||||
|
# invokes add_test() using the results.
|
||||||
|
#
|
||||||
|
# The 'ExternalData_Add_Target' function creates a custom target to manage
|
||||||
|
# local instances of data files stored externally:
|
||||||
|
# ExternalData_Add_Target(
|
||||||
|
# <target> # Name of data management target
|
||||||
|
# )
|
||||||
|
# It creates custom commands in the target as necessary to make data files
|
||||||
|
# available for each DATA{} reference previously evaluated by other functions
|
||||||
|
# provided by this module. A list of URL templates must be provided in the
|
||||||
|
# variable ExternalData_URL_TEMPLATES using the placeholders "%(algo)" and
|
||||||
|
# "%(hash)" in each template. Data fetch rules try each URL template in order
|
||||||
|
# by substituting the hash algorithm name for "%(algo)" and the hash value for
|
||||||
|
# "%(hash)".
|
||||||
|
#
|
||||||
|
# The following hash algorithms are supported:
|
||||||
|
# %(algo) <ext> Description
|
||||||
|
# ------- ----- -----------
|
||||||
|
# MD5 .md5 Message-Digest Algorithm 5, RFC 1321
|
||||||
|
# SHA1 .sha1 US Secure Hash Algorithm 1, RFC 3174
|
||||||
|
# SHA224 .sha224 US Secure Hash Algorithms, RFC 4634
|
||||||
|
# SHA256 .sha256 US Secure Hash Algorithms, RFC 4634
|
||||||
|
# SHA384 .sha384 US Secure Hash Algorithms, RFC 4634
|
||||||
|
# SHA512 .sha512 US Secure Hash Algorithms, RFC 4634
|
||||||
|
# Note that the hashes are used only for unique data identification and
|
||||||
|
# download verification. This is not security software.
|
||||||
|
#
|
||||||
|
# Example usage:
|
||||||
|
# include(ExternalData)
|
||||||
|
# set(ExternalData_URL_TEMPLATES "file:///local/%(algo)/%(hash)"
|
||||||
|
# "http://data.org/%(algo)/%(hash)")
|
||||||
|
# ExternalData_Add_Test(MyData
|
||||||
|
# NAME MyTest
|
||||||
|
# COMMAND MyExe DATA{MyInput.png}
|
||||||
|
# )
|
||||||
|
# ExternalData_Add_Target(MyData)
|
||||||
|
# When test "MyTest" runs the "DATA{MyInput.png}" argument will be replaced by
|
||||||
|
# the full path to a real instance of the data file "MyInput.png" on disk. If
|
||||||
|
# the source tree contains a content link such as "MyInput.png.md5" then the
|
||||||
|
# "MyData" target creates a real "MyInput.png" in the build tree.
|
||||||
|
#
|
||||||
|
# The DATA{} syntax can be told to fetch a file series using the form
|
||||||
|
# "DATA{<name>,:}", where the ":" is literal. If the source tree contains a
|
||||||
|
# group of files or content links named like a series then a reference to one
|
||||||
|
# member adds rules to fetch all of them. Although all members of a series
|
||||||
|
# are fetched, only the file originally named by the DATA{} argument is
|
||||||
|
# substituted for it. The default configuration recognizes file series names
|
||||||
|
# ending with "#.ext", "_#.ext", ".#.ext", or "-#.ext" where "#" is a sequence
|
||||||
|
# of decimal digits and ".ext" is any single extension. Configure it with a
|
||||||
|
# regex that parses <number> and <suffix> parts from the end of <name>:
|
||||||
|
# ExternalData_SERIES_PARSE = regex of the form (<number>)(<suffix>)$
|
||||||
|
# For more complicated cases set:
|
||||||
|
# ExternalData_SERIES_PARSE = regex with at least two () groups
|
||||||
|
# ExternalData_SERIES_PARSE_PREFIX = <prefix> regex group number, if any
|
||||||
|
# ExternalData_SERIES_PARSE_NUMBER = <number> regex group number
|
||||||
|
# ExternalData_SERIES_PARSE_SUFFIX = <suffix> regex group number
|
||||||
|
# Configure series number matching with a regex that matches the
|
||||||
|
# <number> part of series members named <prefix><number><suffix>:
|
||||||
|
# ExternalData_SERIES_MATCH = regex matching <number> in all series members
|
||||||
|
# Note that the <suffix> of a series does not include a hash-algorithm
|
||||||
|
# extension.
|
||||||
|
#
|
||||||
|
# The DATA{} syntax can alternatively match files associated with the named
|
||||||
|
# file and contained in the same directory. Associated files may be specified
|
||||||
|
# by options using the syntax DATA{<name>,<opt1>,<opt2>,...}. Each option may
|
||||||
|
# specify one file by name or specify a regular expression to match file names
|
||||||
|
# using the syntax REGEX:<regex>. For example, the arguments
|
||||||
|
# DATA{MyData/MyInput.mhd,MyInput.img} # File pair
|
||||||
|
# DATA{MyData/MyFrames00.png,REGEX:MyFrames[0-9]+\\.png} # Series
|
||||||
|
# will pass MyInput.mha and MyFrames00.png on the command line but ensure
|
||||||
|
# that the associated files are present next to them.
|
||||||
|
#
|
||||||
|
# The DATA{} syntax may reference a directory using a trailing slash and a
|
||||||
|
# list of associated files. The form DATA{<name>/,<opt1>,<opt2>,...} adds
|
||||||
|
# rules to fetch any files in the directory that match one of the associated
|
||||||
|
# file options. For example, the argument DATA{MyDataDir/,REGEX:.*} will pass
|
||||||
|
# the full path to a MyDataDir directory on the command line and ensure that
|
||||||
|
# the directory contains files corresponding to every file or content link in
|
||||||
|
# the MyDataDir source directory.
|
||||||
|
#
|
||||||
|
# The variable ExternalData_LINK_CONTENT may be set to the name of a supported
|
||||||
|
# hash algorithm to enable automatic conversion of real data files referenced
|
||||||
|
# by the DATA{} syntax into content links. For each such <file> a content
|
||||||
|
# link named "<file><ext>" is created. The original file is renamed to the
|
||||||
|
# form ".ExternalData_<algo>_<hash>" to stage it for future transmission to
|
||||||
|
# one of the locations in the list of URL templates (by means outside the
|
||||||
|
# scope of this module). The data fetch rule created for the content link
|
||||||
|
# will use the staged object if it cannot be found using any URL template.
|
||||||
|
#
|
||||||
|
# The variable ExternalData_OBJECT_STORES may be set to a list of local
|
||||||
|
# directories that store objects using the layout <dir>/%(algo)/%(hash).
|
||||||
|
# These directories will be searched first for a needed object. If the object
|
||||||
|
# is not available in any store then it will be fetched remotely using the URL
|
||||||
|
# templates and added to the first local store listed. If no stores are
|
||||||
|
# specified the default is a location inside the build tree.
|
||||||
|
#
|
||||||
|
# The variable ExternalData_SOURCE_ROOT may be set to the highest source
|
||||||
|
# directory containing any path named by a DATA{} reference. The default is
|
||||||
|
# CMAKE_SOURCE_DIR. ExternalData_SOURCE_ROOT and CMAKE_SOURCE_DIR must refer
|
||||||
|
# to directories within a single source distribution (e.g. they come together
|
||||||
|
# in one tarball).
|
||||||
|
#
|
||||||
|
# The variable ExternalData_BINARY_ROOT may be set to the directory to hold
|
||||||
|
# the real data files named by expanded DATA{} references. The default is
|
||||||
|
# CMAKE_BINARY_DIR. The directory layout will mirror that of content links
|
||||||
|
# under ExternalData_SOURCE_ROOT.
|
||||||
|
#
|
||||||
|
# Variables ExternalData_TIMEOUT_INACTIVITY and ExternalData_TIMEOUT_ABSOLUTE
|
||||||
|
# set the download inactivity and absolute timeouts, in seconds. The defaults
|
||||||
|
# are 60 seconds and 300 seconds, respectively. Set either timeout to 0
|
||||||
|
# seconds to disable enforcement.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2010-2013 Kitware, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
# see accompanying file Copyright.txt for details.
|
||||||
|
#
|
||||||
|
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the License for more information.
|
||||||
|
#=============================================================================
|
||||||
|
# (To distribute this file outside of CMake, substitute the full
|
||||||
|
# License text for the above reference.)
|
||||||
|
|
||||||
|
function(ExternalData_add_test target)
|
||||||
|
ExternalData_expand_arguments("${target}" testArgs ${ARGN})
|
||||||
|
add_test(${testArgs})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(ExternalData_add_target target)
|
||||||
|
if(NOT ExternalData_URL_TEMPLATES)
|
||||||
|
message(FATAL_ERROR "ExternalData_URL_TEMPLATES is not set!")
|
||||||
|
endif()
|
||||||
|
if(NOT ExternalData_OBJECT_STORES)
|
||||||
|
set(ExternalData_OBJECT_STORES ${CMAKE_BINARY_DIR}/ExternalData/Objects)
|
||||||
|
endif()
|
||||||
|
set(config ${CMAKE_CURRENT_BINARY_DIR}/${target}_config.cmake)
|
||||||
|
configure_file(${_ExternalData_SELF_DIR}/ExternalData_config.cmake.in ${config} @ONLY)
|
||||||
|
|
||||||
|
set(files "")
|
||||||
|
|
||||||
|
# Set "_ExternalData_FILE_${file}" for each output file to avoid duplicate
|
||||||
|
# rules. Use local data first to prefer real files over content links.
|
||||||
|
|
||||||
|
# Custom commands to copy or link local data.
|
||||||
|
get_property(data_local GLOBAL PROPERTY _ExternalData_${target}_LOCAL)
|
||||||
|
foreach(entry IN LISTS data_local)
|
||||||
|
string(REPLACE "|" ";" tuple "${entry}")
|
||||||
|
list(GET tuple 0 file)
|
||||||
|
list(GET tuple 1 name)
|
||||||
|
if(NOT DEFINED "_ExternalData_FILE_${file}")
|
||||||
|
set("_ExternalData_FILE_${file}" 1)
|
||||||
|
add_custom_command(
|
||||||
|
COMMENT "Generating ${file}"
|
||||||
|
OUTPUT "${file}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -Drelative_top=${CMAKE_BINARY_DIR}
|
||||||
|
-Dfile=${file} -Dname=${name}
|
||||||
|
-DExternalData_ACTION=local
|
||||||
|
-DExternalData_CONFIG=${config}
|
||||||
|
-P ${_ExternalData_SELF}
|
||||||
|
MAIN_DEPENDENCY "${name}"
|
||||||
|
)
|
||||||
|
list(APPEND files "${file}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Custom commands to fetch remote data.
|
||||||
|
get_property(data_fetch GLOBAL PROPERTY _ExternalData_${target}_FETCH)
|
||||||
|
foreach(entry IN LISTS data_fetch)
|
||||||
|
string(REPLACE "|" ";" tuple "${entry}")
|
||||||
|
list(GET tuple 0 file)
|
||||||
|
list(GET tuple 1 name)
|
||||||
|
list(GET tuple 2 ext)
|
||||||
|
set(stamp "${ext}-stamp")
|
||||||
|
if(NOT DEFINED "_ExternalData_FILE_${file}")
|
||||||
|
set("_ExternalData_FILE_${file}" 1)
|
||||||
|
add_custom_command(
|
||||||
|
# Users care about the data file, so hide the hash/timestamp file.
|
||||||
|
COMMENT "Generating ${file}"
|
||||||
|
# The hash/timestamp file is the output from the build perspective.
|
||||||
|
# List the real file as a second output in case it is a broken link.
|
||||||
|
# The files must be listed in this order so CMake can hide from the
|
||||||
|
# make tool that a symlink target may not be newer than the input.
|
||||||
|
OUTPUT "${file}${stamp}" "${file}"
|
||||||
|
# Run the data fetch/update script.
|
||||||
|
COMMAND ${CMAKE_COMMAND} -Drelative_top=${CMAKE_BINARY_DIR}
|
||||||
|
-Dfile=${file} -Dname=${name} -Dext=${ext}
|
||||||
|
-DExternalData_ACTION=fetch
|
||||||
|
-DExternalData_CONFIG=${config}
|
||||||
|
-P ${_ExternalData_SELF}
|
||||||
|
# Update whenever the object hash changes.
|
||||||
|
MAIN_DEPENDENCY "${name}${ext}"
|
||||||
|
)
|
||||||
|
list(APPEND files "${file}${stamp}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Custom target to drive all update commands.
|
||||||
|
add_custom_target(${target} ALL DEPENDS ${files})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(ExternalData_expand_arguments target outArgsVar)
|
||||||
|
# Replace DATA{} references with real arguments.
|
||||||
|
set(data_regex "DATA{([^{}\r\n]*)}")
|
||||||
|
set(other_regex "([^D]|D[^A]|DA[^T]|DAT[^A]|DATA[^{])+|.")
|
||||||
|
set(outArgs "")
|
||||||
|
foreach(arg IN LISTS ARGN)
|
||||||
|
if("x${arg}" MATCHES "${data_regex}")
|
||||||
|
# Split argument into DATA{}-pieces and other pieces.
|
||||||
|
string(REGEX MATCHALL "${data_regex}|${other_regex}" pieces "${arg}")
|
||||||
|
# Compose output argument with DATA{}-pieces replaced.
|
||||||
|
set(outArg "")
|
||||||
|
foreach(piece IN LISTS pieces)
|
||||||
|
if("x${piece}" MATCHES "^x${data_regex}$")
|
||||||
|
# Replace this DATA{}-piece with a file path.
|
||||||
|
string(REGEX REPLACE "${data_regex}" "\\1" data "${piece}")
|
||||||
|
_ExternalData_arg("${target}" "${piece}" "${data}" file)
|
||||||
|
set(outArg "${outArg}${file}")
|
||||||
|
else()
|
||||||
|
# No replacement needed for this piece.
|
||||||
|
set(outArg "${outArg}${piece}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
list(APPEND outArgs "${outArg}")
|
||||||
|
else()
|
||||||
|
# No replacements needed in this argument.
|
||||||
|
list(APPEND outArgs "${arg}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set("${outArgsVar}" "${outArgs}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
# Private helper interface
|
||||||
|
|
||||||
|
set(_ExternalData_REGEX_ALGO "MD5|SHA1|SHA224|SHA256|SHA384|SHA512")
|
||||||
|
set(_ExternalData_REGEX_EXT "md5|sha1|sha224|sha256|sha384|sha512")
|
||||||
|
set(_ExternalData_SELF "${CMAKE_CURRENT_LIST_FILE}")
|
||||||
|
get_filename_component(_ExternalData_SELF_DIR "${_ExternalData_SELF}" PATH)
|
||||||
|
|
||||||
|
function(_ExternalData_compute_hash var_hash algo file)
|
||||||
|
if("${algo}" MATCHES "^${_ExternalData_REGEX_ALGO}$")
|
||||||
|
file("${algo}" "${file}" hash)
|
||||||
|
set("${var_hash}" "${hash}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Hash algorithm ${algo} unimplemented.")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_random var)
|
||||||
|
string(RANDOM LENGTH 6 random)
|
||||||
|
set("${var}" "${random}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_exact_regex regex_var string)
|
||||||
|
string(REGEX REPLACE "([][+.*()^])" "\\\\\\1" regex "${string}")
|
||||||
|
set("${regex_var}" "${regex}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_atomic_write file content)
|
||||||
|
_ExternalData_random(random)
|
||||||
|
set(tmp "${file}.tmp${random}")
|
||||||
|
file(WRITE "${tmp}" "${content}")
|
||||||
|
file(RENAME "${tmp}" "${file}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_link_content name var_ext)
|
||||||
|
if("${ExternalData_LINK_CONTENT}" MATCHES "^(${_ExternalData_REGEX_ALGO})$")
|
||||||
|
set(algo "${ExternalData_LINK_CONTENT}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Unknown hash algorithm specified by ExternalData_LINK_CONTENT:\n"
|
||||||
|
" ${ExternalData_LINK_CONTENT}")
|
||||||
|
endif()
|
||||||
|
_ExternalData_compute_hash(hash "${algo}" "${name}")
|
||||||
|
get_filename_component(dir "${name}" PATH)
|
||||||
|
set(staged "${dir}/.ExternalData_${algo}_${hash}")
|
||||||
|
string(TOLOWER ".${algo}" ext)
|
||||||
|
_ExternalData_atomic_write("${name}${ext}" "${hash}\n")
|
||||||
|
file(RENAME "${name}" "${staged}")
|
||||||
|
set("${var_ext}" "${ext}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
file(RELATIVE_PATH relname "${ExternalData_SOURCE_ROOT}" "${name}${ext}")
|
||||||
|
message(STATUS "Linked ${relname} to ExternalData ${algo}/${hash}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_arg target arg options var_file)
|
||||||
|
# Separate data path from the options.
|
||||||
|
string(REPLACE "," ";" options "${options}")
|
||||||
|
list(GET options 0 data)
|
||||||
|
list(REMOVE_AT options 0)
|
||||||
|
|
||||||
|
# Interpret trailing slashes as directories.
|
||||||
|
set(data_is_directory 0)
|
||||||
|
if("x${data}" MATCHES "^x(.*)([/\\])$")
|
||||||
|
set(data_is_directory 1)
|
||||||
|
set(data "${CMAKE_MATCH_1}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Convert to full path.
|
||||||
|
if(IS_ABSOLUTE "${data}")
|
||||||
|
set(absdata "${data}")
|
||||||
|
else()
|
||||||
|
set(absdata "${CMAKE_CURRENT_SOURCE_DIR}/${data}")
|
||||||
|
endif()
|
||||||
|
get_filename_component(absdata "${absdata}" ABSOLUTE)
|
||||||
|
|
||||||
|
# Convert to relative path under the source tree.
|
||||||
|
if(NOT ExternalData_SOURCE_ROOT)
|
||||||
|
set(ExternalData_SOURCE_ROOT "${CMAKE_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
set(top_src "${ExternalData_SOURCE_ROOT}")
|
||||||
|
file(RELATIVE_PATH reldata "${top_src}" "${absdata}")
|
||||||
|
if(IS_ABSOLUTE "${reldata}" OR "${reldata}" MATCHES "^\\.\\./")
|
||||||
|
message(FATAL_ERROR "Data file referenced by argument\n"
|
||||||
|
" ${arg}\n"
|
||||||
|
"does not lie under the top-level source directory\n"
|
||||||
|
" ${top_src}\n")
|
||||||
|
endif()
|
||||||
|
if(data_is_directory AND NOT IS_DIRECTORY "${top_src}/${reldata}")
|
||||||
|
message(FATAL_ERROR "Data directory referenced by argument\n"
|
||||||
|
" ${arg}\n"
|
||||||
|
"corresponds to source tree path\n"
|
||||||
|
" ${reldata}\n"
|
||||||
|
"that does not exist as a directory!")
|
||||||
|
endif()
|
||||||
|
if(NOT ExternalData_BINARY_ROOT)
|
||||||
|
set(ExternalData_BINARY_ROOT "${CMAKE_BINARY_DIR}")
|
||||||
|
endif()
|
||||||
|
set(top_bin "${ExternalData_BINARY_ROOT}")
|
||||||
|
|
||||||
|
# Handle in-source builds gracefully.
|
||||||
|
if("${top_src}" STREQUAL "${top_bin}")
|
||||||
|
if(ExternalData_LINK_CONTENT)
|
||||||
|
message(WARNING "ExternalData_LINK_CONTENT cannot be used in-source")
|
||||||
|
set(ExternalData_LINK_CONTENT 0)
|
||||||
|
endif()
|
||||||
|
set(top_same 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(external "") # Entries external to the source tree.
|
||||||
|
set(internal "") # Entries internal to the source tree.
|
||||||
|
set(have_original ${data_is_directory})
|
||||||
|
|
||||||
|
# Process options.
|
||||||
|
set(series_option "")
|
||||||
|
set(associated_files "")
|
||||||
|
set(associated_regex "")
|
||||||
|
foreach(opt ${options})
|
||||||
|
if("x${opt}" MATCHES "^xREGEX:[^:/]+$")
|
||||||
|
# Regular expression to match associated files.
|
||||||
|
string(REGEX REPLACE "^REGEX:" "" regex "${opt}")
|
||||||
|
list(APPEND associated_regex "${regex}")
|
||||||
|
elseif("x${opt}" MATCHES "^x:$")
|
||||||
|
# Activate series matching.
|
||||||
|
set(series_option "${opt}")
|
||||||
|
elseif("x${opt}" MATCHES "^[^][:/*?]+$")
|
||||||
|
# Specific associated file.
|
||||||
|
list(APPEND associated_files "${opt}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unknown option \"${opt}\" in argument\n"
|
||||||
|
" ${arg}\n")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(series_option)
|
||||||
|
if(data_is_directory)
|
||||||
|
message(FATAL_ERROR "Series option \"${series_option}\" not allowed with directories.")
|
||||||
|
endif()
|
||||||
|
if(associated_files OR associated_regex)
|
||||||
|
message(FATAL_ERROR "Series option \"${series_option}\" not allowed with associated files.")
|
||||||
|
endif()
|
||||||
|
# Load a whole file series.
|
||||||
|
_ExternalData_arg_series()
|
||||||
|
elseif(data_is_directory)
|
||||||
|
if(associated_files OR associated_regex)
|
||||||
|
# Load listed/matching associated files in the directory.
|
||||||
|
_ExternalData_arg_associated()
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Data directory referenced by argument\n"
|
||||||
|
" ${arg}\n"
|
||||||
|
"must list associated files.")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Load the named data file.
|
||||||
|
_ExternalData_arg_single()
|
||||||
|
if(associated_files OR associated_regex)
|
||||||
|
# Load listed/matching associated files.
|
||||||
|
_ExternalData_arg_associated()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT have_original)
|
||||||
|
message(FATAL_ERROR "Data file referenced by argument\n"
|
||||||
|
" ${arg}\n"
|
||||||
|
"corresponds to source tree path\n"
|
||||||
|
" ${reldata}\n"
|
||||||
|
"that does not exist as a file (with or without an extension)!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(external)
|
||||||
|
# Make the series available in the build tree.
|
||||||
|
set_property(GLOBAL APPEND PROPERTY
|
||||||
|
_ExternalData_${target}_FETCH "${external}")
|
||||||
|
set_property(GLOBAL APPEND PROPERTY
|
||||||
|
_ExternalData_${target}_LOCAL "${internal}")
|
||||||
|
set("${var_file}" "${top_bin}/${reldata}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
# The whole series is in the source tree.
|
||||||
|
set("${var_file}" "${top_src}/${reldata}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
macro(_ExternalData_arg_associated)
|
||||||
|
# Associated files lie in the same directory.
|
||||||
|
if(data_is_directory)
|
||||||
|
set(reldir "${reldata}")
|
||||||
|
else()
|
||||||
|
get_filename_component(reldir "${reldata}" PATH)
|
||||||
|
endif()
|
||||||
|
if(reldir)
|
||||||
|
set(reldir "${reldir}/")
|
||||||
|
endif()
|
||||||
|
_ExternalData_exact_regex(reldir_regex "${reldir}")
|
||||||
|
|
||||||
|
# Find files named explicitly.
|
||||||
|
foreach(file ${associated_files})
|
||||||
|
_ExternalData_exact_regex(file_regex "${file}")
|
||||||
|
_ExternalData_arg_find_files("${reldir}${file}" "${reldir_regex}${file_regex}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Find files matching the given regular expressions.
|
||||||
|
set(all "")
|
||||||
|
set(sep "")
|
||||||
|
foreach(regex ${associated_regex})
|
||||||
|
set(all "${all}${sep}${reldir_regex}${regex}")
|
||||||
|
set(sep "|")
|
||||||
|
endforeach()
|
||||||
|
_ExternalData_arg_find_files("${reldir}" "${all}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(_ExternalData_arg_single)
|
||||||
|
# Match only the named data by itself.
|
||||||
|
_ExternalData_exact_regex(data_regex "${reldata}")
|
||||||
|
_ExternalData_arg_find_files("${reldata}" "${data_regex}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(_ExternalData_arg_series)
|
||||||
|
# Configure series parsing and matching.
|
||||||
|
set(series_parse_prefix "")
|
||||||
|
set(series_parse_number "\\1")
|
||||||
|
set(series_parse_suffix "\\2")
|
||||||
|
if(ExternalData_SERIES_PARSE)
|
||||||
|
if(ExternalData_SERIES_PARSE_NUMBER AND ExternalData_SERIES_PARSE_SUFFIX)
|
||||||
|
if(ExternalData_SERIES_PARSE_PREFIX)
|
||||||
|
set(series_parse_prefix "\\${ExternalData_SERIES_PARSE_PREFIX}")
|
||||||
|
endif()
|
||||||
|
set(series_parse_number "\\${ExternalData_SERIES_PARSE_NUMBER}")
|
||||||
|
set(series_parse_suffix "\\${ExternalData_SERIES_PARSE_SUFFIX}")
|
||||||
|
elseif(NOT "x${ExternalData_SERIES_PARSE}" MATCHES "^x\\([^()]*\\)\\([^()]*\\)\\$$")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"ExternalData_SERIES_PARSE is set to\n"
|
||||||
|
" ${ExternalData_SERIES_PARSE}\n"
|
||||||
|
"which is not of the form\n"
|
||||||
|
" (<number>)(<suffix>)$\n"
|
||||||
|
"Fix the regular expression or set variables\n"
|
||||||
|
" ExternalData_SERIES_PARSE_PREFIX = <prefix> regex group number, if any\n"
|
||||||
|
" ExternalData_SERIES_PARSE_NUMBER = <number> regex group number\n"
|
||||||
|
" ExternalData_SERIES_PARSE_SUFFIX = <suffix> regex group number\n"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
set(series_parse "${ExternalData_SERIES_PARSE}")
|
||||||
|
else()
|
||||||
|
set(series_parse "([0-9]*)(\\.[^./]*)$")
|
||||||
|
endif()
|
||||||
|
if(ExternalData_SERIES_MATCH)
|
||||||
|
set(series_match "${ExternalData_SERIES_MATCH}")
|
||||||
|
else()
|
||||||
|
set(series_match "[_.-]?[0-9]*")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Parse the base, number, and extension components of the series.
|
||||||
|
string(REGEX REPLACE "${series_parse}" "${series_parse_prefix};${series_parse_number};${series_parse_suffix}" tuple "${reldata}")
|
||||||
|
list(LENGTH tuple len)
|
||||||
|
if(NOT "${len}" EQUAL 3)
|
||||||
|
message(FATAL_ERROR "Data file referenced by argument\n"
|
||||||
|
" ${arg}\n"
|
||||||
|
"corresponds to path\n"
|
||||||
|
" ${reldata}\n"
|
||||||
|
"that does not match regular expression\n"
|
||||||
|
" ${series_parse}")
|
||||||
|
endif()
|
||||||
|
list(GET tuple 0 relbase)
|
||||||
|
list(GET tuple 2 ext)
|
||||||
|
|
||||||
|
# Glob files that might match the series.
|
||||||
|
# Then match base, number, and extension.
|
||||||
|
_ExternalData_exact_regex(series_base "${relbase}")
|
||||||
|
_ExternalData_exact_regex(series_ext "${ext}")
|
||||||
|
_ExternalData_arg_find_files("${relbase}*${ext}"
|
||||||
|
"${series_base}${series_match}${series_ext}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
function(_ExternalData_arg_find_files pattern regex)
|
||||||
|
file(GLOB globbed RELATIVE "${top_src}" "${top_src}/${pattern}*")
|
||||||
|
foreach(entry IN LISTS globbed)
|
||||||
|
if("x${entry}" MATCHES "^x(.*)(\\.(${_ExternalData_REGEX_EXT}))$")
|
||||||
|
set(relname "${CMAKE_MATCH_1}")
|
||||||
|
set(alg "${CMAKE_MATCH_2}")
|
||||||
|
else()
|
||||||
|
set(relname "${entry}")
|
||||||
|
set(alg "")
|
||||||
|
endif()
|
||||||
|
if("x${relname}" MATCHES "^x${regex}$" AND NOT IS_DIRECTORY "${top_src}/${entry}")
|
||||||
|
set(name "${top_src}/${relname}")
|
||||||
|
set(file "${top_bin}/${relname}")
|
||||||
|
if(alg)
|
||||||
|
list(APPEND external "${file}|${name}|${alg}")
|
||||||
|
elseif(ExternalData_LINK_CONTENT)
|
||||||
|
_ExternalData_link_content("${name}" alg)
|
||||||
|
list(APPEND external "${file}|${name}|${alg}")
|
||||||
|
elseif(NOT top_same)
|
||||||
|
list(APPEND internal "${file}|${name}")
|
||||||
|
endif()
|
||||||
|
if("${relname}" STREQUAL "${reldata}")
|
||||||
|
set(have_original 1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(external "${external}" PARENT_SCOPE)
|
||||||
|
set(internal "${internal}" PARENT_SCOPE)
|
||||||
|
set(have_original "${have_original}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
# Private script mode interface
|
||||||
|
|
||||||
|
if(CMAKE_GENERATOR OR NOT ExternalData_ACTION)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ExternalData_CONFIG)
|
||||||
|
include(${ExternalData_CONFIG})
|
||||||
|
endif()
|
||||||
|
if(NOT ExternalData_URL_TEMPLATES)
|
||||||
|
message(FATAL_ERROR "No ExternalData_URL_TEMPLATES set!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(_ExternalData_link_or_copy src dst)
|
||||||
|
# Create a temporary file first.
|
||||||
|
get_filename_component(dst_dir "${dst}" PATH)
|
||||||
|
file(MAKE_DIRECTORY "${dst_dir}")
|
||||||
|
_ExternalData_random(random)
|
||||||
|
set(tmp "${dst}.tmp${random}")
|
||||||
|
if(UNIX)
|
||||||
|
# Create a symbolic link.
|
||||||
|
set(tgt "${src}")
|
||||||
|
if(relative_top)
|
||||||
|
# Use relative path if files are close enough.
|
||||||
|
file(RELATIVE_PATH relsrc "${relative_top}" "${src}")
|
||||||
|
file(RELATIVE_PATH relfile "${relative_top}" "${dst}")
|
||||||
|
if(NOT IS_ABSOLUTE "${relsrc}" AND NOT "${relsrc}" MATCHES "^\\.\\./" AND
|
||||||
|
NOT IS_ABSOLUTE "${reldst}" AND NOT "${reldst}" MATCHES "^\\.\\./")
|
||||||
|
file(RELATIVE_PATH tgt "${dst_dir}" "${src}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${tgt}" "${tmp}" RESULT_VARIABLE result)
|
||||||
|
else()
|
||||||
|
# Create a copy.
|
||||||
|
execute_process(COMMAND "${CMAKE_COMMAND}" -E copy "${src}" "${tmp}" RESULT_VARIABLE result)
|
||||||
|
endif()
|
||||||
|
if(result)
|
||||||
|
file(REMOVE "${tmp}")
|
||||||
|
message(FATAL_ERROR "Failed to create\n ${tmp}\nfrom\n ${obj}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Atomically create/replace the real destination.
|
||||||
|
file(RENAME "${tmp}" "${dst}")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_download_file url file err_var msg_var)
|
||||||
|
set(retry 3)
|
||||||
|
while(retry)
|
||||||
|
math(EXPR retry "${retry} - 1")
|
||||||
|
if(ExternalData_TIMEOUT_INACTIVITY)
|
||||||
|
set(inactivity_timeout INACTIVITY_TIMEOUT ${ExternalData_TIMEOUT_INACTIVITY})
|
||||||
|
elseif(NOT "${ExternalData_TIMEOUT_INACTIVITY}" EQUAL 0)
|
||||||
|
set(inactivity_timeout INACTIVITY_TIMEOUT 60)
|
||||||
|
else()
|
||||||
|
set(inactivity_timeout "")
|
||||||
|
endif()
|
||||||
|
if(ExternalData_TIMEOUT_ABSOLUTE)
|
||||||
|
set(absolute_timeout TIMEOUT ${ExternalData_TIMEOUT_ABSOLUTE})
|
||||||
|
elseif(NOT "${ExternalData_TIMEOUT_ABSOLUTE}" EQUAL 0)
|
||||||
|
set(absolute_timeout TIMEOUT 300)
|
||||||
|
else()
|
||||||
|
set(absolute_timeout "")
|
||||||
|
endif()
|
||||||
|
file(DOWNLOAD "${url}" "${file}" STATUS status LOG log ${inactivity_timeout} ${absolute_timeout} SHOW_PROGRESS)
|
||||||
|
list(GET status 0 err)
|
||||||
|
list(GET status 1 msg)
|
||||||
|
if(err)
|
||||||
|
if("${msg}" MATCHES "HTTP response code said error" AND
|
||||||
|
"${log}" MATCHES "error: 503")
|
||||||
|
set(msg "temporarily unavailable")
|
||||||
|
endif()
|
||||||
|
elseif("${log}" MATCHES "\nHTTP[^\n]* 503")
|
||||||
|
set(err TRUE)
|
||||||
|
set(msg "temporarily unavailable")
|
||||||
|
endif()
|
||||||
|
if(NOT err OR NOT "${msg}" MATCHES "partial|timeout|temporarily")
|
||||||
|
break()
|
||||||
|
elseif(retry)
|
||||||
|
message(STATUS "[download terminated: ${msg}, retries left: ${retry}]")
|
||||||
|
endif()
|
||||||
|
endwhile()
|
||||||
|
set("${err_var}" "${err}" PARENT_SCOPE)
|
||||||
|
set("${msg_var}" "${msg}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(_ExternalData_download_object name hash algo var_obj)
|
||||||
|
# Search all object stores for an existing object.
|
||||||
|
foreach(dir ${ExternalData_OBJECT_STORES})
|
||||||
|
set(obj "${dir}/${algo}/${hash}")
|
||||||
|
if(EXISTS "${obj}")
|
||||||
|
message(STATUS "Found object: \"${obj}\"")
|
||||||
|
set("${var_obj}" "${obj}" PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Download object to the first store.
|
||||||
|
list(GET ExternalData_OBJECT_STORES 0 store)
|
||||||
|
set(obj "${store}/${algo}/${hash}")
|
||||||
|
|
||||||
|
_ExternalData_random(random)
|
||||||
|
set(tmp "${obj}.tmp${random}")
|
||||||
|
set(found 0)
|
||||||
|
set(tried "")
|
||||||
|
foreach(url_template IN LISTS ExternalData_URL_TEMPLATES)
|
||||||
|
string(REPLACE "%(hash)" "${hash}" url_tmp "${url_template}")
|
||||||
|
string(REPLACE "%(algo)" "${algo}" url "${url_tmp}")
|
||||||
|
message(STATUS "Fetching \"${url}\"")
|
||||||
|
_ExternalData_download_file("${url}" "${tmp}" err errMsg)
|
||||||
|
set(tried "${tried}\n ${url}")
|
||||||
|
if(err)
|
||||||
|
set(tried "${tried} (${errMsg})")
|
||||||
|
else()
|
||||||
|
# Verify downloaded object.
|
||||||
|
_ExternalData_compute_hash(dl_hash "${algo}" "${tmp}")
|
||||||
|
if("${dl_hash}" STREQUAL "${hash}")
|
||||||
|
set(found 1)
|
||||||
|
break()
|
||||||
|
else()
|
||||||
|
set(tried "${tried} (wrong hash ${algo}=${dl_hash})")
|
||||||
|
if("$ENV{ExternalData_DEBUG_DOWNLOAD}" MATCHES ".")
|
||||||
|
file(RENAME "${tmp}" "${store}/${algo}/${dl_hash}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
file(REMOVE "${tmp}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
get_filename_component(dir "${name}" PATH)
|
||||||
|
set(staged "${dir}/.ExternalData_${algo}_${hash}")
|
||||||
|
|
||||||
|
if(found)
|
||||||
|
file(RENAME "${tmp}" "${obj}")
|
||||||
|
message(STATUS "Downloaded object: \"${obj}\"")
|
||||||
|
elseif(EXISTS "${staged}")
|
||||||
|
set(obj "${staged}")
|
||||||
|
message(STATUS "Staged object: \"${obj}\"")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Object ${algo}=${hash} not found at:${tried}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set("${var_obj}" "${obj}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
if("${ExternalData_ACTION}" STREQUAL "fetch")
|
||||||
|
foreach(v ExternalData_OBJECT_STORES file name ext)
|
||||||
|
if(NOT DEFINED "${v}")
|
||||||
|
message(FATAL_ERROR "No \"-D${v}=\" value provided!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(READ "${name}${ext}" hash)
|
||||||
|
string(STRIP "${hash}" hash)
|
||||||
|
|
||||||
|
if("${ext}" MATCHES "^\\.(${_ExternalData_REGEX_EXT})$")
|
||||||
|
string(TOUPPER "${CMAKE_MATCH_1}" algo)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unknown hash algorithm extension \"${ext}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
_ExternalData_download_object("${name}" "${hash}" "${algo}" obj)
|
||||||
|
|
||||||
|
# Check if file already corresponds to the object.
|
||||||
|
set(stamp "${ext}-stamp")
|
||||||
|
set(file_up_to_date 0)
|
||||||
|
if(EXISTS "${file}" AND EXISTS "${file}${stamp}")
|
||||||
|
file(READ "${file}${stamp}" f_hash)
|
||||||
|
string(STRIP "${f_hash}" f_hash)
|
||||||
|
if("${f_hash}" STREQUAL "${hash}")
|
||||||
|
#message(STATUS "File already corresponds to object")
|
||||||
|
set(file_up_to_date 1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(file_up_to_date)
|
||||||
|
# Touch the file to convince the build system it is up to date.
|
||||||
|
execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${file}")
|
||||||
|
else()
|
||||||
|
_ExternalData_link_or_copy("${obj}" "${file}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Atomically update the hash/timestamp file to record the object referenced.
|
||||||
|
_ExternalData_atomic_write("${file}${stamp}" "${hash}\n")
|
||||||
|
elseif("${ExternalData_ACTION}" STREQUAL "local")
|
||||||
|
foreach(v file name)
|
||||||
|
if(NOT DEFINED "${v}")
|
||||||
|
message(FATAL_ERROR "No \"-D${v}=\" value provided!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
_ExternalData_link_or_copy("${name}" "${file}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unknown ExternalData_ACTION=[${ExternalData_ACTION}]")
|
||||||
|
endif()
|
|
@ -0,0 +1,4 @@
|
||||||
|
set(ExternalData_OBJECT_STORES "@ExternalData_OBJECT_STORES@")
|
||||||
|
set(ExternalData_URL_TEMPLATES "@ExternalData_URL_TEMPLATES@")
|
||||||
|
set(ExternalData_TIMEOUT_INACTIVITY "@ExternalData_TIMEOUT_INACTIVITY@")
|
||||||
|
set(ExternalData_TIMEOUT_ABSOLUTE "@ExternalData_TIMEOUT_ABSOLUTE@")
|
|
@ -312,6 +312,19 @@ if(BUILD_TESTING)
|
||||||
|
|
||||||
ADD_TEST_MACRO(Module.CheckTypeSize CheckTypeSize)
|
ADD_TEST_MACRO(Module.CheckTypeSize CheckTypeSize)
|
||||||
|
|
||||||
|
add_test(Module.ExternalData ${CMAKE_CTEST_COMMAND}
|
||||||
|
--build-and-test
|
||||||
|
"${CMake_SOURCE_DIR}/Tests/Module/ExternalData"
|
||||||
|
"${CMake_BINARY_DIR}/Tests/Module/ExternalData"
|
||||||
|
--build-generator ${CMAKE_TEST_GENERATOR}
|
||||||
|
--build-project ExternalDataTest
|
||||||
|
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
|
||||||
|
--build-noclean
|
||||||
|
--force-new-ctest-process
|
||||||
|
--test-command ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE} -V
|
||||||
|
)
|
||||||
|
list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Module/ExternalData")
|
||||||
|
|
||||||
ADD_TEST_MACRO(Module.GenerateExportHeader GenerateExportHeader)
|
ADD_TEST_MACRO(Module.GenerateExportHeader GenerateExportHeader)
|
||||||
|
|
||||||
if (APPLE OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if (APPLE OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8.10.20130115)
|
||||||
|
project(ExternalDataTest NONE)
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
include(ExternalData)
|
||||||
|
|
||||||
|
if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" MATCHES "^/")
|
||||||
|
set(slash /)
|
||||||
|
endif()
|
||||||
|
set(ExternalData_URL_TEMPLATES
|
||||||
|
"file://${slash}${CMAKE_CURRENT_SOURCE_DIR}/%(algo)/%(hash)"
|
||||||
|
)
|
||||||
|
set(ExternalData_BINARY_ROOT "${CMAKE_CURRENT_BINARY_DIR}/ExternalData")
|
||||||
|
file(REMOVE_RECURSE ${ExternalData_BINARY_ROOT}) # clean test
|
||||||
|
|
||||||
|
ExternalData_Add_Test(Data1
|
||||||
|
NAME Data1Check
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-D Data=DATA{Data.dat}
|
||||||
|
-D SeriesA=DATA{SeriesA.dat,:}
|
||||||
|
-D SeriesB=DATA{SeriesB.dat,:}
|
||||||
|
-D SeriesC=DATA{SeriesC.dat,:}
|
||||||
|
-D SeriesD=DATA{SeriesD.dat,:}
|
||||||
|
-D SeriesAn=DATA{SeriesAn1.dat,:}
|
||||||
|
-D SeriesBn=DATA{SeriesBn_1.dat,:}
|
||||||
|
-D SeriesCn=DATA{SeriesCn.1.dat,:}
|
||||||
|
-D SeriesDn=DATA{SeriesDn-1.dat,:}
|
||||||
|
-D SeriesMixed=DATA{SeriesMixed.1.dat,:}
|
||||||
|
-D Paired=DATA{PairedA.dat,PairedB.dat}
|
||||||
|
-D Meta=DATA{MetaTop.dat,REGEX:Meta[ABC].dat}
|
||||||
|
-D Directory=DATA{Directory/,A.dat,REGEX:[BC].dat}
|
||||||
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/Data1Check.cmake
|
||||||
|
)
|
||||||
|
ExternalData_Add_Target(Data1)
|
||||||
|
|
||||||
|
add_subdirectory(Data2)
|
||||||
|
add_subdirectory(Data3)
|
|
@ -0,0 +1 @@
|
||||||
|
8c018830e3efa5caf3c7415028335a57
|
|
@ -0,0 +1,52 @@
|
||||||
|
file(STRINGS "${Data}" lines LIMIT_INPUT 1024)
|
||||||
|
if(NOT "x${lines}" STREQUAL "xInput file already transformed.")
|
||||||
|
message(SEND_ERROR "Input file:\n ${Data}\ndoes not have expected content, but [[${lines}]]")
|
||||||
|
endif()
|
||||||
|
set(SeriesAn1 "1\\.dat")
|
||||||
|
set(SeriesBn1 "_1\\.dat")
|
||||||
|
set(SeriesCn1 "\\.1\\.dat")
|
||||||
|
set(SeriesDn1 "-1\\.dat")
|
||||||
|
set(SeriesAl 1 2 3)
|
||||||
|
set(SeriesBl _1 _2 _3)
|
||||||
|
set(SeriesCl .1 .2 .3)
|
||||||
|
set(SeriesDl -1 -2 -3)
|
||||||
|
foreach(s A B C D)
|
||||||
|
foreach(n "" ${Series${s}l})
|
||||||
|
string(REGEX REPLACE "\\.dat$" "${n}.dat" file "${Series${s}}")
|
||||||
|
if(NOT EXISTS "${file}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${file}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
foreach(s A B C D)
|
||||||
|
foreach(n ${Series${s}l})
|
||||||
|
string(REGEX REPLACE "${Series${s}n1}$" "${n}.dat" file "${Series${s}n}")
|
||||||
|
if(NOT EXISTS "${file}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${file}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
foreach(n .1 .2 .3 .4)
|
||||||
|
string(REGEX REPLACE "\\.1\\.dat$" "${n}.dat" file "${SeriesMixed}")
|
||||||
|
if(NOT EXISTS "${file}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${file}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
foreach(n A B)
|
||||||
|
string(REGEX REPLACE "A\\.dat$" "${n}.dat" file "${Paired}")
|
||||||
|
if(NOT EXISTS "${file}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${file}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
foreach(n Top A B C)
|
||||||
|
string(REGEX REPLACE "Top\\.dat$" "${n}.dat" file "${Meta}")
|
||||||
|
if(NOT EXISTS "${file}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${file}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
foreach(n A B C)
|
||||||
|
set(file "${Directory}/${n}.dat")
|
||||||
|
if(NOT EXISTS "${file}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${file}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
|
@ -0,0 +1 @@
|
||||||
|
8c018830e3efa5caf3c7415028335a57
|
|
@ -0,0 +1,11 @@
|
||||||
|
set(ExternalData_SERIES_PARSE "([0-9]+)(_\\.my\\.dat)$")
|
||||||
|
set(ExternalData_SERIES_MATCH "([0-9]+)")
|
||||||
|
ExternalData_Add_Test(Data2
|
||||||
|
NAME Data2Check
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-D Data2=DATA{../Data2.dat}
|
||||||
|
-D Data2b=DATA{${CMAKE_CURRENT_SOURCE_DIR}/../Data2b.dat}
|
||||||
|
-D SeriesC=DATA{SeriesC_1_.my.dat,:}
|
||||||
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/Data2Check.cmake
|
||||||
|
)
|
||||||
|
ExternalData_Add_Target(Data2)
|
|
@ -0,0 +1,12 @@
|
||||||
|
foreach(d "${Data2}" "${Data2b}")
|
||||||
|
file(STRINGS "${d}" lines LIMIT_INPUT 1024)
|
||||||
|
if(NOT "x${lines}" STREQUAL "xInput file already transformed.")
|
||||||
|
message(SEND_ERROR "Input file:\n ${d}\ndoes not have expected content, but [[${lines}]]")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
foreach(n 1 2 3)
|
||||||
|
string(REGEX REPLACE "_1_\\.my\\.dat$" "_${n}_.my.dat" SeriesCFile "${SeriesC}")
|
||||||
|
if(NOT EXISTS "${SeriesCFile}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${SeriesCFile}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
8c018830e3efa5caf3c7415028335a57
|
|
@ -0,0 +1,14 @@
|
||||||
|
set(Store0 ${CMAKE_BINARY_DIR}/ExternalData/Other)
|
||||||
|
set(Store1 ${CMAKE_BINARY_DIR}/ExternalData/Objects)
|
||||||
|
set(ExternalData_OBJECT_STORES ${Store0} ${Store1})
|
||||||
|
ExternalData_Add_Test(Data3
|
||||||
|
NAME Data3Check
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-D Data=DATA{Data.dat}
|
||||||
|
-D Other=DATA{Other.dat}
|
||||||
|
-D Store0=${Store0}
|
||||||
|
-D Store1=${Store1}
|
||||||
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/Data3Check.cmake
|
||||||
|
)
|
||||||
|
ExternalData_Add_Target(Data3)
|
||||||
|
add_dependencies(Data3 Data1 Data2)
|
|
@ -0,0 +1 @@
|
||||||
|
8c018830e3efa5caf3c7415028335a57
|
|
@ -0,0 +1,25 @@
|
||||||
|
if(NOT EXISTS "${Data}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${Data}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
if(NOT EXISTS "${Other}")
|
||||||
|
message(SEND_ERROR "Input file:\n ${Other}\ndoes not exist!")
|
||||||
|
endif()
|
||||||
|
# Verify that the 'Data' object was found in the second store location left
|
||||||
|
# from Data1 target downloads and that the 'Other' object was downloaded to
|
||||||
|
# our first store location. Neither object should exist in the other store.
|
||||||
|
foreach(should_exist
|
||||||
|
"${Store0}/MD5/aaad162b85f60d1eb57ca71a23e8efd7"
|
||||||
|
"${Store1}/MD5/8c018830e3efa5caf3c7415028335a57"
|
||||||
|
)
|
||||||
|
if(NOT EXISTS ${should_exist})
|
||||||
|
message(SEND_ERROR "Store file:\n ${should_exist}\nshould exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
foreach(should_not_exist
|
||||||
|
"${Store0}/MD5/8c018830e3efa5caf3c7415028335a57"
|
||||||
|
"${Store1}/MD5/aaad162b85f60d1eb57ca71a23e8efd7"
|
||||||
|
)
|
||||||
|
if(EXISTS ${should_not_exist})
|
||||||
|
message(SEND_ERROR "Store file:\n ${should_not_exist}\nshould not exist!")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
|
@ -0,0 +1 @@
|
||||||
|
aaad162b85f60d1eb57ca71a23e8efd7
|
|
@ -0,0 +1 @@
|
||||||
|
9d980b06c2f0fec3d4872d68175b9822
|
|
@ -0,0 +1 @@
|
||||||
|
8f4add4581551facf27237e6577fd662
|
|
@ -0,0 +1 @@
|
||||||
|
c1030719c95f3435d8abc39c0d442946
|
|
@ -0,0 +1 @@
|
||||||
|
* -crlf
|
|
@ -0,0 +1 @@
|
||||||
|
MetaTop
|
|
@ -0,0 +1 @@
|
||||||
|
Series.3
|
|
@ -0,0 +1 @@
|
||||||
|
Series.1
|
|
@ -0,0 +1 @@
|
||||||
|
PairedA
|
|
@ -0,0 +1 @@
|
||||||
|
Input file already transformed.
|
|
@ -0,0 +1 @@
|
||||||
|
MetaB
|
|
@ -0,0 +1 @@
|
||||||
|
MetaA
|
|
@ -0,0 +1 @@
|
||||||
|
Another input file already transformed.
|
|
@ -0,0 +1 @@
|
||||||
|
MetaC
|
|
@ -0,0 +1 @@
|
||||||
|
SeriesMixed.1
|
|
@ -0,0 +1 @@
|
||||||
|
PairedB
|
|
@ -0,0 +1 @@
|
||||||
|
Series
|
|
@ -0,0 +1 @@
|
||||||
|
Series.2
|
|
@ -0,0 +1 @@
|
||||||
|
9d980b06c2f0fec3d4872d68175b9822
|
|
@ -0,0 +1 @@
|
||||||
|
8f4add4581551facf27237e6577fd662
|
|
@ -0,0 +1 @@
|
||||||
|
c1030719c95f3435d8abc39c0d442946
|
|
@ -0,0 +1 @@
|
||||||
|
08cfcf221f76ace7b906b312284e73d7
|
|
@ -0,0 +1 @@
|
||||||
|
401767f22a456b3522953722090a2c36
|
|
@ -0,0 +1 @@
|
||||||
|
ecfa1ecd417d4253af81ae04d1bd6581
|
|
@ -0,0 +1 @@
|
||||||
|
* -crlf
|
|
@ -0,0 +1 @@
|
||||||
|
SeriesMixed.2
|
|
@ -0,0 +1 @@
|
||||||
|
* -crlf
|
|
@ -0,0 +1 @@
|
||||||
|
SeriesMixed.3
|
|
@ -0,0 +1 @@
|
||||||
|
* -crlf
|
|
@ -0,0 +1 @@
|
||||||
|
SeriesMixed.4
|
|
@ -0,0 +1 @@
|
||||||
|
f41c94425d01ecbbee70440b951cb058
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
f41c94425d01ecbbee70440b951cb058
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
f41c94425d01ecbbee70440b951cb058
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
f41c94425d01ecbbee70440b951cb058
|
|
@ -0,0 +1 @@
|
||||||
|
31eff09e84fca01415f8cd9d82ec432b
|
|
@ -0,0 +1 @@
|
||||||
|
f7ab5a04aae9cb9a520e70b20b9c8ed7
|
|
@ -0,0 +1 @@
|
||||||
|
30ba0acdee9096b3b9fc6c69362c6b42
|
|
@ -0,0 +1 @@
|
||||||
|
ce38ea6c3c1e00fa6405dd64b8bf6da0
|
|
@ -0,0 +1 @@
|
||||||
|
2af59a7022024974f3b8521b7ed8137c996a79f1
|
|
@ -0,0 +1 @@
|
||||||
|
3b679da7908562fe1cc28db47ffb89bae025f4551dceb343a5869174
|
|
@ -0,0 +1 @@
|
||||||
|
969171a0dd70d49ce096bd3e8178c7e26c711c9b20dbcaa3853d869d3871f133
|
|
@ -46,6 +46,7 @@ macro(add_RunCMake_test test)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
add_RunCMake_test(CMP0019)
|
add_RunCMake_test(CMP0019)
|
||||||
|
add_RunCMake_test(ExternalData)
|
||||||
add_RunCMake_test(GeneratorExpression)
|
add_RunCMake_test(GeneratorExpression)
|
||||||
add_RunCMake_test(TargetPropertyGeneratorExpressions)
|
add_RunCMake_test(TargetPropertyGeneratorExpressions)
|
||||||
add_RunCMake_test(Languages)
|
add_RunCMake_test(Languages)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,8 @@
|
||||||
|
CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
|
||||||
|
Unknown hash algorithm specified by ExternalData_LINK_CONTENT:
|
||||||
|
|
||||||
|
BAD
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
.*
|
||||||
|
BadHashAlgo1.cmake:3 \(ExternalData_Expand_Arguments\)
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,3 @@
|
||||||
|
include(ExternalData)
|
||||||
|
set(ExternalData_LINK_CONTENT BAD)
|
||||||
|
ExternalData_Expand_Arguments(Data args DATA{BadHashAlgo1.txt})
|
|
@ -0,0 +1 @@
|
||||||
|
Sample input file that should not be transformed.
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,9 @@
|
||||||
|
CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
|
||||||
|
Unknown option "Bad/Option" in argument
|
||||||
|
|
||||||
|
DATA{Data.txt,Bad/Option}
|
||||||
|
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
.*
|
||||||
|
BadOption1.cmake:2 \(ExternalData_Add_Test\)
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,5 @@
|
||||||
|
include(ExternalData)
|
||||||
|
ExternalData_Add_Test(Data
|
||||||
|
NAME Test
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo DATA{Data.txt,Bad/Option}
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,9 @@
|
||||||
|
CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
|
||||||
|
Unknown option "Bad:Option" in argument
|
||||||
|
|
||||||
|
DATA{Data.txt,Bad:Option}
|
||||||
|
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
.*
|
||||||
|
BadOption2.cmake:2 \(ExternalData_Add_Test\)
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,5 @@
|
||||||
|
include(ExternalData)
|
||||||
|
ExternalData_Add_Test(Data
|
||||||
|
NAME Test
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo DATA{Data.txt,Bad:Option}
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,19 @@
|
||||||
|
CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
|
||||||
|
ExternalData_SERIES_PARSE is set to
|
||||||
|
|
||||||
|
NotASeriesRegex
|
||||||
|
|
||||||
|
which is not of the form
|
||||||
|
|
||||||
|
\(<number>\)\(<suffix>\)\$
|
||||||
|
|
||||||
|
Fix the regular expression or set variables
|
||||||
|
|
||||||
|
ExternalData_SERIES_PARSE_PREFIX = <prefix> regex group number, if any
|
||||||
|
ExternalData_SERIES_PARSE_NUMBER = <number> regex group number
|
||||||
|
ExternalData_SERIES_PARSE_SUFFIX = <suffix> regex group number
|
||||||
|
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
.*
|
||||||
|
BadSeries1.cmake:3 \(ExternalData_Expand_Arguments\)
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,3 @@
|
||||||
|
include(ExternalData)
|
||||||
|
set(ExternalData_SERIES_PARSE NotASeriesRegex)
|
||||||
|
ExternalData_Expand_Arguments(Data args DATA{Data.txt,:})
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,16 @@
|
||||||
|
CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
|
||||||
|
Data file referenced by argument
|
||||||
|
|
||||||
|
DATA{Data.txt,:}
|
||||||
|
|
||||||
|
corresponds to path
|
||||||
|
|
||||||
|
Data.txt
|
||||||
|
|
||||||
|
that does not match regular expression
|
||||||
|
|
||||||
|
\(x\)\(y\)\$
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
.*
|
||||||
|
BadSeries2.cmake:3 \(ExternalData_Expand_Arguments\)
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,3 @@
|
||||||
|
include(ExternalData)
|
||||||
|
set(ExternalData_SERIES_PARSE "(x)(y)$")
|
||||||
|
ExternalData_Expand_Arguments(Data args DATA{Data.txt,:})
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
|
@ -0,0 +1,6 @@
|
||||||
|
CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
|
||||||
|
Series option ":" not allowed with associated files.
|
||||||
|
Call Stack \(most recent call first\):
|
||||||
|
.*
|
||||||
|
BadSeries3.cmake:2 \(ExternalData_Expand_Arguments\)
|
||||||
|
CMakeLists.txt:3 \(include\)
|
|
@ -0,0 +1,2 @@
|
||||||
|
include(ExternalData)
|
||||||
|
ExternalData_Expand_Arguments(Data args DATA{PairA.txt,PairB.txt,:})
|
|
@ -0,0 +1,3 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
project(${RunCMake_TEST} NONE)
|
||||||
|
include(${RunCMake_TEST}.cmake)
|
|
@ -0,0 +1 @@
|
||||||
|
e8bb14af900b998b5a3df7e21dd07d58
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue