Add CHANGES file with high level software history
authorDavid Neto <dneto@google.com>
Fri, 22 Apr 2016 00:50:11 +0000 (20:50 -0400)
committerDavid Neto <dneto@google.com>
Fri, 22 Apr 2016 19:19:45 +0000 (15:19 -0400)
Add a high level version number for SPIRV-Tools, beginning
with v2016.0-dev.  The README describes the format of the
version number.

The high level version number is extracted from the CHANGES
file.  That works around:
 - stale-bait for when we don't add tags to the repository
 - our inability to add tags to the repository

Option --version causes spirv-as, spirv-dis, and spirv-val to
show the high level version number.

Add spvSoftwareVersionString to return the C-string for
the high level version number.

Add spvSoftwareVersionDetailsString() so that clients can get
more information if they want to.
Also allows us to clean up the uses in the tool executables files,
so now only one file includes build-version.inc.

Move the update-build-version logic to the only
CMakeLists file that needs it.

The update build version script takes a new argument
to name the output file.

13 files changed:
CHANGES [new file with mode: 0644]
CMakeLists.txt
README.md
include/spirv-tools/libspirv.h
source/CMakeLists.txt
source/software_version.cpp [new file with mode: 0644]
test/CMakeLists.txt
test/SoftwareVersion.cpp [new file with mode: 0644]
tools/CMakeLists.txt
tools/as/as.cpp
tools/dis/dis.cpp
tools/val/val.cpp
utils/update_build_version.py

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..f30d2f8
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,19 @@
+Revision history for SPIRV-Tools
+
+v2016.0-dev 2016-04-22
+
+ - Adds v<year>.<index> versioning, with "-dev" indicating
+   work in progress.  The intent is to more easly report
+   and summarize functionality when SPIRV-Tools is incorporated
+   in downstream projects.
+
+ - Summary of functionality (See the README.md for more):
+   - Supports SPIR-V 1.1 Rev 1
+   - Supports SPIR-V 1.0 Rev 5
+   - Supports GLSL std450 extended instructions 1.0 Rev 3
+   - Supports OpenCL extended instructions 1.0 Rev 2
+   - Assembler, disassembler are complete
+     - Supports floating point widths of 16, 32, 64 bits
+     - Supports integer widths up to 64 bits
+   - Validator is incomplete
+   - Supported on Linux, OSX, Android, Windows
index d6d57f0..ba61667 100644 (file)
@@ -133,11 +133,6 @@ endif()
 
 find_host_package(PythonInterp)
 
-add_custom_target(spirv-tools-build-version
-  ${PYTHON_EXECUTABLE}
-  ${CMAKE_CURRENT_SOURCE_DIR}/utils/update_build_version.py
-  ${spirv-tools_SOURCE_DIR}
-  COMMENT "Update build-version.inc in the Spirv-tools build directory (if necessary).")
 
 # Defaults to OFF if the user didn't set it.
 option(SPIRV_SKIP_EXECUTABLES
index 831c9ca..a798b6c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -18,6 +18,23 @@ SPIR-V is defined by the Khronos Group Inc.
 See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification,
 headers, and XML registry.
 
+## Verisoning SPIRV-Tools
+
+See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version.
+
+SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with
+an optional `-dev` suffix to indicate work in progress.  For exampe, the
+following versions are ordered from oldest to newest:
+
+* `v2016.0`
+* `v2016.1-dev`
+* `v2016.1`
+* `v2016.2-dev`
+* `v2016.2`
+
+Use the `--version` option on each command line tool to see the software
+version.  An API call reports the software version as a C-style string.
+
 ## Supported features
 
 ### Assembler, binary parser, and disassembler
index d594f43..5dcf4ff 100644 (file)
@@ -329,6 +329,16 @@ typedef spv_context_t* spv_context;
 
 // Platform API
 
+// Returns the SPIRV-Tools software version as a null-terminated string.
+// The contents of the underlying storage is valid for the remainder of
+// the process.
+const char* spvSoftwareVersionString();
+// Returns a null-terminated string containing the name of the project,
+// the software version string, and commit details.
+// The contents of the underlying storage is valid for the remainder of
+// the process.
+const char* spvSoftwareVersionDetailsString();
+
 // Certain target environments impose additional restrictions on SPIR-V, so it's
 // often necessary to specify which one applies.  SPV_ENV_UNIVERSAL means
 // environment-agnostic SPIR-V.
index 62be13a..3920720 100644 (file)
@@ -102,6 +102,24 @@ set_source_files_properties(
   ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp
   PROPERTIES OBJECT_DEPENDS "${EXTINST_CPP_DEPENDS}")
 
+set(SPIRV_TOOLS_BUILD_VERSION_INC
+       ${spirv-tools_BINARY_DIR}/build-version.inc)
+set(SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR
+       ${spirv-tools_SOURCE_DIR}/utils/update_build_version.py)
+set(SPIRV_TOOLS_CHANGES_FILE
+       ${spirv-tools_SOURCE_DIR}/CHANGES)
+add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC}
+   COMMAND ${PYTHON_EXECUTABLE}
+           ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
+           ${spirv-tools_SOURCE_DIR} ${SPIRV_TOOLS_BUILD_VERSION_INC}
+   DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR}
+           ${SPIRV_TOOLS_CHANGES_FILE}
+   COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).")
+# Convenience target for standalone generation of the build-version.inc file.
+# This is not required for any dependence chain.
+add_custom_target(spirv-tools-build-version
+   DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC})
+
 set(SPIRV_SOURCES
   ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h
   ${spirv-tools_SOURCE_DIR}/include/spirv/spirv.h
@@ -135,6 +153,7 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/opcode.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/operand.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/print.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/table.cpp
@@ -148,6 +167,22 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_ssa.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_types.cpp)
 
+# The software_version.cpp file includes build-version.inc.
+# Rebuild the software_version.cpp object file if it is older than
+# build-version.inc or whenever build-version.inc itself is out of
+# date.  In the latter case, rebuild build-version.inc first.
+# CMake is not smart enough to detect this dependency automatically.
+# Without this, the dependency detection system for #included files
+# does not kick in on a clean build for the following reason:  The
+# build will fail early because it doesn't know how to build the
+# missing source file build-version.inc. That occurs before the
+# preprocessor is run on software_version.cpp to detect the
+# #include dependency.
+set_source_files_properties(
+  ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp
+  PROPERTIES OBJECT_DEPENDS "${SPIRV_TOOLS_BUILD_VERSION_INC}")
+
+
 add_library(${SPIRV_TOOLS} ${SPIRV_SOURCES})
 spvtools_default_compile_options(${SPIRV_TOOLS})
 target_include_directories(${SPIRV_TOOLS}
diff --git a/source/software_version.cpp b/source/software_version.cpp
new file mode 100644 (file)
index 0000000..b20a655
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (c) 2015-2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "spirv-tools/libspirv.h"
+
+namespace {
+
+const char* kBuildVersions[] = {
+#include "build-version.inc"
+};
+
+}  // anonymous namespace
+
+const char* spvSoftwareVersionString() {
+  return kBuildVersions[0];
+}
+
+const char* spvSoftwareVersionDetailsString() {
+  return kBuildVersions[1];
+}
index 9048e87..f66f57c 100644 (file)
@@ -59,6 +59,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
       ${CMAKE_CURRENT_SOURCE_DIR}/OperandCapabilities.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/Operand.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/OperandPattern.cpp
+      ${CMAKE_CURRENT_SOURCE_DIR}/SoftwareVersion.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/TextAdvance.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/TextDestroy.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/TextLiteral.cpp
diff --git a/test/SoftwareVersion.cpp b/test/SoftwareVersion.cpp
new file mode 100644 (file)
index 0000000..2867681
--- /dev/null
@@ -0,0 +1,77 @@
+// Copyright (c) 2015-2016 Google Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "UnitSPIRV.h"
+
+#include <sstream>
+
+#include "gmock/gmock.h"
+
+using ::testing::AnyOf;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::StartsWith;
+
+namespace {
+
+void CheckFormOfHighLevelVersion(const std::string& version) {
+  std::istringstream s(version);
+  char v = 'x';
+  int year = -1;
+  char period = 'x';
+  int index = -1;
+  s >> v >> year >> period >> index;
+  EXPECT_THAT(v, Eq('v'));
+  EXPECT_THAT(year, Ge(2016));
+  EXPECT_THAT(period, Eq('.'));
+  EXPECT_THAT(index, Ge(0));
+  EXPECT_TRUE(s.good() || s.eof());
+
+  std::string rest;
+  s >> rest;
+  EXPECT_THAT(rest, AnyOf("", "-dev"));
+}
+
+TEST(SoftwareVersion, ShortIsCorrectForm) {
+  SCOPED_TRACE("short form");
+  CheckFormOfHighLevelVersion(spvSoftwareVersionString());
+}
+
+TEST(SoftwareVersion, DetailedIsCorrectForm) {
+  const std::string detailed_version(spvSoftwareVersionDetailsString());
+  EXPECT_THAT(detailed_version, StartsWith("SPIRV-Tools v"));
+
+  // Parse the high level version.
+  const std::string from_v =
+      detailed_version.substr(detailed_version.find_first_of('v'));
+  const size_t first_space_after_v_or_npos = from_v.find_first_of(' ');
+  SCOPED_TRACE(detailed_version);
+  CheckFormOfHighLevelVersion(from_v.substr(0, first_space_after_v_or_npos));
+
+  // We don't actually care about what comes after the version number.
+}
+
+}  // anonymous namespace
index e5df750..702bc77 100644 (file)
@@ -31,19 +31,16 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
   spvtools_default_compile_options(spirv-as)
   target_link_libraries(spirv-as PRIVATE ${SPIRV_TOOLS})
   target_include_directories(spirv-as PRIVATE ${spirv-tools_BINARY_DIR})
-  add_dependencies(${SPIRV_TOOLS} spirv-tools-build-version)
 
   add_executable(spirv-dis ${CMAKE_CURRENT_SOURCE_DIR}/dis/dis.cpp)
   spvtools_default_compile_options(spirv-dis)
   target_link_libraries(spirv-dis PRIVATE ${SPIRV_TOOLS})
   target_include_directories(spirv-dis PRIVATE ${spirv-tools_BINARY_DIR})
-  add_dependencies(${SPIRV_TOOLS} spirv-tools-build-version)
 
   add_executable(spirv-val ${CMAKE_CURRENT_SOURCE_DIR}/val/val.cpp)
   spvtools_default_compile_options(spirv-val)
   target_link_libraries(spirv-val PRIVATE ${SPIRV_TOOLS})
   target_include_directories(spirv-val PRIVATE ${spirv-tools_BINARY_DIR})
-  add_dependencies(${SPIRV_TOOLS} spirv-tools-build-version)
 
   install(TARGETS ${SPIRV_INSTALL_TARGETS}
     RUNTIME DESTINATION bin
index aa23fe6..aec1b73 100644 (file)
@@ -52,10 +52,6 @@ Options:
       argv0, argv0);
 }
 
-const char kBuildVersion[] =
-#include "build-version.inc"
-    ;
-
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
   const char* outFile = nullptr;
@@ -87,7 +83,7 @@ int main(int argc, char** argv) {
         case '-': {
           // Long options
           if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s\n", kBuildVersion);
+            printf("%s\n", spvSoftwareVersionDetailsString());
             printf("Target: %s\n",
                    spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1));
             return 0;
index 66ea813..675f77f 100644 (file)
@@ -59,10 +59,6 @@ Options:
       argv0, argv0);
 }
 
-const char kBuildVersion[] =
-#include "build-version.inc"
-    ;
-
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
   const char* outFile = nullptr;
@@ -100,7 +96,7 @@ int main(int argc, char** argv) {
             print_usage(argv[0]);
             return 0;
           } else if (0 == strcmp(argv[argi], "--version")) {
-            printf("%s\n", kBuildVersion);
+            printf("%s\n", spvSoftwareVersionDetailsString());
             printf("Target: %s\n",
                    spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1));
             return 0;
index d5b88d5..d7e998a 100644 (file)
@@ -50,10 +50,6 @@ Options:
       argv0, argv0);
 }
 
-const char kBuildVersion[] =
-#include "build-version.inc"
-    ;
-
 int main(int argc, char** argv) {
   const char* inFile = nullptr;
   spv_target_env target_env = SPV_ENV_UNIVERSAL_1_1;
@@ -62,7 +58,7 @@ int main(int argc, char** argv) {
     const char* cur_arg = argv[argi];
     if ('-' == cur_arg[0]) {
       if (0 == strcmp(cur_arg, "--version")) {
-        printf("%s\n", kBuildVersion);
+        printf("%s\n", spvSoftwareVersionDetailsString());
         printf("Targets:\n  %s\n  %s\n",
                spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
                spvTargetEnvDescription(SPV_ENV_VULKAN_1_0));
index 9828f8a..0f68bf5 100755 (executable)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Updates build-version.inc in the current directory, unless the update is
-# identical to the existing content.
+# Updates an output file with version info unless the new content is the same
+# as the existing content.
 #
-# Args: <spirv-tools_dir>
+# Args: <spirv-tools_dir> <output-file>
 #
-# For each directory, there will be a line in build-version.inc containing that
-# directory's "git describe" output enclosed in double quotes and appropriately
-# escaped.
+# The output file will contain a line of text consisting of two C source syntax
+# string literals separated by a comma:
+#  - The software version deduced from the CHANGES file in the given directory.
+#  - A longer string with the project name, the software version number, and
+#    git commit information for the directory.  The commit information
+#    is the output of "git describe" if that succeeds, or "git rev-parse HEAD"
+#    if that succeeds, or otherwise a message containing the phrase "unknown hash".
+# The string contents are escaped as necessary.
 
 from __future__ import print_function
 
 import datetime
 import os.path
+import re
 import subprocess
 import sys
 
-OUTFILE = 'build-version.inc'
-
 
 def command_output(cmd, dir):
     """Runs a command in a directory and returns its standard output stream.
@@ -50,6 +54,23 @@ def command_output(cmd, dir):
     return stdout
 
 
+def deduceSoftwareVersion(dir):
+    """Returns a software version number parsed from the CHANGES file
+    in the given dir.
+
+    The CHANGES file describes most recent versions first.
+    """
+
+    pattern = re.compile('(v\d+\.\d+(-dev)) \d\d\d\d-\d\d-\d\d$')
+    changes_file = os.path.join(dir, 'CHANGES')
+    with open(changes_file) as f:
+        for line in f.readlines():
+            match = pattern.match(line)
+            if match:
+                return match.group(1)
+    raise Exception('No version number found in {}'.format(changes_file))
+
+
 def describe(dir):
     """Returns a string describing the current Git HEAD version as descriptively
     as possible.
@@ -72,15 +93,19 @@ def describe(dir):
 
 
 def main():
-    if len(sys.argv) != 2:
-        print('usage: {0} <spirv-tools_dir>'.format(sys.argv[0]))
+    if len(sys.argv) != 3:
+        print('usage: {0} <spirv-tools_dir> <output-file>'.format(sys.argv[0]))
         sys.exit(1)
 
-    new_content = '"spirv-tools {}\\n"\n'.format(
+    output_file = sys.argv[2]
+
+    software_version = deduceSoftwareVersion(sys.argv[1])
+    new_content = '"{}", "SPIRV-Tools {} {}"\n'.format(
+        software_version, software_version,
         describe(sys.argv[1]).replace('"', '\\"'))
-    if os.path.isfile(OUTFILE) and new_content == open(OUTFILE, 'r').read():
+    if os.path.isfile(output_file) and new_content == open(output_file, 'r').read():
         sys.exit(0)
-    open(OUTFILE, 'w').write(new_content)
+    open(output_file, 'w').write(new_content)
 
 if __name__ == '__main__':
     main()