summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Modules/ExternalData.cmake761
-rw-r--r--Modules/ExternalData_config.cmake.in4
-rw-r--r--Modules/Platform/Windows-MSVC.cmake5
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/cmLocalVisualStudio6Generator.cxx3
-rw-r--r--Source/cmNinjaNormalTargetGenerator.cxx2
-rw-r--r--Source/cmSystemTools.cxx51
-rw-r--r--Source/cmTarget.cxx4
-rw-r--r--Source/cmTargetCompileDefinitionsCommand.h2
-rw-r--r--Source/cmTargetIncludeDirectoriesCommand.h2
-rw-r--r--Source/cmTargetPropCommandBase.h1
-rw-r--r--Source/kwsys/DynamicLoader.cxx52
-rw-r--r--Source/kwsys/Glob.cxx2
-rw-r--r--Source/kwsys/IOStream.cxx1
-rw-r--r--Source/kwsys/ProcessUNIX.c2
-rw-r--r--Source/kwsys/SystemInformation.cxx5
-rw-r--r--Source/kwsys/SystemTools.cxx4
-rw-r--r--Source/kwsys/testCommandLineArguments.cxx16
-rw-r--r--Source/kwsys/testDynamicLoader.cxx2
-rw-r--r--Source/kwsys/testProcess.c22
-rw-r--r--Source/kwsys/testSystemTools.cxx26
-rw-r--r--Tests/CMakeLists.txt14
-rw-r--r--Tests/Module/ExternalData/CMakeLists.txt38
-rw-r--r--Tests/Module/ExternalData/Data.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data1Check.cmake52
-rw-r--r--Tests/Module/ExternalData/Data2.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data2/CMakeLists.txt11
-rw-r--r--Tests/Module/ExternalData/Data2/Data2Check.cmake12
-rw-r--r--Tests/Module/ExternalData/Data2/SeriesC_1_.my.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data2/SeriesC_2_.my.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data2/SeriesC_3_.my.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data2b.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data3/CMakeLists.txt14
-rw-r--r--Tests/Module/ExternalData/Data3/Data.dat.md51
-rw-r--r--Tests/Module/ExternalData/Data3/Data3Check.cmake25
-rw-r--r--Tests/Module/ExternalData/Data3/Other.dat.md51
-rw-r--r--Tests/Module/ExternalData/Directory/A.dat.md51
-rw-r--r--Tests/Module/ExternalData/Directory/B.dat.md51
-rw-r--r--Tests/Module/ExternalData/Directory/C.dat.md51
-rw-r--r--Tests/Module/ExternalData/MD5/.gitattributes1
-rw-r--r--Tests/Module/ExternalData/MD5/08cfcf221f76ace7b906b312284e73d71
-rw-r--r--Tests/Module/ExternalData/MD5/30ba0acdee9096b3b9fc6c69362c6b421
-rw-r--r--Tests/Module/ExternalData/MD5/31eff09e84fca01415f8cd9d82ec432b1
-rw-r--r--Tests/Module/ExternalData/MD5/401767f22a456b3522953722090a2c361
-rw-r--r--Tests/Module/ExternalData/MD5/8c018830e3efa5caf3c7415028335a571
-rw-r--r--Tests/Module/ExternalData/MD5/8f4add4581551facf27237e6577fd6621
-rw-r--r--Tests/Module/ExternalData/MD5/9d980b06c2f0fec3d4872d68175b98221
-rw-r--r--Tests/Module/ExternalData/MD5/aaad162b85f60d1eb57ca71a23e8efd71
-rw-r--r--Tests/Module/ExternalData/MD5/c1030719c95f3435d8abc39c0d4429461
-rw-r--r--Tests/Module/ExternalData/MD5/ce38ea6c3c1e00fa6405dd64b8bf6da01
-rw-r--r--Tests/Module/ExternalData/MD5/ecfa1ecd417d4253af81ae04d1bd65811
-rw-r--r--Tests/Module/ExternalData/MD5/f41c94425d01ecbbee70440b951cb0581
-rw-r--r--Tests/Module/ExternalData/MD5/f7ab5a04aae9cb9a520e70b20b9c8ed71
-rw-r--r--Tests/Module/ExternalData/MetaA.dat.md51
-rw-r--r--Tests/Module/ExternalData/MetaB.dat.md51
-rw-r--r--Tests/Module/ExternalData/MetaC.dat.md51
-rw-r--r--Tests/Module/ExternalData/MetaTop.dat.md51
-rw-r--r--Tests/Module/ExternalData/PairedA.dat.md51
-rw-r--r--Tests/Module/ExternalData/PairedB.dat.md51
-rw-r--r--Tests/Module/ExternalData/SHA1/.gitattributes1
-rw-r--r--Tests/Module/ExternalData/SHA1/2af59a7022024974f3b8521b7ed8137c996a79f11
-rw-r--r--Tests/Module/ExternalData/SHA224/.gitattributes1
-rw-r--r--Tests/Module/ExternalData/SHA224/3b679da7908562fe1cc28db47ffb89bae025f4551dceb343a58691741
-rw-r--r--Tests/Module/ExternalData/SHA256/.gitattributes1
-rw-r--r--Tests/Module/ExternalData/SHA256/969171a0dd70d49ce096bd3e8178c7e26c711c9b20dbcaa3853d869d3871f1331
-rw-r--r--Tests/Module/ExternalData/SeriesA.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesA1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesA2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesA3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesAn1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesAn2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesAn3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesB.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesB_1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesB_2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesB_3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesBn_1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesBn_2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesBn_3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesC.1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesC.2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesC.3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesC.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesCn.1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesCn.2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesCn.3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesD-1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesD-2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesD-3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesD.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesDn-1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesDn-2.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesDn-3.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesMixed.1.dat.md51
-rw-r--r--Tests/Module/ExternalData/SeriesMixed.2.dat.sha11
-rw-r--r--Tests/Module/ExternalData/SeriesMixed.3.dat.sha2241
-rw-r--r--Tests/Module/ExternalData/SeriesMixed.4.dat.sha2561
-rw-r--r--Tests/RunCMake/CMakeLists.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadHashAlgo1-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadHashAlgo1-stderr.txt8
-rw-r--r--Tests/RunCMake/ExternalData/BadHashAlgo1.cmake3
-rw-r--r--Tests/RunCMake/ExternalData/BadHashAlgo1.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadOption1-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadOption1-stderr.txt9
-rw-r--r--Tests/RunCMake/ExternalData/BadOption1.cmake5
-rw-r--r--Tests/RunCMake/ExternalData/BadOption2-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadOption2-stderr.txt9
-rw-r--r--Tests/RunCMake/ExternalData/BadOption2.cmake5
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries1-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries1-stderr.txt19
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries1.cmake3
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries2-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries2-stderr.txt16
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries2.cmake3
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries3-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries3-stderr.txt6
-rw-r--r--Tests/RunCMake/ExternalData/BadSeries3.cmake2
-rw-r--r--Tests/RunCMake/ExternalData/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/ExternalData/Data.txt.md51
-rw-r--r--Tests/RunCMake/ExternalData/Directory1-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/Directory1-stderr.txt14
-rw-r--r--Tests/RunCMake/ExternalData/Directory1.cmake6
-rw-r--r--Tests/RunCMake/ExternalData/Directory1/DirData1.txt0
-rw-r--r--Tests/RunCMake/ExternalData/Directory2-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/Directory2-stderr.txt10
-rw-r--r--Tests/RunCMake/ExternalData/Directory2.cmake6
-rw-r--r--Tests/RunCMake/ExternalData/Directory2.md51
-rw-r--r--Tests/RunCMake/ExternalData/Directory2/DirData2.txt0
-rw-r--r--Tests/RunCMake/ExternalData/Directory3-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/Directory3-stderr.txt14
-rw-r--r--Tests/RunCMake/ExternalData/Directory3.cmake6
-rw-r--r--Tests/RunCMake/ExternalData/Directory3/DirData3.txt0
-rw-r--r--Tests/RunCMake/ExternalData/Directory4-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/Directory4-stderr.txt6
-rw-r--r--Tests/RunCMake/ExternalData/Directory4.cmake6
-rw-r--r--Tests/RunCMake/ExternalData/Directory4/DirData4.txt0
-rw-r--r--Tests/RunCMake/ExternalData/Directory5-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/Directory5-stderr.txt14
-rw-r--r--Tests/RunCMake/ExternalData/Directory5.cmake6
-rw-r--r--Tests/RunCMake/ExternalData/LinkContentMD5-stdout.txt3
-rw-r--r--Tests/RunCMake/ExternalData/LinkContentMD5.cmake22
-rw-r--r--Tests/RunCMake/ExternalData/LinkContentSHA1-stdout.txt3
-rw-r--r--Tests/RunCMake/ExternalData/LinkContentSHA1.cmake22
-rw-r--r--Tests/RunCMake/ExternalData/MissingData-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/MissingData-stderr.txt14
-rw-r--r--Tests/RunCMake/ExternalData/MissingData.cmake5
-rw-r--r--Tests/RunCMake/ExternalData/NoLinkInSource-stderr.txt6
-rw-r--r--Tests/RunCMake/ExternalData/NoLinkInSource-stdout.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NoLinkInSource.cmake14
-rw-r--r--Tests/RunCMake/ExternalData/NoURLTemplates-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NoURLTemplates-stderr.txt5
-rw-r--r--Tests/RunCMake/ExternalData/NoURLTemplates.cmake2
-rw-r--r--Tests/RunCMake/ExternalData/NormalData1-stdout.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NormalData1.cmake13
-rw-r--r--Tests/RunCMake/ExternalData/NormalData2-stdout.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NormalData2.cmake14
-rw-r--r--Tests/RunCMake/ExternalData/NormalData3-stdout.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NormalData3.cmake14
-rw-r--r--Tests/RunCMake/ExternalData/NormalDataSub1-stdout.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NormalDataSub1.cmake13
-rw-r--r--Tests/RunCMake/ExternalData/NotUnderRoot-result.txt1
-rw-r--r--Tests/RunCMake/ExternalData/NotUnderRoot-stderr.txt12
-rw-r--r--Tests/RunCMake/ExternalData/NotUnderRoot.cmake5
-rw-r--r--Tests/RunCMake/ExternalData/RunCMakeTest.cmake24
-rw-r--r--Tests/RunCMake/ExternalData/SubDirectory1-stdout.txt3
-rw-r--r--Tests/RunCMake/ExternalData/SubDirectory1.cmake5
-rw-r--r--Tests/RunCMake/ExternalData/SubDirectory1/CMakeLists.txt29
-rw-r--r--Tests/RunCMake/ExternalData/SubDirectory1/Data.txt.md51
168 files changed, 1558 insertions, 77 deletions
diff --git a/Modules/ExternalData.cmake b/Modules/ExternalData.cmake
new file mode 100644
index 0000000..9d84f8d
--- /dev/null
+++ b/Modules/ExternalData.cmake
@@ -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()
diff --git a/Modules/ExternalData_config.cmake.in b/Modules/ExternalData_config.cmake.in
new file mode 100644
index 0000000..0858f53
--- /dev/null
+++ b/Modules/ExternalData_config.cmake.in
@@ -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@")
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index 5cbf9ee..3a38d8f 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -41,6 +41,7 @@ set(WIN32 1)
if(CMAKE_SYSTEM_NAME MATCHES "WindowsCE")
set(CMAKE_CREATE_WIN32_EXE "/subsystem:windowsce /entry:WinMainCRTStartup")
set(CMAKE_CREATE_CONSOLE_EXE "/subsystem:windowsce /entry:mainACRTStartup")
+ set(WINCE 1)
else()
set(CMAKE_CREATE_WIN32_EXE "/subsystem:windows")
set(CMAKE_CREATE_CONSOLE_EXE "/subsystem:console")
@@ -122,7 +123,7 @@ endif()
# default to Debug builds
set(CMAKE_BUILD_TYPE_INIT Debug)
-if(CMAKE_SYSTEM_NAME MATCHES "WindowsCE")
+if(WINCE)
string(TOUPPER "${MSVC_C_ARCHITECTURE_ID}" _MSVC_C_ARCHITECTURE_ID_UPPER)
string(TOUPPER "${MSVC_CXX_ARCHITECTURE_ID}" _MSVC_CXX_ARCHITECTURE_ID_UPPER)
@@ -170,7 +171,7 @@ set(_MACHINE_ARCH_FLAG ${MSVC_C_ARCHITECTURE_ID})
if(NOT _MACHINE_ARCH_FLAG)
set(_MACHINE_ARCH_FLAG ${MSVC_CXX_ARCHITECTURE_ID})
endif()
-if(CMAKE_SYSTEM_NAME MATCHES "WindowsCE")
+if(WINCE)
if(_MACHINE_ARCH_FLAG MATCHES "ARM")
set(_MACHINE_ARCH_FLAG "THUMB")
elseif(_MACHINE_ARCH_FLAG MATCHES "SH")
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 2010f63..8a54237 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -2,5 +2,5 @@
set(CMake_VERSION_MAJOR 2)
set(CMake_VERSION_MINOR 8)
set(CMake_VERSION_PATCH 10)
-set(CMake_VERSION_TWEAK 20130130)
+set(CMake_VERSION_TWEAK 20130205)
#set(CMake_VERSION_RC 1)
diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx
index 617eb4e..c35288c 100644
--- a/Source/cmLocalVisualStudio6Generator.cxx
+++ b/Source/cmLocalVisualStudio6Generator.cxx
@@ -325,6 +325,9 @@ void cmLocalVisualStudio6Generator::WriteDSPFile(std::ostream& fout,
if(!cmSystemTools::FileExists(source.c_str()))
{
cmSystemTools::ReplaceString(source, "$(IntDir)/", "");
+ // Make sure the path exists for the file
+ std::string path = cmSystemTools::GetFilenamePath(source);
+ cmSystemTools::MakeDirectory(path.c_str());
#if defined(_WIN32) || defined(__CYGWIN__)
std::ofstream sourceFout(source.c_str(),
std::ios::binary | std::ios::out
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 65967a5..7e48cd7 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -190,7 +190,7 @@ cmNinjaNormalTargetGenerator
linkOptionVar += cmTarget::GetTargetTypeName(targetType);
const std::string linkOption =
GetMakefile()->GetSafeDefinition(linkOptionVar.c_str());
- rspcontent = "$in " + linkOption + " $LINK_PATH $LINK_LIBRARIES";
+ rspcontent = "$in_newline "+linkOption+" $LINK_PATH $LINK_LIBRARIES";
vars.Objects = responseFlag.c_str();
vars.LinkLibraries = "";
}
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index c2521d9..525efb4 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -1180,46 +1180,35 @@ bool cmSystemTools::CopyFileIfDifferent(const char* source,
bool cmSystemTools::RenameFile(const char* oldname, const char* newname)
{
#ifdef _WIN32
- /* On Windows the move functions will not replace existing files.
- Check if the destination exists. */
- struct stat newFile;
- if(stat(newname, &newFile) == 0)
+# ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+# endif
+ /* Windows MoveFileEx may not replace read-only or in-use files. If it
+ fails then remove the read-only attribute from any existing destination.
+ Try multiple times since we may be racing against another process
+ creating/opening the destination file just before our MoveFileEx. */
+ int tries = 5;
+ while(!MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING) && --tries)
{
- /* The destination exists. We have to replace it carefully. The
- MoveFileEx function does what we need but is not available on
- Win9x. */
- OSVERSIONINFO osv;
- DWORD attrs;
-
- /* Make sure the destination is not read only. */
- attrs = GetFileAttributes(newname);
- if(attrs & FILE_ATTRIBUTE_READONLY)
+ // Try again only if failure was due to access permissions.
+ if(GetLastError() != ERROR_ACCESS_DENIED)
{
- SetFileAttributes(newname, attrs & ~FILE_ATTRIBUTE_READONLY);
+ return false;
}
-
- /* Check the windows version number. */
- osv.dwOSVersionInfoSize = sizeof(osv);
- GetVersionEx(&osv);
- if(osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ DWORD attrs = GetFileAttributes(newname);
+ if((attrs != INVALID_FILE_ATTRIBUTES) &&
+ (attrs & FILE_ATTRIBUTE_READONLY))
{
- /* This is Win9x. There is no MoveFileEx implementation. We
- cannot quite rename the file atomically. Just delete the
- destination and then move the file. */
- DeleteFile(newname);
- return MoveFile(oldname, newname) != 0;
+ // Remove the read-only attribute from the destination file.
+ SetFileAttributes(newname, attrs & ~FILE_ATTRIBUTE_READONLY);
}
else
{
- /* This is not Win9x. Use the MoveFileEx implementation. */
- return MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING) != 0;
+ // The file may be temporarily in use so wait a bit.
+ cmSystemTools::Delay(100);
}
}
- else
- {
- /* The destination does not exist. Just move the file. */
- return MoveFile(oldname, newname) != 0;
- }
+ return tries > 0;
#else
/* On UNIX we have an OS-provided call to do this atomically. */
return rename(oldname, newname) == 0;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 1a6b7ce..ca0e24b 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -2812,10 +2812,10 @@ std::vector<std::string> cmTarget::GetIncludeDirectories(const char *config)
if(uniqueIncludes.insert(inc).second)
{
- includes.push_back(*li);
+ includes.push_back(inc);
if (debugIncludes)
{
- usedIncludes += " * " + *li + "\n";
+ usedIncludes += " * " + inc + "\n";
}
}
}
diff --git a/Source/cmTargetCompileDefinitionsCommand.h b/Source/cmTargetCompileDefinitionsCommand.h
index 3b43820..c93cacb 100644
--- a/Source/cmTargetCompileDefinitionsCommand.h
+++ b/Source/cmTargetCompileDefinitionsCommand.h
@@ -75,7 +75,7 @@ public:
;
}
- cmTypeMacro(cmTargetCompileDefinitionsCommand, cmCommand);
+ cmTypeMacro(cmTargetCompileDefinitionsCommand, cmTargetPropCommandBase);
private:
virtual void HandleImportedTarget(const std::string &tgt);
diff --git a/Source/cmTargetIncludeDirectoriesCommand.h b/Source/cmTargetIncludeDirectoriesCommand.h
index d02cb4a..2bc7bef 100644
--- a/Source/cmTargetIncludeDirectoriesCommand.h
+++ b/Source/cmTargetIncludeDirectoriesCommand.h
@@ -79,7 +79,7 @@ public:
;
}
- cmTypeMacro(cmTargetIncludeDirectoriesCommand, cmCommand);
+ cmTypeMacro(cmTargetIncludeDirectoriesCommand, cmTargetPropCommandBase);
private:
virtual void HandleImportedTarget(const std::string &tgt);
diff --git a/Source/cmTargetPropCommandBase.h b/Source/cmTargetPropCommandBase.h
index a5b4ff8..8047a48 100644
--- a/Source/cmTargetPropCommandBase.h
+++ b/Source/cmTargetPropCommandBase.h
@@ -31,6 +31,7 @@ public:
bool HandleArguments(std::vector<std::string> const& args,
const char *prop, ArgumentFlags flags = NO_FLAGS);
+ cmTypeMacro(cmTargetPropCommandBase, cmCommand);
protected:
std::string Property;
cmTarget *Target;
diff --git a/Source/kwsys/DynamicLoader.cxx b/Source/kwsys/DynamicLoader.cxx
index c4ee095..fd83752 100644
--- a/Source/kwsys/DynamicLoader.cxx
+++ b/Source/kwsys/DynamicLoader.cxx
@@ -428,6 +428,58 @@ const char* DynamicLoader::LastError()
} // namespace KWSYS_NAMESPACE
#endif
+#ifdef __MINT__
+#define DYNAMICLOADER_DEFINED 1
+#define _GNU_SOURCE /* for program_invocation_name */
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <dld.h>
+
+namespace KWSYS_NAMESPACE
+{
+
+//----------------------------------------------------------------------------
+DynamicLoader::LibraryHandle DynamicLoader::OpenLibrary(const char* libname )
+{
+ char *name = (char *)calloc(1, strlen(libname) + 1);
+ dld_init(program_invocation_name);
+ strncpy(name, libname, strlen(libname));
+ dld_link(libname);
+ return (void *)name;
+}
+
+//----------------------------------------------------------------------------
+int DynamicLoader::CloseLibrary(DynamicLoader::LibraryHandle lib)
+{
+ dld_unlink_by_file((char *)lib, 0);
+ free(lib);
+ return 0;
+}
+
+//----------------------------------------------------------------------------
+DynamicLoader::SymbolPointer DynamicLoader::GetSymbolAddress(
+ DynamicLoader::LibraryHandle lib, const char* sym)
+{
+ // Hack to cast pointer-to-data to pointer-to-function.
+ union
+ {
+ void* pvoid;
+ DynamicLoader::SymbolPointer psym;
+ } result;
+ result.pvoid = dld_get_symbol(sym);
+ return result.psym;
+}
+
+//----------------------------------------------------------------------------
+const char* DynamicLoader::LastError()
+{
+ return dld_strerror(dld_errno);
+}
+
+} // namespace KWSYS_NAMESPACE
+#endif
+
// ---------------------------------------------------------------
// 6. Implementation for default UNIX machines.
// if nothing has been defined then use this
diff --git a/Source/kwsys/Glob.cxx b/Source/kwsys/Glob.cxx
index 513eb64..46a7e4f 100644
--- a/Source/kwsys/Glob.cxx
+++ b/Source/kwsys/Glob.cxx
@@ -399,7 +399,7 @@ bool Glob::FindFiles(const kwsys_stl::string& inexpr)
if ( last_slash > 0 )
{
//kwsys_ios::cout << "I can skip: " << fexpr.substr(0, last_slash)
- //<< kwsys_ios::endl;
+ // << kwsys_ios::endl;
skip = last_slash;
}
if ( skip == 0 )
diff --git a/Source/kwsys/IOStream.cxx b/Source/kwsys/IOStream.cxx
index 57b696e..a31f8c8 100644
--- a/Source/kwsys/IOStream.cxx
+++ b/Source/kwsys/IOStream.cxx
@@ -272,6 +272,7 @@ namespace KWSYS_NAMESPACE
// Create one public symbol in this object file to avoid warnings from
// archivers.
+void IOStreamSymbolToAvoidWarning();
void IOStreamSymbolToAvoidWarning()
{
}
diff --git a/Source/kwsys/ProcessUNIX.c b/Source/kwsys/ProcessUNIX.c
index 2db1254..fc9e8bf 100644
--- a/Source/kwsys/ProcessUNIX.c
+++ b/Source/kwsys/ProcessUNIX.c
@@ -102,7 +102,7 @@ static inline void kwsysProcess_usleep(unsigned int msec)
* pipes' file handles to be non-blocking and just poll them directly
* without select().
*/
-#if !defined(__BEOS__) && !defined(__VMS)
+#if !defined(__BEOS__) && !defined(__VMS) && !defined(__MINT__)
# define KWSYSPE_USE_SELECT 1
#endif
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx
index b20d724..f057e0f 100644
--- a/Source/kwsys/SystemInformation.cxx
+++ b/Source/kwsys/SystemInformation.cxx
@@ -2466,7 +2466,9 @@ bool SystemInformationImplementation::RetrieveCPUPowerManagement()
#endif
}
-void SystemInformationStripLeadingSpace(kwsys_stl::string& str)
+#if USE_CPUID
+// Used only in USE_CPUID implementation below.
+static void SystemInformationStripLeadingSpace(kwsys_stl::string& str)
{
// Because some manufacturers have leading white space - we have to post-process the name.
kwsys_stl::string::size_type pos = str.find_first_not_of(" ");
@@ -2475,6 +2477,7 @@ void SystemInformationStripLeadingSpace(kwsys_stl::string& str)
str = str.substr(pos);
}
}
+#endif
/** */
bool SystemInformationImplementation::RetrieveExtendedCPUIdentity()
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index b75993e..881c49a 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -3037,7 +3037,7 @@ void SystemTools::CheckTranslationPath(kwsys_stl::string & path)
path.erase(path.end()-1, path.end());
}
-void
+static void
SystemToolsAppendComponents(
kwsys_stl::vector<kwsys_stl::string>& out_components,
kwsys_stl::vector<kwsys_stl::string>::const_iterator first,
@@ -4704,7 +4704,7 @@ bool SystemTools::ParseURL( const kwsys_stl::string& URL,
// ----------------------------------------------------------------------
// These must NOT be initialized. Default initialization to zero is
// necessary.
-unsigned int SystemToolsManagerCount;
+static unsigned int SystemToolsManagerCount;
SystemToolsTranslationMap *SystemTools::TranslationMap;
SystemToolsTranslationMap *SystemTools::LongPathMap;
#ifdef __CYGWIN__
diff --git a/Source/kwsys/testCommandLineArguments.cxx b/Source/kwsys/testCommandLineArguments.cxx
index e75a87e..72e6544 100644
--- a/Source/kwsys/testCommandLineArguments.cxx
+++ b/Source/kwsys/testCommandLineArguments.cxx
@@ -24,9 +24,9 @@
#include <stddef.h> /* size_t */
#include <string.h> /* strcmp */
-void* random_ptr = reinterpret_cast<void*>(0x123);
+static void* random_ptr = reinterpret_cast<void*>(0x123);
-int argument(const char* arg, const char* value, void* call_data)
+static int argument(const char* arg, const char* value, void* call_data)
{
kwsys_ios::cout << "Got argument: \"" << arg << "\" value: \"" << (value?value:"(null)") << "\"" << kwsys_ios::endl;
if ( call_data != random_ptr )
@@ -37,7 +37,7 @@ int argument(const char* arg, const char* value, void* call_data)
return 1;
}
-int unknown_argument(const char* argument, void* call_data)
+static int unknown_argument(const char* argument, void* call_data)
{
kwsys_ios::cout << "Got unknown argument: \"" << argument << "\"" << kwsys_ios::endl;
if ( call_data != random_ptr )
@@ -48,12 +48,12 @@ int unknown_argument(const char* argument, void* call_data)
return 1;
}
-bool CompareTwoItemsOnList(bool i1, bool i2) { return i1 == i2; }
-bool CompareTwoItemsOnList(int i1, int i2) { return i1 == i2; }
-bool CompareTwoItemsOnList(double i1, double i2) { return i1 == i2; }
-bool CompareTwoItemsOnList(const char* i1,
+static bool CompareTwoItemsOnList(bool i1, bool i2) { return i1 == i2; }
+static bool CompareTwoItemsOnList(int i1, int i2) { return i1 == i2; }
+static bool CompareTwoItemsOnList(double i1, double i2) { return i1 == i2; }
+static bool CompareTwoItemsOnList(const char* i1,
const char* i2) { return strcmp(i1, i2) == 0; }
-bool CompareTwoItemsOnList(const kwsys_stl::string& i1,
+static bool CompareTwoItemsOnList(const kwsys_stl::string& i1,
const kwsys_stl::string& i2) { return i1 == i2; }
int testCommandLineArguments(int argc, char* argv[])
diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx
index cbfb65b..61c1572 100644
--- a/Source/kwsys/testDynamicLoader.cxx
+++ b/Source/kwsys/testDynamicLoader.cxx
@@ -35,7 +35,7 @@
// left on disk.
#include <testSystemTools.h>
-kwsys_stl::string GetLibName(const char* lname)
+static kwsys_stl::string GetLibName(const char* lname)
{
// Construct proper name of lib
kwsys_stl::string slname;
diff --git a/Source/kwsys/testProcess.c b/Source/kwsys/testProcess.c
index 269b84b..6d5eb71 100644
--- a/Source/kwsys/testProcess.c
+++ b/Source/kwsys/testProcess.c
@@ -47,7 +47,7 @@ int runChild(const char* cmd[], int state, int exception, int value,
int share, int output, int delay, double timeout, int poll,
int repeat, int disown);
-int test1(int argc, const char* argv[])
+static int test1(int argc, const char* argv[])
{
(void)argc; (void)argv;
fprintf(stdout, "Output on stdout from test returning 0.\n");
@@ -55,7 +55,7 @@ int test1(int argc, const char* argv[])
return 0;
}
-int test2(int argc, const char* argv[])
+static int test2(int argc, const char* argv[])
{
(void)argc; (void)argv;
fprintf(stdout, "Output on stdout from test returning 123.\n");
@@ -63,7 +63,7 @@ int test2(int argc, const char* argv[])
return 123;
}
-int test3(int argc, const char* argv[])
+static int test3(int argc, const char* argv[])
{
(void)argc; (void)argv;
fprintf(stdout, "Output before sleep on stdout from timeout test.\n");
@@ -80,7 +80,7 @@ int test3(int argc, const char* argv[])
return 0;
}
-int test4(int argc, const char* argv[])
+static int test4(int argc, const char* argv[])
{
/* Prepare a pointer to an invalid address. Don't use null, because
dereferencing null is undefined behaviour and compilers are free to
@@ -109,7 +109,7 @@ int test4(int argc, const char* argv[])
return 0;
}
-int test5(int argc, const char* argv[])
+static int test5(int argc, const char* argv[])
{
int r;
const char* cmd[4];
@@ -132,7 +132,7 @@ int test5(int argc, const char* argv[])
}
#define TEST6_SIZE (4096*2)
-void test6(int argc, const char* argv[])
+static void test6(int argc, const char* argv[])
{
int i;
char runaway[TEST6_SIZE+1];
@@ -156,7 +156,7 @@ void test6(int argc, const char* argv[])
delaying 1/10th of a second should ever have to poll. */
#define MINPOLL 5
#define MAXPOLL 20
-int test7(int argc, const char* argv[])
+static int test7(int argc, const char* argv[])
{
(void)argc; (void)argv;
fprintf(stdout, "Output on stdout before sleep.\n");
@@ -176,7 +176,7 @@ int test7(int argc, const char* argv[])
return 0;
}
-int test8(int argc, const char* argv[])
+static int test8(int argc, const char* argv[])
{
/* Create a disowned grandchild to test handling of processes
that exit before their children. */
@@ -200,7 +200,7 @@ int test8(int argc, const char* argv[])
return r;
}
-int test8_grandchild(int argc, const char* argv[])
+static int test8_grandchild(int argc, const char* argv[])
{
(void)argc; (void)argv;
fprintf(stdout, "Output on stdout from grandchild before sleep.\n");
@@ -221,7 +221,7 @@ int test8_grandchild(int argc, const char* argv[])
return 0;
}
-int runChild2(kwsysProcess* kp,
+static int runChild2(kwsysProcess* kp,
const char* cmd[], int state, int exception, int value,
int share, int output, int delay, double timeout,
int poll, int disown)
@@ -505,7 +505,7 @@ int main(int argc, const char* argv[])
fprintf(stderr, "Output on stderr after test %d.\n", n);
fflush(stdout);
fflush(stderr);
-#if _WIN32
+#if defined(_WIN32)
if(argv0) { free(argv0); }
#endif
return r;
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx
index 3ac0cb3..1690fd5 100644
--- a/Source/kwsys/testSystemTools.cxx
+++ b/Source/kwsys/testSystemTools.cxx
@@ -32,7 +32,7 @@
#include <string.h> /* strcmp */
//----------------------------------------------------------------------------
-const char* toUnixPaths[][2] =
+static const char* toUnixPaths[][2] =
{
{ "/usr/local/bin/passwd", "/usr/local/bin/passwd" },
{ "/usr/lo cal/bin/pa sswd", "/usr/lo cal/bin/pa sswd" },
@@ -52,8 +52,8 @@ const char* toUnixPaths[][2] =
{0, 0}
};
-bool CheckConvertToUnixSlashes(kwsys_stl::string input,
- kwsys_stl::string output)
+static bool CheckConvertToUnixSlashes(kwsys_stl::string input,
+ kwsys_stl::string output)
{
kwsys_stl::string result = input;
kwsys::SystemTools::ConvertToUnixSlashes(result);
@@ -69,17 +69,17 @@ bool CheckConvertToUnixSlashes(kwsys_stl::string input,
}
//----------------------------------------------------------------------------
-const char* checkEscapeChars[][4] =
+static const char* checkEscapeChars[][4] =
{
{ "1 foo 2 bar 2", "12", "\\", "\\1 foo \\2 bar \\2"},
{ " {} ", "{}", "#", " #{#} "},
{0, 0, 0, 0}
};
-bool CheckEscapeChars(kwsys_stl::string input,
- const char *chars_to_escape,
- char escape_char,
- kwsys_stl::string output)
+static bool CheckEscapeChars(kwsys_stl::string input,
+ const char *chars_to_escape,
+ char escape_char,
+ kwsys_stl::string output)
{
kwsys_stl::string result = kwsys::SystemTools::EscapeChars(
input.c_str(), chars_to_escape, escape_char);
@@ -95,7 +95,7 @@ bool CheckEscapeChars(kwsys_stl::string input,
}
//----------------------------------------------------------------------------
-bool CheckFileOperations()
+static bool CheckFileOperations()
{
bool res = true;
@@ -129,7 +129,7 @@ bool CheckFileOperations()
}
//----------------------------------------------------------------------------
-bool CheckStringOperations()
+static bool CheckStringOperations()
{
bool res = true;
@@ -329,7 +329,7 @@ bool CheckStringOperations()
//----------------------------------------------------------------------------
-bool CheckPutEnv(const char* env, const char* name, const char* value)
+static bool CheckPutEnv(const char* env, const char* name, const char* value)
{
if(!kwsys::SystemTools::PutEnv(env))
{
@@ -348,7 +348,7 @@ bool CheckPutEnv(const char* env, const char* name, const char* value)
return true;
}
-bool CheckUnPutEnv(const char* env, const char* name)
+static bool CheckUnPutEnv(const char* env, const char* name)
{
if(!kwsys::SystemTools::UnPutEnv(env))
{
@@ -365,7 +365,7 @@ bool CheckUnPutEnv(const char* env, const char* name)
return true;
}
-bool CheckEnvironmentOperations()
+static bool CheckEnvironmentOperations()
{
bool res = true;
res &= CheckPutEnv("A=B", "A", "B");
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 0c75892..0876a07 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -312,6 +312,19 @@ if(BUILD_TESTING)
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)
if (APPLE OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
@@ -1977,6 +1990,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
--output-log "${CMake_BINARY_DIR}/Tests/CTestTestParallel/testOutput.log"
)
+ set(CTestLimitDashJ_EXTRA_OPTIONS --force-new-ctest-process)
add_test_macro(CTestLimitDashJ ${CMAKE_CTEST_COMMAND} -j 4
--output-on-failure -C "\${CTestTest_CONFIG}")
diff --git a/Tests/Module/ExternalData/CMakeLists.txt b/Tests/Module/ExternalData/CMakeLists.txt
new file mode 100644
index 0000000..607e9b9
--- /dev/null
+++ b/Tests/Module/ExternalData/CMakeLists.txt
@@ -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)
diff --git a/Tests/Module/ExternalData/Data.dat.md5 b/Tests/Module/ExternalData/Data.dat.md5
new file mode 100644
index 0000000..70e39bd
--- /dev/null
+++ b/Tests/Module/ExternalData/Data.dat.md5
@@ -0,0 +1 @@
+8c018830e3efa5caf3c7415028335a57
diff --git a/Tests/Module/ExternalData/Data1Check.cmake b/Tests/Module/ExternalData/Data1Check.cmake
new file mode 100644
index 0000000..b99d7ae
--- /dev/null
+++ b/Tests/Module/ExternalData/Data1Check.cmake
@@ -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()
diff --git a/Tests/Module/ExternalData/Data2.dat.md5 b/Tests/Module/ExternalData/Data2.dat.md5
new file mode 100644
index 0000000..70e39bd
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2.dat.md5
@@ -0,0 +1 @@
+8c018830e3efa5caf3c7415028335a57
diff --git a/Tests/Module/ExternalData/Data2/CMakeLists.txt b/Tests/Module/ExternalData/Data2/CMakeLists.txt
new file mode 100644
index 0000000..c5b79ac
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2/CMakeLists.txt
@@ -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)
diff --git a/Tests/Module/ExternalData/Data2/Data2Check.cmake b/Tests/Module/ExternalData/Data2/Data2Check.cmake
new file mode 100644
index 0000000..d5b0c7b
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2/Data2Check.cmake
@@ -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()
diff --git a/Tests/Module/ExternalData/Data2/SeriesC_1_.my.dat.md5 b/Tests/Module/ExternalData/Data2/SeriesC_1_.my.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2/SeriesC_1_.my.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/Data2/SeriesC_2_.my.dat.md5 b/Tests/Module/ExternalData/Data2/SeriesC_2_.my.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2/SeriesC_2_.my.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/Data2/SeriesC_3_.my.dat.md5 b/Tests/Module/ExternalData/Data2/SeriesC_3_.my.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2/SeriesC_3_.my.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/Data2b.dat.md5 b/Tests/Module/ExternalData/Data2b.dat.md5
new file mode 100644
index 0000000..70e39bd
--- /dev/null
+++ b/Tests/Module/ExternalData/Data2b.dat.md5
@@ -0,0 +1 @@
+8c018830e3efa5caf3c7415028335a57
diff --git a/Tests/Module/ExternalData/Data3/CMakeLists.txt b/Tests/Module/ExternalData/Data3/CMakeLists.txt
new file mode 100644
index 0000000..a7c2b6e
--- /dev/null
+++ b/Tests/Module/ExternalData/Data3/CMakeLists.txt
@@ -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)
diff --git a/Tests/Module/ExternalData/Data3/Data.dat.md5 b/Tests/Module/ExternalData/Data3/Data.dat.md5
new file mode 100644
index 0000000..70e39bd
--- /dev/null
+++ b/Tests/Module/ExternalData/Data3/Data.dat.md5
@@ -0,0 +1 @@
+8c018830e3efa5caf3c7415028335a57
diff --git a/Tests/Module/ExternalData/Data3/Data3Check.cmake b/Tests/Module/ExternalData/Data3/Data3Check.cmake
new file mode 100644
index 0000000..de98839
--- /dev/null
+++ b/Tests/Module/ExternalData/Data3/Data3Check.cmake
@@ -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()
diff --git a/Tests/Module/ExternalData/Data3/Other.dat.md5 b/Tests/Module/ExternalData/Data3/Other.dat.md5
new file mode 100644
index 0000000..5312faa
--- /dev/null
+++ b/Tests/Module/ExternalData/Data3/Other.dat.md5
@@ -0,0 +1 @@
+aaad162b85f60d1eb57ca71a23e8efd7
diff --git a/Tests/Module/ExternalData/Directory/A.dat.md5 b/Tests/Module/ExternalData/Directory/A.dat.md5
new file mode 100644
index 0000000..4a78fc7
--- /dev/null
+++ b/Tests/Module/ExternalData/Directory/A.dat.md5
@@ -0,0 +1 @@
+9d980b06c2f0fec3d4872d68175b9822
diff --git a/Tests/Module/ExternalData/Directory/B.dat.md5 b/Tests/Module/ExternalData/Directory/B.dat.md5
new file mode 100644
index 0000000..4557a21
--- /dev/null
+++ b/Tests/Module/ExternalData/Directory/B.dat.md5
@@ -0,0 +1 @@
+8f4add4581551facf27237e6577fd662
diff --git a/Tests/Module/ExternalData/Directory/C.dat.md5 b/Tests/Module/ExternalData/Directory/C.dat.md5
new file mode 100644
index 0000000..a7f23dd
--- /dev/null
+++ b/Tests/Module/ExternalData/Directory/C.dat.md5
@@ -0,0 +1 @@
+c1030719c95f3435d8abc39c0d442946
diff --git a/Tests/Module/ExternalData/MD5/.gitattributes b/Tests/Module/ExternalData/MD5/.gitattributes
new file mode 100644
index 0000000..3e51d39
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/.gitattributes
@@ -0,0 +1 @@
+* -crlf
diff --git a/Tests/Module/ExternalData/MD5/08cfcf221f76ace7b906b312284e73d7 b/Tests/Module/ExternalData/MD5/08cfcf221f76ace7b906b312284e73d7
new file mode 100644
index 0000000..a689e3c
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/08cfcf221f76ace7b906b312284e73d7
@@ -0,0 +1 @@
+MetaTop
diff --git a/Tests/Module/ExternalData/MD5/30ba0acdee9096b3b9fc6c69362c6b42 b/Tests/Module/ExternalData/MD5/30ba0acdee9096b3b9fc6c69362c6b42
new file mode 100644
index 0000000..5491241
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/30ba0acdee9096b3b9fc6c69362c6b42
@@ -0,0 +1 @@
+Series.3
diff --git a/Tests/Module/ExternalData/MD5/31eff09e84fca01415f8cd9d82ec432b b/Tests/Module/ExternalData/MD5/31eff09e84fca01415f8cd9d82ec432b
new file mode 100644
index 0000000..4d0475e
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/31eff09e84fca01415f8cd9d82ec432b
@@ -0,0 +1 @@
+Series.1
diff --git a/Tests/Module/ExternalData/MD5/401767f22a456b3522953722090a2c36 b/Tests/Module/ExternalData/MD5/401767f22a456b3522953722090a2c36
new file mode 100644
index 0000000..9dec116
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/401767f22a456b3522953722090a2c36
@@ -0,0 +1 @@
+PairedA
diff --git a/Tests/Module/ExternalData/MD5/8c018830e3efa5caf3c7415028335a57 b/Tests/Module/ExternalData/MD5/8c018830e3efa5caf3c7415028335a57
new file mode 100644
index 0000000..fa701e2
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/8c018830e3efa5caf3c7415028335a57
@@ -0,0 +1 @@
+Input file already transformed.
diff --git a/Tests/Module/ExternalData/MD5/8f4add4581551facf27237e6577fd662 b/Tests/Module/ExternalData/MD5/8f4add4581551facf27237e6577fd662
new file mode 100644
index 0000000..69ba09c
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/8f4add4581551facf27237e6577fd662
@@ -0,0 +1 @@
+MetaB
diff --git a/Tests/Module/ExternalData/MD5/9d980b06c2f0fec3d4872d68175b9822 b/Tests/Module/ExternalData/MD5/9d980b06c2f0fec3d4872d68175b9822
new file mode 100644
index 0000000..000e7b2
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/9d980b06c2f0fec3d4872d68175b9822
@@ -0,0 +1 @@
+MetaA
diff --git a/Tests/Module/ExternalData/MD5/aaad162b85f60d1eb57ca71a23e8efd7 b/Tests/Module/ExternalData/MD5/aaad162b85f60d1eb57ca71a23e8efd7
new file mode 100644
index 0000000..df0510c
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/aaad162b85f60d1eb57ca71a23e8efd7
@@ -0,0 +1 @@
+Another input file already transformed.
diff --git a/Tests/Module/ExternalData/MD5/c1030719c95f3435d8abc39c0d442946 b/Tests/Module/ExternalData/MD5/c1030719c95f3435d8abc39c0d442946
new file mode 100644
index 0000000..3fac5e6
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/c1030719c95f3435d8abc39c0d442946
@@ -0,0 +1 @@
+MetaC
diff --git a/Tests/Module/ExternalData/MD5/ce38ea6c3c1e00fa6405dd64b8bf6da0 b/Tests/Module/ExternalData/MD5/ce38ea6c3c1e00fa6405dd64b8bf6da0
new file mode 100644
index 0000000..362d4b4
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/ce38ea6c3c1e00fa6405dd64b8bf6da0
@@ -0,0 +1 @@
+SeriesMixed.1
diff --git a/Tests/Module/ExternalData/MD5/ecfa1ecd417d4253af81ae04d1bd6581 b/Tests/Module/ExternalData/MD5/ecfa1ecd417d4253af81ae04d1bd6581
new file mode 100644
index 0000000..8c414f5
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/ecfa1ecd417d4253af81ae04d1bd6581
@@ -0,0 +1 @@
+PairedB
diff --git a/Tests/Module/ExternalData/MD5/f41c94425d01ecbbee70440b951cb058 b/Tests/Module/ExternalData/MD5/f41c94425d01ecbbee70440b951cb058
new file mode 100644
index 0000000..3503da4
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/f41c94425d01ecbbee70440b951cb058
@@ -0,0 +1 @@
+Series
diff --git a/Tests/Module/ExternalData/MD5/f7ab5a04aae9cb9a520e70b20b9c8ed7 b/Tests/Module/ExternalData/MD5/f7ab5a04aae9cb9a520e70b20b9c8ed7
new file mode 100644
index 0000000..29d727b
--- /dev/null
+++ b/Tests/Module/ExternalData/MD5/f7ab5a04aae9cb9a520e70b20b9c8ed7
@@ -0,0 +1 @@
+Series.2
diff --git a/Tests/Module/ExternalData/MetaA.dat.md5 b/Tests/Module/ExternalData/MetaA.dat.md5
new file mode 100644
index 0000000..4a78fc7
--- /dev/null
+++ b/Tests/Module/ExternalData/MetaA.dat.md5
@@ -0,0 +1 @@
+9d980b06c2f0fec3d4872d68175b9822
diff --git a/Tests/Module/ExternalData/MetaB.dat.md5 b/Tests/Module/ExternalData/MetaB.dat.md5
new file mode 100644
index 0000000..4557a21
--- /dev/null
+++ b/Tests/Module/ExternalData/MetaB.dat.md5
@@ -0,0 +1 @@
+8f4add4581551facf27237e6577fd662
diff --git a/Tests/Module/ExternalData/MetaC.dat.md5 b/Tests/Module/ExternalData/MetaC.dat.md5
new file mode 100644
index 0000000..a7f23dd
--- /dev/null
+++ b/Tests/Module/ExternalData/MetaC.dat.md5
@@ -0,0 +1 @@
+c1030719c95f3435d8abc39c0d442946
diff --git a/Tests/Module/ExternalData/MetaTop.dat.md5 b/Tests/Module/ExternalData/MetaTop.dat.md5
new file mode 100644
index 0000000..1906cbf
--- /dev/null
+++ b/Tests/Module/ExternalData/MetaTop.dat.md5
@@ -0,0 +1 @@
+08cfcf221f76ace7b906b312284e73d7
diff --git a/Tests/Module/ExternalData/PairedA.dat.md5 b/Tests/Module/ExternalData/PairedA.dat.md5
new file mode 100644
index 0000000..1ffe035
--- /dev/null
+++ b/Tests/Module/ExternalData/PairedA.dat.md5
@@ -0,0 +1 @@
+401767f22a456b3522953722090a2c36
diff --git a/Tests/Module/ExternalData/PairedB.dat.md5 b/Tests/Module/ExternalData/PairedB.dat.md5
new file mode 100644
index 0000000..89c942b
--- /dev/null
+++ b/Tests/Module/ExternalData/PairedB.dat.md5
@@ -0,0 +1 @@
+ecfa1ecd417d4253af81ae04d1bd6581
diff --git a/Tests/Module/ExternalData/SHA1/.gitattributes b/Tests/Module/ExternalData/SHA1/.gitattributes
new file mode 100644
index 0000000..3e51d39
--- /dev/null
+++ b/Tests/Module/ExternalData/SHA1/.gitattributes
@@ -0,0 +1 @@
+* -crlf
diff --git a/Tests/Module/ExternalData/SHA1/2af59a7022024974f3b8521b7ed8137c996a79f1 b/Tests/Module/ExternalData/SHA1/2af59a7022024974f3b8521b7ed8137c996a79f1
new file mode 100644
index 0000000..a388540
--- /dev/null
+++ b/Tests/Module/ExternalData/SHA1/2af59a7022024974f3b8521b7ed8137c996a79f1
@@ -0,0 +1 @@
+SeriesMixed.2
diff --git a/Tests/Module/ExternalData/SHA224/.gitattributes b/Tests/Module/ExternalData/SHA224/.gitattributes
new file mode 100644
index 0000000..3e51d39
--- /dev/null
+++ b/Tests/Module/ExternalData/SHA224/.gitattributes
@@ -0,0 +1 @@
+* -crlf
diff --git a/Tests/Module/ExternalData/SHA224/3b679da7908562fe1cc28db47ffb89bae025f4551dceb343a5869174 b/Tests/Module/ExternalData/SHA224/3b679da7908562fe1cc28db47ffb89bae025f4551dceb343a5869174
new file mode 100644
index 0000000..e806d98
--- /dev/null
+++ b/Tests/Module/ExternalData/SHA224/3b679da7908562fe1cc28db47ffb89bae025f4551dceb343a5869174
@@ -0,0 +1 @@
+SeriesMixed.3
diff --git a/Tests/Module/ExternalData/SHA256/.gitattributes b/Tests/Module/ExternalData/SHA256/.gitattributes
new file mode 100644
index 0000000..3e51d39
--- /dev/null
+++ b/Tests/Module/ExternalData/SHA256/.gitattributes
@@ -0,0 +1 @@
+* -crlf
diff --git a/Tests/Module/ExternalData/SHA256/969171a0dd70d49ce096bd3e8178c7e26c711c9b20dbcaa3853d869d3871f133 b/Tests/Module/ExternalData/SHA256/969171a0dd70d49ce096bd3e8178c7e26c711c9b20dbcaa3853d869d3871f133
new file mode 100644
index 0000000..e3d1e0c
--- /dev/null
+++ b/Tests/Module/ExternalData/SHA256/969171a0dd70d49ce096bd3e8178c7e26c711c9b20dbcaa3853d869d3871f133
@@ -0,0 +1 @@
+SeriesMixed.4
diff --git a/Tests/Module/ExternalData/SeriesA.dat.md5 b/Tests/Module/ExternalData/SeriesA.dat.md5
new file mode 100644
index 0000000..be2d687
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesA.dat.md5
@@ -0,0 +1 @@
+f41c94425d01ecbbee70440b951cb058
diff --git a/Tests/Module/ExternalData/SeriesA1.dat.md5 b/Tests/Module/ExternalData/SeriesA1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesA1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesA2.dat.md5 b/Tests/Module/ExternalData/SeriesA2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesA2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesA3.dat.md5 b/Tests/Module/ExternalData/SeriesA3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesA3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesAn1.dat.md5 b/Tests/Module/ExternalData/SeriesAn1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesAn1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesAn2.dat.md5 b/Tests/Module/ExternalData/SeriesAn2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesAn2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesAn3.dat.md5 b/Tests/Module/ExternalData/SeriesAn3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesAn3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesB.dat.md5 b/Tests/Module/ExternalData/SeriesB.dat.md5
new file mode 100644
index 0000000..be2d687
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesB.dat.md5
@@ -0,0 +1 @@
+f41c94425d01ecbbee70440b951cb058
diff --git a/Tests/Module/ExternalData/SeriesB_1.dat.md5 b/Tests/Module/ExternalData/SeriesB_1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesB_1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesB_2.dat.md5 b/Tests/Module/ExternalData/SeriesB_2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesB_2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesB_3.dat.md5 b/Tests/Module/ExternalData/SeriesB_3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesB_3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesBn_1.dat.md5 b/Tests/Module/ExternalData/SeriesBn_1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesBn_1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesBn_2.dat.md5 b/Tests/Module/ExternalData/SeriesBn_2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesBn_2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesBn_3.dat.md5 b/Tests/Module/ExternalData/SeriesBn_3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesBn_3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesC.1.dat.md5 b/Tests/Module/ExternalData/SeriesC.1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesC.1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesC.2.dat.md5 b/Tests/Module/ExternalData/SeriesC.2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesC.2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesC.3.dat.md5 b/Tests/Module/ExternalData/SeriesC.3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesC.3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesC.dat.md5 b/Tests/Module/ExternalData/SeriesC.dat.md5
new file mode 100644
index 0000000..be2d687
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesC.dat.md5
@@ -0,0 +1 @@
+f41c94425d01ecbbee70440b951cb058
diff --git a/Tests/Module/ExternalData/SeriesCn.1.dat.md5 b/Tests/Module/ExternalData/SeriesCn.1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesCn.1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesCn.2.dat.md5 b/Tests/Module/ExternalData/SeriesCn.2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesCn.2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesCn.3.dat.md5 b/Tests/Module/ExternalData/SeriesCn.3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesCn.3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesD-1.dat.md5 b/Tests/Module/ExternalData/SeriesD-1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesD-1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesD-2.dat.md5 b/Tests/Module/ExternalData/SeriesD-2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesD-2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesD-3.dat.md5 b/Tests/Module/ExternalData/SeriesD-3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesD-3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesD.dat.md5 b/Tests/Module/ExternalData/SeriesD.dat.md5
new file mode 100644
index 0000000..be2d687
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesD.dat.md5
@@ -0,0 +1 @@
+f41c94425d01ecbbee70440b951cb058
diff --git a/Tests/Module/ExternalData/SeriesDn-1.dat.md5 b/Tests/Module/ExternalData/SeriesDn-1.dat.md5
new file mode 100644
index 0000000..f22e266
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesDn-1.dat.md5
@@ -0,0 +1 @@
+31eff09e84fca01415f8cd9d82ec432b
diff --git a/Tests/Module/ExternalData/SeriesDn-2.dat.md5 b/Tests/Module/ExternalData/SeriesDn-2.dat.md5
new file mode 100644
index 0000000..2b917e7
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesDn-2.dat.md5
@@ -0,0 +1 @@
+f7ab5a04aae9cb9a520e70b20b9c8ed7
diff --git a/Tests/Module/ExternalData/SeriesDn-3.dat.md5 b/Tests/Module/ExternalData/SeriesDn-3.dat.md5
new file mode 100644
index 0000000..b9c9760
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesDn-3.dat.md5
@@ -0,0 +1 @@
+30ba0acdee9096b3b9fc6c69362c6b42
diff --git a/Tests/Module/ExternalData/SeriesMixed.1.dat.md5 b/Tests/Module/ExternalData/SeriesMixed.1.dat.md5
new file mode 100644
index 0000000..f962d8f
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesMixed.1.dat.md5
@@ -0,0 +1 @@
+ce38ea6c3c1e00fa6405dd64b8bf6da0
diff --git a/Tests/Module/ExternalData/SeriesMixed.2.dat.sha1 b/Tests/Module/ExternalData/SeriesMixed.2.dat.sha1
new file mode 100644
index 0000000..43a3540
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesMixed.2.dat.sha1
@@ -0,0 +1 @@
+2af59a7022024974f3b8521b7ed8137c996a79f1
diff --git a/Tests/Module/ExternalData/SeriesMixed.3.dat.sha224 b/Tests/Module/ExternalData/SeriesMixed.3.dat.sha224
new file mode 100644
index 0000000..a18e40e
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesMixed.3.dat.sha224
@@ -0,0 +1 @@
+3b679da7908562fe1cc28db47ffb89bae025f4551dceb343a5869174
diff --git a/Tests/Module/ExternalData/SeriesMixed.4.dat.sha256 b/Tests/Module/ExternalData/SeriesMixed.4.dat.sha256
new file mode 100644
index 0000000..67fc3c2
--- /dev/null
+++ b/Tests/Module/ExternalData/SeriesMixed.4.dat.sha256
@@ -0,0 +1 @@
+969171a0dd70d49ce096bd3e8178c7e26c711c9b20dbcaa3853d869d3871f133
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 35b9a43..e55cf69 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -46,6 +46,7 @@ macro(add_RunCMake_test test)
endmacro()
add_RunCMake_test(CMP0019)
+add_RunCMake_test(ExternalData)
add_RunCMake_test(GeneratorExpression)
add_RunCMake_test(TargetPropertyGeneratorExpressions)
add_RunCMake_test(Languages)
diff --git a/Tests/RunCMake/ExternalData/BadHashAlgo1-result.txt b/Tests/RunCMake/ExternalData/BadHashAlgo1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadHashAlgo1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/BadHashAlgo1-stderr.txt b/Tests/RunCMake/ExternalData/BadHashAlgo1-stderr.txt
new file mode 100644
index 0000000..f68f0be
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadHashAlgo1-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/ExternalData/BadHashAlgo1.cmake b/Tests/RunCMake/ExternalData/BadHashAlgo1.cmake
new file mode 100644
index 0000000..19e2e41
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadHashAlgo1.cmake
@@ -0,0 +1,3 @@
+include(ExternalData)
+set(ExternalData_LINK_CONTENT BAD)
+ExternalData_Expand_Arguments(Data args DATA{BadHashAlgo1.txt})
diff --git a/Tests/RunCMake/ExternalData/BadHashAlgo1.txt b/Tests/RunCMake/ExternalData/BadHashAlgo1.txt
new file mode 100644
index 0000000..bfa2818
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadHashAlgo1.txt
@@ -0,0 +1 @@
+Sample input file that should not be transformed.
diff --git a/Tests/RunCMake/ExternalData/BadOption1-result.txt b/Tests/RunCMake/ExternalData/BadOption1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadOption1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/BadOption1-stderr.txt b/Tests/RunCMake/ExternalData/BadOption1-stderr.txt
new file mode 100644
index 0000000..b63d098
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadOption1-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/ExternalData/BadOption1.cmake b/Tests/RunCMake/ExternalData/BadOption1.cmake
new file mode 100644
index 0000000..1303d7f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadOption1.cmake
@@ -0,0 +1,5 @@
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Data.txt,Bad/Option}
+ )
diff --git a/Tests/RunCMake/ExternalData/BadOption2-result.txt b/Tests/RunCMake/ExternalData/BadOption2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadOption2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/BadOption2-stderr.txt b/Tests/RunCMake/ExternalData/BadOption2-stderr.txt
new file mode 100644
index 0000000..d114c8a
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadOption2-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/ExternalData/BadOption2.cmake b/Tests/RunCMake/ExternalData/BadOption2.cmake
new file mode 100644
index 0000000..6269b06
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadOption2.cmake
@@ -0,0 +1,5 @@
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Data.txt,Bad:Option}
+ )
diff --git a/Tests/RunCMake/ExternalData/BadSeries1-result.txt b/Tests/RunCMake/ExternalData/BadSeries1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/BadSeries1-stderr.txt b/Tests/RunCMake/ExternalData/BadSeries1-stderr.txt
new file mode 100644
index 0000000..3099be5
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries1-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/ExternalData/BadSeries1.cmake b/Tests/RunCMake/ExternalData/BadSeries1.cmake
new file mode 100644
index 0000000..7d71210
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries1.cmake
@@ -0,0 +1,3 @@
+include(ExternalData)
+set(ExternalData_SERIES_PARSE NotASeriesRegex)
+ExternalData_Expand_Arguments(Data args DATA{Data.txt,:})
diff --git a/Tests/RunCMake/ExternalData/BadSeries2-result.txt b/Tests/RunCMake/ExternalData/BadSeries2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/BadSeries2-stderr.txt b/Tests/RunCMake/ExternalData/BadSeries2-stderr.txt
new file mode 100644
index 0000000..3a02c25
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries2-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/ExternalData/BadSeries2.cmake b/Tests/RunCMake/ExternalData/BadSeries2.cmake
new file mode 100644
index 0000000..e81993b
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries2.cmake
@@ -0,0 +1,3 @@
+include(ExternalData)
+set(ExternalData_SERIES_PARSE "(x)(y)$")
+ExternalData_Expand_Arguments(Data args DATA{Data.txt,:})
diff --git a/Tests/RunCMake/ExternalData/BadSeries3-result.txt b/Tests/RunCMake/ExternalData/BadSeries3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/BadSeries3-stderr.txt b/Tests/RunCMake/ExternalData/BadSeries3-stderr.txt
new file mode 100644
index 0000000..594cb6f6
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries3-stderr.txt
@@ -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\)
diff --git a/Tests/RunCMake/ExternalData/BadSeries3.cmake b/Tests/RunCMake/ExternalData/BadSeries3.cmake
new file mode 100644
index 0000000..b640df8
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/BadSeries3.cmake
@@ -0,0 +1,2 @@
+include(ExternalData)
+ExternalData_Expand_Arguments(Data args DATA{PairA.txt,PairB.txt,:})
diff --git a/Tests/RunCMake/ExternalData/CMakeLists.txt b/Tests/RunCMake/ExternalData/CMakeLists.txt
new file mode 100644
index 0000000..e8db6b0
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ExternalData/Data.txt.md5 b/Tests/RunCMake/ExternalData/Data.txt.md5
new file mode 100644
index 0000000..93b3485
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Data.txt.md5
@@ -0,0 +1 @@
+e8bb14af900b998b5a3df7e21dd07d58
diff --git a/Tests/RunCMake/ExternalData/Directory1-result.txt b/Tests/RunCMake/ExternalData/Directory1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/Directory1-stderr.txt b/Tests/RunCMake/ExternalData/Directory1-stderr.txt
new file mode 100644
index 0000000..85c250f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory1-stderr.txt
@@ -0,0 +1,14 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Data file referenced by argument
+
+ DATA{Directory1}
+
+ corresponds to source tree path
+
+ Directory1
+
+ that does not exist as a file \(with or without an extension\)!
+Call Stack \(most recent call first\):
+ .*
+ Directory1.cmake:3 \(ExternalData_Add_Test\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/Directory1.cmake b/Tests/RunCMake/ExternalData/Directory1.cmake
new file mode 100644
index 0000000..68f1b54
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory1.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Directory1}
+ )
diff --git a/Tests/RunCMake/ExternalData/Directory1/DirData1.txt b/Tests/RunCMake/ExternalData/Directory1/DirData1.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory1/DirData1.txt
diff --git a/Tests/RunCMake/ExternalData/Directory2-result.txt b/Tests/RunCMake/ExternalData/Directory2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/Directory2-stderr.txt b/Tests/RunCMake/ExternalData/Directory2-stderr.txt
new file mode 100644
index 0000000..92c9a2f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory2-stderr.txt
@@ -0,0 +1,10 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Data directory referenced by argument
+
+ DATA{Directory2/}
+
+ must list associated files.
+Call Stack \(most recent call first\):
+ .*
+ Directory2.cmake:3 \(ExternalData_Add_Test\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/Directory2.cmake b/Tests/RunCMake/ExternalData/Directory2.cmake
new file mode 100644
index 0000000..30b992e
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory2.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Directory2/}
+ )
diff --git a/Tests/RunCMake/ExternalData/Directory2.md5 b/Tests/RunCMake/ExternalData/Directory2.md5
new file mode 100644
index 0000000..93b3485
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory2.md5
@@ -0,0 +1 @@
+e8bb14af900b998b5a3df7e21dd07d58
diff --git a/Tests/RunCMake/ExternalData/Directory2/DirData2.txt b/Tests/RunCMake/ExternalData/Directory2/DirData2.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory2/DirData2.txt
diff --git a/Tests/RunCMake/ExternalData/Directory3-result.txt b/Tests/RunCMake/ExternalData/Directory3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/Directory3-stderr.txt b/Tests/RunCMake/ExternalData/Directory3-stderr.txt
new file mode 100644
index 0000000..56a341e
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory3-stderr.txt
@@ -0,0 +1,14 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Data file referenced by argument
+
+ DATA{Directory3/\*}
+
+ corresponds to source tree path
+
+ Directory3/.
+
+ that does not exist as a file \(with or without an extension\)!
+Call Stack \(most recent call first\):
+ .*
+ Directory3.cmake:3 \(ExternalData_Add_Test\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/Directory3.cmake b/Tests/RunCMake/ExternalData/Directory3.cmake
new file mode 100644
index 0000000..55d8be9
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory3.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Directory3/*}
+ )
diff --git a/Tests/RunCMake/ExternalData/Directory3/DirData3.txt b/Tests/RunCMake/ExternalData/Directory3/DirData3.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory3/DirData3.txt
diff --git a/Tests/RunCMake/ExternalData/Directory4-result.txt b/Tests/RunCMake/ExternalData/Directory4-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory4-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/Directory4-stderr.txt b/Tests/RunCMake/ExternalData/Directory4-stderr.txt
new file mode 100644
index 0000000..dcb8522
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory4-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Series option ":" not allowed with directories.
+Call Stack \(most recent call first\):
+ .*
+ Directory4.cmake:3 \(ExternalData_Add_Test\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/Directory4.cmake b/Tests/RunCMake/ExternalData/Directory4.cmake
new file mode 100644
index 0000000..7d3d638
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory4.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Directory4/,:}
+ )
diff --git a/Tests/RunCMake/ExternalData/Directory4/DirData4.txt b/Tests/RunCMake/ExternalData/Directory4/DirData4.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory4/DirData4.txt
diff --git a/Tests/RunCMake/ExternalData/Directory5-result.txt b/Tests/RunCMake/ExternalData/Directory5-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory5-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/Directory5-stderr.txt b/Tests/RunCMake/ExternalData/Directory5-stderr.txt
new file mode 100644
index 0000000..8e54aec
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory5-stderr.txt
@@ -0,0 +1,14 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Data directory referenced by argument
+
+ DATA{Directory5/}
+
+ corresponds to source tree path
+
+ Directory5
+
+ that does not exist as a directory!
+Call Stack \(most recent call first\):
+ .*
+ Directory5.cmake:3 \(ExternalData_Add_Test\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/Directory5.cmake b/Tests/RunCMake/ExternalData/Directory5.cmake
new file mode 100644
index 0000000..931589e
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/Directory5.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{Directory5/}
+ )
diff --git a/Tests/RunCMake/ExternalData/LinkContentMD5-stdout.txt b/Tests/RunCMake/ExternalData/LinkContentMD5-stdout.txt
new file mode 100644
index 0000000..f77fca9
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/LinkContentMD5-stdout.txt
@@ -0,0 +1,3 @@
+-- Linked ToLink.txt.md5 to ExternalData MD5/c18ff9804c8deec9eaeb17063cda8b7b
+-- Raw data correctly transformed to content link!
+-- Staged content exists!
diff --git a/Tests/RunCMake/ExternalData/LinkContentMD5.cmake b/Tests/RunCMake/ExternalData/LinkContentMD5.cmake
new file mode 100644
index 0000000..41b4dee
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/LinkContentMD5.cmake
@@ -0,0 +1,22 @@
+include(ExternalData)
+set(ExternalData_LINK_CONTENT MD5)
+set(ExternalData_SOURCE_ROOT ${CMAKE_CURRENT_BINARY_DIR})
+set(ExternalData_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/ExternalData)
+set(input ${CMAKE_CURRENT_BINARY_DIR}/ToLink.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/ExternalData/ToLink.txt)
+set(staged "${CMAKE_CURRENT_BINARY_DIR}/.ExternalData_MD5_c18ff9804c8deec9eaeb17063cda8b7b")
+file(REMOVE ${staged})
+file(REMOVE ${input}.md5)
+file(WRITE ${input} "To be transformed into a content link.")
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Raw data correctly transformed to content link!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
+if(EXISTS "${staged}")
+ message(STATUS "Staged content exists!")
+else()
+ message(FATAL_ERROR "Staged content missing!")
+endif()
diff --git a/Tests/RunCMake/ExternalData/LinkContentSHA1-stdout.txt b/Tests/RunCMake/ExternalData/LinkContentSHA1-stdout.txt
new file mode 100644
index 0000000..9a8eb2e
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/LinkContentSHA1-stdout.txt
@@ -0,0 +1,3 @@
+-- Linked ToLink.txt.sha1 to ExternalData SHA1/114ee5dda251457fd2df63bec91d3b8db43aba58
+-- Raw data correctly transformed to content link!
+-- Staged content exists!
diff --git a/Tests/RunCMake/ExternalData/LinkContentSHA1.cmake b/Tests/RunCMake/ExternalData/LinkContentSHA1.cmake
new file mode 100644
index 0000000..f78501c
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/LinkContentSHA1.cmake
@@ -0,0 +1,22 @@
+include(ExternalData)
+set(ExternalData_LINK_CONTENT SHA1)
+set(ExternalData_SOURCE_ROOT ${CMAKE_CURRENT_BINARY_DIR})
+set(ExternalData_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/ExternalData)
+set(input ${CMAKE_CURRENT_BINARY_DIR}/ToLink.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/ExternalData/ToLink.txt)
+set(staged "${CMAKE_CURRENT_BINARY_DIR}/.ExternalData_SHA1_114ee5dda251457fd2df63bec91d3b8db43aba58")
+file(REMOVE ${staged})
+file(REMOVE ${input}.sha1)
+file(WRITE ${input} "To be transformed into a content link.")
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Raw data correctly transformed to content link!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
+if(EXISTS "${staged}")
+ message(STATUS "Staged content exists!")
+else()
+ message(FATAL_ERROR "Staged content missing!")
+endif()
diff --git a/Tests/RunCMake/ExternalData/MissingData-result.txt b/Tests/RunCMake/ExternalData/MissingData-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/MissingData-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/MissingData-stderr.txt b/Tests/RunCMake/ExternalData/MissingData-stderr.txt
new file mode 100644
index 0000000..e794f95
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/MissingData-stderr.txt
@@ -0,0 +1,14 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Data file referenced by argument
+
+ DATA{MissingData.txt}
+
+ corresponds to source tree path
+
+ MissingData.txt
+
+ that does not exist as a file \(with or without an extension\)!
+Call Stack \(most recent call first\):
+ .*
+ MissingData.cmake:2 \(ExternalData_Add_Test\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/MissingData.cmake b/Tests/RunCMake/ExternalData/MissingData.cmake
new file mode 100644
index 0000000..b3c8a5c
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/MissingData.cmake
@@ -0,0 +1,5 @@
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{MissingData.txt}
+ )
diff --git a/Tests/RunCMake/ExternalData/NoLinkInSource-stderr.txt b/Tests/RunCMake/ExternalData/NoLinkInSource-stderr.txt
new file mode 100644
index 0000000..496ad8a
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NoLinkInSource-stderr.txt
@@ -0,0 +1,6 @@
+CMake Warning at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ ExternalData_LINK_CONTENT cannot be used in-source
+Call Stack \(most recent call first\):
+ .*
+ NoLinkInSource.cmake:8 \(ExternalData_Expand_Arguments\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/NoLinkInSource-stdout.txt b/Tests/RunCMake/ExternalData/NoLinkInSource-stdout.txt
new file mode 100644
index 0000000..18946f0
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NoLinkInSource-stdout.txt
@@ -0,0 +1 @@
+-- Data reference correctly not transformed!
diff --git a/Tests/RunCMake/ExternalData/NoLinkInSource.cmake b/Tests/RunCMake/ExternalData/NoLinkInSource.cmake
new file mode 100644
index 0000000..cbf45eb
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NoLinkInSource.cmake
@@ -0,0 +1,14 @@
+include(ExternalData)
+set(ExternalData_LINK_CONTENT MD5)
+set(ExternalData_SOURCE_ROOT ${CMAKE_CURRENT_BINARY_DIR})
+set(ExternalData_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR})
+set(input ${CMAKE_CURRENT_BINARY_DIR}/ToLink.txt)
+file(REMOVE ${input}.md5)
+file(WRITE ${input} "To be transformed into a content link.")
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${input}")
+ message(STATUS "Data reference correctly not transformed!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected no transformation.")
+endif()
diff --git a/Tests/RunCMake/ExternalData/NoURLTemplates-result.txt b/Tests/RunCMake/ExternalData/NoURLTemplates-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NoURLTemplates-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/NoURLTemplates-stderr.txt b/Tests/RunCMake/ExternalData/NoURLTemplates-stderr.txt
new file mode 100644
index 0000000..ad059d4
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NoURLTemplates-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ ExternalData_URL_TEMPLATES is not set!
+Call Stack \(most recent call first\):
+ NoURLTemplates.cmake:2 \(ExternalData_Add_Target\)
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ExternalData/NoURLTemplates.cmake b/Tests/RunCMake/ExternalData/NoURLTemplates.cmake
new file mode 100644
index 0000000..8f0e069
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NoURLTemplates.cmake
@@ -0,0 +1,2 @@
+include(ExternalData)
+ExternalData_Add_Target(Data)
diff --git a/Tests/RunCMake/ExternalData/NormalData1-stdout.txt b/Tests/RunCMake/ExternalData/NormalData1-stdout.txt
new file mode 100644
index 0000000..2f2c770
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalData1-stdout.txt
@@ -0,0 +1 @@
+-- Data reference correctly transformed!
diff --git a/Tests/RunCMake/ExternalData/NormalData1.cmake b/Tests/RunCMake/ExternalData/NormalData1.cmake
new file mode 100644
index 0000000..d6cc384
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalData1.cmake
@@ -0,0 +1,13 @@
+include(ExternalData)
+set(ExternalData_URL_TEMPLATES
+ "file:///${CMAKE_CURRENT_SOURCE_DIR}/%(algo)/%(hash)"
+ )
+set(input Data.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
diff --git a/Tests/RunCMake/ExternalData/NormalData2-stdout.txt b/Tests/RunCMake/ExternalData/NormalData2-stdout.txt
new file mode 100644
index 0000000..2f2c770
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalData2-stdout.txt
@@ -0,0 +1 @@
+-- Data reference correctly transformed!
diff --git a/Tests/RunCMake/ExternalData/NormalData2.cmake b/Tests/RunCMake/ExternalData/NormalData2.cmake
new file mode 100644
index 0000000..c979b48
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalData2.cmake
@@ -0,0 +1,14 @@
+include(ExternalData)
+set(ExternalData_URL_TEMPLATES
+ "file:///${CMAKE_CURRENT_SOURCE_DIR}/%(algo)/%(hash)"
+ )
+set(ExternalData_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/ExternalData)
+set(input Data.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/ExternalData/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
diff --git a/Tests/RunCMake/ExternalData/NormalData3-stdout.txt b/Tests/RunCMake/ExternalData/NormalData3-stdout.txt
new file mode 100644
index 0000000..2f2c770
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalData3-stdout.txt
@@ -0,0 +1 @@
+-- Data reference correctly transformed!
diff --git a/Tests/RunCMake/ExternalData/NormalData3.cmake b/Tests/RunCMake/ExternalData/NormalData3.cmake
new file mode 100644
index 0000000..e991122
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalData3.cmake
@@ -0,0 +1,14 @@
+include(ExternalData)
+set(ExternalData_URL_TEMPLATES
+ "file:///${CMAKE_CURRENT_SOURCE_DIR}/%(algo)/%(hash)"
+ )
+set(ExternalData_BINARY_ROOT ${CMAKE_CURRENT_BINARY_DIR}/ExternalData)
+set(input ${CMAKE_CURRENT_SOURCE_DIR}/Data.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/ExternalData/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
diff --git a/Tests/RunCMake/ExternalData/NormalDataSub1-stdout.txt b/Tests/RunCMake/ExternalData/NormalDataSub1-stdout.txt
new file mode 100644
index 0000000..2f2c770
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalDataSub1-stdout.txt
@@ -0,0 +1 @@
+-- Data reference correctly transformed!
diff --git a/Tests/RunCMake/ExternalData/NormalDataSub1.cmake b/Tests/RunCMake/ExternalData/NormalDataSub1.cmake
new file mode 100644
index 0000000..015e94c
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NormalDataSub1.cmake
@@ -0,0 +1,13 @@
+include(ExternalData)
+set(ExternalData_URL_TEMPLATES
+ "file:///${CMAKE_CURRENT_SOURCE_DIR}/%(algo)/%(hash)"
+ )
+set(input SubDirectory1/Data.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/SubDirectory1/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
diff --git a/Tests/RunCMake/ExternalData/NotUnderRoot-result.txt b/Tests/RunCMake/ExternalData/NotUnderRoot-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NotUnderRoot-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/ExternalData/NotUnderRoot-stderr.txt b/Tests/RunCMake/ExternalData/NotUnderRoot-stderr.txt
new file mode 100644
index 0000000..1f2eb1c
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NotUnderRoot-stderr.txt
@@ -0,0 +1,12 @@
+CMake Error at .*/Modules/ExternalData.cmake:[0-9]+ \(message\):
+ Data file referenced by argument
+
+ DATA{../NotUnderRoot.txt}
+
+ does not lie under the top-level source directory
+
+ .*/Tests/RunCMake/ExternalData
+
+Call Stack \(most recent call first\):
+ .*
+ NotUnderRoot.cmake:2 \(ExternalData_Add_Test\)
diff --git a/Tests/RunCMake/ExternalData/NotUnderRoot.cmake b/Tests/RunCMake/ExternalData/NotUnderRoot.cmake
new file mode 100644
index 0000000..c7942d6
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/NotUnderRoot.cmake
@@ -0,0 +1,5 @@
+include(ExternalData)
+ExternalData_Add_Test(Data
+ NAME Test
+ COMMAND ${CMAKE_COMMAND} -E echo DATA{../NotUnderRoot.txt}
+ )
diff --git a/Tests/RunCMake/ExternalData/RunCMakeTest.cmake b/Tests/RunCMake/ExternalData/RunCMakeTest.cmake
new file mode 100644
index 0000000..5ee46c9
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/RunCMakeTest.cmake
@@ -0,0 +1,24 @@
+include(RunCMake)
+
+run_cmake(BadHashAlgo1)
+run_cmake(BadOption1)
+run_cmake(BadOption2)
+run_cmake(BadSeries1)
+run_cmake(BadSeries2)
+run_cmake(BadSeries3)
+run_cmake(Directory1)
+run_cmake(Directory2)
+run_cmake(Directory3)
+run_cmake(Directory4)
+run_cmake(Directory5)
+run_cmake(LinkContentMD5)
+run_cmake(LinkContentSHA1)
+run_cmake(MissingData)
+run_cmake(NoLinkInSource)
+run_cmake(NoURLTemplates)
+run_cmake(NormalData1)
+run_cmake(NormalData2)
+run_cmake(NormalData3)
+run_cmake(NormalDataSub1)
+run_cmake(NotUnderRoot)
+run_cmake(SubDirectory1)
diff --git a/Tests/RunCMake/ExternalData/SubDirectory1-stdout.txt b/Tests/RunCMake/ExternalData/SubDirectory1-stdout.txt
new file mode 100644
index 0000000..03924cb
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/SubDirectory1-stdout.txt
@@ -0,0 +1,3 @@
+-- Data reference correctly transformed in parent dir 1!
+-- Data reference correctly transformed in parent dir 2!
+-- Data reference correctly transformed in current dir!
diff --git a/Tests/RunCMake/ExternalData/SubDirectory1.cmake b/Tests/RunCMake/ExternalData/SubDirectory1.cmake
new file mode 100644
index 0000000..2989471
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/SubDirectory1.cmake
@@ -0,0 +1,5 @@
+include(ExternalData)
+set(ExternalData_URL_TEMPLATES
+ "file:///${CMAKE_CURRENT_SOURCE_DIR}/%(algo)/%(hash)"
+ )
+add_subdirectory(SubDirectory1)
diff --git a/Tests/RunCMake/ExternalData/SubDirectory1/CMakeLists.txt b/Tests/RunCMake/ExternalData/SubDirectory1/CMakeLists.txt
new file mode 100644
index 0000000..881ff5c
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/SubDirectory1/CMakeLists.txt
@@ -0,0 +1,29 @@
+set(input ../Data.txt)
+set(output ${CMAKE_BINARY_DIR}/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed in parent dir 1!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
+
+set(input ${CMAKE_CURRENT_SOURCE_DIR}/../Data.txt)
+set(output ${CMAKE_BINARY_DIR}/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed in parent dir 2!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
+
+set(input Data.txt)
+set(output ${CMAKE_CURRENT_BINARY_DIR}/Data.txt)
+ExternalData_Expand_Arguments(Data args DATA{${input}})
+if("x${args}" STREQUAL "x${output}")
+ message(STATUS "Data reference correctly transformed in current dir!")
+else()
+ message(FATAL_ERROR "Data reference transformed to:\n ${args}\n"
+ "but we expected:\n ${output}")
+endif()
diff --git a/Tests/RunCMake/ExternalData/SubDirectory1/Data.txt.md5 b/Tests/RunCMake/ExternalData/SubDirectory1/Data.txt.md5
new file mode 100644
index 0000000..93b3485
--- /dev/null
+++ b/Tests/RunCMake/ExternalData/SubDirectory1/Data.txt.md5
@@ -0,0 +1 @@
+e8bb14af900b998b5a3df7e21dd07d58