680 lines
23 KiB
Python
680 lines
23 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
# Copyright 2017 The Glslang Authors. All rights reserved.
|
||
|
# Copyright (c) 2018 Valve Corporation
|
||
|
# Copyright (c) 2018 LunarG, 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.
|
||
|
|
||
|
# 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.
|
||
|
|
||
|
Python Compatibility
|
||
|
--------------------
|
||
|
|
||
|
This program can be used with Python 2.7 and Python 3.
|
||
|
|
||
|
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.
|
||
|
|
||
|
- 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".
|
||
|
For example, this field can be set to "origin/sdk-1.1.77" to
|
||
|
select the end of the sdk-1.1.77 branch.
|
||
|
|
||
|
- 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.
|
||
|
Typically, this list might contain "TRAVIS" and/or "APPVEYOR" because
|
||
|
each of these CI systems sets an environment variable with its own
|
||
|
name to "true". Note that this could also be (ab)used to control
|
||
|
the processing of the repo with any environment variable. The default
|
||
|
is an empty list, which means that the repo is always processed.
|
||
|
|
||
|
- 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"
|
||
|
|
||
|
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.
|
||
|
|
||
|
"""
|
||
|
|
||
|
from __future__ import print_function
|
||
|
|
||
|
import argparse
|
||
|
import json
|
||
|
import distutils.dir_util
|
||
|
import os.path
|
||
|
import subprocess
|
||
|
import sys
|
||
|
import platform
|
||
|
import multiprocessing
|
||
|
import shlex
|
||
|
import shutil
|
||
|
|
||
|
KNOWN_GOOD_FILE_NAME = 'known_good.json'
|
||
|
|
||
|
CONFIG_MAP = {
|
||
|
'debug': 'Debug',
|
||
|
'release': 'Release',
|
||
|
'relwithdebinfo': 'RelWithDebInfo',
|
||
|
'minsizerel': 'MinSizeRel'
|
||
|
}
|
||
|
|
||
|
VERBOSE = False
|
||
|
|
||
|
DEVNULL = open(os.devnull, 'wb')
|
||
|
|
||
|
|
||
|
def command_output(cmd, directory, fail_ok=False):
|
||
|
"""Runs a command in a directory and returns its standard output stream.
|
||
|
|
||
|
Captures the standard error stream and prints it if error.
|
||
|
|
||
|
Raises a RuntimeError if the command fails to launch or otherwise fails.
|
||
|
"""
|
||
|
if VERBOSE:
|
||
|
print('In {d}: {cmd}'.format(d=directory, cmd=cmd))
|
||
|
p = subprocess.Popen(
|
||
|
cmd, cwd=directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||
|
(stdout, stderr) = p.communicate()
|
||
|
if p.returncode != 0:
|
||
|
print('*** Error ***\nstderr contents:\n{}'.format(stderr))
|
||
|
if not fail_ok:
|
||
|
raise RuntimeError('Failed to run {} in {}'.format(cmd, directory))
|
||
|
if VERBOSE:
|
||
|
print(stdout)
|
||
|
return stdout
|
||
|
|
||
|
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 []
|
||
|
# 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)
|
||
|
# Check if platform is one to build on
|
||
|
self.on_build_platform = False
|
||
|
if self.build_platforms == [] or platform.system().lower() in self.build_platforms:
|
||
|
self.on_build_platform = True
|
||
|
|
||
|
def Clone(self):
|
||
|
distutils.dir_util.mkpath(self.repo_dir)
|
||
|
command_output(['git', 'clone', self.url, '.'], self.repo_dir)
|
||
|
|
||
|
def Fetch(self):
|
||
|
command_output(['git', 'fetch', 'origin'], self.repo_dir)
|
||
|
|
||
|
def Checkout(self):
|
||
|
print('Checking out {n} in {d}'.format(n=self.name, d=self.repo_dir))
|
||
|
if self._args.do_clean_repo:
|
||
|
shutil.rmtree(self.repo_dir, ignore_errors=True)
|
||
|
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)
|
||
|
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:
|
||
|
shutil.rmtree(self.build_dir)
|
||
|
if self._args.do_clean_install:
|
||
|
shutil.rmtree(self.install_dir)
|
||
|
|
||
|
# Create and change to build directory
|
||
|
distutils.dir_util.mkpath(self.build_dir)
|
||
|
os.chdir(self.build_dir)
|
||
|
|
||
|
cmake_cmd = [
|
||
|
'cmake', self.repo_dir,
|
||
|
'-DCMAKE_INSTALL_PREFIX=' + self.install_dir
|
||
|
]
|
||
|
|
||
|
# 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):
|
||
|
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(option)
|
||
|
|
||
|
# Set build config for single-configuration generators
|
||
|
if platform.system() == 'Linux' or platform.system() == 'Darwin':
|
||
|
cmake_cmd.append('-DCMAKE_BUILD_TYPE={config}'.format(
|
||
|
config=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':
|
||
|
if self._args.arch == '64' or self._args.arch == 'x64' or self._args.arch == 'win64':
|
||
|
cmake_cmd.append('-A')
|
||
|
cmake_cmd.append('x64')
|
||
|
|
||
|
# 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])
|
||
|
|
||
|
if VERBOSE:
|
||
|
print("CMake command: " + " ".join(cmake_cmd))
|
||
|
|
||
|
ret_code = subprocess.call(cmake_cmd)
|
||
|
if ret_code != 0:
|
||
|
sys.exit(ret_code)
|
||
|
|
||
|
def CMakeBuild(self):
|
||
|
"""Build CMake command for the build phase and execute it"""
|
||
|
cmake_cmd = ['cmake', '--build', self.build_dir, '--target', 'install']
|
||
|
if self._args.do_clean:
|
||
|
cmake_cmd.append('--clean-first')
|
||
|
|
||
|
if platform.system() == 'Windows':
|
||
|
cmake_cmd.append('--config')
|
||
|
cmake_cmd.append(CONFIG_MAP[self._args.config])
|
||
|
|
||
|
# Speed up the build.
|
||
|
if platform.system() == 'Linux' or platform.system() == 'Darwin':
|
||
|
cmake_cmd.append('--')
|
||
|
num_make_jobs = multiprocessing.cpu_count()
|
||
|
env_make_jobs = os.environ.get('MAKE_JOBS', None)
|
||
|
if env_make_jobs is not None:
|
||
|
try:
|
||
|
num_make_jobs = min(num_make_jobs, int(env_make_jobs))
|
||
|
except ValueError:
|
||
|
print('warning: environment variable MAKE_JOBS has non-numeric value "{}". '
|
||
|
'Using {} (CPU count) instead.'.format(env_make_jobs, num_make_jobs))
|
||
|
cmake_cmd.append('-j{}'.format(num_make_jobs))
|
||
|
if platform.system() == 'Windows':
|
||
|
cmake_cmd.append('--')
|
||
|
cmake_cmd.append('/maxcpucount')
|
||
|
|
||
|
if VERBOSE:
|
||
|
print("CMake command: " + " ".join(cmake_cmd))
|
||
|
|
||
|
ret_code = subprocess.call(cmake_cmd)
|
||
|
if ret_code != 0:
|
||
|
sys.exit(ret_code)
|
||
|
|
||
|
def Build(self, repos, repo_dict):
|
||
|
"""Build the dependent repo"""
|
||
|
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))
|
||
|
|
||
|
# 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()
|
||
|
|
||
|
|
||
|
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.
|
||
|
"""
|
||
|
def escape(path):
|
||
|
return path.replace('\\', '\\\\')
|
||
|
install_names = GetInstallNames(args)
|
||
|
with open(filename, 'w') as helper_file:
|
||
|
for repo in repos:
|
||
|
if install_names and repo.name in install_names and repo.on_build_platform:
|
||
|
helper_file.write('set({var} "{dir}" CACHE STRING "" FORCE)\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/master'")
|
||
|
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(
|
||
|
'--arch',
|
||
|
dest='arch',
|
||
|
choices=['32', '64', 'x86', 'x64', 'win32', 'win64'],
|
||
|
type=str.lower,
|
||
|
help="Set build files architecture (Windows)",
|
||
|
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(
|
||
|
'--generator',
|
||
|
dest='generator',
|
||
|
help="Set the CMake generator",
|
||
|
default=None)
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
save_cwd = os.getcwd()
|
||
|
|
||
|
# Create working "top" directory if needed
|
||
|
distutils.dir_util.mkpath(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 a platform whitelist, skip the repo
|
||
|
# unless we are building on a whitelisted platform.
|
||
|
if not repo.on_build_platform:
|
||
|
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 not env 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()
|