[PATCH] new timestamp command

Nils Gladitz gladitz at scivis.de
Thu Sep 27 07:10:52 EDT 2012


---
 Source/cmBootstrapCommands.cxx          |    2 +
 Source/cmTimestampCommand.cxx           |  147 +++++++++++++++++++++++++++++++
 Source/cmTimestampCommand.h             |  107 ++++++++++++++++++++++
 Tests/CMakeTests/CMakeLists.txt         |    1 +
 Tests/CMakeTests/TimestampTest.cmake.in |   60 +++++++++++++
 5 files changed, 317 insertions(+)
 create mode 100644 Source/cmTimestampCommand.cxx
 create mode 100644 Source/cmTimestampCommand.h
 create mode 100644 Tests/CMakeTests/TimestampTest.cmake.in

diff --git a/Source/cmBootstrapCommands.cxx b/Source/cmBootstrapCommands.cxx
index 9097a74..f651d37 100644
--- a/Source/cmBootstrapCommands.cxx
+++ b/Source/cmBootstrapCommands.cxx
@@ -89,6 +89,7 @@
 #include "cmStringCommand.cxx"
 #include "cmSubdirCommand.cxx"
 #include "cmTargetLinkLibrariesCommand.cxx"
+#include "cmTimestampCommand.cxx"
 #include "cmTryCompileCommand.cxx"
 #include "cmTryRunCommand.cxx"
 #include "cmUnsetCommand.cxx"
@@ -164,6 +165,7 @@ void GetBootstrapCommands(std::list<cmCommand*>& commands)
   commands.push_back(new cmStringCommand);
   commands.push_back(new cmSubdirCommand);
   commands.push_back(new cmTargetLinkLibrariesCommand);
+  commands.push_back(new cmTimestampCommand);
   commands.push_back(new cmTryCompileCommand);
   commands.push_back(new cmTryRunCommand);
   commands.push_back(new cmUnsetCommand);
diff --git a/Source/cmTimestampCommand.cxx b/Source/cmTimestampCommand.cxx
new file mode 100644
index 0000000..0405026
--- /dev/null
+++ b/Source/cmTimestampCommand.cxx
@@ -0,0 +1,147 @@
+/*============================================================================
+  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 "cmTimestampCommand.h"
+
+#include <ctime>
+#include <cstring>
+
+//----------------------------------------------------------------------------
+namespace
+{
+  std::string 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);
+  }
+}
+
+//----------------------------------------------------------------------------
+bool cmTimestampCommand
+::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
+{
+  if(args.size() < 1)
+    {
+    this->SetError("must be called with at least one argument.");
+    return false;
+    }
+
+  const std::string &outputVariable = args[0];
+
+  int index = 1;
+
+  std::string formatString;
+  if(args.size() > index && args[index] != "UTC")
+  {
+    formatString = args[index++];
+  }
+
+  bool utcFlag = false;
+  if(args.size() > index)
+  {
+    if(args[index] == "UTC")
+    {
+      utcFlag = true;
+    }
+    else
+    {
+      std::string e = "does not recognize option " + args[index];
+      this->SetError(e.c_str());
+      return false;
+    }
+  }
+
+  if(formatString.empty())
+  {
+  formatString = "%Y-%m-%dT%H:%M:%S";
+  if(utcFlag) formatString += "Z";
+  }
+
+  std::string timestamp = CreateTimestamp(formatString, utcFlag);
+  this->Makefile->AddDefinition(outputVariable.c_str(), timestamp.c_str());
+
+  return true;
+}
+
+//----------------------------------------------------------------------------
+std::string cmTimestampCommand::CreateTimestamp(
+  const std::string& formatString, bool utcFlag)
+{
+  std::string result;
+  const char* notFound = "NOTFOUND";
+
+  std::time_t currentTimeT = std::time(0);
+  if(currentTimeT == std::time_t(-1)) return notFound;
+
+  struct std::tm timeStruct;
+  std::memset(&timeStruct, 0, sizeof(std::tm));
+
+  if(utcFlag)
+  {
+    std::tm* ptr = std::gmtime(&currentTimeT);
+    if(ptr == 0) return notFound;
+
+    timeStruct = *ptr;
+  }
+  else
+  {
+    std::tm* ptr = std::localtime(&currentTimeT);
+    if(ptr == 0) return notFound;
+
+    timeStruct = *ptr;
+  }
+
+  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;
+}
diff --git a/Source/cmTimestampCommand.h b/Source/cmTimestampCommand.h
new file mode 100644
index 0000000..cb9ce51
--- /dev/null
+++ b/Source/cmTimestampCommand.h
@@ -0,0 +1,107 @@
+/*============================================================================
+  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 cmTimestampCommand_h
+#define cmTimestampCommand_h
+
+#include "cmCommand.h"
+
+class cmMakefile;
+namespace cmsys
+{
+  class RegularExpression;
+}
+
+/** \class cmTimestampCommand
+ * \brief Generates a timestamp representing the current date time
+ *
+ */
+class cmTimestampCommand : public cmCommand
+{
+public:
+  /**
+   * This is a virtual constructor for the command.
+   */
+  virtual cmCommand* Clone()
+    {
+    return new cmTimestampCommand;
+    }
+
+  /**
+   * This is called when the command is first encountered in
+   * the CMakeLists.txt file.
+   */
+  virtual bool InitialPass(std::vector<std::string> const& args,
+                           cmExecutionStatus &status);
+
+  /**
+   * This determines if the command is invoked when in script mode.
+   */
+  virtual bool IsScriptable() const { return true; }
+
+  /**
+   * The name of the command as specified in CMakeList.txt.
+   */
+  virtual const char* GetName() const { return "timestamp";}
+
+  /**
+   * Succinct documentation.
+   */
+  virtual const char* GetTerseDocumentation() const
+    {
+  return "Creates timestamps.";
+    }
+
+  /**
+   * More documentation.
+   */
+  virtual const char* GetFullDocumentation() const
+    {
+    return
+      "  timestamp(<output variable> [<format string>] [UTC])\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(cmTimestampCommand, cmCommand);
+
+private:
+  static std::string CreateTimestamp(
+  const std::string& formatString, bool utcFlag);
+
+};
+
+
+#endif
diff --git a/Tests/CMakeTests/CMakeLists.txt b/Tests/CMakeTests/CMakeLists.txt
index d34d4a6..e182e02 100644
--- a/Tests/CMakeTests/CMakeLists.txt
+++ b/Tests/CMakeTests/CMakeLists.txt
@@ -25,6 +25,7 @@ AddCMakeTest(ModuleNotices "")
 AddCMakeTest(GetProperty "")
 AddCMakeTest(If "")
 AddCMakeTest(String "")
+AddCMakeTest(Timestamp "")
 AddCMakeTest(Math "")
 AddCMakeTest(CMakeMinimumRequired "")
 AddCMakeTest(CompilerIdVendor "")
diff --git a/Tests/CMakeTests/TimestampTest.cmake.in b/Tests/CMakeTests/TimestampTest.cmake.in
new file mode 100644
index 0000000..0ff6e2e
--- /dev/null
+++ b/Tests/CMakeTests/TimestampTest.cmake.in
@@ -0,0 +1,60 @@
+set(LOCAL_DEFAULT_TIMESTAMP_REGEX 
+	"[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]")
+set(UTC_DEFAULT_TIMESTAMP_REGEX 
+	"[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]Z")
+
+set(CUSTOM_FORMAT_WITH_SECOND_REGEX "([0-5][0-9])|60")
+
+macro(timestamp_require_match actual required)
+	if(NOT "${${actual}}" MATCHES "^${${required}}$")
+		message(FATAL_ERROR
+			"${actual}=[${${actual}}] does not match ${required}=[${${required}}]") 
+	else()
+		message(STATUS
+			"${actual}=[${${actual}}] matches ${required}=[${${required}}]") 
+	endif()
+endmacro()
+	
+
+timestamp(LOCAL_DEFAULT_TIMESTAMP)
+timestamp_require_match(LOCAL_DEFAULT_TIMESTAMP LOCAL_DEFAULT_TIMESTAMP_REGEX)
+
+timestamp(UTC_DEFAULT_TIMESTAMP UTC)
+timestamp_require_match(UTC_DEFAULT_TIMESTAMP UTC_DEFAULT_TIMESTAMP_REGEX)
+
+timestamp(LOCAL_CUSTOM_FORMAT "%S")
+timestamp_require_match(LOCAL_CUSTOM_FORMAT CUSTOM_FORMAT_WITH_SECOND_REGEX)
+	
+timestamp(UTC_CUSTOM_FORMAT "%S" UTC)
+timestamp_require_match(UTC_CUSTOM_FORMAT CUSTOM_FORMAT_WITH_SECOND_REGEX)
+
+timestamp(UNKNOWN_SPECIFIER "%g")
+if(NOT "${UNKNOWN_SPECIFIER}" STREQUAL "%g")
+	message(FATAL_ERROR "unknown specifier %g should have been 
+		copied as-is. UNKNOWN_SPECIFIER=[${UNKNOWN_SPECIFIER}]")
+endif()
+
+timestamp(INCOMPLETE_SPECIFIER "foobar%")
+if(NOT "${INCOMPLETE_SPECIFIER}" STREQUAL "foobar%")
+	message(FATAL_ERROR "incomplete specifier should have been copied as-is.
+		INCOMPLETE_SPECIFIER=[${INCOMPLETE_SPECIFIER}]")
+endif()
+
+timestamp(TIMESTAMP_WITH_ALL_SPECIFIERS "%d;%H;%I;%j;%m;%M;%S;%U;%w;%y;%Y")
+message(STATUS 
+	"TIMESTAMP_WITH_ALL_SPECIFIERS=[${TIMESTAMP_WITH_ALL_SPECIFIERS}]")
+
+list(LENGTH TIMESTAMP_WITH_ALL_SPECIFIERS TIMESTAMP_WITH_ALL_SPECIFIERS_LENGTH)
+
+set(EXPECTED_LIST_LENGTH 11)
+if(NOT TIMESTAMP_WITH_ALL_SPECIFIERS_LENGTH EQUAL ${EXPECTED_LIST_LENGTH})
+	message(FATAL_ERROR "expected ${EXPECTED_LIST_LENGTH} entries in output "
+		"with all specifiers; found ${TIMESTAMP_WITH_ALL_SPECIFIERS_LENGTH}")
+endif()
+
+foreach(LIST_ENTRY IN LISTS TIMESTAMP_WITH_ALL_SPECIFIERS)
+	message(STATUS "evaluating sanity of list entry: [${LIST_ENTRY}]")
+	if(NOT LIST_ENTRY MATCHES "^[0-9]+$")
+		message(FATAL_ERROR "expected decimal digits; found [${LIST_ENTRY}]")
+	endif()
+endforeach()
-- 
1.7.9.5


--------------080006030509000102050501--


More information about the cmake-developers mailing list