From 994262e5cc05abfb96da9af38f8d84988405ea5b Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 10 Sep 2008 10:11:48 -0400 Subject: ENH: Improve find_package version numbering Make the number of version components specified explicitly available. Set variables for unspecified version components to "0" instead of leaving them unset. This simplifies version number handling for find- and config-modules. Also support a fourth "tweak" version component since some packages use them. --- Modules/readme.txt | 19 ++-- Source/cmFindPackageCommand.cxx | 151 +++++++++++---------------- Source/cmFindPackageCommand.h | 2 + Tests/FindPackageTest/CMakeLists.txt | 1 + Tests/FindPackageTest/FindVersionTestA.cmake | 18 ++-- Tests/FindPackageTest/FindVersionTestB.cmake | 12 ++- Tests/FindPackageTest/FindVersionTestC.cmake | 6 ++ Tests/FindPackageTest/FindVersionTestD.cmake | 18 ++++ 8 files changed, 123 insertions(+), 104 deletions(-) create mode 100644 Tests/FindPackageTest/FindVersionTestD.cmake diff --git a/Modules/readme.txt b/Modules/readme.txt index 8d4615a..8e4474e 100644 --- a/Modules/readme.txt +++ b/Modules/readme.txt @@ -67,20 +67,25 @@ line. A FindXXX.cmake module will typically be loaded by the command - FIND_PACKAGE(XXX [major[.minor[.patch]]] [EXACT] + FIND_PACKAGE(XXX [major[.minor[.patch[.tweak]]]] [EXACT] [QUIET] [REQUIRED [components...]]) If any version numbers are given to the command it will set the -variable XXX_FIND_VERSION to contain the whole version. The variables -XXX_FIND_VERSION_MAJOR, XXX_FIND_VERSION_MINOR, and -XXX_FIND_VERSION_PATCH will be set to contain the corresponding -portions of the version number. The variable XXX_FIND_VERSION_EXACT -will indicate whether an exact version is requested. +following variables before loading the module: + + XXX_FIND_VERSION = full requested version string + XXX_FIND_VERSION_MAJOR = major version if requested, else 0 + XXX_FIND_VERSION_MINOR = minor version if requested, else 0 + XXX_FIND_VERSION_PATCH = patch version if requested, else 0 + XXX_FIND_VERSION_TWEAK = tweak version if requested, else 0 + XXX_FIND_VERSION_COUNT = number of version components, 0 to 4 + XXX_FIND_VERSION_EXACT = true if EXACT option was given + If the find module supports versioning it should locate a version of the package that is compatible with the version requested. If a compatible version of the package cannot be found the module should not report success. The version of the package found should be stored -in the version variables named above. +in "XXX_VERSION..." version variables docmented by the module. If the QUIET option is given to the command it will set the variable XXX_FIND_QUIETLY to true before loading the FindXXX.cmake module. If diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index d60acea..1594fcf 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -67,14 +67,16 @@ cmFindPackageCommand::cmFindPackageCommand() this->VersionMajor = 0; this->VersionMinor = 0; this->VersionPatch = 0; + this->VersionTweak = 0; this->VersionCount = 0; this->VersionExact = false; this->VersionFoundMajor = 0; this->VersionFoundMinor = 0; this->VersionFoundPatch = 0; + this->VersionFoundTweak = 0; this->VersionFoundCount = 0; this->CommandDocumentation = - " find_package( [major[.minor[.patch]]] [EXACT] [QUIET]\n" + " find_package( [version] [EXACT] [QUIET]\n" " [[REQUIRED|COMPONENTS] [components...]])\n" "Finds and loads settings from an external project. " "_FOUND will be set to indicate whether the package was found. " @@ -86,8 +88,8 @@ cmFindPackageCommand::cmFindPackageCommand() "A package-specific list of components may be listed after the " "REQUIRED option or after the COMPONENTS option if no REQUIRED " "option is given. " - "The \"[major[.minor[.patch]]]\" version argument specifies a desired " - "version with which the package found should be compatible. " + "The [version] argument requests a version with which the package found " + "should be compatible (format is major[.minor[.patch[.tweak]]]). " "The EXACT option requests that the version be matched exactly. " "Version support is currently provided only on a package-by-package " "basis (details below).\n" @@ -109,7 +111,7 @@ cmFindPackageCommand::cmFindPackageCommand() "check the module documentation. " "If no module is found the command proceeds to Config mode.\n" "The complete Config mode command signature is:\n" - " find_package( [major[.minor[.patch]]] [EXACT] [QUIET]\n" + " find_package( [version] [EXACT] [QUIET]\n" " [[REQUIRED|COMPONENTS] [components...]] [NO_MODULE]\n" " [NAMES name1 [name2 ...]]\n" " [CONFIGS config1 [config2 ...]]\n" @@ -153,9 +155,9 @@ cmFindPackageCommand::cmFindPackageCommand() "a configuration file a fatal error is always generated because user " "intervention is required." "\n" - "When the \"[major[.minor[.patch]]]\" version argument is specified " - "Config mode will only find a version of the package that claims " - "compatibility with the requested version. " + "When the [version] argument is given Config mode will only find a " + "version of the package that claims compatibility with the requested " + "version (format is major[.minor[.patch[.tweak]]]). " "If the EXACT option is given only a version of the package claiming " "an exact match of the requested version may be found. " "CMake does not establish any convention for the meaning of version " @@ -173,22 +175,26 @@ cmFindPackageCommand::cmFindPackageCommand() "variables have been defined:\n" " PACKAGE_FIND_NAME = the name\n" " PACKAGE_FIND_VERSION = full requested version string\n" - " PACKAGE_FIND_VERSION_MAJOR = requested major version, if any\n" - " PACKAGE_FIND_VERSION_MINOR = requested minor version, if any\n" - " PACKAGE_FIND_VERSION_PATCH = requested patch version, if any\n" + " PACKAGE_FIND_VERSION_MAJOR = major version if requested, else 0\n" + " PACKAGE_FIND_VERSION_MINOR = minor version if requested, else 0\n" + " PACKAGE_FIND_VERSION_PATCH = patch version if requested, else 0\n" + " PACKAGE_FIND_VERSION_TWEAK = tweak version if requested, else 0\n" + " PACKAGE_FIND_VERSION_COUNT = number of version components, 0 to 4\n" "The version file checks whether it satisfies the requested version " "and sets these variables:\n" - " PACKAGE_VERSION = package version (major[.minor[.patch]])\n" + " PACKAGE_VERSION = full provided version string\n" " PACKAGE_VERSION_EXACT = true if version is exact match\n" " PACKAGE_VERSION_COMPATIBLE = true if version is compatible\n" "These variables are checked by the find_package command to determine " "whether the configuration file provides an acceptable version. " "They are not available after the find_package call returns. " "If the version is acceptable the following variables are set:\n" - " _VERSION = package version (major[.minor[.patch]])\n" - " _VERSION_MAJOR = major from major[.minor[.patch]], if any\n" - " _VERSION_MINOR = minor from major[.minor[.patch]], if any\n" - " _VERSION_PATCH = patch from major[.minor[.patch]], if any\n" + " _VERSION = full provided version string\n" + " _VERSION_MAJOR = major version if provided, else 0\n" + " _VERSION_MINOR = minor version if provided, else 0\n" + " _VERSION_PATCH = patch version if provided, else 0\n" + " _VERSION_TWEAK = tweak version if provided, else 0\n" + " _VERSION_COUNT = number of version components, 0 to 4\n" "and the corresponding package configuration file is loaded. " "When multiple package configuration files are available whose version " "files claim compatibility with the version requested it is unspecified " @@ -469,10 +475,13 @@ bool cmFindPackageCommand unsigned int parsed_major; unsigned int parsed_minor; unsigned int parsed_patch; - this->VersionCount = sscanf(this->Version.c_str(), "%u.%u.%u", - &parsed_major, &parsed_minor, &parsed_patch); + unsigned int parsed_tweak; + this->VersionCount = sscanf(this->Version.c_str(), "%u.%u.%u.%u", + &parsed_major, &parsed_minor, + &parsed_patch, &parsed_tweak); switch(this->VersionCount) { + case 4: this->VersionTweak = parsed_tweak; // no break! case 3: this->VersionPatch = parsed_patch; // no break! case 2: this->VersionMinor = parsed_minor; // no break! case 1: this->VersionMajor = parsed_major; // no break! @@ -565,25 +574,16 @@ void cmFindPackageCommand::SetModuleVariables(const std::string& components) ver += "_FIND_VERSION"; this->Makefile->AddDefinition(ver.c_str(), this->Version.c_str()); char buf[64]; - switch(this->VersionCount) - { - case 3: - { - sprintf(buf, "%u", this->VersionPatch); - this->Makefile->AddDefinition((ver+"_PATCH").c_str(), buf); - } // no break - case 2: - { - sprintf(buf, "%u", this->VersionMinor); - this->Makefile->AddDefinition((ver+"_MINOR").c_str(), buf); - } // no break - case 1: - { - sprintf(buf, "%u", this->VersionMajor); - this->Makefile->AddDefinition((ver+"_MAJOR").c_str(), buf); - } // no break - default: break; - } + sprintf(buf, "%u", this->VersionMajor); + this->Makefile->AddDefinition((ver+"_MAJOR").c_str(), buf); + sprintf(buf, "%u", this->VersionMinor); + this->Makefile->AddDefinition((ver+"_MINOR").c_str(), buf); + sprintf(buf, "%u", this->VersionPatch); + this->Makefile->AddDefinition((ver+"_PATCH").c_str(), buf); + sprintf(buf, "%u", this->VersionTweak); + this->Makefile->AddDefinition((ver+"_TWEAK").c_str(), buf); + sprintf(buf, "%u", this->VersionCount); + this->Makefile->AddDefinition((ver+"_COUNT").c_str(), buf); // Tell the module whether an exact version has been requested. std::string exact = this->Name; @@ -1234,36 +1234,17 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file) this->Makefile->AddDefinition("PACKAGE_FIND_NAME", this->Name.c_str()); this->Makefile->AddDefinition("PACKAGE_FIND_VERSION", this->Version.c_str()); - if(this->VersionCount >= 3) - { - char buf[64]; - sprintf(buf, "%u", this->VersionPatch); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_PATCH", buf); - } - else - { - this->Makefile->RemoveDefinition("PACKAGE_FIND_VERSION_PATCH"); - } - if(this->VersionCount >= 2) - { - char buf[64]; - sprintf(buf, "%u", this->VersionMinor); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MINOR", buf); - } - else - { - this->Makefile->RemoveDefinition("PACKAGE_FIND_VERSION_MINOR"); - } - if(this->VersionCount >= 1) - { - char buf[64]; - sprintf(buf, "%u", this->VersionMajor); - this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MAJOR", buf); - } - else - { - this->Makefile->RemoveDefinition("PACKAGE_FIND_VERSION_MAJOR"); - } + char buf[64]; + sprintf(buf, "%u", this->VersionMajor); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MAJOR", buf); + sprintf(buf, "%u", this->VersionMinor); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_MINOR", buf); + sprintf(buf, "%u", this->VersionPatch); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_PATCH", buf); + sprintf(buf, "%u", this->VersionTweak); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_TWEAK", buf); + sprintf(buf, "%u", this->VersionCount); + this->Makefile->AddDefinition("PACKAGE_FIND_VERSION_COUNT", buf); // Load the version check file. bool found = false; @@ -1286,11 +1267,14 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file) unsigned int parsed_major; unsigned int parsed_minor; unsigned int parsed_patch; + unsigned int parsed_tweak; this->VersionFoundCount = - sscanf(this->VersionFound.c_str(), "%u.%u.%u", - &parsed_major, &parsed_minor, &parsed_patch); + sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", + &parsed_major, &parsed_minor, + &parsed_patch, &parsed_tweak); switch(this->VersionFoundCount) { + case 4: this->VersionFoundTweak = parsed_tweak; // no break! case 3: this->VersionFoundPatch = parsed_patch; // no break! case 2: this->VersionFoundMinor = parsed_minor; // no break! case 1: this->VersionFoundMajor = parsed_major; // no break! @@ -1321,27 +1305,18 @@ void cmFindPackageCommand::StoreVersionFound() this->Makefile->AddDefinition(ver.c_str(), this->VersionFound.c_str()); } - // Store the portions that could be parsed. + // Store the version components. char buf[64]; - switch(this->VersionFoundCount) - { - case 3: - { - sprintf(buf, "%u", this->VersionFoundPatch); - this->Makefile->AddDefinition((ver+"_PATCH").c_str(), buf); - } // no break - case 2: - { - sprintf(buf, "%u", this->VersionFoundMinor); - this->Makefile->AddDefinition((ver+"_MINOR").c_str(), buf); - } // no break - case 1: - { - sprintf(buf, "%u", this->VersionFoundMajor); - this->Makefile->AddDefinition((ver+"_MAJOR").c_str(), buf); - } // no break - default: break; - } + sprintf(buf, "%u", this->VersionFoundMajor); + this->Makefile->AddDefinition((ver+"_MAJOR").c_str(), buf); + sprintf(buf, "%u", this->VersionFoundMinor); + this->Makefile->AddDefinition((ver+"_MINOR").c_str(), buf); + sprintf(buf, "%u", this->VersionFoundPatch); + this->Makefile->AddDefinition((ver+"_PATCH").c_str(), buf); + sprintf(buf, "%u", this->VersionFoundTweak); + this->Makefile->AddDefinition((ver+"_TWEAK").c_str(), buf); + sprintf(buf, "%u", this->VersionFoundCount); + this->Makefile->AddDefinition((ver+"_COUNT").c_str(), buf); } //---------------------------------------------------------------------------- diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h index cf27965..cf7b93c 100644 --- a/Source/cmFindPackageCommand.h +++ b/Source/cmFindPackageCommand.h @@ -111,6 +111,7 @@ private: unsigned int VersionMajor; unsigned int VersionMinor; unsigned int VersionPatch; + unsigned int VersionTweak; unsigned int VersionCount; bool VersionExact; cmStdString FileFound; @@ -118,6 +119,7 @@ private: unsigned int VersionFoundMajor; unsigned int VersionFoundMinor; unsigned int VersionFoundPatch; + unsigned int VersionFoundTweak; unsigned int VersionFoundCount; bool Quiet; bool Required; diff --git a/Tests/FindPackageTest/CMakeLists.txt b/Tests/FindPackageTest/CMakeLists.txt index f02cd70..a3e24bb 100644 --- a/Tests/FindPackageTest/CMakeLists.txt +++ b/Tests/FindPackageTest/CMakeLists.txt @@ -27,6 +27,7 @@ LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) FIND_PACKAGE(VersionTestA 1) FIND_PACKAGE(VersionTestB 1.2) FIND_PACKAGE(VersionTestC 1.2.3) +FIND_PACKAGE(VersionTestD 1.2.3.4) #----------------------------------------------------------------------------- diff --git a/Tests/FindPackageTest/FindVersionTestA.cmake b/Tests/FindPackageTest/FindVersionTestA.cmake index eedf371..55c67e2 100644 --- a/Tests/FindPackageTest/FindVersionTestA.cmake +++ b/Tests/FindPackageTest/FindVersionTestA.cmake @@ -4,9 +4,15 @@ ENDIF(NOT "${VersionTestA_FIND_VERSION}" STREQUAL "1") IF(NOT "${VersionTestA_FIND_VERSION_MAJOR}" STREQUAL "1") MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_MAJOR=${VersionTestA_FIND_VERSION_MAJOR} is not 1") ENDIF(NOT "${VersionTestA_FIND_VERSION_MAJOR}" STREQUAL "1") -IF(DEFINED VersionTestA_FIND_VERSION_MINOR) - MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_MINOR should not be defined") -ENDIF(DEFINED VersionTestA_FIND_VERSION_MINOR) -IF(DEFINED VersionTestA_FIND_VERSION_PATCH) - MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_PATCH should not be defined") -ENDIF(DEFINED VersionTestA_FIND_VERSION_PATCH) +IF(NOT "${VersionTestA_FIND_VERSION_MINOR}" STREQUAL "0") + MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_MINOR=${VersionTestA_FIND_VERSION_MINOR} is not 0") +ENDIF(NOT "${VersionTestA_FIND_VERSION_MINOR}" STREQUAL "0") +IF(NOT "${VersionTestA_FIND_VERSION_PATCH}" STREQUAL "0") + MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_PATCH=${VersionTestA_FIND_VERSION_PATCH} is not 0") +ENDIF(NOT "${VersionTestA_FIND_VERSION_PATCH}" STREQUAL "0") +IF(NOT "${VersionTestA_FIND_VERSION_TWEAK}" STREQUAL "0") + MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_TWEAK=${VersionTestA_FIND_VERSION_TWEAK} is not 0") +ENDIF(NOT "${VersionTestA_FIND_VERSION_TWEAK}" STREQUAL "0") +IF(NOT "${VersionTestA_FIND_VERSION_COUNT}" STREQUAL "1") + MESSAGE(SEND_ERROR "VersionTestA_FIND_VERSION_COUNT=${VersionTestA_FIND_VERSION_COUNT} is not 1") +ENDIF(NOT "${VersionTestA_FIND_VERSION_COUNT}" STREQUAL "1") diff --git a/Tests/FindPackageTest/FindVersionTestB.cmake b/Tests/FindPackageTest/FindVersionTestB.cmake index 787d200..03173c6 100644 --- a/Tests/FindPackageTest/FindVersionTestB.cmake +++ b/Tests/FindPackageTest/FindVersionTestB.cmake @@ -7,6 +7,12 @@ ENDIF(NOT "${VersionTestB_FIND_VERSION_MAJOR}" STREQUAL "1") IF(NOT "${VersionTestB_FIND_VERSION_MINOR}" STREQUAL "2") MESSAGE(SEND_ERROR "VersionTestB_FIND_VERSION_MINOR=${VersionTestB_FIND_VERSION_MINOR} is not 2") ENDIF(NOT "${VersionTestB_FIND_VERSION_MINOR}" STREQUAL "2") -IF(DEFINED VersionTestB_FIND_VERSION_PATCH) - MESSAGE(SEND_ERROR "VersionTestB_FIND_VERSION_PATCH should not be defined") -ENDIF(DEFINED VersionTestB_FIND_VERSION_PATCH) +IF(NOT "${VersionTestB_FIND_VERSION_PATCH}" STREQUAL "0") + MESSAGE(SEND_ERROR "VersionTestB_FIND_VERSION_PATCH=${VersionTestB_FIND_VERSION_PATCH} is not 0") +ENDIF(NOT "${VersionTestB_FIND_VERSION_PATCH}" STREQUAL "0") +IF(NOT "${VersionTestB_FIND_VERSION_TWEAK}" STREQUAL "0") + MESSAGE(SEND_ERROR "VersionTestB_FIND_VERSION_TWEAK=${VersionTestB_FIND_VERSION_TWEAK} is not 0") +ENDIF(NOT "${VersionTestB_FIND_VERSION_TWEAK}" STREQUAL "0") +IF(NOT "${VersionTestB_FIND_VERSION_COUNT}" STREQUAL "2") + MESSAGE(SEND_ERROR "VersionTestB_FIND_VERSION_COUNT=${VersionTestB_FIND_VERSION_COUNT} is not 2") +ENDIF(NOT "${VersionTestB_FIND_VERSION_COUNT}" STREQUAL "2") diff --git a/Tests/FindPackageTest/FindVersionTestC.cmake b/Tests/FindPackageTest/FindVersionTestC.cmake index 26ce050..1344cbc 100644 --- a/Tests/FindPackageTest/FindVersionTestC.cmake +++ b/Tests/FindPackageTest/FindVersionTestC.cmake @@ -10,3 +10,9 @@ ENDIF(NOT "${VersionTestC_FIND_VERSION_MINOR}" STREQUAL "2") IF(NOT "${VersionTestC_FIND_VERSION_PATCH}" STREQUAL "3") MESSAGE(SEND_ERROR "VersionTestC_FIND_VERSION_PATCH=${VersionTestC_FIND_VERSION_PATCH} is not 3") ENDIF(NOT "${VersionTestC_FIND_VERSION_PATCH}" STREQUAL "3") +IF(NOT "${VersionTestC_FIND_VERSION_TWEAK}" STREQUAL "0") + MESSAGE(SEND_ERROR "VersionTestC_FIND_VERSION_TWEAK=${VersionTestC_FIND_VERSION_TWEAK} is not 0") +ENDIF(NOT "${VersionTestC_FIND_VERSION_TWEAK}" STREQUAL "0") +IF(NOT "${VersionTestC_FIND_VERSION_COUNT}" STREQUAL "3") + MESSAGE(SEND_ERROR "VersionTestC_FIND_VERSION_COUNT=${VersionTestC_FIND_VERSION_COUNT} is not 3") +ENDIF(NOT "${VersionTestC_FIND_VERSION_COUNT}" STREQUAL "3") diff --git a/Tests/FindPackageTest/FindVersionTestD.cmake b/Tests/FindPackageTest/FindVersionTestD.cmake new file mode 100644 index 0000000..d3e3f50 --- /dev/null +++ b/Tests/FindPackageTest/FindVersionTestD.cmake @@ -0,0 +1,18 @@ +IF(NOT "${VersionTestD_FIND_VERSION}" STREQUAL "1.2.3.4") + MESSAGE(SEND_ERROR "VersionTestD_FIND_VERSION=${VersionTestD_FIND_VERSION} is not 1.2.3.4") +ENDIF(NOT "${VersionTestD_FIND_VERSION}" STREQUAL "1.2.3.4") +IF(NOT "${VersionTestD_FIND_VERSION_MAJOR}" STREQUAL "1") + MESSAGE(SEND_ERROR "VersionTestD_FIND_VERSION_MAJOR=${VersionTestD_FIND_VERSION_MAJOR} is not 1") +ENDIF(NOT "${VersionTestD_FIND_VERSION_MAJOR}" STREQUAL "1") +IF(NOT "${VersionTestD_FIND_VERSION_MINOR}" STREQUAL "2") + MESSAGE(SEND_ERROR "VersionTestD_FIND_VERSION_MINOR=${VersionTestD_FIND_VERSION_MINOR} is not 2") +ENDIF(NOT "${VersionTestD_FIND_VERSION_MINOR}" STREQUAL "2") +IF(NOT "${VersionTestD_FIND_VERSION_PATCH}" STREQUAL "3") + MESSAGE(SEND_ERROR "VersionTestD_FIND_VERSION_PATCH=${VersionTestD_FIND_VERSION_PATCH} is not 3") +ENDIF(NOT "${VersionTestD_FIND_VERSION_PATCH}" STREQUAL "3") +IF(NOT "${VersionTestD_FIND_VERSION_TWEAK}" STREQUAL "4") + MESSAGE(SEND_ERROR "VersionTestD_FIND_VERSION_TWEAK=${VersionTestD_FIND_VERSION_TWEAK} is not 4") +ENDIF(NOT "${VersionTestD_FIND_VERSION_TWEAK}" STREQUAL "4") +IF(NOT "${VersionTestD_FIND_VERSION_COUNT}" STREQUAL "4") + MESSAGE(SEND_ERROR "VersionTestD_FIND_VERSION_COUNT=${VersionTestD_FIND_VERSION_COUNT} is not 4") +ENDIF(NOT "${VersionTestD_FIND_VERSION_COUNT}" STREQUAL "4") -- cgit v0.12