[Cmake-commits] CMake branch, next, updated. v3.1.0-2018-g3425056

Brad King brad.king at kitware.com
Fri Jan 16 09:56:25 EST 2015


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, next has been updated
       via  342505665bd8bdc82ae5ceb3429ba5625564669a (commit)
       via  764b578d06dc02c9a2ff350a231de903933a1b0a (commit)
      from  cd80dc58e67eacb7e256670cd15962a624f76bd5 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=342505665bd8bdc82ae5ceb3429ba5625564669a
commit 342505665bd8bdc82ae5ceb3429ba5625564669a
Merge: cd80dc5 764b578
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Fri Jan 16 09:56:24 2015 -0500
Commit:     CMake Topic Stage <kwrobot at kitware.com>
CommitDate: Fri Jan 16 09:56:24 2015 -0500

    Merge topic 'cdash_upload_file_mode' into next
    
    764b578d Add support to ctest_submit to upload files to cdash with CDASH_UPLOAD.


http://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=764b578d06dc02c9a2ff350a231de903933a1b0a
commit 764b578d06dc02c9a2ff350a231de903933a1b0a
Author:     Bill Hoffman <bill.hoffman at kitware.com>
AuthorDate: Tue Dec 23 11:03:14 2014 -0500
Commit:     Brad King <brad.king at kitware.com>
CommitDate: Fri Jan 16 09:55:31 2015 -0500

    Add support to ctest_submit to upload files to cdash with CDASH_UPLOAD.
    
    This adds support for the new cdash API where arbitrary files can be
    uploaded to the CDash server.  This CDash API communicates via json
    files so the json parser jsoncpp was added to the Utilities directory.
    In addition since CDash tracks the md5 sum of the files uploaded the
    --mtime option was added to cmake -E tar so that tar files could be
    created that would have the same md5sum with the same content. The first
    supported file upload type to CDash is a GCOV with branches coverage tar
    file. To support this a Modules/CTestCoverageCollectGCOV.cmake was added
    to run gcov for a project via a CMake function.

diff --git a/Help/command/ctest_submit.rst b/Help/command/ctest_submit.rst
index d9b0b78..316a43d 100644
--- a/Help/command/ctest_submit.rst
+++ b/Help/command/ctest_submit.rst
@@ -37,3 +37,14 @@ timed-out submission before attempting to re-submit.
 
 The RETRY_COUNT option specifies how many times to retry a timed-out
 submission.
+
+::
+
+  ctest_submit([CDASH_UPLOAD file]
+               [CDASH_UPLOAD_TYPE type_string])
+
+This second signature is used to upload files to CDash via the CDash
+file upload API. The api first sends a request to upload to CDash along
+with the md5 sum of the file. If CDash does not already have the file,
+then it is uploaded. Along with the file, a CDash type string is specified
+to tell CDash which handler to use to process the data.
diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index 5196485..965eede 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -63,6 +63,7 @@ All Modules
    /module/CPack
    /module/CPackWIX
    /module/CTest
+   /module/CTestCoverageCollectGCOV
    /module/CTestScriptMode
    /module/CTestUseLaunchers
    /module/Dart
diff --git a/Help/module/CTestCoverageCollectGCOV.rst b/Help/module/CTestCoverageCollectGCOV.rst
new file mode 100644
index 0000000..4c5deca
--- /dev/null
+++ b/Help/module/CTestCoverageCollectGCOV.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/CTestCoverageCollectGCOV.cmake
diff --git a/Modules/CTestCoverageCollectGCOV.cmake b/Modules/CTestCoverageCollectGCOV.cmake
new file mode 100644
index 0000000..29fd504
--- /dev/null
+++ b/Modules/CTestCoverageCollectGCOV.cmake
@@ -0,0 +1,123 @@
+#.rst:
+# CTestCoverageCollectGCOV
+# ------------------------
+#
+# This module provides the function ``ctest_coverage_collect_gcov``.
+# The function will run gcov on the .gcda files in a binary tree and then
+# package all of the .gcov files into a tar file with a data.json.
+# This file can be sent to a CDash server for display with the
+# :command:`ctest_submit(CDASH_UPLOAD)` command.
+#
+# .. command:: cdash_coverage_collect_gcov
+#
+#   ::
+#
+#     ctest_coverage_collect_gcov(
+#           TARBALL <tarfile> # required
+#           SOURCE <source_dir> # optional, else CTEST_SOURCE_DIRECTORY
+#           BUILD <build_dir> # optional, else CTEST_BINARY_DIRECTORY
+#           GCOV_COMMAND <gcov_command> # optional, else CTEST_COVERAGE_COMMAND
+#     )
+#
+#   Run gcov and package a tar file for cdash. ``<tarfile>`` is the name
+#   of the tarfile that is created and it will be placed in ``<binary_dir>``.
+#   ``<source_dir>`` is the source directory for the build and ``<binary_dir>``
+#   is the binary directory for the build. The ``<gcov_command>`` is a full
+#   path to ``gcov`` for the machine.
+
+#=============================================================================
+# Copyright 2014-2015 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.)
+include(CMakeParseArguments)
+function(ctest_coverage_collect_gcov)
+  set(options "")
+  set(oneValueArgs TARBALL SOURCE BUILD GCOV_COMMAND)
+  set(multiValueArgs "")
+  cmake_parse_arguments(GCOV  "${options}" "${oneValueArgs}"
+    "${multiValueArgs}" "" ${ARGN} )
+  if(NOT DEFINED GCOV_TARBALL)
+    message(FATAL_ERROR
+      "TARBALL must be specified. for ctest_coverage_collect_gcov")
+  endif()
+  if(NOT DEFINED GCOV_SOURCE)
+    set(source_dir "${CTEST_SOURCE_DIRECTORY}")
+  else()
+    set(source_dir "${GCOV_SOURCE}")
+  endif()
+  if(NOT DEFINED GCOV_BUILD)
+    set(binary_dir "${CTEST_BINARY_DIRECTORY}")
+  else()
+    set(binary_dir "${GCOV_BUILD}")
+  endif()
+  if(NOT DEFINED GCOV_GCOV_COMMAND)
+    set(gcov_command "${CTEST_COVERAGE_COMMAND}")
+  else()
+    set(gcov_command "${GCOV_GCOV_COMMAND}")
+  endif()
+  # run gcov on each gcda file in the binary tree
+  set(gcda_files)
+  set(label_files)
+  # look for gcda files in the target directories
+  # could do a glob from the top of the binary tree but
+  # this will be faster and only look where the files will be
+  file(STRINGS "${binary_dir}/CMakeFiles/TargetDirectories.txt" target_dirs)
+  foreach(target_dir ${target_dirs})
+    file(GLOB_RECURSE gfiles RELATIVE ${binary_dir} "${target_dir}/*.gcda")
+    list(LENGTH gfiles len)
+    # if we have gcda files then also grab the labels file for that target
+    if(${len} GREATER 0)
+      file(GLOB_RECURSE lfiles RELATIVE ${binary_dir}
+        "${target_dir}/Labels.txt")
+      list(APPEND gcda_files ${gfiles})
+      list(APPEND label_files ${lfiles})
+    endif()
+  endforeach()
+  # return early if no coverage files were found
+  list(LENGTH gcda_files len)
+  if(len EQUAL 0)
+    message("ctest_coverage_collect_gcov: No .gcda files found, "
+      "ignoring coverage request.")
+    return()
+  endif()
+  # setup the dir for the coverage files
+  set(coverage_dir "${binary_dir}/Testing/CoverageInfo")
+  file(MAKE_DIRECTORY  "${coverage_dir}")
+  # call gcov on each .gcda file
+  foreach (gcda_file ${gcda_files})
+    # get the directory of the gcda file
+    get_filename_component(gcda_file ${binary_dir}/${gcda_file} ABSOLUTE)
+    get_filename_component(gcov_dir ${gcda_file} DIRECTORY)
+    # run gcov, this will produce the .gcov file in the current
+    # working directory
+    execute_process(COMMAND
+      ${gcov_command} -b -o ${gcov_dir} ${gcda_file}
+      OUTPUT_VARIABLE out
+      WORKING_DIRECTORY ${coverage_dir})
+  endforeach()
+  # create json file with project information
+  file(WRITE ${coverage_dir}/data.json
+    "{
+    \"Source\": \"${source_dir}\",
+    \"Binary\": \"${binary_dir}\"
+}")
+  # collect the gcov files
+  set(gcov_files)
+  file(GLOB_RECURSE gcov_files RELATIVE ${binary_dir} "${binary_dir}/*.gcov")
+  # tar up the coverage info with the same date so that the md5
+  # sum will be the same for the tar file independent of file time
+  # stamps
+  execute_process(COMMAND
+    ${CMAKE_COMMAND} -E tar cvfj ${GCOV_TARBALL}
+    --mtime=1970-01-01\ 0:0:0\ UTC ${gcov_files}
+    ${coverage_dir}/data.json  ${label_files}
+    WORKING_DIRECTORY ${binary_dir})
+endfunction()
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 96f4709..78bef91 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -533,6 +533,7 @@ set(CTEST_SRCS cmCTest.cxx
   CTest/cmCTestConfigureHandler.cxx
   CTest/cmCTestCoverageCommand.cxx
   CTest/cmCTestCoverageHandler.cxx
+  CTest/cmCTestCurl.cxx
   CTest/cmParseMumpsCoverage.cxx
   CTest/cmParseCacheCoverage.cxx
   CTest/cmParseGTMCoverage.cxx
@@ -583,7 +584,7 @@ set(CTEST_SRCS cmCTest.cxx
 
 # Build CTestLib
 add_library(CTestLib ${CTEST_SRCS})
-target_link_libraries(CTestLib CMakeLib ${CMAKE_CURL_LIBRARIES} ${CMAKE_XMLRPC_LIBRARIES})
+target_link_libraries(CTestLib CMakeLib ${CMAKE_CURL_LIBRARIES} ${CMAKE_XMLRPC_LIBRARIES} cmjsoncpp)
 
 #
 # Sources for CPack
diff --git a/Source/CTest/cmCTestCurl.cxx b/Source/CTest/cmCTestCurl.cxx
new file mode 100644
index 0000000..b354592
--- /dev/null
+++ b/Source/CTest/cmCTestCurl.cxx
@@ -0,0 +1,258 @@
+#include "cmCTestCurl.h"
+#include "cmSystemTools.h"
+#include "cmCTest.h"
+
+cmCTestCurl::cmCTestCurl(cmCTest* ctest)
+{
+  this->CTest = ctest;
+  this->SetProxyType();
+  this->UseHttp10 = false;
+  // In windows, this will init the winsock stuff
+  ::curl_global_init(CURL_GLOBAL_ALL);
+  // default is to verify https
+  this->VerifyPeerOff = false;
+  this->VerifyHostOff = false;
+  this->TimeOutSeconds = 0;
+}
+
+namespace
+{
+static size_t
+curlWriteMemoryCallback(void *ptr, size_t size, size_t nmemb,
+  void *data)
+{
+  int realsize = (int)(size * nmemb);
+
+  std::vector<char> *vec
+    = static_cast<std::vector<char>* >(data);
+  const char* chPtr = static_cast<char*>(ptr);
+  vec->insert(vec->end(), chPtr, chPtr + realsize);
+  return realsize;
+}
+
+static size_t
+curlDebugCallback(CURL *, curl_infotype, char *chPtr,
+  size_t size, void *data)
+{
+  std::vector<char> *vec
+    = static_cast<std::vector<char>* >(data);
+  vec->insert(vec->end(), chPtr, chPtr + size);
+
+  return size;
+}
+
+}
+void cmCTestCurl::SetCurlOptions(std::vector<std::string> const& args)
+{
+  for( std::vector<std::string>::const_iterator i = args.begin();
+       i != args.end(); ++i)
+    {
+    if(*i == "CURLOPT_SSL_VERIFYPEER_OFF")
+      {
+      this->VerifyPeerOff = true;
+      }
+    if(*i == "CURLOPT_SSL_VERIFYHOST_OFF")
+      {
+      this->VerifyHostOff = true;
+      }
+    }
+}
+
+bool cmCTestCurl::InitCurl()
+{
+  this->Curl = curl_easy_init();
+  if(!this->Curl)
+    {
+    return false;
+    }
+  if(this->VerifyPeerOff)
+    {
+    curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYPEER, 0);
+    }
+  if(this->VerifyHostOff)
+    {
+    curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYHOST, 0);
+    }
+  if(this->HTTPProxy.size())
+    {
+    curl_easy_setopt(this->Curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
+    curl_easy_setopt(this->Curl, CURLOPT_PROXYTYPE, this->HTTPProxyType);
+    if (this->HTTPProxyAuth.size() > 0)
+      {
+      curl_easy_setopt(this->Curl, CURLOPT_PROXYUSERPWD,
+                       this->HTTPProxyAuth.c_str());
+      }
+    }
+  if(this->UseHttp10)
+    {
+    curl_easy_setopt(this->Curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+    }
+  // enable HTTP ERROR parsing
+  curl_easy_setopt(this->Curl, CURLOPT_FAILONERROR, 1);
+  return true;
+}
+
+
+bool cmCTestCurl::UploadFile(std::string const& local_file,
+                             std::string const& url,
+                             std::string const& fields,
+                             std::string& response)
+{
+  response = "";
+  if(!this->InitCurl())
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE, "Initialization of curl failed");
+    return false;
+    }
+  /* enable uploading */
+  curl_easy_setopt(this->Curl, CURLOPT_UPLOAD, 1);
+  // if there is little to no activity for too long stop submitting
+  if(this->TimeOutSeconds)
+    {
+    ::curl_easy_setopt(this->Curl, CURLOPT_LOW_SPEED_LIMIT, 1);
+    ::curl_easy_setopt(this->Curl, CURLOPT_LOW_SPEED_TIME,
+                       this->TimeOutSeconds);
+    }
+  /* HTTP PUT please */
+  ::curl_easy_setopt(this->Curl, CURLOPT_PUT, 1);
+  ::curl_easy_setopt(this->Curl, CURLOPT_VERBOSE, 1);
+
+  FILE* ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
+  if(!ftpfile)
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Could not open file for upload: " << local_file << "\n");
+    return false;
+    }
+  // set the url
+  std::string upload_url = url;
+  upload_url += "?";
+  upload_url += fields;
+  ::curl_easy_setopt(this->Curl, CURLOPT_URL, upload_url.c_str());
+  // now specify which file to upload
+  ::curl_easy_setopt(this->Curl, CURLOPT_INFILE, ftpfile);
+  unsigned long filelen = cmSystemTools::FileLength(local_file);
+  // and give the size of the upload (optional)
+  ::curl_easy_setopt(this->Curl, CURLOPT_INFILESIZE,
+                     static_cast<long>(filelen));
+  ::curl_easy_setopt(this->Curl, CURLOPT_WRITEFUNCTION,
+                     curlWriteMemoryCallback);
+  ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGFUNCTION,
+                     curlDebugCallback);
+  std::vector<char> responseData;
+  std::vector<char> debugData;
+  ::curl_easy_setopt(this->Curl, CURLOPT_FILE, (void *)&responseData);
+  ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGDATA, (void *)&debugData);
+  ::curl_easy_setopt(this->Curl, CURLOPT_FAILONERROR, 1);
+  // Now run off and do what you've been told!
+  ::curl_easy_perform(this->Curl);
+  ::fclose(ftpfile);
+  ::curl_global_cleanup();
+
+  if ( responseData.size() > 0 )
+    {
+    response = std::string(responseData.begin(), responseData.end());
+    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+               "Curl response: [" << response << "]\n");
+    }
+  std::string curlDebug;
+  if ( debugData.size() > 0 )
+    {
+    curlDebug = std::string(debugData.begin(), debugData.end());
+    cmCTestLog(this->CTest, DEBUG, "Curl debug: [" << curlDebug << "]\n");
+    }
+  if(response.size() == 0)
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE, "No response from server.\n" <<
+      curlDebug);
+    return false;
+    }
+  return true;
+}
+
+bool cmCTestCurl::HttpRequest(std::string const& url,
+                              std::string const& fields,
+                              std::string& response)
+{
+  response = "";
+  cmCTestLog(this->CTest, DEBUG, "HttpRequest\n"
+             << "url: " << url << "\n"
+             << "fields " << fields << "\n");
+  if(!this->InitCurl())
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE, "Initialization of curl failed");
+    return false;
+    }
+  curl_easy_setopt(this->Curl, CURLOPT_POST, 1);
+  curl_easy_setopt(this->Curl, CURLOPT_POSTFIELDS, fields.c_str());
+  ::curl_easy_setopt(this->Curl, CURLOPT_URL, url.c_str());
+  ::curl_easy_setopt(this->Curl, CURLOPT_FOLLOWLOCATION, 1);
+  //set response options
+  ::curl_easy_setopt(this->Curl, CURLOPT_WRITEFUNCTION,
+                     curlWriteMemoryCallback);
+  ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGFUNCTION,
+        curlDebugCallback);
+  std::vector<char> responseData;
+  std::vector<char> debugData;
+  ::curl_easy_setopt(this->Curl, CURLOPT_FILE, (void *)&responseData);
+  ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGDATA, (void *)&debugData);
+  ::curl_easy_setopt(this->Curl, CURLOPT_FAILONERROR, 1);
+
+  CURLcode res = ::curl_easy_perform(this->Curl);
+
+  ::curl_easy_cleanup(this->Curl);
+  ::curl_global_cleanup();
+  if ( responseData.size() > 0 )
+    {
+    response = std::string(responseData.begin(), responseData.end());
+    cmCTestLog(this->CTest, DEBUG, "Curl response: [" << response << "]\n");
+    }
+  if ( debugData.size() > 0 )
+    {
+    std::string curlDebug = std::string(debugData.begin(), debugData.end());
+    cmCTestLog(this->CTest, DEBUG, "Curl debug: [" << curlDebug << "]\n");
+    }
+  cmCTestLog(this->CTest, DEBUG, "Curl res: " << res << "\n");
+  return (res == 0);
+}
+
+void cmCTestCurl::SetProxyType()
+{
+  if ( cmSystemTools::GetEnv("HTTP_PROXY") )
+    {
+    this->HTTPProxy = cmSystemTools::GetEnv("HTTP_PROXY");
+    if ( cmSystemTools::GetEnv("HTTP_PROXY_PORT") )
+      {
+      this->HTTPProxy += ":";
+      this->HTTPProxy += cmSystemTools::GetEnv("HTTP_PROXY_PORT");
+      }
+    if ( cmSystemTools::GetEnv("HTTP_PROXY_TYPE") )
+      {
+      // this is the default
+      this->HTTPProxyType = CURLPROXY_HTTP;
+      std::string type = cmSystemTools::GetEnv("HTTP_PROXY_TYPE");
+      // HTTP/SOCKS4/SOCKS5
+      if ( type == "HTTP" )
+        {
+        this->HTTPProxyType = CURLPROXY_HTTP;
+        }
+      else if ( type == "SOCKS4" )
+        {
+        this->HTTPProxyType = CURLPROXY_SOCKS4;
+        }
+      else if ( type == "SOCKS5" )
+        {
+        this->HTTPProxyType = CURLPROXY_SOCKS5;
+        }
+      }
+    if ( cmSystemTools::GetEnv("HTTP_PROXY_USER") )
+      {
+      this->HTTPProxyAuth = cmSystemTools::GetEnv("HTTP_PROXY_USER");
+      }
+    if ( cmSystemTools::GetEnv("HTTP_PROXY_PASSWD") )
+      {
+      this->HTTPProxyAuth += ":";
+      this->HTTPProxyAuth += cmSystemTools::GetEnv("HTTP_PROXY_PASSWD");
+      }
+    }
+}
diff --git a/Source/CTest/cmCTestCurl.h b/Source/CTest/cmCTestCurl.h
new file mode 100644
index 0000000..bb5c74a
--- /dev/null
+++ b/Source/CTest/cmCTestCurl.h
@@ -0,0 +1,50 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2000-2009 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.
+============================================================================*/
+#ifndef cmCTestCurl_h
+#define cmCTestCurl_h
+#include "cm_curl.h"
+#include "cmStandardIncludes.h"
+class cmCTest;
+
+class cmCTestCurl
+{
+public:
+  cmCTestCurl(cmCTest*);
+  bool UploadFile(std::string const& url,
+                  std::string const& file,
+                  std::string const& fields,
+                  std::string& response);
+  bool HttpRequest(std::string const& url,
+                   std::string const& fields,
+                   std::string& response);
+  // currently only supports CURLOPT_SSL_VERIFYPEER_OFF
+  // and CURLOPT_SSL_VERIFYHOST_OFF
+  void SetCurlOptions(std::vector<std::string> const& args);
+  void SetUseHttp10On() { this->UseHttp10 = true;}
+  void SetTimeOutSeconds(int s) { this->TimeOutSeconds = s;}
+protected:
+  void SetProxyType();
+  bool InitCurl();
+private:
+  cmCTest* CTest;
+  CURL* Curl;
+  std::string HTTPProxyAuth;
+  std::string HTTPProxy;
+  curl_proxytype HTTPProxyType;
+  bool VerifyHostOff;
+  bool VerifyPeerOff;
+  bool UseHttp10;
+  int TimeOutSeconds;
+};
+
+
+#endif
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index 4005a63..dcd7982 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -27,7 +27,8 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
     = this->Makefile->GetDefinition("CTEST_TRIGGER_SITE");
   bool ctestDropSiteCDash
     = this->Makefile->IsOn("CTEST_DROP_SITE_CDASH");
-
+  const char* ctestProjectName
+    = this->Makefile->GetDefinition("CTEST_PROJECT_NAME");
   if ( !ctestDropMethod )
     {
     ctestDropMethod = "http";
@@ -43,7 +44,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
     // error: CDash requires CTEST_DROP_LOCATION definition
     // in CTestConfig.cmake
     }
-
+  this->CTest->SetCTestConfiguration("ProjectName", ctestProjectName);
   this->CTest->SetCTestConfiguration("DropMethod", ctestDropMethod);
   this->CTest->SetCTestConfiguration("DropSite", ctestDropSite);
   this->CTest->SetCTestConfiguration("DropLocation", ctestDropLocation);
@@ -144,6 +145,13 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
   static_cast<cmCTestSubmitHandler*>(handler)->SetOption("InternalTest",
     this->InternalTest ? "ON" : "OFF");
 
+  if(this->CDashUploadFile.size())
+    {
+    static_cast<cmCTestSubmitHandler*>(handler)->
+      SetOption("CDashUploadFile", this->CDashUploadFile.c_str());
+    static_cast<cmCTestSubmitHandler*>(handler)->
+      SetOption("CDashUploadType", this->CDashUploadType.c_str());
+    }
   return handler;
 }
 
@@ -178,6 +186,16 @@ bool cmCTestSubmitCommand::CheckArgumentKeyword(std::string const& arg)
     return true;
     }
 
+  if(arg == "CDASH_UPLOAD")
+    {
+    this->ArgumentDoing = ArgumentDoingCDashUpload;
+    return true;
+    }
+  if(arg == "CDASH_UPLOAD_TYPE")
+    {
+    this->ArgumentDoing = ArgumentDoingCDashUploadType;
+    return true;
+    }
   if(arg == "INTERNAL_TEST_CHECKSUM")
     {
     this->InternalTest = true;
@@ -240,6 +258,17 @@ bool cmCTestSubmitCommand::CheckArgumentValue(std::string const& arg)
     return true;
     }
 
+  if(this->ArgumentDoing == ArgumentDoingCDashUpload)
+    {
+    this->CDashUploadFile = arg;
+    return true;
+    }
+  if(this->ArgumentDoing == ArgumentDoingCDashUploadType)
+    {
+    this->CDashUploadType = arg;
+    return true;
+    }
+
   // Look for other arguments.
   return this->Superclass::CheckArgumentValue(arg);
 }
diff --git a/Source/CTest/cmCTestSubmitCommand.h b/Source/CTest/cmCTestSubmitCommand.h
index 3673fbd..cb0ac61 100644
--- a/Source/CTest/cmCTestSubmitCommand.h
+++ b/Source/CTest/cmCTestSubmitCommand.h
@@ -64,6 +64,8 @@ protected:
     ArgumentDoingFiles,
     ArgumentDoingRetryDelay,
     ArgumentDoingRetryCount,
+    ArgumentDoingCDashUpload,
+    ArgumentDoingCDashUploadType,
     ArgumentDoingLast2
   };
 
@@ -74,6 +76,8 @@ protected:
   cmCTest::SetOfStrings Files;
   std::string RetryCount;
   std::string RetryDelay;
+  std::string CDashUploadFile;
+  std::string CDashUploadType;
 };
 
 
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index fea94ba..53a8982 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -10,7 +10,8 @@
   See the License for more information.
 ============================================================================*/
 #include "cmCTestSubmitHandler.h"
-
+#include "cmCTestScriptHandler.h"
+#include "cmake.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 #include "cmGeneratedFileStream.h"
@@ -23,8 +24,10 @@
 // For XML-RPC submission
 #include "cm_xmlrpc.h"
 
+#include <cm_jsoncpp_reader.h>
 // For curl submission
 #include "cm_curl.h"
+#include "cmCTestCurl.h"
 
 #include <sys/stat.h>
 
@@ -1055,9 +1058,165 @@ bool cmCTestSubmitHandler::SubmitUsingXMLRPC(std::string const&,
 }
 #endif
 
+void cmCTestSubmitHandler::ConstructCDashURL(std::string& dropMethod,
+                                             std::string& url)
+{
+  dropMethod = this->CTest->GetCTestConfiguration("DropMethod");
+  url = dropMethod;
+  url += "://";
+  if ( this->CTest->GetCTestConfiguration("DropSiteUser").size() > 0 )
+    {
+    url += this->CTest->GetCTestConfiguration("DropSiteUser");
+    cmCTestLog(this->CTest, HANDLER_OUTPUT,
+               this->CTest->GetCTestConfiguration("DropSiteUser").c_str());
+    if ( this->CTest->GetCTestConfiguration("DropSitePassword").size() > 0 )
+      {
+      url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword");
+      cmCTestLog(this->CTest, HANDLER_OUTPUT, ":******");
+      }
+    url += "@";
+    }
+  url += this->CTest->GetCTestConfiguration("DropSite") +
+    this->CTest->GetCTestConfiguration("DropLocation");
+}
+
+
+int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file,
+                                                std::string const& typeString)
+{
+  if(!cmSystemTools::FileExists(file))
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Upload file not found: " << file << "\n");
+    return -1;
+    }
+  cmCTestCurl curl(this->CTest);
+  std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
+  std::vector<std::string> args;
+  cmSystemTools::ExpandListArgument(curlopt, args);
+  curl.SetCurlOptions(args);
+  curl.SetTimeOutSeconds(SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
+  std::string dropMethod;
+  std::string url;
+  this->ConstructCDashURL(dropMethod, url);
+  std::string::size_type pos = url.find("submit.php?");
+  url = url.substr(0, pos+10);
+  if ( ! (dropMethod == "http" || dropMethod == "https" ) )
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Only http and https are supported for CDASH_UPLOAD\n");
+    return -1;
+    }
+  char md5sum[33];
+  md5sum[32] = 0;
+  cmSystemTools::ComputeFileMD5(file, md5sum);
+  // 1. request the buildid and check to see if the file
+  //    has already been uploaded
+  // TODO I added support for subproject. You would need to add
+  // a "&subproject=subprojectname" to the first POST.
+  cmCTestScriptHandler* ch =
+    static_cast<cmCTestScriptHandler*>(this->CTest->GetHandler("script"));
+  cmake* cm =  ch->GetCMake();
+  const char* subproject = cm->GetProperty("SubProject", cmProperty::GLOBAL);
+  std::ostringstream str;
+  str << "project="
+      << this->CTest->GetCTestConfiguration("ProjectName") << "&";
+  if(subproject)
+    {
+    str << "subproject=" << subproject << "&";
+    }
+  str << "stamp=" << this->CTest->GetCurrentTag() << "-"
+      << this->CTest->GetTestModelString() << "&"
+      << "model=" << this->CTest->GetTestModelString() << "&"
+      << "build=" << this->CTest->GetCTestConfiguration("BuildName") << "&"
+      << "site=" << this->CTest->GetCTestConfiguration("Site") << "&"
+      << "track=" << this->CTest->GetTestModelString() << "&"
+      << "starttime=" << (int)cmSystemTools::GetTime() << "&"
+      << "endtime=" << (int)cmSystemTools::GetTime() << "&"
+      << "datafilesmd5[0]=" << md5sum << "&"
+      << "type=" << typeString;
+  std::string fields = str.str();
+  cmCTestLog(this->CTest, DEBUG, "fields: " << fields << "\nurl:"
+             << url << "\nfile: " << file << "\n");
+  std::string response;
+  if(!curl.HttpRequest(url, fields, response))
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Error in HttpRequest\n" << response);
+    return -1;
+    }
+  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+             "Request upload response: [" << response << "]\n");
+  Json::Value json;
+  Json::Reader reader;
+  if(!reader.parse(response, json))
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "error parsing json string [" << response << "]\n"
+               << reader.getFormattedErrorMessages() << "\n");
+    return -1;
+    }
+  if(json["status"].asInt() != 0)
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Bad status returned from CDash: "
+               << json["status"].asInt());
+    return -1;
+    }
+  if(json["datafilesmd5"].isArray())
+    {
+    int datares = json["datafilesmd5"][0].asInt();
+    if(datares == 1)
+      {
+      cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                 "File already exists on CDash, skip upload "
+                 << file << "\n");
+      return 0;
+      }
+    }
+  else
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "bad datafilesmd5 value in response "
+               << response << "\n");
+    return -1;
+    }
+
+  std::string upload_as = cmSystemTools::GetFilenameName(file);
+  std::ostringstream fstr;
+  fstr << "type=" << typeString << "&"
+       << "md5=" << md5sum << "&"
+       << "filename=" << upload_as << "&"
+       << "buildid=" << json["buildid"].asString();
+  if(!curl.UploadFile(file, url, fstr.str(), response))
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "error uploading to CDash. "
+               << file << " " << url << " " << fstr.str());
+    return -1;
+    }
+  if(!reader.parse(response, json))
+    {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "error parsing json string [" << response << "]\n"
+               << reader.getFormattedErrorMessages() << "\n");
+    return -1;
+    }
+  cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+             "Upload file response: [" << response << "]\n");
+  return 0;
+}
+
 //----------------------------------------------------------------------------
 int cmCTestSubmitHandler::ProcessHandler()
 {
+  const char* cdashUploadFile = this->GetOption("CDashUploadFile");
+  const char* cdashUploadType = this->GetOption("CDashUploadType");
+  if(cdashUploadFile && cdashUploadType)
+    {
+    return this->HandleCDashUploadFile(std::string(cdashUploadFile),
+                                       std::string(cdashUploadType));
+    }
   std::string iscdash = this->CTest->GetCTestConfiguration("IsCDash");
   // cdash does not need to trigger so just return true
   if(iscdash.size())
diff --git a/Source/CTest/cmCTestSubmitHandler.h b/Source/CTest/cmCTestSubmitHandler.h
index accabd1..f9cd894 100644
--- a/Source/CTest/cmCTestSubmitHandler.h
+++ b/Source/CTest/cmCTestSubmitHandler.h
@@ -41,6 +41,11 @@ public:
   /** Specify a set of files to submit.  */
   void SelectFiles(cmCTest::SetOfStrings const& files);
 
+  // handle the cdash file upload protocol
+  int HandleCDashUploadFile(std::string const& file, std::string const& type);
+
+  void ConstructCDashURL(std::string& dropMethod, std::string& url);
+
 private:
   void SetLogFile(std::ostream* ost) { this->LogFile = ost; }
 
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx
index a2aecac..fc4c40d 100644
--- a/Source/cmArchiveWrite.cxx
+++ b/Source/cmArchiveWrite.cxx
@@ -247,6 +247,11 @@ bool cmArchiveWrite::AddPath(const char* path,
   return true;
 }
 
+// function from libarchive
+extern "C"
+{
+  time_t __archive_get_date(time_t, const char*);
+}
 //----------------------------------------------------------------------------
 bool cmArchiveWrite::AddFile(const char* file,
                              size_t skip, const char* prefix)
@@ -275,6 +280,17 @@ bool cmArchiveWrite::AddFile(const char* file,
     this->Error += cm_archive_error_string(this->Disk);
     return false;
     }
+  if(this->MTime.size())
+    {
+    time_t t = __archive_get_date(0, this->MTime.c_str());
+    if (t == -1)
+      {
+      this->Error = "__archive_get_date: Unable to parse mtime: ";
+      this->Error += "[" + this->MTime + "]";
+      return false;
+      }
+    archive_entry_set_mtime(e, t, 0);
+    }
   // Clear acl and xattr fields not useful for distribution.
   archive_entry_acl_clear(e);
   archive_entry_xattr_clear(e);
diff --git a/Source/cmArchiveWrite.h b/Source/cmArchiveWrite.h
index a6dcc0e..987e95c 100644
--- a/Source/cmArchiveWrite.h
+++ b/Source/cmArchiveWrite.h
@@ -74,6 +74,7 @@ public:
   // std::cout.
   void SetVerbose(bool v) { this->Verbose = v; }
 
+  void SetMTime(const char* t) { if(t) {this->MTime = t;}}
 private:
   bool Okay() const { return this->Error.empty(); }
   bool AddPath(const char* path, size_t skip, const char* prefix);
@@ -90,6 +91,7 @@ private:
   struct archive* Disk;
   bool Verbose;
   std::string Error;
+  std::string MTime;
 };
 
 #endif
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index c769154..372d41c 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -1469,7 +1469,7 @@ bool cmSystemTools::IsPathToFramework(const char* path)
 bool cmSystemTools::CreateTar(const char* outFileName,
                               const std::vector<std::string>& files,
                               cmTarCompression compressType,
-                              bool verbose)
+                              bool verbose, const char* mtime)
 {
 #if defined(CMAKE_BUILD_WITH_CMAKE)
   std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
@@ -1501,6 +1501,10 @@ bool cmSystemTools::CreateTar(const char* outFileName,
     }
   cmArchiveWrite a(fout, compress,
                    cmArchiveWrite::TypeTAR);
+  if(mtime)
+    {
+    a.SetMTime(mtime);
+    }
   a.SetVerbose(verbose);
   for(std::vector<std::string>::const_iterator i = files.begin();
       i != files.end(); ++i)
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 09ceea6..1c7208f 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -394,9 +394,9 @@ public:
                       bool verbose);
   static bool CreateTar(const char* outFileName,
                         const std::vector<std::string>& files,
-                        cmTarCompression compressType, bool verbose);
-  static bool ExtractTar(const char* inFileName,
-                         bool verbose);
+                        cmTarCompression compressType, bool verbose,
+                        const char* mtime=0);
+  static bool ExtractTar(const char* inFileName, bool verbose);
   // This should be called first thing in main
   // it will keep child processes from inheriting the
   // stdin and stdout of this process.  This is important
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 70d98d2..3465e55 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -729,9 +729,21 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
       std::string flags = args[2];
       std::string outFile = args[3];
       std::vector<std::string> files;
+      std::string mtime;
       for (std::string::size_type cc = 4; cc < args.size(); cc ++)
         {
-        files.push_back(args[cc]);
+        // if the first 8 chars of an arg are --mtime= and that arg
+        // is not a file on disk, treat it is the --mtime= flag and
+        // not a file
+        if( (args[cc].substr(0, 8) == "--mtime=") &&
+            !cmSystemTools::FileExists(args[cc]))
+          {
+          mtime = args[cc].substr(8);
+          }
+        else
+          {
+          files.push_back(args[cc]);
+          }
         }
       cmSystemTools::cmTarCompression compress =
         cmSystemTools::TarCompressNone;
@@ -773,8 +785,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
         }
       else if ( flags.find_first_of('c') != flags.npos )
         {
+        const char* mtimestr = 0;
+        if(mtime.size())
+          {
+          mtimestr = mtime.c_str();
+          }
         if ( !cmSystemTools::CreateTar(
-               outFile.c_str(), files, compress, verbose) )
+               outFile.c_str(), files, compress, verbose, mtimestr) )
           {
           cmSystemTools::Error("Problem creating tar: ", outFile.c_str());
           return 1;

-----------------------------------------------------------------------

Summary of changes:


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list