aboutsummaryrefslogtreecommitdiff
path: root/tools/Vulkan-Tools/scripts/update_deps.py
diff options
context:
space:
mode:
authorLizzy Fleckenstein <lizzy@vlhl.dev>2026-03-31 01:30:36 +0200
committerLizzy Fleckenstein <lizzy@vlhl.dev>2026-03-31 01:30:36 +0200
commit8e2ff15dbd3fe70fe2b52397b1eaba3fe2d7a5e8 (patch)
tree925fa596210d1a1f01e00e0743a643f4552e7a7a /tools/Vulkan-Tools/scripts/update_deps.py
parent1f17b4df127bd280e50d93a46ae93df704adc2b0 (diff)
parent90bf5bc4fd8bea0d300f6564af256a51a34124b8 (diff)
downloadusermoji-8e2ff15dbd3fe70fe2b52397b1eaba3fe2d7a5e8.tar.xz
add tools/Vulkan-Tools
Diffstat (limited to 'tools/Vulkan-Tools/scripts/update_deps.py')
-rwxr-xr-xtools/Vulkan-Tools/scripts/update_deps.py804
1 files changed, 804 insertions, 0 deletions
diff --git a/tools/Vulkan-Tools/scripts/update_deps.py b/tools/Vulkan-Tools/scripts/update_deps.py
new file mode 100755
index 00000000..7d300865
--- /dev/null
+++ b/tools/Vulkan-Tools/scripts/update_deps.py
@@ -0,0 +1,804 @@
+#!/usr/bin/env python3
+
+# Copyright 2017 The Glslang Authors. All rights reserved.
+# Copyright (c) 2018-2023 Valve Corporation
+# Copyright (c) 2018-2023 LunarG, Inc.
+# Copyright (c) 2023-2023 RasterGrid Kft.
+#
+# 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.
+
+# This script was heavily leveraged from KhronosGroup/glslang
+# update_glslang_sources.py.
+"""update_deps.py
+
+Get and build dependent repositories using known-good commits.
+
+Purpose
+-------
+
+This program is intended to assist a developer of this repository
+(the "home" repository) by gathering and building the repositories that
+this home repository depend on. It also checks out each dependent
+repository at a "known-good" commit in order to provide stability in
+the dependent repositories.
+
+Known-Good JSON Database
+------------------------
+
+This program expects to find a file named "known-good.json" in the
+same directory as the program file. This JSON file is tailored for
+the needs of the home repository by including its dependent repositories.
+
+Program Options
+---------------
+
+See the help text (update_deps.py --help) for a complete list of options.
+
+Program Operation
+-----------------
+
+The program uses the user's current directory at the time of program
+invocation as the location for fetching and building the dependent
+repositories. The user can override this by using the "--dir" option.
+
+For example, a directory named "build" in the repository's root directory
+is a good place to put the dependent repositories because that directory
+is not tracked by Git. (See the .gitignore file.) The "external" directory
+may also be a suitable location.
+A user can issue:
+
+$ cd My-Repo
+$ mkdir build
+$ cd build
+$ ../scripts/update_deps.py
+
+or, to do the same thing, but using the --dir option:
+
+$ cd My-Repo
+$ mkdir build
+$ scripts/update_deps.py --dir=build
+
+With these commands, the "build" directory is considered the "top"
+directory where the program clones the dependent repositories. The
+JSON file configures the build and install working directories to be
+within this "top" directory.
+
+Note that the "dir" option can also specify an absolute path:
+
+$ cd My-Repo
+$ scripts/update_deps.py --dir=/tmp/deps
+
+The "top" dir is then /tmp/deps (Linux filesystem example) and is
+where this program will clone and build the dependent repositories.
+
+Helper CMake Config File
+------------------------
+
+When the program finishes building the dependencies, it writes a file
+named "helper.cmake" to the "top" directory that contains CMake commands
+for setting CMake variables for locating the dependent repositories.
+This helper file can be used to set up the CMake build files for this
+"home" repository.
+
+A complete sequence might look like:
+
+$ git clone git@github.com:My-Group/My-Repo.git
+$ cd My-Repo
+$ mkdir build
+$ cd build
+$ ../scripts/update_deps.py
+$ cmake -C helper.cmake ..
+$ cmake --build .
+
+JSON File Schema
+----------------
+
+There's no formal schema for the "known-good" JSON file, but here is
+a description of its elements. All elements are required except those
+marked as optional. Please see the "known_good.json" file for
+examples of all of these elements.
+
+- name
+
+The name of the dependent repository. This field can be referenced
+by the "deps.repo_name" structure to record a dependency.
+
+- api
+
+The name of the API the dependency is specific to (e.g. "vulkan").
+
+- url
+
+Specifies the URL of the repository.
+Example: https://github.com/KhronosGroup/Vulkan-Loader.git
+
+- sub_dir
+
+The directory where the program clones the repository, relative to
+the "top" directory.
+
+- build_dir
+
+The directory used to build the repository, relative to the "top"
+directory.
+
+- install_dir
+
+The directory used to store the installed build artifacts, relative
+to the "top" directory.
+
+- commit
+
+The commit used to checkout the repository. This can be a SHA-1
+object name or a refname used with the remote name "origin".
+
+- deps (optional)
+
+An array of pairs consisting of a CMake variable name and a
+repository name to specify a dependent repo and a "link" to
+that repo's install artifacts. For example:
+
+"deps" : [
+ {
+ "var_name" : "VULKAN_HEADERS_INSTALL_DIR",
+ "repo_name" : "Vulkan-Headers"
+ }
+]
+
+which represents that this repository depends on the Vulkan-Headers
+repository and uses the VULKAN_HEADERS_INSTALL_DIR CMake variable to
+specify the location where it expects to find the Vulkan-Headers install
+directory.
+Note that the "repo_name" element must match the "name" element of some
+other repository in the JSON file.
+
+- prebuild (optional)
+- prebuild_linux (optional) (For Linux and MacOS)
+- prebuild_windows (optional)
+
+A list of commands to execute before building a dependent repository.
+This is useful for repositories that require the execution of some
+sort of "update" script or need to clone an auxillary repository like
+googletest.
+
+The commands listed in "prebuild" are executed first, and then the
+commands for the specific platform are executed.
+
+- custom_build (optional)
+
+A list of commands to execute as a custom build instead of using
+the built in CMake way of building. Requires "build_step" to be
+set to "custom"
+
+You can insert the following keywords into the commands listed in
+"custom_build" if they require runtime information (like whether the
+build config is "Debug" or "Release").
+
+Keywords:
+{0} reference to a dictionary of repos and their attributes
+{1} reference to the command line arguments set before start
+{2} reference to the CONFIG_MAP value of config.
+
+Example:
+{2} returns the CONFIG_MAP value of config e.g. debug -> Debug
+{1}.config returns the config variable set when you ran update_dep.py
+{0}[Vulkan-Headers][repo_root] returns the repo_root variable from
+ the Vulkan-Headers GoodRepo object.
+
+- cmake_options (optional)
+
+A list of options to pass to CMake during the generation phase.
+
+- ci_only (optional)
+
+A list of environment variables where one must be set to "true"
+(case-insensitive) in order for this repo to be fetched and built.
+This list can be used to specify repos that should be built only in CI.
+
+- build_step (optional)
+
+Specifies if the dependent repository should be built or not. This can
+have a value of 'build', 'custom', or 'skip'. The dependent repositories are
+built by default.
+
+- build_platforms (optional)
+
+A list of platforms the repository will be built on.
+Legal options include:
+"windows"
+"linux"
+"darwin"
+"android"
+
+Builds on all platforms by default.
+
+Note
+----
+
+The "sub_dir", "build_dir", and "install_dir" elements are all relative
+to the effective "top" directory. Specifying absolute paths is not
+supported. However, the "top" directory specified with the "--dir"
+option can be a relative or absolute path.
+
+"""
+
+import argparse
+import json
+import os
+import os.path
+import subprocess
+import sys
+import platform
+import multiprocessing
+import shlex
+import shutil
+import stat
+import time
+
+KNOWN_GOOD_FILE_NAME = 'known_good.json'
+
+CONFIG_MAP = {
+ 'debug': 'Debug',
+ 'release': 'Release',
+ 'relwithdebinfo': 'RelWithDebInfo',
+ 'minsizerel': 'MinSizeRel'
+}
+
+# NOTE: CMake also uses the VERBOSE environment variable. This is intentional.
+VERBOSE = os.getenv("VERBOSE")
+
+DEVNULL = open(os.devnull, 'wb')
+
+
+def on_rm_error( func, path, exc_info):
+ """Error handler for recursively removing a directory. The
+ shutil.rmtree function can fail on Windows due to read-only files.
+ This handler will change the permissions for the file and continue.
+ """
+ os.chmod( path, stat.S_IWRITE )
+ os.unlink( path )
+
+def make_or_exist_dirs(path):
+ "Wrapper for os.makedirs that tolerates the directory already existing"
+ # Could use os.makedirs(path, exist_ok=True) if we drop python2
+ if not os.path.isdir(path):
+ os.makedirs(path)
+
+def command_output(cmd, directory):
+ # Runs a command in a directory and returns its standard output stream.
+ # Captures the standard error stream and prints it an error occurs.
+ # Raises a RuntimeError if the command fails to launch or otherwise fails.
+ if VERBOSE:
+ print('In {d}: {cmd}'.format(d=directory, cmd=cmd))
+
+ result = subprocess.run(cmd, cwd=directory, capture_output=True, text=True)
+
+ if result.returncode != 0:
+ print(f'{result.stderr}', file=sys.stderr)
+ raise RuntimeError(f'Failed to run {cmd} in {directory}')
+
+ if VERBOSE:
+ print(result.stdout)
+ return result.stdout
+
+def run_cmake_command(cmake_cmd):
+ # NOTE: Because CMake is an exectuable that runs executables
+ # stdout/stderr are mixed together. So this combines the outputs
+ # and prints them properly in case there is a non-zero exit code.
+ result = subprocess.run(cmake_cmd,
+ stdout = subprocess.PIPE,
+ stderr = subprocess.STDOUT,
+ text = True
+ )
+
+ if VERBOSE:
+ print(result.stdout)
+ print(f"CMake command: {cmake_cmd} ", flush=True)
+
+ if result.returncode != 0:
+ print(result.stdout, file=sys.stderr)
+ sys.exit(result.returncode)
+
+def escape(path):
+ return path.replace('\\', '/')
+
+class GoodRepo(object):
+ """Represents a repository at a known-good commit."""
+
+ def __init__(self, json, args):
+ """Initializes this good repo object.
+
+ Args:
+ 'json': A fully populated JSON object describing the repo.
+ 'args': Results from ArgumentParser
+ """
+ self._json = json
+ self._args = args
+ # Required JSON elements
+ self.name = json['name']
+ self.url = json['url']
+ self.sub_dir = json['sub_dir']
+ self.commit = json['commit']
+ # Optional JSON elements
+ self.build_dir = None
+ self.install_dir = None
+ if json.get('build_dir'):
+ self.build_dir = os.path.normpath(json['build_dir'])
+ if json.get('install_dir'):
+ self.install_dir = os.path.normpath(json['install_dir'])
+ self.deps = json['deps'] if ('deps' in json) else []
+ self.prebuild = json['prebuild'] if ('prebuild' in json) else []
+ self.prebuild_linux = json['prebuild_linux'] if (
+ 'prebuild_linux' in json) else []
+ self.prebuild_windows = json['prebuild_windows'] if (
+ 'prebuild_windows' in json) else []
+ self.custom_build = json['custom_build'] if ('custom_build' in json) else []
+ self.cmake_options = json['cmake_options'] if (
+ 'cmake_options' in json) else []
+ self.ci_only = json['ci_only'] if ('ci_only' in json) else []
+ self.build_step = json['build_step'] if ('build_step' in json) else 'build'
+ self.build_platforms = json['build_platforms'] if ('build_platforms' in json) else []
+ self.optional = set(json.get('optional', []))
+ self.api = json['api'] if ('api' in json) else None
+ # Absolute paths for a repo's directories
+ dir_top = os.path.abspath(args.dir)
+ self.repo_dir = os.path.join(dir_top, self.sub_dir)
+ if self.build_dir:
+ self.build_dir = os.path.join(dir_top, self.build_dir)
+ if self.install_dir:
+ self.install_dir = os.path.join(dir_top, self.install_dir)
+
+ # By default the target platform is the host platform.
+ target_platform = platform.system().lower()
+ # However, we need to account for cross-compiling.
+ for cmake_var in self._args.cmake_var:
+ if "android.toolchain.cmake" in cmake_var:
+ target_platform = 'android'
+
+ self.on_build_platform = False
+ if self.build_platforms == [] or target_platform in self.build_platforms:
+ self.on_build_platform = True
+
+ def Clone(self, retries=10, retry_seconds=60):
+ if VERBOSE:
+ print('Cloning {n} into {d}'.format(n=self.name, d=self.repo_dir))
+ for retry in range(retries):
+ make_or_exist_dirs(self.repo_dir)
+ try:
+ command_output(['git', 'clone', self.url, '.'], self.repo_dir)
+ # If we get here, we didn't raise an error
+ return
+ except RuntimeError as e:
+ print("Error cloning on iteration {}/{}: {}".format(retry + 1, retries, e))
+ if retry + 1 < retries:
+ if retry_seconds > 0:
+ print("Waiting {} seconds before trying again".format(retry_seconds))
+ time.sleep(retry_seconds)
+ if os.path.isdir(self.repo_dir):
+ print("Removing old tree {}".format(self.repo_dir))
+ shutil.rmtree(self.repo_dir, onerror=on_rm_error)
+ continue
+
+ # If we get here, we've exhausted our retries.
+ print("Failed to clone {} on all retries.".format(self.url))
+ raise e
+
+ def Fetch(self, retries=10, retry_seconds=60):
+ for retry in range(retries):
+ try:
+ command_output(['git', 'fetch', 'origin'], self.repo_dir)
+ # if we get here, we didn't raise an error, and we're done
+ return
+ except RuntimeError as e:
+ print("Error fetching on iteration {}/{}: {}".format(retry + 1, retries, e))
+ if retry + 1 < retries:
+ if retry_seconds > 0:
+ print("Waiting {} seconds before trying again".format(retry_seconds))
+ time.sleep(retry_seconds)
+ continue
+
+ # If we get here, we've exhausted our retries.
+ print("Failed to fetch {} on all retries.".format(self.url))
+ raise e
+
+ def Checkout(self):
+ if VERBOSE:
+ print('Checking out {n} in {d}'.format(n=self.name, d=self.repo_dir))
+
+ if os.path.exists(os.path.join(self.repo_dir, '.git')):
+ url_changed = command_output(['git', 'config', '--get', 'remote.origin.url'], self.repo_dir).strip() != self.url
+ else:
+ url_changed = False
+
+ if self._args.do_clean_repo or url_changed:
+ if os.path.isdir(self.repo_dir):
+ if VERBOSE:
+ print('Clearing directory {d}'.format(d=self.repo_dir))
+ shutil.rmtree(self.repo_dir, onerror = on_rm_error)
+ if not os.path.exists(os.path.join(self.repo_dir, '.git')):
+ self.Clone()
+ self.Fetch()
+ if len(self._args.ref):
+ command_output(['git', 'checkout', self._args.ref], self.repo_dir)
+ else:
+ command_output(['git', 'checkout', self.commit], self.repo_dir)
+
+ if VERBOSE:
+ print(command_output(['git', 'status'], self.repo_dir))
+
+ def CustomPreProcess(self, cmd_str, repo_dict):
+ return cmd_str.format(repo_dict, self._args, CONFIG_MAP[self._args.config])
+
+ def PreBuild(self):
+ """Execute any prebuild steps from the repo root"""
+ for p in self.prebuild:
+ command_output(shlex.split(p), self.repo_dir)
+ if platform.system() == 'Linux' or platform.system() == 'Darwin':
+ for p in self.prebuild_linux:
+ command_output(shlex.split(p), self.repo_dir)
+ if platform.system() == 'Windows':
+ for p in self.prebuild_windows:
+ command_output(shlex.split(p), self.repo_dir)
+
+ def CustomBuild(self, repo_dict):
+ """Execute any custom_build steps from the repo root"""
+ for p in self.custom_build:
+ cmd = self.CustomPreProcess(p, repo_dict)
+ command_output(shlex.split(cmd), self.repo_dir)
+
+ def CMakeConfig(self, repos):
+ """Build CMake command for the configuration phase and execute it"""
+ if self._args.do_clean_build:
+ if os.path.isdir(self.build_dir):
+ shutil.rmtree(self.build_dir, onerror=on_rm_error)
+ if self._args.do_clean_install:
+ if os.path.isdir(self.install_dir):
+ shutil.rmtree(self.install_dir, onerror=on_rm_error)
+
+ # Create and change to build directory
+ make_or_exist_dirs(self.build_dir)
+ os.chdir(self.build_dir)
+
+ cmake_cmd = [
+ 'cmake', self.repo_dir,
+ '-DCMAKE_INSTALL_PREFIX=' + self.install_dir
+ ]
+
+ # Allow users to pass in arbitrary cache variables
+ for cmake_var in self._args.cmake_var:
+ pieces = cmake_var.split('=', 1)
+ cmake_cmd.append('-D{}={}'.format(pieces[0], pieces[1]))
+
+ # For each repo this repo depends on, generate a CMake variable
+ # definitions for "...INSTALL_DIR" that points to that dependent
+ # repo's install dir.
+ for d in self.deps:
+ dep_commit = [r for r in repos if r.name == d['repo_name']]
+ if len(dep_commit) and dep_commit[0].on_build_platform:
+ cmake_cmd.append('-D{var_name}={install_dir}'.format(
+ var_name=d['var_name'],
+ install_dir=dep_commit[0].install_dir))
+
+ # Add any CMake options
+ for option in self.cmake_options:
+ cmake_cmd.append(escape(option.format(**self.__dict__)))
+
+ # Set build config for single-configuration generators (this is a no-op on multi-config generators)
+ cmake_cmd.append(f'-D CMAKE_BUILD_TYPE={CONFIG_MAP[self._args.config]}')
+
+ # Use the CMake -A option to select the platform architecture
+ # without needing a Visual Studio generator.
+ if platform.system() == 'Windows' and not self._args.generator in ["Ninja", "MinGW Makefiles"]:
+ cmake_cmd.append('-A')
+ if self._args.arch.lower() == '64' or self._args.arch == 'x64' or self._args.arch == 'win64':
+ cmake_cmd.append('x64')
+ elif self._args.arch == 'arm64':
+ cmake_cmd.append('arm64')
+ else:
+ cmake_cmd.append('Win32')
+
+ # Apply a generator, if one is specified. This can be used to supply
+ # a specific generator for the dependent repositories to match
+ # that of the main repository.
+ if self._args.generator is not None:
+ cmake_cmd.extend(['-G', self._args.generator])
+
+ # Removes warnings related to unused CLI
+ # EX: Setting CMAKE_CXX_COMPILER for a C project
+ if not VERBOSE:
+ cmake_cmd.append("--no-warn-unused-cli")
+
+ run_cmake_command(cmake_cmd)
+
+ def CMakeBuild(self):
+ """Build CMake command for the build phase and execute it"""
+ cmake_cmd = ['cmake', '--build', self.build_dir, '--target', 'install', '--config', CONFIG_MAP[self._args.config]]
+ if self._args.do_clean:
+ cmake_cmd.append('--clean-first')
+
+ # Xcode / Ninja are parallel by default.
+ if self._args.generator != "Ninja" or self._args.generator != "Xcode":
+ cmake_cmd.append('--parallel')
+ cmake_cmd.append(format(multiprocessing.cpu_count()))
+
+ run_cmake_command(cmake_cmd)
+
+ def Build(self, repos, repo_dict):
+ """Build the dependent repo and time how long it took"""
+ if VERBOSE:
+ print('Building {n} in {d}'.format(n=self.name, d=self.repo_dir))
+ print('Build dir = {b}'.format(b=self.build_dir))
+ print('Install dir = {i}\n'.format(i=self.install_dir))
+
+ start = time.time()
+
+ # Run any prebuild commands
+ self.PreBuild()
+
+ if self.build_step == 'custom':
+ self.CustomBuild(repo_dict)
+ return
+
+ # Build and execute CMake command for creating build files
+ self.CMakeConfig(repos)
+
+ # Build and execute CMake command for the build
+ self.CMakeBuild()
+
+ total_time = time.time() - start
+
+ print(f"Installed {self.name} ({self.commit}) in {total_time} seconds", flush=True)
+
+ def IsOptional(self, opts):
+ return len(self.optional.intersection(opts)) > 0
+
+def GetGoodRepos(args):
+ """Returns the latest list of GoodRepo objects.
+
+ The known-good file is expected to be in the same
+ directory as this script unless overridden by the 'known_good_dir'
+ parameter.
+ """
+ if args.known_good_dir:
+ known_good_file = os.path.join( os.path.abspath(args.known_good_dir),
+ KNOWN_GOOD_FILE_NAME)
+ else:
+ known_good_file = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), KNOWN_GOOD_FILE_NAME)
+ with open(known_good_file) as known_good:
+ return [
+ GoodRepo(repo, args)
+ for repo in json.loads(known_good.read())['repos']
+ ]
+
+
+def GetInstallNames(args):
+ """Returns the install names list.
+
+ The known-good file is expected to be in the same
+ directory as this script unless overridden by the 'known_good_dir'
+ parameter.
+ """
+ if args.known_good_dir:
+ known_good_file = os.path.join(os.path.abspath(args.known_good_dir),
+ KNOWN_GOOD_FILE_NAME)
+ else:
+ known_good_file = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), KNOWN_GOOD_FILE_NAME)
+ with open(known_good_file) as known_good:
+ install_info = json.loads(known_good.read())
+ if install_info.get('install_names'):
+ return install_info['install_names']
+ else:
+ return None
+
+
+def CreateHelper(args, repos, filename):
+ """Create a CMake config helper file.
+
+ The helper file is intended to be used with 'cmake -C <file>'
+ to build this home repo using the dependencies built by this script.
+
+ The install_names dictionary represents the CMake variables used by the
+ home repo to locate the install dirs of the dependent repos.
+ This information is baked into the CMake files of the home repo and so
+ this dictionary is kept with the repo via the json file.
+ """
+ install_names = GetInstallNames(args)
+ with open(filename, 'w') as helper_file:
+ for repo in repos:
+ # If the repo has an API tag and that does not match
+ # the target API then skip it
+ if repo.api is not None and repo.api != args.api:
+ continue
+ if install_names and repo.name in install_names and repo.on_build_platform:
+ helper_file.write('set({var} "{dir}" CACHE STRING "")\n'
+ .format(
+ var=install_names[repo.name],
+ dir=escape(repo.install_dir)))
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Get and build dependent repos at known-good commits')
+ parser.add_argument(
+ '--known_good_dir',
+ dest='known_good_dir',
+ help="Specify directory for known_good.json file.")
+ parser.add_argument(
+ '--dir',
+ dest='dir',
+ default='.',
+ help="Set target directory for repository roots. Default is \'.\'.")
+ parser.add_argument(
+ '--ref',
+ dest='ref',
+ default='',
+ help="Override 'commit' with git reference. E.g., 'origin/main'")
+ parser.add_argument(
+ '--no-build',
+ dest='do_build',
+ action='store_false',
+ help=
+ "Clone/update repositories and generate build files without performing compilation",
+ default=True)
+ parser.add_argument(
+ '--clean',
+ dest='do_clean',
+ action='store_true',
+ help="Clean files generated by compiler and linker before building",
+ default=False)
+ parser.add_argument(
+ '--clean-repo',
+ dest='do_clean_repo',
+ action='store_true',
+ help="Delete repository directory before building",
+ default=False)
+ parser.add_argument(
+ '--clean-build',
+ dest='do_clean_build',
+ action='store_true',
+ help="Delete build directory before building",
+ default=False)
+ parser.add_argument(
+ '--clean-install',
+ dest='do_clean_install',
+ action='store_true',
+ help="Delete install directory before building",
+ default=False)
+ parser.add_argument(
+ '--skip-existing-install',
+ dest='skip_existing_install',
+ action='store_true',
+ help="Skip build if install directory exists",
+ default=False)
+ parser.add_argument(
+ '--arch',
+ dest='arch',
+ choices=['32', '64', 'x86', 'x64', 'win32', 'win64', 'arm64'],
+ type=str.lower,
+ help="Set build files architecture (Visual Studio Generator Only)",
+ default='64')
+ parser.add_argument(
+ '--config',
+ dest='config',
+ choices=['debug', 'release', 'relwithdebinfo', 'minsizerel'],
+ type=str.lower,
+ help="Set build files configuration",
+ default='debug')
+ parser.add_argument(
+ '--api',
+ dest='api',
+ default='vulkan',
+ choices=['vulkan'],
+ help="Target API")
+ parser.add_argument(
+ '--generator',
+ dest='generator',
+ help="Set the CMake generator",
+ default=None)
+ parser.add_argument(
+ '--optional',
+ dest='optional',
+ type=lambda a: set(a.lower().split(',')),
+ help="Comma-separated list of 'optional' resources that may be skipped. Only 'tests' is currently supported as 'optional'",
+ default=set())
+ parser.add_argument(
+ '--cmake_var',
+ dest='cmake_var',
+ action='append',
+ metavar='VAR[=VALUE]',
+ help="Add CMake command line option -D'VAR'='VALUE' to the CMake generation command line; may be used multiple times",
+ default=[])
+
+ args = parser.parse_args()
+ save_cwd = os.getcwd()
+
+ # Create working "top" directory if needed
+ make_or_exist_dirs(args.dir)
+ abs_top_dir = os.path.abspath(args.dir)
+
+ repos = GetGoodRepos(args)
+ repo_dict = {}
+
+ print('Starting builds in {d}'.format(d=abs_top_dir))
+ for repo in repos:
+ # If the repo has an API tag and that does not match
+ # the target API then skip it
+ if repo.api is not None and repo.api != args.api:
+ continue
+
+ # If the repo has a platform whitelist, skip the repo
+ # unless we are building on a whitelisted platform.
+ if not repo.on_build_platform:
+ continue
+
+ # Skip building the repo if its install directory already exists
+ # and requested via an option. This is useful for cases where the
+ # install directory is restored from a cache that is known to be up
+ # to date.
+ if args.skip_existing_install and os.path.isdir(repo.install_dir):
+ print('Skipping build for repo {n} due to existing install directory'.format(n=repo.name))
+ continue
+
+ # Skip test-only repos if the --tests option was not passed in
+ if repo.IsOptional(args.optional):
+ continue
+
+ field_list = ('url',
+ 'sub_dir',
+ 'commit',
+ 'build_dir',
+ 'install_dir',
+ 'deps',
+ 'prebuild',
+ 'prebuild_linux',
+ 'prebuild_windows',
+ 'custom_build',
+ 'cmake_options',
+ 'ci_only',
+ 'build_step',
+ 'build_platforms',
+ 'repo_dir',
+ 'on_build_platform')
+ repo_dict[repo.name] = {field: getattr(repo, field) for field in field_list}
+
+ # If the repo has a CI whitelist, skip the repo unless
+ # one of the CI's environment variable is set to true.
+ if len(repo.ci_only):
+ do_build = False
+ for env in repo.ci_only:
+ if env not in os.environ:
+ continue
+ if os.environ[env].lower() == 'true':
+ do_build = True
+ break
+ if not do_build:
+ continue
+
+ # Clone/update the repository
+ repo.Checkout()
+
+ # Build the repository
+ if args.do_build and repo.build_step != 'skip':
+ repo.Build(repos, repo_dict)
+
+ # Need to restore original cwd in order for CreateHelper to find json file
+ os.chdir(save_cwd)
+ CreateHelper(args, repos, os.path.join(abs_top_dir, 'helper.cmake'))
+
+ sys.exit(0)
+
+
+if __name__ == '__main__':
+ main()