CMake: Add TIMESTAMP subcommand to string and file commands

This commit is contained in:
Nils Gladitz 2012-10-05 21:55:07 +02:00 committed by David Cole
parent 2bb2745fa8
commit 711e2b3b5c
24 changed files with 424 additions and 1 deletions

View File

@ -89,6 +89,7 @@
#include "cmStringCommand.cxx"
#include "cmSubdirCommand.cxx"
#include "cmTargetLinkLibrariesCommand.cxx"
#include "cmTimestamp.cxx"
#include "cmTryCompileCommand.cxx"
#include "cmTryRunCommand.cxx"
#include "cmUnsetCommand.cxx"

View File

@ -17,6 +17,8 @@
#include "cmFileTimeComparison.h"
#include "cmCryptoHash.h"
#include "cmTimestamp.h"
#if defined(CMAKE_BUILD_WITH_CMAKE)
#include "cm_curl.h"
#endif
@ -161,6 +163,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());
@ -3241,3 +3247,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;
}

View File

@ -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. (If the file is a build input, use "
@ -200,6 +202,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 the empty string \"\".\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"
@ -260,6 +268,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);
};

View File

@ -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.CurrentTime(formatString, utcFlag);
this->Makefile->AddDefinition(outputVariable.c_str(), result.c_str());
return true;
}

View File

@ -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 "
@ -142,7 +143,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 the empty string \"\".\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);
@ -165,6 +192,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
{

134
Source/cmTimestamp.cxx Normal file
View File

@ -0,0 +1,134 @@
/*============================================================================
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::CurrentTime(
const std::string& formatString, bool utcFlag)
{
time_t currentTimeT = time(0);
if(currentTimeT == time_t(-1)) return std::string();
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 std::string();
time_t currentTimeT = info.st_mtime;
#else
struct stat info;
std::memset(&info, 0, sizeof(info));
if(stat(path, &info) != 0)
return std::string();
time_t currentTimeT = info.st_mtime;
#endif
return CreateTimestampFromTimeT(currentTimeT, formatString, utcFlag);
}
std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,
std::string formatString, bool utcFlag)
{
if(formatString.empty())
{
formatString = "%Y-%m-%dT%H:%M:%S";
if(utcFlag) formatString += "Z";
}
struct tm timeStruct;
std::memset(&timeStruct, 0, sizeof(timeStruct));
if(utcFlag)
{
tm* ptr = gmtime(&timeT);
if(ptr == 0) return std::string();
timeStruct = *ptr;
}
else
{
struct tm* ptr = localtime(&timeT);
if(ptr == 0) return std::string();
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, struct 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];
size_t size = strftime(buffer, sizeof(buffer),
formatString.c_str(), &timeStruct);
return std::string(buffer, size);
}

40
Source/cmTimestamp.h Normal file
View File

@ -0,0 +1,40 @@
/*============================================================================
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 <time.h>
/** \class cmTimestamp
* \brief Utility class to generate sting representation of a timestamp
*
*/
class cmTimestamp
{
public:
cmTimestamp() {}
std::string CurrentTime(const std::string& formatString, bool utcFlag);
std::string FileModificationTime(const char* path,
const std::string& formatString, bool utcFlag);
private:
std::string CreateTimestampFromTimeT(time_t timeT,
std::string formatString, bool utcFlag);
std::string AddTimestampComponent(char flag, struct tm& timeStruct);
};
#endif

View File

@ -0,0 +1 @@
file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")

View File

@ -0,0 +1,2 @@
file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/DoesNotExist.cmake" output)
message("~${output}~")

View File

@ -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()

View File

@ -0,0 +1,2 @@
file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" output UTC)
message("~${output}~")

View File

@ -36,6 +36,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 "~~")
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
@ -58,6 +66,10 @@ check_cmake_test(File
SHA256-Works
SHA384-Works
SHA512-Works
TIMESTAMP-NoFile
TIMESTAMP-BadArg1
TIMESTAMP-NotBogus
TIMESTAMP-Works
)
file(GLOB hum)

View File

@ -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()

View File

@ -0,0 +1 @@
string(TIMESTAMP)

View File

@ -0,0 +1 @@
string(TIMESTAMP output_variable "%Y" UTF)

View File

@ -0,0 +1 @@
string(TIMESTAMP output_variable "%Y" UTC UTC)

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output "%S")
message("~${output}~")

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output "%S")
message("~${output}~")

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output UTC)
message("~${output}~")

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output)
message("~${output}~")

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output UTC)
message("~${output}~")

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output "foobar%")
message("~${output}~")

View File

@ -0,0 +1,2 @@
string(TIMESTAMP output "%g")
message("~${output}~")

View File

@ -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: