scripts: Port codegen script from VVL
authorMike Schuchardt <mikes@lunarg.com>
Mon, 22 Jul 2019 23:57:15 +0000 (16:57 -0700)
committerTobin Ehlis <tobine@google.com>
Wed, 24 Jul 2019 12:48:13 +0000 (06:48 -0600)
CMakeLists.txt
scripts/common_codegen.py
scripts/generate_source.py [new file with mode: 0755]
scripts/kvt_genvk.py

index e6ba640..4c921b8 100644 (file)
@@ -137,6 +137,16 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
     endif()
 endif()
 
+# Optional codegen target
+if(PYTHONINTERP_FOUND)
+    add_custom_target(VulkanTools_generated_source
+                      COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/generate_source.py
+                              ${VulkanRegistry_DIR} --incremental
+                      )
+else()
+    message("WARNING: VulkanTools_generated_source target requires python 3")
+endif()
+
 if(APPLE)
     include(mac_common.cmake)
 endif()
index 8954532..d56ebac 100644 (file)
 #
 # Author: Mark Lobodzinski <mark@lunarg.com>
 
-import os,re,sys,string
-import xml.etree.ElementTree as etree
-from generator import *
-from collections import namedtuple
+import os
 
 # Copyright text prefixing all headers (list of strings).
 prefixStrings = [
@@ -71,3 +68,7 @@ def GetFeatureProtect(interface):
     if platform is not None:
         protect = platform_dict[platform]
     return protect
+
+# helper to define paths relative to the repo root
+def repo_relative(path):
+    return os.path.abspath(os.path.join(os.path.dirname(__file__), '..', path))
diff --git a/scripts/generate_source.py b/scripts/generate_source.py
new file mode 100755 (executable)
index 0000000..c4be24f
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+# Copyright (c) 2019 The Khronos Group Inc.
+# Copyright (c) 2019 Valve Corporation
+# Copyright (c) 2019 LunarG, Inc.
+# Copyright (c) 2019 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Mike Schuchardt <mikes@lunarg.com>
+
+import argparse
+import filecmp
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+import common_codegen
+
+# files to exclude from --verify check
+verify_exclude = ['.clang-format']
+
+def main(argv):
+    parser = argparse.ArgumentParser(description='Generate source code for this repository')
+    parser.add_argument('registry', metavar='REGISTRY_PATH', help='path to the Vulkan-Headers registry directory')
+    group = parser.add_mutually_exclusive_group()
+    group.add_argument('-i', '--incremental', action='store_true', help='only update repo files that change')
+    group.add_argument('-v', '--verify', action='store_true', help='verify repo files match generator output')
+    args = parser.parse_args(argv)
+
+    gen_cmds = [[common_codegen.repo_relative('scripts/kvt_genvk.py'),
+                 '-registry', os.path.abspath(os.path.join(args.registry,  'vk.xml')),
+                 '-quiet',
+                 filename] for filename in ['vk_typemap_helper.h',
+                                            'mock_icd.h',
+                                            'mock_icd.cpp']]
+
+    repo_dir = common_codegen.repo_relative('icd/generated')
+
+    # get directory where generators will run
+    if args.verify or args.incremental:
+        # generate in temp directory so we can compare or copy later
+        temp_obj = tempfile.TemporaryDirectory(prefix='VulkanLoader_generated_source_')
+        temp_dir = temp_obj.name
+        gen_dir = temp_dir
+    else:
+        # generate directly in the repo
+        gen_dir = repo_dir
+
+    # run each code generator
+    for cmd in gen_cmds:
+        print(' '.join(cmd))
+        try:
+            subprocess.check_call([sys.executable] + cmd, cwd=gen_dir)
+        except Exception as e:
+            print('ERROR:', str(e))
+            return 1
+
+    # optional post-generation steps
+    if args.verify:
+        # compare contents of temp dir and repo
+        temp_files = set(os.listdir(temp_dir))
+        repo_files = set(os.listdir(repo_dir))
+        files_match = True
+        for filename in sorted((temp_files | repo_files) - set(verify_exclude)):
+            if filename not in repo_files:
+                print('ERROR: Missing repo file', filename)
+                files_match = False
+            elif filename not in temp_files:
+                print('ERROR: Missing generator for', filename)
+                files_match = False
+            elif not filecmp.cmp(os.path.join(temp_dir, filename),
+                               os.path.join(repo_dir, filename),
+                               shallow=False):
+                print('ERROR: Repo files do not match generator output for', filename)
+                files_match = False
+
+        # return code for test scripts
+        if files_match:
+            print('SUCCESS: Repo files match generator output')
+            return 0
+        return 1
+
+    elif args.incremental:
+        # copy missing or differing files from temp directory to repo
+        for filename in os.listdir(temp_dir):
+            temp_filename = os.path.join(temp_dir, filename)
+            repo_filename = os.path.join(repo_dir, filename)
+            if not os.path.exists(repo_filename) or \
+               not filecmp.cmp(temp_filename, repo_filename, shallow=False):
+                print('update', repo_filename)
+                shutil.copyfile(temp_filename, repo_filename)
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
+
index 598f000..1d2f255 100644 (file)
@@ -26,7 +26,6 @@ def startTimer(timeit):
 
 def endTimer(timeit, msg):
     global startTime
-    endTime = time.clock()
     if timeit:
         endTime = time.process_time()
         write(msg, endTime - startTime, file=sys.stderr)
@@ -34,7 +33,7 @@ def endTimer(timeit, msg):
 
 # Turn a list of strings into a regexp string matching exactly those strings
 def makeREstring(list, default = None):
-    if len(list) > 0 or default == None:
+    if len(list) > 0 or default is None:
         return '^(' + '|'.join(list) + ')$'
     else:
         return default
@@ -112,7 +111,7 @@ def makeGenOpts(args):
     # Defaults for generating re-inclusion protection wrappers (or not)
     protectFeature = protect
 
-    # An API style convention object
+    # An API style conventions object
     conventions = VulkanConventions()
 
     # Helper file generator options for typemap_helper.h
@@ -291,6 +290,10 @@ if __name__ == '__main__':
 
     args = parser.parse_args()
 
+    # default scripts path to be same as registry
+    if not args.scripts:
+        args.scripts = os.path.dirname(args.registry)
+
     scripts_directory_path = os.path.dirname(os.path.abspath(__file__))
     registry_headers_path = os.path.join(scripts_directory_path, args.scripts)
     sys.path.insert(0, registry_headers_path)