Index: include/llvm/Support/VersionTuple.h =================================================================== --- /dev/null +++ include/llvm/Support/VersionTuple.h @@ -0,0 +1,154 @@ +//===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the llvm::VersionTuple class, which represents a version in +/// the form major[.minor[.subminor]]. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_SUPPORT_VERSIONTUPLE_H +#define LLVM_SUPPORT_VERSIONTUPLE_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +namespace llvm { + +/// Represents a version number in the form major[.minor[.subminor[.build]]]. +class VersionTuple { + unsigned Major : 32; + + unsigned Minor : 31; + unsigned HasMinor : 1; + + unsigned Subminor : 31; + unsigned HasSubminor : 1; + + unsigned Build : 31; + unsigned HasBuild : 1; + +public: + VersionTuple() + : Major(0), Minor(0), HasMinor(false), Subminor(0), HasSubminor(false), + Build(0), HasBuild(false) {} + + explicit VersionTuple(unsigned Major) + : Major(Major), Minor(0), HasMinor(false), Subminor(0), + HasSubminor(false), Build(0), HasBuild(false) {} + + explicit VersionTuple(unsigned Major, unsigned Minor) + : Major(Major), Minor(Minor), HasMinor(true), Subminor(0), + HasSubminor(false), Build(0), HasBuild(false) {} + + explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor) + : Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor), + HasSubminor(true), Build(0), HasBuild(false) {} + + explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor, + unsigned Build) + : Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor), + HasSubminor(true), Build(Build), HasBuild(true) {} + + /// Determine whether this version information is empty + /// (e.g., all version components are zero). + bool empty() const { + return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0; + } + + /// Retrieve the major version number. + unsigned getMajor() const { return Major; } + + /// Retrieve the minor version number, if provided. + Optional getMinor() const { + if (!HasMinor) + return None; + return Minor; + } + + /// Retrieve the subminor version number, if provided. + Optional getSubminor() const { + if (!HasSubminor) + return None; + return Subminor; + } + + /// Retrieve the build version number, if provided. + Optional getBuild() const { + if (!HasBuild) + return None; + return Build; + } + + /// Determine if two version numbers are equivalent. If not + /// provided, minor and subminor version numbers are considered to be zero. + friend bool operator==(const VersionTuple &X, const VersionTuple &Y) { + return X.Major == Y.Major && X.Minor == Y.Minor && + X.Subminor == Y.Subminor && X.Build == Y.Build; + } + + /// Determine if two version numbers are not equivalent. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) { + return !(X == Y); + } + + /// Determine whether one version number precedes another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator<(const VersionTuple &X, const VersionTuple &Y) { + return std::tie(X.Major, X.Minor, X.Subminor, X.Build) < + std::tie(Y.Major, Y.Minor, Y.Subminor, Y.Build); + } + + /// Determine whether one version number follows another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator>(const VersionTuple &X, const VersionTuple &Y) { + return Y < X; + } + + /// Determine whether one version number precedes or is + /// equivalent to another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) { + return !(Y < X); + } + + /// Determine whether one version number follows or is + /// equivalent to another. + /// + /// If not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) { + return !(X < Y); + } + + /// Retrieve a string representation of the version number. + std::string getAsString() const; + + /// Try to parse the given string as a version number. + /// \returns \c true if the string does not match the regular expression + /// [0-9]+(\.[0-9]+){0,3} + bool tryParse(StringRef string); +}; + +/// Print a version number. +raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V); + +} // end namespace llvm +#endif // LLVM_SUPPORT_VERSIONTUPLE_H Index: lib/Support/CMakeLists.txt =================================================================== --- lib/Support/CMakeLists.txt +++ lib/Support/CMakeLists.txt @@ -121,6 +121,7 @@ Twine.cpp Unicode.cpp UnicodeCaseFold.cpp + VersionTuple.cpp WithColor.cpp YAMLParser.cpp YAMLTraits.cpp Index: lib/Support/VersionTuple.cpp =================================================================== --- /dev/null +++ lib/Support/VersionTuple.cpp @@ -0,0 +1,110 @@ +//===- VersionTuple.cpp - Version Number Handling ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the VersionTuple class, which represents a version in +// the form major[.minor[.subminor]]. +// +//===----------------------------------------------------------------------===// +#include "llvm/Support/VersionTuple.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +std::string VersionTuple::getAsString() const { + std::string Result; + { + llvm::raw_string_ostream Out(Result); + Out << *this; + } + return Result; +} + +raw_ostream &llvm::operator<<(raw_ostream &Out, const VersionTuple &V) { + Out << V.getMajor(); + if (Optional Minor = V.getMinor()) + Out << '.' << *Minor; + if (Optional Subminor = V.getSubminor()) + Out << '.' << *Subminor; + if (Optional Build = V.getBuild()) + Out << '.' << *Build; + return Out; +} + +static bool parseInt(StringRef &input, unsigned &value) { + assert(value == 0); + if (input.empty()) + return true; + + char next = input[0]; + input = input.substr(1); + if (next < '0' || next > '9') + return true; + value = (unsigned)(next - '0'); + + while (!input.empty()) { + next = input[0]; + if (next < '0' || next > '9') + return false; + input = input.substr(1); + value = value * 10 + (unsigned)(next - '0'); + } + + return false; +} + +bool VersionTuple::tryParse(StringRef input) { + unsigned major = 0, minor = 0, micro = 0, build = 0; + + // Parse the major version, [0-9]+ + if (parseInt(input, major)) + return true; + + if (input.empty()) { + *this = VersionTuple(major); + return false; + } + + // If we're not done, parse the minor version, \.[0-9]+ + if (input[0] != '.') + return true; + input = input.substr(1); + if (parseInt(input, minor)) + return true; + + if (input.empty()) { + *this = VersionTuple(major, minor); + return false; + } + + // If we're not done, parse the micro version, \.[0-9]+ + if (input[0] != '.') + return true; + input = input.substr(1); + if (parseInt(input, micro)) + return true; + + if (input.empty()) { + *this = VersionTuple(major, minor, micro); + return false; + } + + // If we're not done, parse the micro version, \.[0-9]+ + if (input[0] != '.') + return true; + input = input.substr(1); + if (parseInt(input, build)) + return true; + + // If we have characters left over, it's an error. + if (!input.empty()) + return true; + + *this = VersionTuple(major, minor, micro, build); + return false; +} Index: unittests/Support/CMakeLists.txt =================================================================== --- unittests/Support/CMakeLists.txt +++ unittests/Support/CMakeLists.txt @@ -61,6 +61,7 @@ TrailingObjectsTest.cpp TrigramIndexTest.cpp UnicodeTest.cpp + VersionTupleTest.cpp YAMLIOTest.cpp YAMLParserTest.cpp formatted_raw_ostream_test.cpp Index: unittests/Support/VersionTupleTest.cpp =================================================================== --- /dev/null +++ unittests/Support/VersionTupleTest.cpp @@ -0,0 +1,50 @@ +//===- VersionTupleTests.cpp - Version Number Handling Tests --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/VersionTuple.h" +#include "gtest/gtest.h" + +using namespace llvm; + +TEST(VersionTuple, getAsString) { + EXPECT_EQ("0", VersionTuple().getAsString()); + EXPECT_EQ("1", VersionTuple(1).getAsString()); + EXPECT_EQ("1.2", VersionTuple(1, 2).getAsString()); + EXPECT_EQ("1.2.3", VersionTuple(1, 2, 3).getAsString()); + EXPECT_EQ("1.2.3.4", VersionTuple(1, 2, 3, 4).getAsString()); +} + +TEST(VersionTuple, tryParse) { + VersionTuple VT; + + EXPECT_FALSE(VT.tryParse("1")); + EXPECT_EQ("1", VT.getAsString()); + + EXPECT_FALSE(VT.tryParse("1.2")); + EXPECT_EQ("1.2", VT.getAsString()); + + EXPECT_FALSE(VT.tryParse("1.2.3")); + EXPECT_EQ("1.2.3", VT.getAsString()); + + EXPECT_FALSE(VT.tryParse("1.2.3.4")); + EXPECT_EQ("1.2.3.4", VT.getAsString()); + + EXPECT_TRUE(VT.tryParse("")); + EXPECT_TRUE(VT.tryParse("1.")); + EXPECT_TRUE(VT.tryParse("1.2.")); + EXPECT_TRUE(VT.tryParse("1.2.3.")); + EXPECT_TRUE(VT.tryParse("1.2.3.4.")); + EXPECT_TRUE(VT.tryParse("1.2.3.4.5")); + EXPECT_TRUE(VT.tryParse("1-2")); + EXPECT_TRUE(VT.tryParse("1+2")); + EXPECT_TRUE(VT.tryParse(".1")); + EXPECT_TRUE(VT.tryParse(" 1")); + EXPECT_TRUE(VT.tryParse("1 ")); + EXPECT_TRUE(VT.tryParse(".")); +}