15db71995Sopenharmony_ci#!/usr/bin/env python3
25db71995Sopenharmony_ci
35db71995Sopenharmony_ci# Copyright 2017 The Glslang Authors. All rights reserved.
45db71995Sopenharmony_ci# Copyright (c) 2018-2023 Valve Corporation
55db71995Sopenharmony_ci# Copyright (c) 2018-2023 LunarG, Inc.
65db71995Sopenharmony_ci# Copyright (c) 2023-2023 RasterGrid Kft.
75db71995Sopenharmony_ci#
85db71995Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
95db71995Sopenharmony_ci# you may not use this file except in compliance with the License.
105db71995Sopenharmony_ci# You may obtain a copy of the License at
115db71995Sopenharmony_ci#
125db71995Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
135db71995Sopenharmony_ci#
145db71995Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
155db71995Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
165db71995Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
175db71995Sopenharmony_ci# See the License for the specific language governing permissions and
185db71995Sopenharmony_ci# limitations under the License.
195db71995Sopenharmony_ci
205db71995Sopenharmony_ci# This script was heavily leveraged from KhronosGroup/glslang
215db71995Sopenharmony_ci# update_glslang_sources.py.
225db71995Sopenharmony_ci"""update_deps.py
235db71995Sopenharmony_ci
245db71995Sopenharmony_ciGet and build dependent repositories using known-good commits.
255db71995Sopenharmony_ci
265db71995Sopenharmony_ciPurpose
275db71995Sopenharmony_ci-------
285db71995Sopenharmony_ci
295db71995Sopenharmony_ciThis program is intended to assist a developer of this repository
305db71995Sopenharmony_ci(the "home" repository) by gathering and building the repositories that
315db71995Sopenharmony_cithis home repository depend on.  It also checks out each dependent
325db71995Sopenharmony_cirepository at a "known-good" commit in order to provide stability in
335db71995Sopenharmony_cithe dependent repositories.
345db71995Sopenharmony_ci
355db71995Sopenharmony_ciKnown-Good JSON Database
365db71995Sopenharmony_ci------------------------
375db71995Sopenharmony_ci
385db71995Sopenharmony_ciThis program expects to find a file named "known-good.json" in the
395db71995Sopenharmony_cisame directory as the program file.  This JSON file is tailored for
405db71995Sopenharmony_cithe needs of the home repository by including its dependent repositories.
415db71995Sopenharmony_ci
425db71995Sopenharmony_ciProgram Options
435db71995Sopenharmony_ci---------------
445db71995Sopenharmony_ci
455db71995Sopenharmony_ciSee the help text (update_deps.py --help) for a complete list of options.
465db71995Sopenharmony_ci
475db71995Sopenharmony_ciProgram Operation
485db71995Sopenharmony_ci-----------------
495db71995Sopenharmony_ci
505db71995Sopenharmony_ciThe program uses the user's current directory at the time of program
515db71995Sopenharmony_ciinvocation as the location for fetching and building the dependent
525db71995Sopenharmony_cirepositories.  The user can override this by using the "--dir" option.
535db71995Sopenharmony_ci
545db71995Sopenharmony_ciFor example, a directory named "build" in the repository's root directory
555db71995Sopenharmony_ciis a good place to put the dependent repositories because that directory
565db71995Sopenharmony_ciis not tracked by Git. (See the .gitignore file.)  The "external" directory
575db71995Sopenharmony_cimay also be a suitable location.
585db71995Sopenharmony_ciA user can issue:
595db71995Sopenharmony_ci
605db71995Sopenharmony_ci$ cd My-Repo
615db71995Sopenharmony_ci$ mkdir build
625db71995Sopenharmony_ci$ cd build
635db71995Sopenharmony_ci$ ../scripts/update_deps.py
645db71995Sopenharmony_ci
655db71995Sopenharmony_cior, to do the same thing, but using the --dir option:
665db71995Sopenharmony_ci
675db71995Sopenharmony_ci$ cd My-Repo
685db71995Sopenharmony_ci$ mkdir build
695db71995Sopenharmony_ci$ scripts/update_deps.py --dir=build
705db71995Sopenharmony_ci
715db71995Sopenharmony_ciWith these commands, the "build" directory is considered the "top"
725db71995Sopenharmony_cidirectory where the program clones the dependent repositories.  The
735db71995Sopenharmony_ciJSON file configures the build and install working directories to be
745db71995Sopenharmony_ciwithin this "top" directory.
755db71995Sopenharmony_ci
765db71995Sopenharmony_ciNote that the "dir" option can also specify an absolute path:
775db71995Sopenharmony_ci
785db71995Sopenharmony_ci$ cd My-Repo
795db71995Sopenharmony_ci$ scripts/update_deps.py --dir=/tmp/deps
805db71995Sopenharmony_ci
815db71995Sopenharmony_ciThe "top" dir is then /tmp/deps (Linux filesystem example) and is
825db71995Sopenharmony_ciwhere this program will clone and build the dependent repositories.
835db71995Sopenharmony_ci
845db71995Sopenharmony_ciHelper CMake Config File
855db71995Sopenharmony_ci------------------------
865db71995Sopenharmony_ci
875db71995Sopenharmony_ciWhen the program finishes building the dependencies, it writes a file
885db71995Sopenharmony_cinamed "helper.cmake" to the "top" directory that contains CMake commands
895db71995Sopenharmony_cifor setting CMake variables for locating the dependent repositories.
905db71995Sopenharmony_ciThis helper file can be used to set up the CMake build files for this
915db71995Sopenharmony_ci"home" repository.
925db71995Sopenharmony_ci
935db71995Sopenharmony_ciA complete sequence might look like:
945db71995Sopenharmony_ci
955db71995Sopenharmony_ci$ git clone git@github.com:My-Group/My-Repo.git
965db71995Sopenharmony_ci$ cd My-Repo
975db71995Sopenharmony_ci$ mkdir build
985db71995Sopenharmony_ci$ cd build
995db71995Sopenharmony_ci$ ../scripts/update_deps.py
1005db71995Sopenharmony_ci$ cmake -C helper.cmake ..
1015db71995Sopenharmony_ci$ cmake --build .
1025db71995Sopenharmony_ci
1035db71995Sopenharmony_ciJSON File Schema
1045db71995Sopenharmony_ci----------------
1055db71995Sopenharmony_ci
1065db71995Sopenharmony_ciThere's no formal schema for the "known-good" JSON file, but here is
1075db71995Sopenharmony_cia description of its elements.  All elements are required except those
1085db71995Sopenharmony_cimarked as optional.  Please see the "known_good.json" file for
1095db71995Sopenharmony_ciexamples of all of these elements.
1105db71995Sopenharmony_ci
1115db71995Sopenharmony_ci- name
1125db71995Sopenharmony_ci
1135db71995Sopenharmony_ciThe name of the dependent repository.  This field can be referenced
1145db71995Sopenharmony_ciby the "deps.repo_name" structure to record a dependency.
1155db71995Sopenharmony_ci
1165db71995Sopenharmony_ci- api
1175db71995Sopenharmony_ci
1185db71995Sopenharmony_ciThe name of the API the dependency is specific to (e.g. "vulkan").
1195db71995Sopenharmony_ci
1205db71995Sopenharmony_ci- url
1215db71995Sopenharmony_ci
1225db71995Sopenharmony_ciSpecifies the URL of the repository.
1235db71995Sopenharmony_ciExample: https://github.com/KhronosGroup/Vulkan-Loader.git
1245db71995Sopenharmony_ci
1255db71995Sopenharmony_ci- sub_dir
1265db71995Sopenharmony_ci
1275db71995Sopenharmony_ciThe directory where the program clones the repository, relative to
1285db71995Sopenharmony_cithe "top" directory.
1295db71995Sopenharmony_ci
1305db71995Sopenharmony_ci- build_dir
1315db71995Sopenharmony_ci
1325db71995Sopenharmony_ciThe directory used to build the repository, relative to the "top"
1335db71995Sopenharmony_cidirectory.
1345db71995Sopenharmony_ci
1355db71995Sopenharmony_ci- install_dir
1365db71995Sopenharmony_ci
1375db71995Sopenharmony_ciThe directory used to store the installed build artifacts, relative
1385db71995Sopenharmony_cito the "top" directory.
1395db71995Sopenharmony_ci
1405db71995Sopenharmony_ci- commit
1415db71995Sopenharmony_ci
1425db71995Sopenharmony_ciThe commit used to checkout the repository.  This can be a SHA-1
1435db71995Sopenharmony_ciobject name or a refname used with the remote name "origin".
1445db71995Sopenharmony_ci
1455db71995Sopenharmony_ci- deps (optional)
1465db71995Sopenharmony_ci
1475db71995Sopenharmony_ciAn array of pairs consisting of a CMake variable name and a
1485db71995Sopenharmony_cirepository name to specify a dependent repo and a "link" to
1495db71995Sopenharmony_cithat repo's install artifacts.  For example:
1505db71995Sopenharmony_ci
1515db71995Sopenharmony_ci"deps" : [
1525db71995Sopenharmony_ci    {
1535db71995Sopenharmony_ci        "var_name" : "VULKAN_HEADERS_INSTALL_DIR",
1545db71995Sopenharmony_ci        "repo_name" : "Vulkan-Headers"
1555db71995Sopenharmony_ci    }
1565db71995Sopenharmony_ci]
1575db71995Sopenharmony_ci
1585db71995Sopenharmony_ciwhich represents that this repository depends on the Vulkan-Headers
1595db71995Sopenharmony_cirepository and uses the VULKAN_HEADERS_INSTALL_DIR CMake variable to
1605db71995Sopenharmony_cispecify the location where it expects to find the Vulkan-Headers install
1615db71995Sopenharmony_cidirectory.
1625db71995Sopenharmony_ciNote that the "repo_name" element must match the "name" element of some
1635db71995Sopenharmony_ciother repository in the JSON file.
1645db71995Sopenharmony_ci
1655db71995Sopenharmony_ci- prebuild (optional)
1665db71995Sopenharmony_ci- prebuild_linux (optional)  (For Linux and MacOS)
1675db71995Sopenharmony_ci- prebuild_windows (optional)
1685db71995Sopenharmony_ci
1695db71995Sopenharmony_ciA list of commands to execute before building a dependent repository.
1705db71995Sopenharmony_ciThis is useful for repositories that require the execution of some
1715db71995Sopenharmony_cisort of "update" script or need to clone an auxillary repository like
1725db71995Sopenharmony_cigoogletest.
1735db71995Sopenharmony_ci
1745db71995Sopenharmony_ciThe commands listed in "prebuild" are executed first, and then the
1755db71995Sopenharmony_cicommands for the specific platform are executed.
1765db71995Sopenharmony_ci
1775db71995Sopenharmony_ci- custom_build (optional)
1785db71995Sopenharmony_ci
1795db71995Sopenharmony_ciA list of commands to execute as a custom build instead of using
1805db71995Sopenharmony_cithe built in CMake way of building. Requires "build_step" to be
1815db71995Sopenharmony_ciset to "custom"
1825db71995Sopenharmony_ci
1835db71995Sopenharmony_ciYou can insert the following keywords into the commands listed in
1845db71995Sopenharmony_ci"custom_build" if they require runtime information (like whether the
1855db71995Sopenharmony_cibuild config is "Debug" or "Release").
1865db71995Sopenharmony_ci
1875db71995Sopenharmony_ciKeywords:
1885db71995Sopenharmony_ci{0} reference to a dictionary of repos and their attributes
1895db71995Sopenharmony_ci{1} reference to the command line arguments set before start
1905db71995Sopenharmony_ci{2} reference to the CONFIG_MAP value of config.
1915db71995Sopenharmony_ci
1925db71995Sopenharmony_ciExample:
1935db71995Sopenharmony_ci{2} returns the CONFIG_MAP value of config e.g. debug -> Debug
1945db71995Sopenharmony_ci{1}.config returns the config variable set when you ran update_dep.py
1955db71995Sopenharmony_ci{0}[Vulkan-Headers][repo_root] returns the repo_root variable from
1965db71995Sopenharmony_ci                                   the Vulkan-Headers GoodRepo object.
1975db71995Sopenharmony_ci
1985db71995Sopenharmony_ci- cmake_options (optional)
1995db71995Sopenharmony_ci
2005db71995Sopenharmony_ciA list of options to pass to CMake during the generation phase.
2015db71995Sopenharmony_ci
2025db71995Sopenharmony_ci- ci_only (optional)
2035db71995Sopenharmony_ci
2045db71995Sopenharmony_ciA list of environment variables where one must be set to "true"
2055db71995Sopenharmony_ci(case-insensitive) in order for this repo to be fetched and built.
2065db71995Sopenharmony_ciThis list can be used to specify repos that should be built only in CI.
2075db71995Sopenharmony_ci
2085db71995Sopenharmony_ci- build_step (optional)
2095db71995Sopenharmony_ci
2105db71995Sopenharmony_ciSpecifies if the dependent repository should be built or not. This can
2115db71995Sopenharmony_cihave a value of 'build', 'custom',  or 'skip'. The dependent repositories are
2125db71995Sopenharmony_cibuilt by default.
2135db71995Sopenharmony_ci
2145db71995Sopenharmony_ci- build_platforms (optional)
2155db71995Sopenharmony_ci
2165db71995Sopenharmony_ciA list of platforms the repository will be built on.
2175db71995Sopenharmony_ciLegal options include:
2185db71995Sopenharmony_ci"windows"
2195db71995Sopenharmony_ci"linux"
2205db71995Sopenharmony_ci"darwin"
2215db71995Sopenharmony_ci"android"
2225db71995Sopenharmony_ci
2235db71995Sopenharmony_ciBuilds on all platforms by default.
2245db71995Sopenharmony_ci
2255db71995Sopenharmony_ciNote
2265db71995Sopenharmony_ci----
2275db71995Sopenharmony_ci
2285db71995Sopenharmony_ciThe "sub_dir", "build_dir", and "install_dir" elements are all relative
2295db71995Sopenharmony_cito the effective "top" directory.  Specifying absolute paths is not
2305db71995Sopenharmony_cisupported.  However, the "top" directory specified with the "--dir"
2315db71995Sopenharmony_cioption can be a relative or absolute path.
2325db71995Sopenharmony_ci
2335db71995Sopenharmony_ci"""
2345db71995Sopenharmony_ci
2355db71995Sopenharmony_ciimport argparse
2365db71995Sopenharmony_ciimport json
2375db71995Sopenharmony_ciimport os
2385db71995Sopenharmony_ciimport os.path
2395db71995Sopenharmony_ciimport subprocess
2405db71995Sopenharmony_ciimport sys
2415db71995Sopenharmony_ciimport platform
2425db71995Sopenharmony_ciimport multiprocessing
2435db71995Sopenharmony_ciimport shlex
2445db71995Sopenharmony_ciimport shutil
2455db71995Sopenharmony_ciimport stat
2465db71995Sopenharmony_ciimport time
2475db71995Sopenharmony_ci
2485db71995Sopenharmony_ciKNOWN_GOOD_FILE_NAME = 'known_good.json'
2495db71995Sopenharmony_ci
2505db71995Sopenharmony_ciCONFIG_MAP = {
2515db71995Sopenharmony_ci    'debug': 'Debug',
2525db71995Sopenharmony_ci    'release': 'Release',
2535db71995Sopenharmony_ci    'relwithdebinfo': 'RelWithDebInfo',
2545db71995Sopenharmony_ci    'minsizerel': 'MinSizeRel'
2555db71995Sopenharmony_ci}
2565db71995Sopenharmony_ci
2575db71995Sopenharmony_ci# NOTE: CMake also uses the VERBOSE environment variable. This is intentional.
2585db71995Sopenharmony_ciVERBOSE = os.getenv("VERBOSE")
2595db71995Sopenharmony_ci
2605db71995Sopenharmony_ciDEVNULL = open(os.devnull, 'wb')
2615db71995Sopenharmony_ci
2625db71995Sopenharmony_ci
2635db71995Sopenharmony_cidef on_rm_error( func, path, exc_info):
2645db71995Sopenharmony_ci    """Error handler for recursively removing a directory. The
2655db71995Sopenharmony_ci    shutil.rmtree function can fail on Windows due to read-only files.
2665db71995Sopenharmony_ci    This handler will change the permissions for the file and continue.
2675db71995Sopenharmony_ci    """
2685db71995Sopenharmony_ci    os.chmod( path, stat.S_IWRITE )
2695db71995Sopenharmony_ci    os.unlink( path )
2705db71995Sopenharmony_ci
2715db71995Sopenharmony_cidef make_or_exist_dirs(path):
2725db71995Sopenharmony_ci    "Wrapper for os.makedirs that tolerates the directory already existing"
2735db71995Sopenharmony_ci    # Could use os.makedirs(path, exist_ok=True) if we drop python2
2745db71995Sopenharmony_ci    if not os.path.isdir(path):
2755db71995Sopenharmony_ci        os.makedirs(path)
2765db71995Sopenharmony_ci
2775db71995Sopenharmony_cidef command_output(cmd, directory):
2785db71995Sopenharmony_ci    # Runs a command in a directory and returns its standard output stream.
2795db71995Sopenharmony_ci    # Captures the standard error stream and prints it an error occurs.
2805db71995Sopenharmony_ci    # Raises a RuntimeError if the command fails to launch or otherwise fails.
2815db71995Sopenharmony_ci    if VERBOSE:
2825db71995Sopenharmony_ci        print('In {d}: {cmd}'.format(d=directory, cmd=cmd))
2835db71995Sopenharmony_ci
2845db71995Sopenharmony_ci    result = subprocess.run(cmd, cwd=directory, capture_output=True, text=True)
2855db71995Sopenharmony_ci
2865db71995Sopenharmony_ci    if result.returncode != 0:
2875db71995Sopenharmony_ci        print(f'{result.stderr}', file=sys.stderr)
2885db71995Sopenharmony_ci        raise RuntimeError(f'Failed to run {cmd} in {directory}')
2895db71995Sopenharmony_ci
2905db71995Sopenharmony_ci    if VERBOSE:
2915db71995Sopenharmony_ci        print(result.stdout)
2925db71995Sopenharmony_ci    return result.stdout
2935db71995Sopenharmony_ci
2945db71995Sopenharmony_cidef run_cmake_command(cmake_cmd):
2955db71995Sopenharmony_ci    # NOTE: Because CMake is an exectuable that runs executables
2965db71995Sopenharmony_ci    # stdout/stderr are mixed together. So this combines the outputs
2975db71995Sopenharmony_ci    # and prints them properly in case there is a non-zero exit code.
2985db71995Sopenharmony_ci    result = subprocess.run(cmake_cmd,
2995db71995Sopenharmony_ci        stdout = subprocess.PIPE,
3005db71995Sopenharmony_ci        stderr = subprocess.STDOUT,
3015db71995Sopenharmony_ci        text = True
3025db71995Sopenharmony_ci    )
3035db71995Sopenharmony_ci
3045db71995Sopenharmony_ci    if VERBOSE:
3055db71995Sopenharmony_ci        print(result.stdout)
3065db71995Sopenharmony_ci        print(f"CMake command: {cmake_cmd} ", flush=True)
3075db71995Sopenharmony_ci
3085db71995Sopenharmony_ci    if result.returncode != 0:
3095db71995Sopenharmony_ci        print(result.stdout, file=sys.stderr)
3105db71995Sopenharmony_ci        sys.exit(result.returncode)
3115db71995Sopenharmony_ci
3125db71995Sopenharmony_cidef escape(path):
3135db71995Sopenharmony_ci    return path.replace('\\', '/')
3145db71995Sopenharmony_ci
3155db71995Sopenharmony_ciclass GoodRepo(object):
3165db71995Sopenharmony_ci    """Represents a repository at a known-good commit."""
3175db71995Sopenharmony_ci
3185db71995Sopenharmony_ci    def __init__(self, json, args):
3195db71995Sopenharmony_ci        """Initializes this good repo object.
3205db71995Sopenharmony_ci
3215db71995Sopenharmony_ci        Args:
3225db71995Sopenharmony_ci        'json':  A fully populated JSON object describing the repo.
3235db71995Sopenharmony_ci        'args':  Results from ArgumentParser
3245db71995Sopenharmony_ci        """
3255db71995Sopenharmony_ci        self._json = json
3265db71995Sopenharmony_ci        self._args = args
3275db71995Sopenharmony_ci        # Required JSON elements
3285db71995Sopenharmony_ci        self.name = json['name']
3295db71995Sopenharmony_ci        self.url = json['url']
3305db71995Sopenharmony_ci        self.sub_dir = json['sub_dir']
3315db71995Sopenharmony_ci        self.commit = json['commit']
3325db71995Sopenharmony_ci        # Optional JSON elements
3335db71995Sopenharmony_ci        self.build_dir = None
3345db71995Sopenharmony_ci        self.install_dir = None
3355db71995Sopenharmony_ci        if json.get('build_dir'):
3365db71995Sopenharmony_ci            self.build_dir = os.path.normpath(json['build_dir'])
3375db71995Sopenharmony_ci        if json.get('install_dir'):
3385db71995Sopenharmony_ci            self.install_dir = os.path.normpath(json['install_dir'])
3395db71995Sopenharmony_ci        self.deps = json['deps'] if ('deps' in json) else []
3405db71995Sopenharmony_ci        self.prebuild = json['prebuild'] if ('prebuild' in json) else []
3415db71995Sopenharmony_ci        self.prebuild_linux = json['prebuild_linux'] if (
3425db71995Sopenharmony_ci            'prebuild_linux' in json) else []
3435db71995Sopenharmony_ci        self.prebuild_windows = json['prebuild_windows'] if (
3445db71995Sopenharmony_ci            'prebuild_windows' in json) else []
3455db71995Sopenharmony_ci        self.custom_build = json['custom_build'] if ('custom_build' in json) else []
3465db71995Sopenharmony_ci        self.cmake_options = json['cmake_options'] if (
3475db71995Sopenharmony_ci            'cmake_options' in json) else []
3485db71995Sopenharmony_ci        self.ci_only = json['ci_only'] if ('ci_only' in json) else []
3495db71995Sopenharmony_ci        self.build_step = json['build_step'] if ('build_step' in json) else 'build'
3505db71995Sopenharmony_ci        self.build_platforms = json['build_platforms'] if ('build_platforms' in json) else []
3515db71995Sopenharmony_ci        self.optional = set(json.get('optional', []))
3525db71995Sopenharmony_ci        self.api = json['api'] if ('api' in json) else None
3535db71995Sopenharmony_ci        # Absolute paths for a repo's directories
3545db71995Sopenharmony_ci        dir_top = os.path.abspath(args.dir)
3555db71995Sopenharmony_ci        self.repo_dir = os.path.join(dir_top, self.sub_dir)
3565db71995Sopenharmony_ci        if self.build_dir:
3575db71995Sopenharmony_ci            self.build_dir = os.path.join(dir_top, self.build_dir)
3585db71995Sopenharmony_ci        if self.install_dir:
3595db71995Sopenharmony_ci            self.install_dir = os.path.join(dir_top, self.install_dir)
3605db71995Sopenharmony_ci
3615db71995Sopenharmony_ci        # By default the target platform is the host platform.
3625db71995Sopenharmony_ci        target_platform = platform.system().lower()
3635db71995Sopenharmony_ci        # However, we need to account for cross-compiling.
3645db71995Sopenharmony_ci        for cmake_var in self._args.cmake_var:
3655db71995Sopenharmony_ci            if "android.toolchain.cmake" in cmake_var:
3665db71995Sopenharmony_ci                target_platform = 'android'
3675db71995Sopenharmony_ci
3685db71995Sopenharmony_ci        self.on_build_platform = False
3695db71995Sopenharmony_ci        if self.build_platforms == [] or target_platform in self.build_platforms:
3705db71995Sopenharmony_ci            self.on_build_platform = True
3715db71995Sopenharmony_ci
3725db71995Sopenharmony_ci    def Clone(self, retries=10, retry_seconds=60):
3735db71995Sopenharmony_ci        if VERBOSE:
3745db71995Sopenharmony_ci            print('Cloning {n} into {d}'.format(n=self.name, d=self.repo_dir))
3755db71995Sopenharmony_ci        for retry in range(retries):
3765db71995Sopenharmony_ci            make_or_exist_dirs(self.repo_dir)
3775db71995Sopenharmony_ci            try:
3785db71995Sopenharmony_ci                command_output(['git', 'clone', self.url, '.'], self.repo_dir)
3795db71995Sopenharmony_ci                # If we get here, we didn't raise an error
3805db71995Sopenharmony_ci                return
3815db71995Sopenharmony_ci            except RuntimeError as e:
3825db71995Sopenharmony_ci                print("Error cloning on iteration {}/{}: {}".format(retry + 1, retries, e))
3835db71995Sopenharmony_ci                if retry + 1 < retries:
3845db71995Sopenharmony_ci                    if retry_seconds > 0:
3855db71995Sopenharmony_ci                        print("Waiting {} seconds before trying again".format(retry_seconds))
3865db71995Sopenharmony_ci                        time.sleep(retry_seconds)
3875db71995Sopenharmony_ci                    if os.path.isdir(self.repo_dir):
3885db71995Sopenharmony_ci                        print("Removing old tree {}".format(self.repo_dir))
3895db71995Sopenharmony_ci                        shutil.rmtree(self.repo_dir, onerror=on_rm_error)
3905db71995Sopenharmony_ci                    continue
3915db71995Sopenharmony_ci
3925db71995Sopenharmony_ci                # If we get here, we've exhausted our retries.
3935db71995Sopenharmony_ci                print("Failed to clone {} on all retries.".format(self.url))
3945db71995Sopenharmony_ci                raise e
3955db71995Sopenharmony_ci
3965db71995Sopenharmony_ci    def Fetch(self, retries=10, retry_seconds=60):
3975db71995Sopenharmony_ci        for retry in range(retries):
3985db71995Sopenharmony_ci            try:
3995db71995Sopenharmony_ci                command_output(['git', 'fetch', 'origin'], self.repo_dir)
4005db71995Sopenharmony_ci                # if we get here, we didn't raise an error, and we're done
4015db71995Sopenharmony_ci                return
4025db71995Sopenharmony_ci            except RuntimeError as e:
4035db71995Sopenharmony_ci                print("Error fetching on iteration {}/{}: {}".format(retry + 1, retries, e))
4045db71995Sopenharmony_ci                if retry + 1 < retries:
4055db71995Sopenharmony_ci                    if retry_seconds > 0:
4065db71995Sopenharmony_ci                        print("Waiting {} seconds before trying again".format(retry_seconds))
4075db71995Sopenharmony_ci                        time.sleep(retry_seconds)
4085db71995Sopenharmony_ci                    continue
4095db71995Sopenharmony_ci
4105db71995Sopenharmony_ci                # If we get here, we've exhausted our retries.
4115db71995Sopenharmony_ci                print("Failed to fetch {} on all retries.".format(self.url))
4125db71995Sopenharmony_ci                raise e
4135db71995Sopenharmony_ci
4145db71995Sopenharmony_ci    def Checkout(self):
4155db71995Sopenharmony_ci        if VERBOSE:
4165db71995Sopenharmony_ci            print('Checking out {n} in {d}'.format(n=self.name, d=self.repo_dir))
4175db71995Sopenharmony_ci
4185db71995Sopenharmony_ci        if self._args.do_clean_repo:
4195db71995Sopenharmony_ci            if os.path.isdir(self.repo_dir):
4205db71995Sopenharmony_ci                shutil.rmtree(self.repo_dir, onerror = on_rm_error)
4215db71995Sopenharmony_ci        if not os.path.exists(os.path.join(self.repo_dir, '.git')):
4225db71995Sopenharmony_ci            self.Clone()
4235db71995Sopenharmony_ci        self.Fetch()
4245db71995Sopenharmony_ci        if len(self._args.ref):
4255db71995Sopenharmony_ci            command_output(['git', 'checkout', self._args.ref], self.repo_dir)
4265db71995Sopenharmony_ci        else:
4275db71995Sopenharmony_ci            command_output(['git', 'checkout', self.commit], self.repo_dir)
4285db71995Sopenharmony_ci
4295db71995Sopenharmony_ci        if VERBOSE:
4305db71995Sopenharmony_ci            print(command_output(['git', 'status'], self.repo_dir))
4315db71995Sopenharmony_ci
4325db71995Sopenharmony_ci    def CustomPreProcess(self, cmd_str, repo_dict):
4335db71995Sopenharmony_ci        return cmd_str.format(repo_dict, self._args, CONFIG_MAP[self._args.config])
4345db71995Sopenharmony_ci
4355db71995Sopenharmony_ci    def PreBuild(self):
4365db71995Sopenharmony_ci        """Execute any prebuild steps from the repo root"""
4375db71995Sopenharmony_ci        for p in self.prebuild:
4385db71995Sopenharmony_ci            command_output(shlex.split(p), self.repo_dir)
4395db71995Sopenharmony_ci        if platform.system() == 'Linux' or platform.system() == 'Darwin':
4405db71995Sopenharmony_ci            for p in self.prebuild_linux:
4415db71995Sopenharmony_ci                command_output(shlex.split(p), self.repo_dir)
4425db71995Sopenharmony_ci        if platform.system() == 'Windows':
4435db71995Sopenharmony_ci            for p in self.prebuild_windows:
4445db71995Sopenharmony_ci                command_output(shlex.split(p), self.repo_dir)
4455db71995Sopenharmony_ci
4465db71995Sopenharmony_ci    def CustomBuild(self, repo_dict):
4475db71995Sopenharmony_ci        """Execute any custom_build steps from the repo root"""
4485db71995Sopenharmony_ci
4495db71995Sopenharmony_ci        # It's not uncommon for builds to not support universal binaries
4505db71995Sopenharmony_ci        if self._args.OSX_ARCHITECTURES:
4515db71995Sopenharmony_ci            print("Universal Binaries not supported for custom builds", file=sys.stderr)
4525db71995Sopenharmony_ci            exit(-1)
4535db71995Sopenharmony_ci
4545db71995Sopenharmony_ci        for p in self.custom_build:
4555db71995Sopenharmony_ci            cmd = self.CustomPreProcess(p, repo_dict)
4565db71995Sopenharmony_ci            command_output(shlex.split(cmd), self.repo_dir)
4575db71995Sopenharmony_ci
4585db71995Sopenharmony_ci    def CMakeConfig(self, repos):
4595db71995Sopenharmony_ci        """Build CMake command for the configuration phase and execute it"""
4605db71995Sopenharmony_ci        if self._args.do_clean_build:
4615db71995Sopenharmony_ci            if os.path.isdir(self.build_dir):
4625db71995Sopenharmony_ci                shutil.rmtree(self.build_dir, onerror=on_rm_error)
4635db71995Sopenharmony_ci        if self._args.do_clean_install:
4645db71995Sopenharmony_ci            if os.path.isdir(self.install_dir):
4655db71995Sopenharmony_ci                shutil.rmtree(self.install_dir, onerror=on_rm_error)
4665db71995Sopenharmony_ci
4675db71995Sopenharmony_ci        # Create and change to build directory
4685db71995Sopenharmony_ci        make_or_exist_dirs(self.build_dir)
4695db71995Sopenharmony_ci        os.chdir(self.build_dir)
4705db71995Sopenharmony_ci
4715db71995Sopenharmony_ci        cmake_cmd = [
4725db71995Sopenharmony_ci            'cmake', self.repo_dir,
4735db71995Sopenharmony_ci            '-DCMAKE_INSTALL_PREFIX=' + self.install_dir
4745db71995Sopenharmony_ci        ]
4755db71995Sopenharmony_ci
4765db71995Sopenharmony_ci        # Allow users to pass in arbitrary cache variables
4775db71995Sopenharmony_ci        for cmake_var in self._args.cmake_var:
4785db71995Sopenharmony_ci            pieces = cmake_var.split('=', 1)
4795db71995Sopenharmony_ci            cmake_cmd.append('-D{}={}'.format(pieces[0], pieces[1]))
4805db71995Sopenharmony_ci
4815db71995Sopenharmony_ci        # For each repo this repo depends on, generate a CMake variable
4825db71995Sopenharmony_ci        # definitions for "...INSTALL_DIR" that points to that dependent
4835db71995Sopenharmony_ci        # repo's install dir.
4845db71995Sopenharmony_ci        for d in self.deps:
4855db71995Sopenharmony_ci            dep_commit = [r for r in repos if r.name == d['repo_name']]
4865db71995Sopenharmony_ci            if len(dep_commit) and dep_commit[0].on_build_platform:
4875db71995Sopenharmony_ci                cmake_cmd.append('-D{var_name}={install_dir}'.format(
4885db71995Sopenharmony_ci                    var_name=d['var_name'],
4895db71995Sopenharmony_ci                    install_dir=dep_commit[0].install_dir))
4905db71995Sopenharmony_ci
4915db71995Sopenharmony_ci        # Add any CMake options
4925db71995Sopenharmony_ci        for option in self.cmake_options:
4935db71995Sopenharmony_ci            cmake_cmd.append(escape(option.format(**self.__dict__)))
4945db71995Sopenharmony_ci
4955db71995Sopenharmony_ci        # Set build config for single-configuration generators (this is a no-op on multi-config generators)
4965db71995Sopenharmony_ci        cmake_cmd.append(f'-D CMAKE_BUILD_TYPE={CONFIG_MAP[self._args.config]}')
4975db71995Sopenharmony_ci
4985db71995Sopenharmony_ci        if self._args.OSX_ARCHITECTURES:
4995db71995Sopenharmony_ci            # CMAKE_OSX_ARCHITECTURES must be a semi-colon seperated list
5005db71995Sopenharmony_ci            cmake_osx_archs = self._args.OSX_ARCHITECTURES.replace(':', ';')
5015db71995Sopenharmony_ci            cmake_cmd.append(f'-D CMAKE_OSX_ARCHITECTURES={cmake_osx_archs}')
5025db71995Sopenharmony_ci
5035db71995Sopenharmony_ci        # Use the CMake -A option to select the platform architecture
5045db71995Sopenharmony_ci        # without needing a Visual Studio generator.
5055db71995Sopenharmony_ci        if platform.system() == 'Windows' and self._args.generator != "Ninja":
5065db71995Sopenharmony_ci            if self._args.arch.lower() == '64' or self._args.arch == 'x64' or self._args.arch == 'win64':
5075db71995Sopenharmony_ci                cmake_cmd.append('-A')
5085db71995Sopenharmony_ci                cmake_cmd.append('x64')
5095db71995Sopenharmony_ci            else:
5105db71995Sopenharmony_ci                cmake_cmd.append('-A')
5115db71995Sopenharmony_ci                cmake_cmd.append('Win32')
5125db71995Sopenharmony_ci
5135db71995Sopenharmony_ci        # Apply a generator, if one is specified.  This can be used to supply
5145db71995Sopenharmony_ci        # a specific generator for the dependent repositories to match
5155db71995Sopenharmony_ci        # that of the main repository.
5165db71995Sopenharmony_ci        if self._args.generator is not None:
5175db71995Sopenharmony_ci            cmake_cmd.extend(['-G', self._args.generator])
5185db71995Sopenharmony_ci
5195db71995Sopenharmony_ci        # Removes warnings related to unused CLI
5205db71995Sopenharmony_ci        # EX: Setting CMAKE_CXX_COMPILER for a C project
5215db71995Sopenharmony_ci        if not VERBOSE:
5225db71995Sopenharmony_ci            cmake_cmd.append("--no-warn-unused-cli")
5235db71995Sopenharmony_ci
5245db71995Sopenharmony_ci        run_cmake_command(cmake_cmd)
5255db71995Sopenharmony_ci
5265db71995Sopenharmony_ci    def CMakeBuild(self):
5275db71995Sopenharmony_ci        """Build CMake command for the build phase and execute it"""
5285db71995Sopenharmony_ci        cmake_cmd = ['cmake', '--build', self.build_dir, '--target', 'install', '--config', CONFIG_MAP[self._args.config]]
5295db71995Sopenharmony_ci        if self._args.do_clean:
5305db71995Sopenharmony_ci            cmake_cmd.append('--clean-first')
5315db71995Sopenharmony_ci
5325db71995Sopenharmony_ci        # Xcode / Ninja are parallel by default.
5335db71995Sopenharmony_ci        if self._args.generator != "Ninja" or self._args.generator != "Xcode":
5345db71995Sopenharmony_ci            cmake_cmd.append('--parallel')
5355db71995Sopenharmony_ci            cmake_cmd.append(format(multiprocessing.cpu_count()))
5365db71995Sopenharmony_ci
5375db71995Sopenharmony_ci        run_cmake_command(cmake_cmd)
5385db71995Sopenharmony_ci
5395db71995Sopenharmony_ci    def Build(self, repos, repo_dict):
5405db71995Sopenharmony_ci        """Build the dependent repo and time how long it took"""
5415db71995Sopenharmony_ci        if VERBOSE:
5425db71995Sopenharmony_ci            print('Building {n} in {d}'.format(n=self.name, d=self.repo_dir))
5435db71995Sopenharmony_ci            print('Build dir = {b}'.format(b=self.build_dir))
5445db71995Sopenharmony_ci            print('Install dir = {i}\n'.format(i=self.install_dir))
5455db71995Sopenharmony_ci
5465db71995Sopenharmony_ci        start = time.time()
5475db71995Sopenharmony_ci
5485db71995Sopenharmony_ci        self.PreBuild()
5495db71995Sopenharmony_ci
5505db71995Sopenharmony_ci        if self.build_step == 'custom':
5515db71995Sopenharmony_ci            self.CustomBuild(repo_dict)
5525db71995Sopenharmony_ci        else:
5535db71995Sopenharmony_ci            self.CMakeConfig(repos)
5545db71995Sopenharmony_ci            self.CMakeBuild()
5555db71995Sopenharmony_ci
5565db71995Sopenharmony_ci        total_time = time.time() - start
5575db71995Sopenharmony_ci
5585db71995Sopenharmony_ci        print(f"Installed {self.name} ({self.commit}) in {total_time} seconds", flush=True)
5595db71995Sopenharmony_ci
5605db71995Sopenharmony_ci    def IsOptional(self, opts):
5615db71995Sopenharmony_ci        return len(self.optional.intersection(opts)) > 0
5625db71995Sopenharmony_ci
5635db71995Sopenharmony_cidef GetGoodRepos(args):
5645db71995Sopenharmony_ci    """Returns the latest list of GoodRepo objects.
5655db71995Sopenharmony_ci
5665db71995Sopenharmony_ci    The known-good file is expected to be in the same
5675db71995Sopenharmony_ci    directory as this script unless overridden by the 'known_good_dir'
5685db71995Sopenharmony_ci    parameter.
5695db71995Sopenharmony_ci    """
5705db71995Sopenharmony_ci    if args.known_good_dir:
5715db71995Sopenharmony_ci        known_good_file = os.path.join( os.path.abspath(args.known_good_dir),
5725db71995Sopenharmony_ci            KNOWN_GOOD_FILE_NAME)
5735db71995Sopenharmony_ci    else:
5745db71995Sopenharmony_ci        known_good_file = os.path.join(
5755db71995Sopenharmony_ci            os.path.dirname(os.path.abspath(__file__)), KNOWN_GOOD_FILE_NAME)
5765db71995Sopenharmony_ci    with open(known_good_file) as known_good:
5775db71995Sopenharmony_ci        return [
5785db71995Sopenharmony_ci            GoodRepo(repo, args)
5795db71995Sopenharmony_ci            for repo in json.loads(known_good.read())['repos']
5805db71995Sopenharmony_ci        ]
5815db71995Sopenharmony_ci
5825db71995Sopenharmony_ci
5835db71995Sopenharmony_cidef GetInstallNames(args):
5845db71995Sopenharmony_ci    """Returns the install names list.
5855db71995Sopenharmony_ci
5865db71995Sopenharmony_ci    The known-good file is expected to be in the same
5875db71995Sopenharmony_ci    directory as this script unless overridden by the 'known_good_dir'
5885db71995Sopenharmony_ci    parameter.
5895db71995Sopenharmony_ci    """
5905db71995Sopenharmony_ci    if args.known_good_dir:
5915db71995Sopenharmony_ci        known_good_file = os.path.join(os.path.abspath(args.known_good_dir),
5925db71995Sopenharmony_ci            KNOWN_GOOD_FILE_NAME)
5935db71995Sopenharmony_ci    else:
5945db71995Sopenharmony_ci        known_good_file = os.path.join(
5955db71995Sopenharmony_ci            os.path.dirname(os.path.abspath(__file__)), KNOWN_GOOD_FILE_NAME)
5965db71995Sopenharmony_ci    with open(known_good_file) as known_good:
5975db71995Sopenharmony_ci        install_info = json.loads(known_good.read())
5985db71995Sopenharmony_ci        if install_info.get('install_names'):
5995db71995Sopenharmony_ci            return install_info['install_names']
6005db71995Sopenharmony_ci        else:
6015db71995Sopenharmony_ci            return None
6025db71995Sopenharmony_ci
6035db71995Sopenharmony_ci
6045db71995Sopenharmony_cidef CreateHelper(args, repos, filename):
6055db71995Sopenharmony_ci    """Create a CMake config helper file.
6065db71995Sopenharmony_ci
6075db71995Sopenharmony_ci    The helper file is intended to be used with 'cmake -C <file>'
6085db71995Sopenharmony_ci    to build this home repo using the dependencies built by this script.
6095db71995Sopenharmony_ci
6105db71995Sopenharmony_ci    The install_names dictionary represents the CMake variables used by the
6115db71995Sopenharmony_ci    home repo to locate the install dirs of the dependent repos.
6125db71995Sopenharmony_ci    This information is baked into the CMake files of the home repo and so
6135db71995Sopenharmony_ci    this dictionary is kept with the repo via the json file.
6145db71995Sopenharmony_ci    """
6155db71995Sopenharmony_ci    install_names = GetInstallNames(args)
6165db71995Sopenharmony_ci    with open(filename, 'w') as helper_file:
6175db71995Sopenharmony_ci        for repo in repos:
6185db71995Sopenharmony_ci            # If the repo has an API tag and that does not match
6195db71995Sopenharmony_ci            # the target API then skip it
6205db71995Sopenharmony_ci            if repo.api is not None and repo.api != args.api:
6215db71995Sopenharmony_ci                continue
6225db71995Sopenharmony_ci            if install_names and repo.name in install_names and repo.on_build_platform:
6235db71995Sopenharmony_ci                helper_file.write('set({var} "{dir}" CACHE STRING "" FORCE)\n'
6245db71995Sopenharmony_ci                                  .format(
6255db71995Sopenharmony_ci                                      var=install_names[repo.name],
6265db71995Sopenharmony_ci                                      dir=escape(repo.install_dir)))
6275db71995Sopenharmony_ci
6285db71995Sopenharmony_ci
6295db71995Sopenharmony_cidef main():
6305db71995Sopenharmony_ci    parser = argparse.ArgumentParser(
6315db71995Sopenharmony_ci        description='Get and build dependent repos at known-good commits')
6325db71995Sopenharmony_ci    parser.add_argument(
6335db71995Sopenharmony_ci        '--known_good_dir',
6345db71995Sopenharmony_ci        dest='known_good_dir',
6355db71995Sopenharmony_ci        help="Specify directory for known_good.json file.")
6365db71995Sopenharmony_ci    parser.add_argument(
6375db71995Sopenharmony_ci        '--dir',
6385db71995Sopenharmony_ci        dest='dir',
6395db71995Sopenharmony_ci        default='.',
6405db71995Sopenharmony_ci        help="Set target directory for repository roots. Default is \'.\'.")
6415db71995Sopenharmony_ci    parser.add_argument(
6425db71995Sopenharmony_ci        '--ref',
6435db71995Sopenharmony_ci        dest='ref',
6445db71995Sopenharmony_ci        default='',
6455db71995Sopenharmony_ci        help="Override 'commit' with git reference. E.g., 'origin/main'")
6465db71995Sopenharmony_ci    parser.add_argument(
6475db71995Sopenharmony_ci        '--no-build',
6485db71995Sopenharmony_ci        dest='do_build',
6495db71995Sopenharmony_ci        action='store_false',
6505db71995Sopenharmony_ci        help=
6515db71995Sopenharmony_ci        "Clone/update repositories and generate build files without performing compilation",
6525db71995Sopenharmony_ci        default=True)
6535db71995Sopenharmony_ci    parser.add_argument(
6545db71995Sopenharmony_ci        '--clean',
6555db71995Sopenharmony_ci        dest='do_clean',
6565db71995Sopenharmony_ci        action='store_true',
6575db71995Sopenharmony_ci        help="Clean files generated by compiler and linker before building",
6585db71995Sopenharmony_ci        default=False)
6595db71995Sopenharmony_ci    parser.add_argument(
6605db71995Sopenharmony_ci        '--clean-repo',
6615db71995Sopenharmony_ci        dest='do_clean_repo',
6625db71995Sopenharmony_ci        action='store_true',
6635db71995Sopenharmony_ci        help="Delete repository directory before building",
6645db71995Sopenharmony_ci        default=False)
6655db71995Sopenharmony_ci    parser.add_argument(
6665db71995Sopenharmony_ci        '--clean-build',
6675db71995Sopenharmony_ci        dest='do_clean_build',
6685db71995Sopenharmony_ci        action='store_true',
6695db71995Sopenharmony_ci        help="Delete build directory before building",
6705db71995Sopenharmony_ci        default=False)
6715db71995Sopenharmony_ci    parser.add_argument(
6725db71995Sopenharmony_ci        '--clean-install',
6735db71995Sopenharmony_ci        dest='do_clean_install',
6745db71995Sopenharmony_ci        action='store_true',
6755db71995Sopenharmony_ci        help="Delete install directory before building",
6765db71995Sopenharmony_ci        default=False)
6775db71995Sopenharmony_ci    parser.add_argument(
6785db71995Sopenharmony_ci        '--skip-existing-install',
6795db71995Sopenharmony_ci        dest='skip_existing_install',
6805db71995Sopenharmony_ci        action='store_true',
6815db71995Sopenharmony_ci        help="Skip build if install directory exists",
6825db71995Sopenharmony_ci        default=False)
6835db71995Sopenharmony_ci    parser.add_argument(
6845db71995Sopenharmony_ci        '--arch',
6855db71995Sopenharmony_ci        dest='arch',
6865db71995Sopenharmony_ci        choices=['32', '64', 'x86', 'x64', 'win32', 'win64'],
6875db71995Sopenharmony_ci        type=str.lower,
6885db71995Sopenharmony_ci        help="Set build files architecture (Visual Studio Generator Only)",
6895db71995Sopenharmony_ci        default='64')
6905db71995Sopenharmony_ci    parser.add_argument(
6915db71995Sopenharmony_ci        '--config',
6925db71995Sopenharmony_ci        dest='config',
6935db71995Sopenharmony_ci        choices=['debug', 'release', 'relwithdebinfo', 'minsizerel'],
6945db71995Sopenharmony_ci        type=str.lower,
6955db71995Sopenharmony_ci        help="Set build files configuration",
6965db71995Sopenharmony_ci        default='debug')
6975db71995Sopenharmony_ci    parser.add_argument(
6985db71995Sopenharmony_ci        '--api',
6995db71995Sopenharmony_ci        dest='api',
7005db71995Sopenharmony_ci        default='vulkan',
7015db71995Sopenharmony_ci        choices=['vulkan'],
7025db71995Sopenharmony_ci        help="Target API")
7035db71995Sopenharmony_ci    parser.add_argument(
7045db71995Sopenharmony_ci        '--generator',
7055db71995Sopenharmony_ci        dest='generator',
7065db71995Sopenharmony_ci        help="Set the CMake generator",
7075db71995Sopenharmony_ci        default=None)
7085db71995Sopenharmony_ci    parser.add_argument(
7095db71995Sopenharmony_ci        '--optional',
7105db71995Sopenharmony_ci        dest='optional',
7115db71995Sopenharmony_ci        type=lambda a: set(a.lower().split(',')),
7125db71995Sopenharmony_ci        help="Comma-separated list of 'optional' resources that may be skipped. Only 'tests' is currently supported as 'optional'",
7135db71995Sopenharmony_ci        default=set())
7145db71995Sopenharmony_ci    parser.add_argument(
7155db71995Sopenharmony_ci        '--cmake_var',
7165db71995Sopenharmony_ci        dest='cmake_var',
7175db71995Sopenharmony_ci        action='append',
7185db71995Sopenharmony_ci        metavar='VAR[=VALUE]',
7195db71995Sopenharmony_ci        help="Add CMake command line option -D'VAR'='VALUE' to the CMake generation command line; may be used multiple times",
7205db71995Sopenharmony_ci        default=[])
7215db71995Sopenharmony_ci    parser.add_argument(
7225db71995Sopenharmony_ci        '--osx-archs',
7235db71995Sopenharmony_ci        dest='OSX_ARCHITECTURES',
7245db71995Sopenharmony_ci        help="Architectures when building a universal binary. Takes a colon seperated list. Ex: arm64:x86_64",
7255db71995Sopenharmony_ci        type=str,
7265db71995Sopenharmony_ci        default=None)
7275db71995Sopenharmony_ci
7285db71995Sopenharmony_ci    args = parser.parse_args()
7295db71995Sopenharmony_ci    save_cwd = os.getcwd()
7305db71995Sopenharmony_ci
7315db71995Sopenharmony_ci    if args.OSX_ARCHITECTURES:
7325db71995Sopenharmony_ci        print(f"Building dependencies as universal binaries targeting {args.OSX_ARCHITECTURES}")
7335db71995Sopenharmony_ci
7345db71995Sopenharmony_ci    # Create working "top" directory if needed
7355db71995Sopenharmony_ci    make_or_exist_dirs(args.dir)
7365db71995Sopenharmony_ci    abs_top_dir = os.path.abspath(args.dir)
7375db71995Sopenharmony_ci
7385db71995Sopenharmony_ci    repos = GetGoodRepos(args)
7395db71995Sopenharmony_ci    repo_dict = {}
7405db71995Sopenharmony_ci
7415db71995Sopenharmony_ci    print('Starting builds in {d}'.format(d=abs_top_dir))
7425db71995Sopenharmony_ci    for repo in repos:
7435db71995Sopenharmony_ci        # If the repo has an API tag and that does not match
7445db71995Sopenharmony_ci        # the target API then skip it
7455db71995Sopenharmony_ci        if repo.api is not None and repo.api != args.api:
7465db71995Sopenharmony_ci            continue
7475db71995Sopenharmony_ci
7485db71995Sopenharmony_ci        # If the repo has a platform whitelist, skip the repo
7495db71995Sopenharmony_ci        # unless we are building on a whitelisted platform.
7505db71995Sopenharmony_ci        if not repo.on_build_platform:
7515db71995Sopenharmony_ci            continue
7525db71995Sopenharmony_ci
7535db71995Sopenharmony_ci        # Skip building the repo if its install directory already exists
7545db71995Sopenharmony_ci        # and requested via an option.  This is useful for cases where the
7555db71995Sopenharmony_ci        # install directory is restored from a cache that is known to be up
7565db71995Sopenharmony_ci        # to date.
7575db71995Sopenharmony_ci        if args.skip_existing_install and os.path.isdir(repo.install_dir):
7585db71995Sopenharmony_ci            print('Skipping build for repo {n} due to existing install directory'.format(n=repo.name))
7595db71995Sopenharmony_ci            continue
7605db71995Sopenharmony_ci
7615db71995Sopenharmony_ci        # Skip test-only repos if the --tests option was not passed in
7625db71995Sopenharmony_ci        if repo.IsOptional(args.optional):
7635db71995Sopenharmony_ci            continue
7645db71995Sopenharmony_ci
7655db71995Sopenharmony_ci        field_list = ('url',
7665db71995Sopenharmony_ci                      'sub_dir',
7675db71995Sopenharmony_ci                      'commit',
7685db71995Sopenharmony_ci                      'build_dir',
7695db71995Sopenharmony_ci                      'install_dir',
7705db71995Sopenharmony_ci                      'deps',
7715db71995Sopenharmony_ci                      'prebuild',
7725db71995Sopenharmony_ci                      'prebuild_linux',
7735db71995Sopenharmony_ci                      'prebuild_windows',
7745db71995Sopenharmony_ci                      'custom_build',
7755db71995Sopenharmony_ci                      'cmake_options',
7765db71995Sopenharmony_ci                      'ci_only',
7775db71995Sopenharmony_ci                      'build_step',
7785db71995Sopenharmony_ci                      'build_platforms',
7795db71995Sopenharmony_ci                      'repo_dir',
7805db71995Sopenharmony_ci                      'on_build_platform')
7815db71995Sopenharmony_ci        repo_dict[repo.name] = {field: getattr(repo, field) for field in field_list}
7825db71995Sopenharmony_ci
7835db71995Sopenharmony_ci        # If the repo has a CI whitelist, skip the repo unless
7845db71995Sopenharmony_ci        # one of the CI's environment variable is set to true.
7855db71995Sopenharmony_ci        if len(repo.ci_only):
7865db71995Sopenharmony_ci            do_build = False
7875db71995Sopenharmony_ci            for env in repo.ci_only:
7885db71995Sopenharmony_ci                if env not in os.environ:
7895db71995Sopenharmony_ci                    continue
7905db71995Sopenharmony_ci                if os.environ[env].lower() == 'true':
7915db71995Sopenharmony_ci                    do_build = True
7925db71995Sopenharmony_ci                    break
7935db71995Sopenharmony_ci            if not do_build:
7945db71995Sopenharmony_ci                continue
7955db71995Sopenharmony_ci
7965db71995Sopenharmony_ci        # Clone/update the repository
7975db71995Sopenharmony_ci        repo.Checkout()
7985db71995Sopenharmony_ci
7995db71995Sopenharmony_ci        # Build the repository
8005db71995Sopenharmony_ci        if args.do_build and repo.build_step != 'skip':
8015db71995Sopenharmony_ci            repo.Build(repos, repo_dict)
8025db71995Sopenharmony_ci
8035db71995Sopenharmony_ci    # Need to restore original cwd in order for CreateHelper to find json file
8045db71995Sopenharmony_ci    os.chdir(save_cwd)
8055db71995Sopenharmony_ci    CreateHelper(args, repos, os.path.join(abs_top_dir, 'helper.cmake'))
8065db71995Sopenharmony_ci
8075db71995Sopenharmony_ci    sys.exit(0)
8085db71995Sopenharmony_ci
8095db71995Sopenharmony_ci
8105db71995Sopenharmony_ciif __name__ == '__main__':
8115db71995Sopenharmony_ci    main()
8125db71995Sopenharmony_ci
813