1617a3babSopenharmony_ci#!/usr/bin/env python3
2617a3babSopenharmony_ci
3617a3babSopenharmony_ci# Copyright 2017 The Glslang Authors. All rights reserved.
4617a3babSopenharmony_ci#
5617a3babSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
6617a3babSopenharmony_ci# you may not use this file except in compliance with the License.
7617a3babSopenharmony_ci# You may obtain a copy of the License at
8617a3babSopenharmony_ci#
9617a3babSopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
10617a3babSopenharmony_ci#
11617a3babSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
12617a3babSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
13617a3babSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14617a3babSopenharmony_ci# See the License for the specific language governing permissions and
15617a3babSopenharmony_ci# limitations under the License.
16617a3babSopenharmony_ci
17617a3babSopenharmony_ci"""Get source files for Glslang and its dependencies from public repositories.
18617a3babSopenharmony_ci"""
19617a3babSopenharmony_ci
20617a3babSopenharmony_ciimport argparse
21617a3babSopenharmony_ciimport json
22617a3babSopenharmony_ciimport os
23617a3babSopenharmony_ciimport subprocess
24617a3babSopenharmony_ciimport sys
25617a3babSopenharmony_ci
26617a3babSopenharmony_ciKNOWN_GOOD_FILE = 'known_good.json'
27617a3babSopenharmony_ci
28617a3babSopenharmony_ciSITE_TO_KNOWN_GOOD_FILE = { 'github' : 'known_good.json',
29617a3babSopenharmony_ci                            'gitlab' : 'known_good_khr.json' }
30617a3babSopenharmony_ci
31617a3babSopenharmony_ci# Maps a site name to its hostname.
32617a3babSopenharmony_ciSITE_TO_HOST = { 'github' : 'https://github.com/',
33617a3babSopenharmony_ci                 'gitlab' : 'git@gitlab.khronos.org:' }
34617a3babSopenharmony_ci
35617a3babSopenharmony_ciVERBOSE = True
36617a3babSopenharmony_ci
37617a3babSopenharmony_ci
38617a3babSopenharmony_cidef command_output(cmd, directory, fail_ok=False):
39617a3babSopenharmony_ci    """Runs a command in a directory and returns its standard output stream.
40617a3babSopenharmony_ci
41617a3babSopenharmony_ci    Captures the standard error stream.
42617a3babSopenharmony_ci
43617a3babSopenharmony_ci    Raises a RuntimeError if the command fails to launch or otherwise fails.
44617a3babSopenharmony_ci    """
45617a3babSopenharmony_ci    if VERBOSE:
46617a3babSopenharmony_ci        print('In {d}: {cmd}'.format(d=directory, cmd=cmd))
47617a3babSopenharmony_ci    p = subprocess.Popen(cmd,
48617a3babSopenharmony_ci                         cwd=directory,
49617a3babSopenharmony_ci                         stdout=subprocess.PIPE)
50617a3babSopenharmony_ci    (stdout, _) = p.communicate()
51617a3babSopenharmony_ci    if p.returncode != 0 and not fail_ok:
52617a3babSopenharmony_ci        raise RuntimeError('Failed to run {} in {}'.format(cmd, directory))
53617a3babSopenharmony_ci    if VERBOSE:
54617a3babSopenharmony_ci        print(stdout)
55617a3babSopenharmony_ci    return stdout
56617a3babSopenharmony_ci
57617a3babSopenharmony_ci
58617a3babSopenharmony_cidef command_retval(cmd, directory):
59617a3babSopenharmony_ci    """Runs a command in a directory and returns its return value.
60617a3babSopenharmony_ci
61617a3babSopenharmony_ci    Captures the standard error stream.
62617a3babSopenharmony_ci    """
63617a3babSopenharmony_ci    p = subprocess.Popen(cmd,
64617a3babSopenharmony_ci                         cwd=directory,
65617a3babSopenharmony_ci                         stdout=subprocess.PIPE)
66617a3babSopenharmony_ci    p.communicate()
67617a3babSopenharmony_ci    return p.returncode
68617a3babSopenharmony_ci
69617a3babSopenharmony_ci
70617a3babSopenharmony_ciclass GoodCommit(object):
71617a3babSopenharmony_ci    """Represents a good commit for a repository."""
72617a3babSopenharmony_ci
73617a3babSopenharmony_ci    def __init__(self, json):
74617a3babSopenharmony_ci        """Initializes this good commit object.
75617a3babSopenharmony_ci
76617a3babSopenharmony_ci        Args:
77617a3babSopenharmony_ci        'json':  A fully populated JSON object describing the commit.
78617a3babSopenharmony_ci        """
79617a3babSopenharmony_ci        self._json = json
80617a3babSopenharmony_ci        self.name = json['name']
81617a3babSopenharmony_ci        self.site = json['site']
82617a3babSopenharmony_ci        self.subrepo = json['subrepo']
83617a3babSopenharmony_ci        self.subdir = json['subdir'] if ('subdir' in json) else '.'
84617a3babSopenharmony_ci        self.commit = json['commit']
85617a3babSopenharmony_ci
86617a3babSopenharmony_ci    def GetUrl(self):
87617a3babSopenharmony_ci        """Returns the URL for the repository."""
88617a3babSopenharmony_ci        host = SITE_TO_HOST[self.site]
89617a3babSopenharmony_ci        return '{host}{subrepo}'.format(
90617a3babSopenharmony_ci                    host=host,
91617a3babSopenharmony_ci                    subrepo=self.subrepo)
92617a3babSopenharmony_ci
93617a3babSopenharmony_ci    def AddRemote(self):
94617a3babSopenharmony_ci        """Add the remote 'known-good' if it does not exist."""
95617a3babSopenharmony_ci        remotes = command_output(['git', 'remote'], self.subdir).splitlines()
96617a3babSopenharmony_ci        if b'known-good' not in remotes:
97617a3babSopenharmony_ci            command_output(['git', 'remote', 'add', 'known-good', self.GetUrl()], self.subdir)
98617a3babSopenharmony_ci
99617a3babSopenharmony_ci    def HasCommit(self):
100617a3babSopenharmony_ci        """Check if the repository contains the known-good commit."""
101617a3babSopenharmony_ci        return 0 == subprocess.call(['git', 'rev-parse', '--verify', '--quiet',
102617a3babSopenharmony_ci                                     self.commit + "^{commit}"],
103617a3babSopenharmony_ci                                    cwd=self.subdir)
104617a3babSopenharmony_ci
105617a3babSopenharmony_ci    def Clone(self):
106617a3babSopenharmony_ci        os.makedirs(self.subdir, exist_ok=True)
107617a3babSopenharmony_ci        command_output(['git', 'clone', self.GetUrl(), '.'], self.subdir)
108617a3babSopenharmony_ci
109617a3babSopenharmony_ci    def Fetch(self):
110617a3babSopenharmony_ci        command_output(['git', 'fetch', 'known-good'], self.subdir)
111617a3babSopenharmony_ci
112617a3babSopenharmony_ci    def Checkout(self):
113617a3babSopenharmony_ci        if not os.path.exists(os.path.join(self.subdir,'.git')):
114617a3babSopenharmony_ci            self.Clone()
115617a3babSopenharmony_ci        self.AddRemote()
116617a3babSopenharmony_ci        if not self.HasCommit():
117617a3babSopenharmony_ci            self.Fetch()
118617a3babSopenharmony_ci        command_output(['git', 'checkout', self.commit], self.subdir)
119617a3babSopenharmony_ci
120617a3babSopenharmony_ci
121617a3babSopenharmony_cidef GetGoodCommits(site):
122617a3babSopenharmony_ci    """Returns the latest list of GoodCommit objects."""
123617a3babSopenharmony_ci    known_good_file = SITE_TO_KNOWN_GOOD_FILE[site]
124617a3babSopenharmony_ci    with open(known_good_file) as known_good:
125617a3babSopenharmony_ci        return [GoodCommit(c) for c in json.loads(known_good.read())['commits']]
126617a3babSopenharmony_ci
127617a3babSopenharmony_ci
128617a3babSopenharmony_cidef main():
129617a3babSopenharmony_ci    parser = argparse.ArgumentParser(description='Get Glslang source dependencies at a known-good commit')
130617a3babSopenharmony_ci    parser.add_argument('--dir', dest='dir', default='.',
131617a3babSopenharmony_ci                        help="Set target directory for Glslang source root. Default is \'.\'.")
132617a3babSopenharmony_ci    parser.add_argument('--site', dest='site', default='github',
133617a3babSopenharmony_ci                        help="Set git server site. Default is github.")
134617a3babSopenharmony_ci
135617a3babSopenharmony_ci    args = parser.parse_args()
136617a3babSopenharmony_ci
137617a3babSopenharmony_ci    commits = GetGoodCommits(args.site)
138617a3babSopenharmony_ci
139617a3babSopenharmony_ci    os.makedirs(args.dir, exist_ok=True)
140617a3babSopenharmony_ci    print('Change directory to {d}'.format(d=args.dir))
141617a3babSopenharmony_ci    os.chdir(args.dir)
142617a3babSopenharmony_ci
143617a3babSopenharmony_ci    # Create the subdirectories in sorted order so that parent git repositories
144617a3babSopenharmony_ci    # are created first.
145617a3babSopenharmony_ci    for c in sorted(commits, key=lambda x: x.subdir):
146617a3babSopenharmony_ci        print('Get {n}\n'.format(n=c.name))
147617a3babSopenharmony_ci        c.Checkout()
148617a3babSopenharmony_ci    sys.exit(0)
149617a3babSopenharmony_ci
150617a3babSopenharmony_ci
151617a3babSopenharmony_ciif __name__ == '__main__':
152617a3babSopenharmony_ci    main()
153