[PATCH] timestamp sub-command for string and file

Nils Gladitz gladitz at scivis.de
Sat Sep 29 05:31:18 EDT 2012


---
 Source/CMakeLists.txt                              |    2 +
 Source/cmFileCommand.cxx                           |   57 ++++++++
 Source/cmFileCommand.h                             |   10 ++
 Source/cmStringCommand.cxx                         |   54 ++++++++
 Source/cmStringCommand.h                           |   30 ++++-
 Source/cmTimestamp.cxx                             |  135 ++++++++++++++++++++
 Source/cmTimestamp.h                               |   42 ++++++
 Tests/CMakeTests/File-TIMESTAMP-BadArg1.cmake      |    1 +
 Tests/CMakeTests/File-TIMESTAMP-NoFile.cmake       |    2 +
 Tests/CMakeTests/File-TIMESTAMP-NotBogus.cmake     |   24 ++++
 Tests/CMakeTests/File-TIMESTAMP-Works.cmake        |    2 +
 Tests/CMakeTests/FileTest.cmake.in                 |   12 ++
 .../String-TIMESTAMP-AllSpecifiers.cmake           |   11 ++
 Tests/CMakeTests/String-TIMESTAMP-BadArg1.cmake    |    1 +
 Tests/CMakeTests/String-TIMESTAMP-BadArg2.cmake    |    1 +
 Tests/CMakeTests/String-TIMESTAMP-BadArg3.cmake    |    1 +
 .../String-TIMESTAMP-CustomFormatLocal.cmake       |    2 +
 .../String-TIMESTAMP-CustomFormatUTC.cmake         |    2 +
 .../String-TIMESTAMP-DefaulFormatUTC.cmake         |    2 +
 .../String-TIMESTAMP-DefaultFormatLocal.cmake      |    2 +
 .../String-TIMESTAMP-DefaultFormatUTC.cmake        |    2 +
 .../String-TIMESTAMP-IncompleteSpecifier.cmake     |    2 +
 .../String-TIMESTAMP-UnknownSpecifier.cmake        |    2 +
 Tests/CMakeTests/StringTest.cmake.in               |   30 +++++
 24 files changed, 428 insertions(+), 1 deletions(-)
 create mode 100644 Source/cmTimestamp.cxx
 create mode 100644 Source/cmTimestamp.h
 create mode 100644 Tests/CMakeTests/File-TIMESTAMP-BadArg1.cmake
 create mode 100644 Tests/CMakeTests/File-TIMESTAMP-NoFile.cmake
 create mode 100644 Tests/CMakeTests/File-TIMESTAMP-NotBogus.cmake
 create mode 100644 Tests/CMakeTests/File-TIMESTAMP-Works.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-AllSpecifiers.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-BadArg1.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-BadArg2.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-BadArg3.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-CustomFormatLocal.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-CustomFormatUTC.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-DefaulFormatUTC.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-DefaultFormatLocal.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-DefaultFormatUTC.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-IncompleteSpecifier.cmake
 create mode 100644 Tests/CMakeTests/String-TIMESTAMP-UnknownSpecifier.cmake

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 5a3e7d1..00a1a0a 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -259,6 +259,8 @@ set(SRCS
   cmSystemTools.h
   cmTarget.cxx
   cmTarget.h
+  cmTimestamp.h
+  cmTimestamp.cxx
   cmTest.cxx
   cmTest.h
   cmTestGenerator.cxx
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 8de24b3..3b074da 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -17,6 +17,8 @@
 #include "cmFileTimeComparison.h"
 #include "cmCryptoHash.h"
 
+#include "cmTimestamp.h"
+
 #if defined(CMAKE_BUILD_WITH_CMAKE)
 #include "cm_curl.h"
 #endif
@@ -160,6 +162,10 @@ bool cmFileCommand
     {
     return this->HandleCMakePathCommand(args, true);
     }
+  else if ( subCommand == "TIMESTAMP" )
+    {
+    return this->HandleTimestampCommand(args);
+    }
 
   std::string e = "does not recognize sub-command "+subCommand;
   this->SetError(e.c_str());
@@ -3246,3 +3252,54 @@ cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args)
   return false;
 #endif
 }
+
+//----------------------------------------------------------------------------
+bool cmFileCommand::HandleTimestampCommand(
+  std::vector<std::string> const& args)
+{
+  if(args.size() < 3)
+    {
+    this->SetError("sub-command TIMESTAMP requires at least two arguments.");
+    return false;
+    }
+  else if(args.size() > 5)
+    {
+    this->SetError("sub-command TIMESTAMP takes at most four arguments.");
+    return false;
+    }
+
+  int argsIndex = 1;
+
+  const std::string& filename = args[argsIndex++];
+
+  const std::string& outputVariable = args[argsIndex++];
+
+  std::string formatString;
+  if(args.size() > argsIndex && args[argsIndex] != "UTC")
+    {
+    formatString = args[argsIndex++];
+    }
+
+  bool utcFlag = false;
+  if(args.size() > argsIndex)
+    {
+    if(args[argsIndex] == "UTC")
+      {
+      utcFlag = true;
+      }
+    else
+      {
+      std::string e = " TIMESTAMP sub-command does not recognize option " +
+          args[argsIndex] + ".";
+      this->SetError(e.c_str());
+      return false;
+      }
+    }
+
+  cmTimestamp timestamp;
+  std::string result = timestamp.FileModificationTime(
+    filename.c_str(), formatString, utcFlag);
+  this->Makefile->AddDefinition(outputVariable.c_str(), result.c_str());
+
+  return true;
+}
diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h
index b4aa903..e780767 100644
--- a/Source/cmFileCommand.h
+++ b/Source/cmFileCommand.h
@@ -87,6 +87,8 @@ public:
       "       [TLS_VERIFY on|off] [TLS_CAINFO file])\n"
       "  file(UPLOAD filename url [INACTIVITY_TIMEOUT timeout]\n"
       "       [TIMEOUT timeout] [STATUS status] [LOG log] [SHOW_PROGRESS])\n"
+      "  file(TIMESTAMP <filename> <output variable>"
+      "       [<format string>] [UTC])\n"
       "WRITE will write a message into a file called 'filename'. It "
       "overwrites the file if it already exists, and creates the file "
       "if it does not exist.\n"
@@ -199,6 +201,12 @@ public:
       "If SHOW_PROGRESS is specified, progress information will be printed "
       "as status messages until the operation is complete."
       "\n"
+      "TIMESTAMP will write a string representation of "
+      "the modification time of <filename> to <output varaible>.\n"
+      "Should the command be unable to obtain a timestamp "
+      "<output variable> will be set to NOTFOUND.\n"
+      "See documentation of the string TIMESTAMP sub-command for more details."
+      "\n"
       "The file() command also provides COPY and INSTALL signatures:\n"
       "  file(<COPY|INSTALL> files... DESTINATION <dir>\n"
       "       [FILE_PERMISSIONS permissions...]\n"
@@ -259,6 +267,8 @@ protected:
   bool HandleInstallCommand(std::vector<std::string> const& args);
   bool HandleDownloadCommand(std::vector<std::string> const& args);
   bool HandleUploadCommand(std::vector<std::string> const& args);
+
+  bool HandleTimestampCommand(std::vector<std::string> const& args);
 };
 
 
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 0193dc9..7b79e68 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -19,6 +19,8 @@
 #include <ctype.h>
 #include <time.h>
 
+#include <cmTimestamp.h>
+
 //----------------------------------------------------------------------------
 bool cmStringCommand
 ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
@@ -87,6 +89,10 @@ bool cmStringCommand
     {
     return this->HandleFindCommand(args);
     }
+  else if(subCommand == "TIMESTAMP")
+    {
+    return this->HandleTimestampCommand(args);
+    }
 
   std::string e = "does not recognize sub-command "+subCommand;
   this->SetError(e.c_str());
@@ -879,3 +885,51 @@ bool cmStringCommand
   this->Makefile->AddDefinition(variableName.c_str(), &*result.begin());
   return true;
 }
+
+//----------------------------------------------------------------------------
+bool cmStringCommand
+::HandleTimestampCommand(std::vector<std::string> const& args)
+{
+  if(args.size() < 2)
+    {
+    this->SetError("sub-command TIMESTAMP requires at least one argument.");
+    return false;
+    }
+  else if(args.size() > 4)
+    {
+    this->SetError("sub-command TIMESTAMP takes at most three arguments.");
+    return false;
+    }
+
+  int argsIndex = 1;
+
+  const std::string &outputVariable = args[argsIndex++];
+
+  std::string formatString;
+  if(args.size() > argsIndex && args[argsIndex] != "UTC")
+    {
+    formatString = args[argsIndex++];
+    }
+
+  bool utcFlag = false;
+  if(args.size() > argsIndex)
+    {
+    if(args[argsIndex] == "UTC")
+      {
+      utcFlag = true;
+      }
+    else
+      {
+      std::string e = " TIMESTAMP sub-command does not recognize option " +
+          args[argsIndex] + ".";
+      this->SetError(e.c_str());
+      return false;
+      }
+    }
+
+  cmTimestamp timestamp;
+  std::string result = timestamp.CreateTimestamp(formatString, utcFlag);
+  this->Makefile->AddDefinition(outputVariable.c_str(), result.c_str());
+
+  return true;
+}
diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h
index 43a0dbe..3902dc7 100644
--- a/Source/cmStringCommand.h
+++ b/Source/cmStringCommand.h
@@ -93,6 +93,7 @@ public:
       "  string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>]\n"
       "         [RANDOM_SEED <seed>] <output variable>)\n"
       "  string(FIND <string> <substring> <output variable> [REVERSE])\n"
+      "  string(TIMESTAMP <output variable> [<format string>] [UTC])\n"
       "REGEX MATCH will match the regular expression once and store the "
       "match in the output variable.\n"
       "REGEX MATCHALL will match the regular expression as many times as "
@@ -141,7 +142,33 @@ public:
       "   ()        Saves a matched subexpression, which can be referenced \n"
       "             in the REGEX REPLACE operation. Additionally it is saved\n"
       "             by all regular expression-related commands, including \n"
-      "             e.g. if( MATCHES ), in the variables CMAKE_MATCH_(0..9).";
+      "             e.g. if( MATCHES ), in the variables CMAKE_MATCH_(0..9).\n"
+      "TIMESTAMP will write a string representation of "
+      "the current date and/or time to <output variable>.\n"
+      "Should the command be unable to obtain a timestamp "
+      "<output variable> will be set to NOTFOUND.\n"
+      "The optional UTC flag requests the current date/time "
+      "representation to be in Coordinated Universal Time (UTC) "
+      "rather than local time.\n"
+      "The optional <format string> may contain the following "
+      "format specifiers: \n"
+      "   %d        The day of the current month (01-31).\n"
+      "   %H        The hour on a 24-hour clock (00-23).\n"
+      "   %I        The hour on a 12-hour clock (01-12).\n"
+      "   %j        The day of the current year (001-366).\n"
+      "   %m        The month of the current year (01-12).\n"
+      "   %M        The minute of the current hour (00-59).\n"
+      "   %S        The second of the current minute.\n"
+      "             60 represents a leap second. (00-60)\n"
+      "   %U        The week number of the current year (00-53).\n"
+      "   %w        The day of the current week. 0 is Sunday. (0-6)\n"
+      "   %y        The last two digits of the current year (00-99)\n"
+      "   %Y        The current year. \n"
+      "Unknown format specifiers will be ignored "
+      "and copied to the output as-is.\n"
+      "If no explicit <format string> is given it will default to:\n"
+      "   %Y-%m-%dT%H:%M:%S    for local time.\n"
+      "   %Y-%m-%dT%H:%M:%SZ   for UTC.";
     }
 
   cmTypeMacro(cmStringCommand, cmCommand);
@@ -164,6 +191,7 @@ protected:
   bool HandleStripCommand(std::vector<std::string> const& args);
   bool HandleRandomCommand(std::vector<std::string> const& args);
   bool HandleFindCommand(std::vector<std::string> const& args);
+  bool HandleTimestampCommand(std::vector<std::string> const& args);
 
   class RegexReplacement
   {
diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx
new file mode 100644
index 0000000..03e7c01
--- /dev/null
+++ b/Source/cmTimestamp.cxx
@@ -0,0 +1,135 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Kitware, Inc., Insight Software Consortium
+
+  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.
+============================================================================*/
+#include "cmTimestamp.h"
+
+#include <cstring>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+//----------------------------------------------------------------------------
+std::string cmTimestamp::CreateTimestamp(
+  const std::string& formatString, bool utcFlag)
+{
+  const char* notFound = "NOTFOUND";
+
+  std::time_t currentTimeT = std::time(0);
+  if(currentTimeT == std::time_t(-1)) return notFound;
+
+  return CreateTimestampFromTimeT(currentTimeT, formatString, utcFlag);
+}
+
+std::string cmTimestamp::FileModificationTime(const char* path,
+  const std::string& formatString, bool utcFlag)
+{
+#ifdef _WIN32
+  struct _stat info;
+  std::memset(&info, 0, sizeof(info));
+
+  if(_stat(path, &info) != 0)
+    return notFound;
+
+  std::time_t currentTimeT = info.st_mtime;
+#else
+  struct stat info;
+  std::memset(&info, 0, sizeof(info));
+
+  if(stat(path, &info) != 0)
+    return notFound;
+
+  std::time_t currentTimeT = info.st_mtime;
+#endif
+
+  return CreateTimestampFromTimeT(currentTimeT, formatString, utcFlag);
+}
+
+std::string cmTimestamp::CreateTimestampFromTimeT(std::time_t timeT,
+    std::string formatString, bool utcFlag)
+{
+  if(formatString.empty())
+    {
+    formatString = "%Y-%m-%dT%H:%M:%S";
+    if(utcFlag) formatString += "Z";
+    }
+
+  struct std::tm timeStruct;
+  std::memset(&timeStruct, 0, sizeof(std::tm));
+
+  if(utcFlag)
+    {
+    std::tm* ptr = std::gmtime(&timeT);
+    if(ptr == 0) return notFound;
+
+    timeStruct = *ptr;
+    }
+  else
+    {
+    std::tm* ptr = std::localtime(&timeT);
+    if(ptr == 0) return notFound;
+
+    timeStruct = *ptr;
+    }
+
+  std::string result;
+  for(std::string::size_type i = 0; i < formatString.size(); ++i)
+    {
+    char c1 = formatString[i];
+    char c2 = (i+1 < formatString.size()) ?
+      formatString[i+1] : 0;
+
+    if(c1 == '%' && c2 != 0)
+      {
+      result += AddTimestampComponent(c2, timeStruct);
+      ++i;
+      }
+    else
+      {
+      result += c1;
+      }
+    }
+
+  return result;
+}
+
+//----------------------------------------------------------------------------
+std::string cmTimestamp::AddTimestampComponent(char flag, std::tm& timeStruct)
+{
+  std::string formatString = "%";
+  formatString += flag;
+
+  switch(flag)
+    {
+    case 'd':
+    case 'H':
+    case 'I':
+    case 'j':
+    case 'm':
+    case 'M':
+    case 'S':
+    case 'U':
+    case 'w':
+    case 'y':
+    case 'Y':
+      break;
+    default:
+      {
+      return formatString;
+      }
+    }
+
+  char buffer[16];
+
+  std::size_t size = std::strftime(buffer, sizeof(buffer),
+    formatString.c_str(), &timeStruct);
+
+  return std::string(buffer, size);
+}
diff --git a/Source/cmTimestamp.h b/Source/cmTimestamp.h
new file mode 100644
index 0000000..8487b8b
--- /dev/null
+++ b/Source/cmTimestamp.h
@@ -0,0 +1,42 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2012 Kitware, Inc., Insight Software Consortium
+
+  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 cmTimestamp_h
+#define cmTimestamp_h
+
+#include <string>
+#include <ctime>
+
+/** \class cmTimestamp
+ * \brief Utility class to generate sting representation of a timestamp
+ *
+ */
+class cmTimestamp
+{
+public:
+  cmTimestamp():notFound("NOTFOUND") {}
+
+  std::string CreateTimestamp(const std::string& formatString, bool utcFlag);
+
+  std::string FileModificationTime(const char* path,
+    const std::string& formatString, bool utcFlag);
+
+private:
+  std::string CreateTimestampFromTimeT(std::time_t timeT,
+      std::string formatString, bool utcFlag);
+
+  std::string AddTimestampComponent(char flag, std::tm& timeStruct);
+
+  const char* notFound;
+};
+
+
+#endif
diff --git a/Tests/CMakeTests/File-TIMESTAMP-BadArg1.cmake b/Tests/CMakeTests/File-TIMESTAMP-BadArg1.cmake
new file mode 100644
index 0000000..cc15c77
--- /dev/null
+++ b/Tests/CMakeTests/File-TIMESTAMP-BadArg1.cmake
@@ -0,0 +1 @@
+file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
diff --git a/Tests/CMakeTests/File-TIMESTAMP-NoFile.cmake b/Tests/CMakeTests/File-TIMESTAMP-NoFile.cmake
new file mode 100644
index 0000000..62390e7
--- /dev/null
+++ b/Tests/CMakeTests/File-TIMESTAMP-NoFile.cmake
@@ -0,0 +1,2 @@
+file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/DoesNotExist.cmake" output)
+message("~${output}~")
diff --git a/Tests/CMakeTests/File-TIMESTAMP-NotBogus.cmake b/Tests/CMakeTests/File-TIMESTAMP-NotBogus.cmake
new file mode 100644
index 0000000..d0e5fe3
--- /dev/null
+++ b/Tests/CMakeTests/File-TIMESTAMP-NotBogus.cmake
@@ -0,0 +1,24 @@
+set(STAMP_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/FileTimestamp-Stamp")
+set(STAMP_FORMAT "%Y-%m-%d")
+
+string(TIMESTAMP timestamp1 "${STAMP_FORMAT}")
+
+file(WRITE "${STAMP_FILENAME}" "foo")
+file(TIMESTAMP "${STAMP_FILENAME}" timestamp2 "${STAMP_FORMAT}")
+
+string(TIMESTAMP timestamp3 "${STAMP_FORMAT}")
+
+message(STATUS "timestamp1 [${timestamp1}]")
+message(STATUS "timestamp2 [${timestamp2}]")
+message(STATUS "timestamp3 [${timestamp3}]")
+
+if(timestamp1 STREQUAL timestamp3)
+  if(NOT timestamp1 STREQUAL timestamp2)
+    message(FATAL_ERROR
+      "timestamp mismatch [${timestamp1}] != [${timestamp2}]")
+  else()
+    message("all timestamps match")
+  endif()
+else()
+  message(WARNING "this test may race when run at midnight")
+endif()
diff --git a/Tests/CMakeTests/File-TIMESTAMP-Works.cmake b/Tests/CMakeTests/File-TIMESTAMP-Works.cmake
new file mode 100644
index 0000000..4351b19
--- /dev/null
+++ b/Tests/CMakeTests/File-TIMESTAMP-Works.cmake
@@ -0,0 +1,2 @@
+file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" output UTC)
+message("~${output}~")
diff --git a/Tests/CMakeTests/FileTest.cmake.in b/Tests/CMakeTests/FileTest.cmake.in
index 3c3d85d..2e84df5 100644
--- a/Tests/CMakeTests/FileTest.cmake.in
+++ b/Tests/CMakeTests/FileTest.cmake.in
@@ -32,6 +32,14 @@ set(SHA384-Works-RESULT 0)
 set(SHA384-Works-STDERR "1de9560b4e030e02051ea408200ffc55d70c97ac64ebf822461a5c786f495c36df43259b14483bc8d364f0106f4971ee")
 set(SHA512-Works-RESULT 0)
 set(SHA512-Works-STDERR "3982a1b4e651768bec70ab1fb97045cb7a659f4ba7203d501c52ab2e803071f9d5fd272022df15f27727fc67f8cd022e710e29010b2a9c0b467c111e2f6abf51")
+set(TIMESTAMP-NoFile-RESULT 0)
+set(TIMESTAMP-NoFile-STDERR "~NOTFOUND~")
+set(TIMESTAMP-BadArg1-RESULT 1)
+set(TIMESTAMP-BadArg1-STDERR "file sub-command TIMESTAMP requires at least two arguments")
+set(TIMESTAMP-NotBogus-RESULT 0)
+set(TIMESTAMP-NotBogus-STDERR "all timestamps match")
+set(TIMESTAMP-Works-RESULT 0)
+set(TIMESTAMP-Works-STDERR "~[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]Z~")
 
 include("@CMAKE_CURRENT_SOURCE_DIR@/CheckCMakeTest.cmake")
 check_cmake_test(File
@@ -52,6 +60,10 @@ check_cmake_test(File
   SHA256-Works
   SHA384-Works
   SHA512-Works
+  TIMESTAMP-NoFile
+  TIMESTAMP-BadArg1
+  TIMESTAMP-NotBogus
+  TIMESTAMP-Works
   )
 
 # Also execute each test listed in FileTestScript.cmake:
diff --git a/Tests/CMakeTests/String-TIMESTAMP-AllSpecifiers.cmake b/Tests/CMakeTests/String-TIMESTAMP-AllSpecifiers.cmake
new file mode 100644
index 0000000..2d0fcc8
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-AllSpecifiers.cmake
@@ -0,0 +1,11 @@
+string(TIMESTAMP output "%d;%H;%I;%j;%m;%M;%S;%U;%w;%y;%Y")
+message("~${output}~")
+
+list(LENGTH output output_length)
+
+set(expected_output_length 11)
+
+if(NOT output_length EQUAL ${expected_output_length})
+    message(FATAL_ERROR "expected ${expected_output_length} entries in output "
+        "with all specifiers; found ${output_length}")
+endif()
diff --git a/Tests/CMakeTests/String-TIMESTAMP-BadArg1.cmake b/Tests/CMakeTests/String-TIMESTAMP-BadArg1.cmake
new file mode 100644
index 0000000..8f2d9f8
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-BadArg1.cmake
@@ -0,0 +1 @@
+string(TIMESTAMP)
diff --git a/Tests/CMakeTests/String-TIMESTAMP-BadArg2.cmake b/Tests/CMakeTests/String-TIMESTAMP-BadArg2.cmake
new file mode 100644
index 0000000..c1e5126
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-BadArg2.cmake
@@ -0,0 +1 @@
+string(TIMESTAMP output_variable "%Y" UTF)
diff --git a/Tests/CMakeTests/String-TIMESTAMP-BadArg3.cmake b/Tests/CMakeTests/String-TIMESTAMP-BadArg3.cmake
new file mode 100644
index 0000000..3d577df
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-BadArg3.cmake
@@ -0,0 +1 @@
+string(TIMESTAMP output_variable "%Y" UTC UTC)
diff --git a/Tests/CMakeTests/String-TIMESTAMP-CustomFormatLocal.cmake b/Tests/CMakeTests/String-TIMESTAMP-CustomFormatLocal.cmake
new file mode 100644
index 0000000..eab2a45
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-CustomFormatLocal.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output "%S")
+message("~${output}~")
diff --git a/Tests/CMakeTests/String-TIMESTAMP-CustomFormatUTC.cmake b/Tests/CMakeTests/String-TIMESTAMP-CustomFormatUTC.cmake
new file mode 100644
index 0000000..eab2a45
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-CustomFormatUTC.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output "%S")
+message("~${output}~")
diff --git a/Tests/CMakeTests/String-TIMESTAMP-DefaulFormatUTC.cmake b/Tests/CMakeTests/String-TIMESTAMP-DefaulFormatUTC.cmake
new file mode 100644
index 0000000..dad6a8d
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-DefaulFormatUTC.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output UTC)
+message("~${output}~")
diff --git a/Tests/CMakeTests/String-TIMESTAMP-DefaultFormatLocal.cmake b/Tests/CMakeTests/String-TIMESTAMP-DefaultFormatLocal.cmake
new file mode 100644
index 0000000..d7c7dde
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-DefaultFormatLocal.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output)
+message("~${output}~")
diff --git a/Tests/CMakeTests/String-TIMESTAMP-DefaultFormatUTC.cmake b/Tests/CMakeTests/String-TIMESTAMP-DefaultFormatUTC.cmake
new file mode 100644
index 0000000..dad6a8d
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-DefaultFormatUTC.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output UTC)
+message("~${output}~")
diff --git a/Tests/CMakeTests/String-TIMESTAMP-IncompleteSpecifier.cmake b/Tests/CMakeTests/String-TIMESTAMP-IncompleteSpecifier.cmake
new file mode 100644
index 0000000..ffc5656
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-IncompleteSpecifier.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output "foobar%")
+message("~${output}~")
diff --git a/Tests/CMakeTests/String-TIMESTAMP-UnknownSpecifier.cmake b/Tests/CMakeTests/String-TIMESTAMP-UnknownSpecifier.cmake
new file mode 100644
index 0000000..0e145e5
--- /dev/null
+++ b/Tests/CMakeTests/String-TIMESTAMP-UnknownSpecifier.cmake
@@ -0,0 +1,2 @@
+string(TIMESTAMP output "%g")
+message("~${output}~")
diff --git a/Tests/CMakeTests/StringTest.cmake.in b/Tests/CMakeTests/StringTest.cmake.in
index 49e7dc9..a9fe428 100644
--- a/Tests/CMakeTests/StringTest.cmake.in
+++ b/Tests/CMakeTests/StringTest.cmake.in
@@ -16,6 +16,26 @@ set(SHA384-Works-RESULT 0)
 set(SHA384-Works-STDERR "1de9560b4e030e02051ea408200ffc55d70c97ac64ebf822461a5c786f495c36df43259b14483bc8d364f0106f4971ee")
 set(SHA512-Works-RESULT 0)
 set(SHA512-Works-STDERR "3982a1b4e651768bec70ab1fb97045cb7a659f4ba7203d501c52ab2e803071f9d5fd272022df15f27727fc67f8cd022e710e29010b2a9c0b467c111e2f6abf51")
+set(TIMESTAMP-BadArg1-RESULT 1)
+set(TIMESTAMP-BadArg1-STDERR "string sub-command TIMESTAMP requires at least one argument")
+set(TIMESTAMP-BadArg2-RESULT 1)
+set(TIMESTAMP-BadArg2-STDERR "string TIMESTAMP sub-command does not recognize option UTF")
+set(TIMESTAMP-BadArg3-RESULT 1)
+set(TIMESTAMP-BadArg3-STDERR "string sub-command TIMESTAMP takes at most three arguments")
+set(TIMESTAMP-DefaultFormatLocal-RESULT 0)
+set(TIMESTAMP-DefaultFormatLocal-STDERR "~[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]~")
+set(TIMESTAMP-DefaultFormatUTC-RESULT 0)
+set(TIMESTAMP-DefaultFormatUTC-STDERR "~[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]Z~")
+set(TIMESTAMP-CustomFormatLocal-RESULT 0)
+set(TIMESTAMP-CustomFormatLocal-STDERR "~([0-5][0-9])|60~")
+set(TIMESTAMP-CustomFormatUTC-RESULT 0)
+set(TIMESTAMP-CustomFormatUTC-STDERR "~([0-5][0-9])|60~")
+set(TIMESTAMP-UnknownSpecifier-RESULT 0)
+set(TIMESTAMP-UnknownSpecifier-STDERR "~%g~")
+set(TIMESTAMP-IncompleteSpecifier-RESULT 0)
+set(TIMESTAMP-IncompleteSpecifier-STDERR "~foobar%~")
+set(TIMESTAMP-AllSpecifiers-RESULT 0)
+set(TIMESTAMP-AllSpecifiers-STDERR "~[0-9]+(;[0-9]+)*~")
 
 include("@CMAKE_CURRENT_SOURCE_DIR@/CheckCMakeTest.cmake")
 check_cmake_test(String
@@ -28,6 +48,16 @@ check_cmake_test(String
   SHA256-Works
   SHA384-Works
   SHA512-Works
+  TIMESTAMP-BadArg1
+  TIMESTAMP-BadArg2
+  TIMESTAMP-BadArg3
+  TIMESTAMP-DefaultFormatLocal
+  TIMESTAMP-DefaultFormatUTC
+  TIMESTAMP-CustomFormatLocal
+  TIMESTAMP-CustomFormatUTC
+  TIMESTAMP-UnknownSpecifier
+  TIMESTAMP-IncompleteSpecifier
+  TIMESTAMP-AllSpecifiers
   )
 
 # Execute each test listed in StringTestScript.cmake:
-- 
1.7.9.msysgit.0


--------------050408040407090404050409--


More information about the cmake-developers mailing list