StringUuid: Implement new string(UUID) sub-command.

This commit is contained in:
Nils Gladitz 2014-08-25 22:44:06 +02:00
parent c2a47a9ac3
commit 328e869433
26 changed files with 478 additions and 0 deletions

View File

@ -36,6 +36,8 @@ String operations.
string(TIMESTAMP <output variable> [<format string>] [UTC]) string(TIMESTAMP <output variable> [<format string>] [UTC])
string(MAKE_C_IDENTIFIER <input string> <output variable>) string(MAKE_C_IDENTIFIER <input string> <output variable>)
string(GENEX_STRIP <input string> <output variable>) string(GENEX_STRIP <input string> <output variable>)
string(UUID <output variable> NAMESPACE <namespace> NAME <name>
TYPE <MD5|SHA1> <UPPER>)
REGEX MATCH will match the regular expression once and store the match REGEX MATCH will match the regular expression once and store the match
in the output variable. in the output variable.
@ -159,3 +161,13 @@ identifier in C.
``GENEX_STRIP`` will strip any ``GENEX_STRIP`` will strip any
:manual:`generator expressions <cmake-generator-expressions(7)>` from the :manual:`generator expressions <cmake-generator-expressions(7)>` from the
``input string`` and store the result in the ``output variable``. ``input string`` and store the result in the ``output variable``.
UUID creates a univerally unique identifier (aka GUID) as per RFC4122
based on the hash of the combined values of <namespace>
(which itself has to be a valid UUID) and <name>.
The hash algorithm can be either ``MD5`` (Version 3 UUID) or
``SHA1`` (Version 5 UUID).
A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``
where each `x` represents a lower case hexadecimal character.
Where required an uppercase representation can be requested
with the optional ``UPPER`` flag.

View File

@ -308,6 +308,7 @@ set(SRCS
cmTest.h cmTest.h
cmTestGenerator.cxx cmTestGenerator.cxx
cmTestGenerator.h cmTestGenerator.h
cmUuid.cxx
cmVariableWatch.cxx cmVariableWatch.cxx
cmVariableWatch.h cmVariableWatch.h
cmVersion.cxx cmVersion.cxx

View File

@ -20,6 +20,7 @@
#include <time.h> #include <time.h>
#include <cmTimestamp.h> #include <cmTimestamp.h>
#include <cmUuid.h>
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
bool cmStringCommand bool cmStringCommand
@ -105,6 +106,10 @@ bool cmStringCommand
{ {
return this->HandleGenexStripCommand(args); return this->HandleGenexStripCommand(args);
} }
else if(subCommand == "UUID")
{
return this->HandleUuidCommand(args);
}
std::string e = "does not recognize sub-command "+subCommand; std::string e = "does not recognize sub-command "+subCommand;
this->SetError(e); this->SetError(e);
@ -981,3 +986,114 @@ bool cmStringCommand
return true; return true;
} }
bool cmStringCommand
::HandleUuidCommand(std::vector<std::string> const& args)
{
#if defined(CMAKE_BUILD_WITH_CMAKE)
unsigned int argsIndex = 1;
if(args.size() < 2)
{
this->SetError("UUID sub-command requires an output variable.");
return false;
}
const std::string &outputVariable = args[argsIndex++];
std::string uuidNamespaceString;
std::string uuidName;
std::string uuidType;
bool uuidUpperCase = false;
while(args.size() > argsIndex)
{
if(args[argsIndex] == "NAMESPACE")
{
++argsIndex;
if(argsIndex >= args.size())
{
this->SetError("UUID sub-command, NAMESPACE requires a value.");
return false;
}
uuidNamespaceString = args[argsIndex++];
}
else if(args[argsIndex] == "NAME")
{
++argsIndex;
if(argsIndex >= args.size())
{
this->SetError("UUID sub-command, NAME requires a value.");
return false;
}
uuidName = args[argsIndex++];
}
else if(args[argsIndex] == "TYPE")
{
++argsIndex;
if(argsIndex >= args.size())
{
this->SetError("UUID sub-command, TYPE requires a value.");
return false;
}
uuidType = args[argsIndex++];
}
else if(args[argsIndex] == "UPPER")
{
++argsIndex;
uuidUpperCase = true;
}
else
{
std::string e = "UUID sub-command does not recognize option " +
args[argsIndex] + ".";
this->SetError(e);
return false;
}
}
std::string uuid;
cmUuid uuidGenerator;
std::vector<unsigned char> uuidNamespace;
if(!uuidGenerator.StringToBinary(uuidNamespaceString, uuidNamespace))
{
this->SetError("UUID sub-command, malformed NAMESPACE UUID.");
return false;
}
if(uuidType == "MD5")
{
uuid = uuidGenerator.FromMd5(uuidNamespace, uuidName);
}
else if(uuidType == "SHA1")
{
uuid = uuidGenerator.FromSha1(uuidNamespace, uuidName);
}
else
{
std::string e = "UUID sub-command, unknown TYPE '" + uuidType + "'.";
this->SetError(e);
return false;
}
if(uuid.empty())
{
this->SetError("UUID sub-command, generation failed.");
return false;
}
if(uuidUpperCase)
{
uuid = cmSystemTools::UpperCase(uuid);
}
this->Makefile->AddDefinition(outputVariable, uuid.c_str());
return true;
#else
cmOStringStream e;
e << args[0] << " not available during bootstrap";
this->SetError(e.str().c_str());
return false;
#endif
}

View File

@ -74,6 +74,7 @@ protected:
bool HandleTimestampCommand(std::vector<std::string> const& args); bool HandleTimestampCommand(std::vector<std::string> const& args);
bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args); bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args);
bool HandleGenexStripCommand(std::vector<std::string> const& args); bool HandleGenexStripCommand(std::vector<std::string> const& args);
bool HandleUuidCommand(std::vector<std::string> const& args);
class RegexReplacement class RegexReplacement
{ {

214
Source/cmUuid.cxx Normal file
View File

@ -0,0 +1,214 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2014 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 "cmUuid.h"
#include <string.h>
#include <cmsys/MD5.h>
#include "cm_sha2.h"
cmUuid::cmUuid()
{
Groups.push_back(4);
Groups.push_back(2);
Groups.push_back(2);
Groups.push_back(2);
Groups.push_back(6);
}
std::string cmUuid::FromMd5(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const
{
std::vector<unsigned char> hashInput;
this->CreateHashInput(uuidNamespace, name, hashInput);
cmsysMD5_s *md5 = cmsysMD5_New();
cmsysMD5_Initialize(md5);
cmsysMD5_Append(md5, &hashInput[0], int(hashInput.size()));
unsigned char digest[16] = {0};
cmsysMD5_Finalize(md5, digest);
cmsysMD5_Delete(md5);
return this->FromDigest(digest, 3);
}
std::string cmUuid::FromSha1(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const
{
std::vector<unsigned char> hashInput;
this->CreateHashInput(uuidNamespace, name, hashInput);
SHA_CTX *sha = new SHA_CTX;
SHA1_Init(sha);
SHA1_Update(sha, &hashInput[0], hashInput.size());
unsigned char digest[SHA1_DIGEST_LENGTH] = {0};
SHA1_Final(digest, sha);
delete sha;
return this->FromDigest(digest, 5);
}
void cmUuid::CreateHashInput(std::vector<unsigned char> const& uuidNamespace,
std::string const& name, std::vector<unsigned char> &output) const
{
output = uuidNamespace;
if(name.size())
{
output.resize(output.size() + name.size());
memcpy(&output[0] + uuidNamespace.size(),
name.c_str(), name.size());
}
}
std::string cmUuid::FromDigest(
const unsigned char* digest, unsigned char version) const
{
typedef unsigned char byte_t;
byte_t uuid[16] = {0};
memcpy(uuid, digest, 16);
uuid[6] &= 0xF;
uuid[6] |= byte_t(version << 4);
uuid[8] &= 0x3F;
uuid[8] |= 0x80;
return this->BinaryToString(uuid);
}
bool cmUuid::StringToBinary(std::string const& input,
std::vector<unsigned char> &output) const
{
output.clear();
output.reserve(16);
if(input.length() != 36)
{
return false;
}
size_t index = 0;
for(size_t i = 0; i < this->Groups.size(); ++i)
{
if(i != 0 && input[index++] != '-')
{
return false;
}
size_t digits = this->Groups[i] * 2;
if(!StringToBinaryImpl(input.substr(index, digits), output))
{
return false;
}
index += digits;
}
return true;
}
std::string cmUuid::BinaryToString(const unsigned char* input) const
{
std::string output;
size_t inputIndex = 0;
for(size_t i = 0; i < this->Groups.size(); ++i)
{
if(i != 0)
{
output += '-';
}
size_t bytes = this->Groups[i];
for(size_t j = 0; j < bytes; ++j)
{
unsigned char byte = input[inputIndex++];
output += this->ByteToHex(byte);
}
}
return output;
}
std::string cmUuid::ByteToHex(unsigned char byte) const
{
std::string result;
for(int i = 0; i < 2; ++i)
{
unsigned char rest = byte % 16;
byte /= 16;
char c = (rest < 0xA) ?
char('0' + rest) :
char('a' + (rest - 0xA));
result = c + result;
}
return result;
}
bool cmUuid::StringToBinaryImpl(std::string const& input,
std::vector<unsigned char> &output) const
{
if(input.size()%2)
{
return false;
}
for(size_t i = 0; i < input.size(); i +=2)
{
char c1 = 0;
if(!IntFromHexDigit(input[i], c1))
{
return false;
}
char c2 = 0;
if(!IntFromHexDigit(input[i + 1], c2))
{
return false;
}
output.push_back(char(c1 << 4 | c2));
}
return true;
}
bool cmUuid::IntFromHexDigit(char input, char& output) const
{
if(input >= '0' && input <= '9')
{
output = char(input - '0');
return true;
}
else if(input >= 'a' && input <= 'f')
{
output = char(input - 'a' + 0xA);
return true;
}
else if(input >= 'A' && input <= 'F')
{
output = char(input - 'A' + 0xA);
return true;
}
else
{
return false;
}
}

55
Source/cmUuid.h Normal file
View File

@ -0,0 +1,55 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2014 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 cmUuid_h
#define cmUuid_h
#include "cmStandardIncludes.h"
/** \class cmUuid
* \brief Utility class to generate UUIDs as defined by RFC4122
*
*/
class cmUuid
{
public:
cmUuid();
std::string FromMd5(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const;
std::string FromSha1(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const;
bool StringToBinary(std::string const& input,
std::vector<unsigned char> &output) const;
private:
std::string ByteToHex(unsigned char byte) const;
void CreateHashInput(std::vector<unsigned char> const& uuidNamespace,
std::string const& name, std::vector<unsigned char> &output) const;
std::string FromDigest(const unsigned char* digest,
unsigned char version) const;
bool StringToBinaryImpl(std::string const& input,
std::vector<unsigned char> &output) const;
std::string BinaryToString(const unsigned char* input) const;
bool IntFromHexDigit(char input, char& output) const;
std::vector<int> Groups;
};
#endif

View File

@ -2,3 +2,11 @@ include(RunCMake)
run_cmake(Concat) run_cmake(Concat)
run_cmake(ConcatNoArgs) run_cmake(ConcatNoArgs)
run_cmake(Uuid)
run_cmake(UuidMissingNamespace)
run_cmake(UuidMissingNamespaceValue)
run_cmake(UuidBadNamespace)
run_cmake(UuidMissingNameValue)
run_cmake(UuidMissingTypeValue)
run_cmake(UuidBadType)

View File

@ -0,0 +1,17 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE MD5)
if(NOT WWW_EXAMPLE_COM_MD5_UUID STREQUAL "5df41881-3aed-3515-88a7-2f4a814cf09e")
message(SEND_ERROR
"UUID did not create the expected MD5 result: ${WWW_EXAMPLE_COM_MD5_UUID}")
endif()
string(UUID WWW_EXAMPLE_COM_SHA1_UUID
NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE SHA1 UPPER)
if(NOT WWW_EXAMPLE_COM_SHA1_UUID STREQUAL "2ED6657D-E927-568B-95E1-2665A8AEA6A2")
message(SEND_ERROR
"UUID did not create the expected SHA1 result: ${WWW_EXAMPLE_COM_SHA1_UUID}")
endif()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at UuidBadNamespace.cmake:3 \(string\):
string UUID sub-command, malformed NAMESPACE UUID.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-fooo-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE MD5)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at UuidBadType.cmake:3 \(string\):
string UUID sub-command, unknown TYPE 'FOO'.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE FOO)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at UuidMissingNameValue.cmake:3 \(string\):
string UUID sub-command, NAME requires a value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAMESPACE ${UUID_DNS_NAMESPACE} TYPE MD5 NAME)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at UuidMissingNamespace.cmake:3 \(string\):
string UUID sub-command, malformed NAMESPACE UUID.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAME www.example.com TYPE MD5)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at UuidMissingNamespaceValue.cmake:3 \(string\):
string UUID sub-command, NAMESPACE requires a value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAME www.example.com TYPE MD5 NAMESPACE)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at UuidMissingTypeValue.cmake:3 \(string\):
string UUID sub-command, TYPE requires a value.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID WWW_EXAMPLE_COM_MD5_UUID
NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE)