This patch introduces several options for the file(DOWNLOAD ...) command, which
would be useful in case of dealing with big files or unstable network
connections.
The added options are:
RETRY_COUNT -- sets maximal amount of download restarts, default value: 1
RETRY_DELAY -- sets delay before restarting download (in seconds),
default value: 0.0
RETRY_MAX_TIME -- sets maximal time spent in downloading file
(in seconds), default value: infinity
RETRY_CONTINUE -- if set, makes cmake try to continue downloading of the
existing chunk, instead of discarding it and starting all over. This option is
not set by default
Notes:
The RETRY_CONTINUE option requires server-side support of http partial get
(content-range header).
Unfortunately, I haven't been able to properly test the RETRY_CONTINUE option,
as I didn't have access to the appropriate server. Any help in this area is
encouraged.
---
Help/command/file.rst| 17 +++
Source/cmFileCommand.cxx | 271 ---
2 files changed, 205 insertions(+), 83 deletions(-)
diff --git a/Help/command/file.rst b/Help/command/file.rst
index 256d16d..f1095b7 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -240,6 +240,23 @@ Additional options to ``DOWNLOAD`` are:
``TLS_CAINFO ``
Specify a custom Certificate Authority file for ``https://`` URLs.
+``RETRY_COUNT ``
+ Set maximal amount of download restarts, default value: 1
+
+``RETRY_DELAY ``
+ Set delay before restarting download, default value: 0.0
+
+``RETRY_MAX_TIME ``
+ Set maximal time spent in downloading file, default value: infinity
+
+``RETRY_CONTINUE``
+ If set, makes cmake try to continue downloading of the existing chunk,
+ instead of discarding it and starting all over. This option is not set
+ by default.
+
+ Note that this option requires server-side support of http partial get
+ (content-range header).
+
For ``https://`` URLs CMake must be built with OpenSSL support. ``TLS/SSL``
certificates are not checked by default. Set ``TLS_VERIFY`` to ``ON`` to
check certificates and/or use ``EXPECTED_HASH`` to verify downloaded content.
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 835b118..bbe8839 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -34,6 +34,9 @@
// include sys/stat.h after sys/types.h
#include
+#include
+#include
+
#include
#include
#include
@@ -2481,6 +2484,11 @@ bool
cmFileCommand::HandleDownloadCommand(std::vector const& args)
std::string hashMatchMSG;
CM_AUTO_PTR hash;
bool showProgress = false;
+ int retryMaxCount = 1;
+ double retryDelayS = 0.0;
+ double retryMaxTimeS = DBL_MAX;
+ bool retryContinue = false;
+ cmsys::ofstream fout;
while (i != args.end()) {
if (*i == "TIMEOUT") {
@@ -2564,7 +2572,34 @@ bool
cmFileCommand::HandleDownloadCommand(std::vector const& args)
return false;
}
hashMatchMSG = algo + " hash";
+} else if (*i == "RETRY_COUNT") {
+ ++i;
+ if (i != args.end()) {
+retryMaxCount = atoi(i->c_str());
+ } else {
+this->SetError("DOWNLOAD missing count for RETRY_COUNT");
+return false;
+ }
+} else if (*i == "RETRY_DELAY") {
+ ++i;
+ if (i != args.end()) {
+retryDelayS = atof(i->c_str());
+ } else {
+this->SetError("DOWNLOAD missing time for RETRY_DELAY");
+return false;
+ }
+} else if (*i == "RETRY_MAX_TIME") {
+ ++i;
+ if (i != args.end()) {
+retryMaxTimeS = atof(i->c_str());
+ } else {
+this->SetError("DOWNLOAD missing time for RETRY_MAX_TIME");
+return false;
+ }
+} else if (*i == "RETRY_CONTINUE") {
+ retryContinue = true;
}
+
++i;
}
// If file exists already, and caller specified an expected md5 or sha,
@@ -2599,110 +2634,171 @@ bool
cmFileCommand::HandleDownloadCommand(std::vector const& args)
return false;
}
- cmsys::ofstream fout(file.c_str(), std::ios::binary);
- if (!fout) {
-this->SetError("DOWNLOAD cannot open file for write.");
-return false;
- }
-
#if defined(_WIN32) && defined(CMAKE_ENCODING_UTF8)
url = fix_file_url_windows(url);
#endif
+ cmFileCommandVectorOfChar chunkDebug;
+
::CURL* curl;
::curl_global_init(CURL_GLOBAL_DEFAULT);
- curl = ::curl_easy_init();
- if (!curl) {
-this->SetError("DOWNLOAD error initializing curl.");
-return false;
- }
- cURLEasyGuard g_curl(curl);
- ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
- check_curl_result(res, "DOWNLOAD cannot set url: ");
+ ::CURLcode res;
+ int tries = 0;
+ double elapsed = 0.0;
+ time_t start, end;
+ while (tries < retryMaxCount && elapsed <= retryMaxTimeS) {
+++tries;
+time();
+
+curl = ::curl_easy_init();
+if (!curl) {
+ this->SetError("DOWNLOAD error initializing curl.");
+ ::curl_global_cleanup();
+ return false;
+}
- // enable HTTP