1#!/usr/bin/env python3 2 3# Copyright 2017 The Glslang Authors. All rights reserved. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Get source files for Glslang and its dependencies from public repositories. 18""" 19 20import argparse 21import json 22import os 23import subprocess 24import sys 25 26KNOWN_GOOD_FILE = 'known_good.json' 27 28SITE_TO_KNOWN_GOOD_FILE = { 'github' : 'known_good.json', 29 'gitlab' : 'known_good_khr.json' } 30 31# Maps a site name to its hostname. 32SITE_TO_HOST = { 'github' : 'https://github.com/', 33 'gitlab' : '[email protected]:' } 34 35VERBOSE = True 36 37 38def command_output(cmd, directory, fail_ok=False): 39 """Runs a command in a directory and returns its standard output stream. 40 41 Captures the standard error stream. 42 43 Raises a RuntimeError if the command fails to launch or otherwise fails. 44 """ 45 if VERBOSE: 46 print('In {d}: {cmd}'.format(d=directory, cmd=cmd)) 47 p = subprocess.Popen(cmd, 48 cwd=directory, 49 stdout=subprocess.PIPE) 50 (stdout, _) = p.communicate() 51 if p.returncode != 0 and not fail_ok: 52 raise RuntimeError('Failed to run {} in {}'.format(cmd, directory)) 53 if VERBOSE: 54 print(stdout) 55 return stdout 56 57 58def command_retval(cmd, directory): 59 """Runs a command in a directory and returns its return value. 60 61 Captures the standard error stream. 62 """ 63 p = subprocess.Popen(cmd, 64 cwd=directory, 65 stdout=subprocess.PIPE) 66 p.communicate() 67 return p.returncode 68 69 70class GoodCommit(object): 71 """Represents a good commit for a repository.""" 72 73 def __init__(self, json): 74 """Initializes this good commit object. 75 76 Args: 77 'json': A fully populated JSON object describing the commit. 78 """ 79 self._json = json 80 self.name = json['name'] 81 self.site = json['site'] 82 self.subrepo = json['subrepo'] 83 self.subdir = json['subdir'] if ('subdir' in json) else '.' 84 self.commit = json['commit'] 85 86 def GetUrl(self): 87 """Returns the URL for the repository.""" 88 host = SITE_TO_HOST[self.site] 89 return '{host}{subrepo}'.format( 90 host=host, 91 subrepo=self.subrepo) 92 93 def AddRemote(self): 94 """Add the remote 'known-good' if it does not exist.""" 95 remotes = command_output(['git', 'remote'], self.subdir).splitlines() 96 if b'known-good' not in remotes: 97 command_output(['git', 'remote', 'add', 'known-good', self.GetUrl()], self.subdir) 98 99 def HasCommit(self): 100 """Check if the repository contains the known-good commit.""" 101 return 0 == subprocess.call(['git', 'rev-parse', '--verify', '--quiet', 102 self.commit + "^{commit}"], 103 cwd=self.subdir) 104 105 def Clone(self): 106 os.makedirs(self.subdir, exist_ok=True) 107 command_output(['git', 'clone', self.GetUrl(), '.'], self.subdir) 108 109 def Fetch(self): 110 command_output(['git', 'fetch', 'known-good'], self.subdir) 111 112 def Checkout(self): 113 if not os.path.exists(os.path.join(self.subdir,'.git')): 114 self.Clone() 115 self.AddRemote() 116 if not self.HasCommit(): 117 self.Fetch() 118 command_output(['git', 'checkout', self.commit], self.subdir) 119 120 121def GetGoodCommits(site): 122 """Returns the latest list of GoodCommit objects.""" 123 known_good_file = SITE_TO_KNOWN_GOOD_FILE[site] 124 with open(known_good_file) as known_good: 125 return [GoodCommit(c) for c in json.loads(known_good.read())['commits']] 126 127 128def main(): 129 parser = argparse.ArgumentParser(description='Get Glslang source dependencies at a known-good commit') 130 parser.add_argument('--dir', dest='dir', default='.', 131 help="Set target directory for Glslang source root. Default is \'.\'.") 132 parser.add_argument('--site', dest='site', default='github', 133 help="Set git server site. Default is github.") 134 135 args = parser.parse_args() 136 137 commits = GetGoodCommits(args.site) 138 139 os.makedirs(args.dir, exist_ok=True) 140 print('Change directory to {d}'.format(d=args.dir)) 141 os.chdir(args.dir) 142 143 # Create the subdirectories in sorted order so that parent git repositories 144 # are created first. 145 for c in sorted(commits, key=lambda x: x.subdir): 146 print('Get {n}\n'.format(n=c.name)) 147 c.Checkout() 148 sys.exit(0) 149 150 151if __name__ == '__main__': 152 main() 153