Merge vk-gl-cts/vulkan-cts-1.3.2 into vk-gl-cts/vulkan-cts-1.3.3
[platform/upstream/VK-GL-CTS.git] / external / fetch_sources.py
1 # -*- coding: utf-8 -*-
2
3 #-------------------------------------------------------------------------
4 # drawElements Quality Program utilities
5 # --------------------------------------
6 #
7 # Copyright 2015 The Android Open Source Project
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 #      http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 #
21 #-------------------------------------------------------------------------
22
23 import os
24 import sys
25 import shutil
26 import tarfile
27 import hashlib
28 import argparse
29 import subprocess
30 import ssl
31 import stat
32
33 sys.path.append(os.path.join(os.path.dirname(__file__), "..", "scripts"))
34
35 from build.common import *
36
37 EXTERNAL_DIR    = os.path.realpath(os.path.normpath(os.path.dirname(__file__)))
38
39 def computeChecksum (data):
40         return hashlib.sha256(data).hexdigest()
41
42 def onReadonlyRemoveError (func, path, exc_info):
43         os.chmod(path, stat.S_IWRITE)
44         os.unlink(path)
45
46 class Source:
47         def __init__(self, baseDir, extractDir):
48                 self.baseDir            = baseDir
49                 self.extractDir         = extractDir
50
51         def clean (self):
52                 fullDstPath = os.path.join(EXTERNAL_DIR, self.baseDir, self.extractDir)
53                 # Remove read-only first
54                 readonlydir = os.path.join(fullDstPath, ".git", "objects", "pack")
55                 if os.path.exists(readonlydir):
56                         shutil.rmtree(readonlydir, onerror = onReadonlyRemoveError )
57                 if os.path.exists(fullDstPath):
58                         shutil.rmtree(fullDstPath, ignore_errors=False)
59
60 class SourcePackage (Source):
61         def __init__(self, url, filename, checksum, baseDir, extractDir = "src", postExtract=None):
62                 Source.__init__(self, baseDir, extractDir)
63                 self.url                        = url
64                 self.filename           = filename
65                 self.checksum           = checksum
66                 self.archiveDir         = "packages"
67                 self.postExtract        = postExtract
68
69         def clean (self):
70                 Source.clean(self)
71                 self.removeArchives()
72
73         def update (self, cmdProtocol = None, force = False):
74                 if not self.isArchiveUpToDate():
75                         self.fetchAndVerifyArchive()
76
77                 if self.getExtractedChecksum() != self.checksum:
78                         Source.clean(self)
79                         self.extract()
80                         self.storeExtractedChecksum(self.checksum)
81
82         def removeArchives (self):
83                 archiveDir = os.path.join(EXTERNAL_DIR, pkg.baseDir, pkg.archiveDir)
84                 if os.path.exists(archiveDir):
85                         shutil.rmtree(archiveDir, ignore_errors=False)
86
87         def isArchiveUpToDate (self):
88                 archiveFile = os.path.join(EXTERNAL_DIR, pkg.baseDir, pkg.archiveDir, pkg.filename)
89                 if os.path.exists(archiveFile):
90                         return computeChecksum(readBinaryFile(archiveFile)) == self.checksum
91                 else:
92                         return False
93
94         def getExtractedChecksumFilePath (self):
95                 return os.path.join(EXTERNAL_DIR, pkg.baseDir, pkg.archiveDir, "extracted")
96
97         def getExtractedChecksum (self):
98                 extractedChecksumFile = self.getExtractedChecksumFilePath()
99
100                 if os.path.exists(extractedChecksumFile):
101                         return readFile(extractedChecksumFile)
102                 else:
103                         return None
104
105         def storeExtractedChecksum (self, checksum):
106                 checksum_bytes = checksum.encode("utf-8")
107                 writeBinaryFile(self.getExtractedChecksumFilePath(), checksum_bytes)
108
109         def connectToUrl (self, url):
110                 result = None
111
112                 if sys.version_info < (3, 0):
113                         from urllib2 import urlopen
114                 else:
115                         from urllib.request import urlopen
116
117                 if args.insecure:
118                         print("Ignoring certificate checks")
119                         ssl_context = ssl._create_unverified_context()
120                         result = urlopen(url, context=ssl_context)
121                 else:
122                         result = urlopen(url)
123
124                 return result
125
126         def fetchAndVerifyArchive (self):
127                 print("Fetching %s" % self.url)
128
129                 req                     = self.connectToUrl(self.url)
130                 data            = req.read()
131                 checksum        = computeChecksum(data)
132                 dstPath         = os.path.join(EXTERNAL_DIR, self.baseDir, self.archiveDir, self.filename)
133
134                 if checksum != self.checksum:
135                         raise Exception("Checksum mismatch for %s, expected %s, got %s" % (self.filename, self.checksum, checksum))
136
137                 if not os.path.exists(os.path.dirname(dstPath)):
138                         os.mkdir(os.path.dirname(dstPath))
139
140                 writeBinaryFile(dstPath, data)
141
142         def extract (self):
143                 print("Extracting %s to %s/%s" % (self.filename, self.baseDir, self.extractDir))
144
145                 srcPath = os.path.join(EXTERNAL_DIR, self.baseDir, self.archiveDir, self.filename)
146                 tmpPath = os.path.join(EXTERNAL_DIR, ".extract-tmp-%s" % self.baseDir)
147                 dstPath = os.path.join(EXTERNAL_DIR, self.baseDir, self.extractDir)
148                 archive = tarfile.open(srcPath)
149
150                 if os.path.exists(tmpPath):
151                         shutil.rmtree(tmpPath, ignore_errors=False)
152
153                 os.mkdir(tmpPath)
154
155                 archive.extractall(tmpPath)
156                 archive.close()
157
158                 extractedEntries = os.listdir(tmpPath)
159                 if len(extractedEntries) != 1 or not os.path.isdir(os.path.join(tmpPath, extractedEntries[0])):
160                         raise Exception("%s doesn't contain single top-level directory" % self.filename)
161
162                 topLevelPath = os.path.join(tmpPath, extractedEntries[0])
163
164                 if not os.path.exists(dstPath):
165                         os.mkdir(dstPath)
166
167                 for entry in os.listdir(topLevelPath):
168                         if os.path.exists(os.path.join(dstPath, entry)):
169                                 raise Exception("%s exists already" % entry)
170
171                         shutil.move(os.path.join(topLevelPath, entry), dstPath)
172
173                 shutil.rmtree(tmpPath, ignore_errors=True)
174
175                 if self.postExtract != None:
176                         self.postExtract(dstPath)
177
178 class SourceFile (Source):
179         def __init__(self, url, filename, checksum, baseDir, extractDir = "src"):
180                 Source.__init__(self, baseDir, extractDir)
181                 self.url                        = url
182                 self.filename           = filename
183                 self.checksum           = checksum
184
185         def update (self, cmdProtocol = None, force = False):
186                 if not self.isFileUpToDate():
187                         Source.clean(self)
188                         self.fetchAndVerifyFile()
189
190         def isFileUpToDate (self):
191                 file = os.path.join(EXTERNAL_DIR, pkg.baseDir, pkg.extractDir, pkg.filename)
192                 if os.path.exists(file):
193                         data = readFile(file)
194                         return computeChecksum(data.encode('utf-8')) == self.checksum
195                 else:
196                         return False
197
198         def connectToUrl (self, url):
199                 result = None
200
201                 if sys.version_info < (3, 0):
202                         from urllib2 import urlopen
203                 else:
204                         from urllib.request import urlopen
205
206                 if args.insecure:
207                         print("Ignoring certificate checks")
208                         ssl_context = ssl._create_unverified_context()
209                         result = urlopen(url, context=ssl_context)
210                 else:
211                         result = urlopen(url)
212
213                 return result
214
215         def fetchAndVerifyFile (self):
216                 print("Fetching %s" % self.url)
217
218                 req                     = self.connectToUrl(self.url)
219                 data            = req.read()
220                 checksum        = computeChecksum(data)
221                 dstPath         = os.path.join(EXTERNAL_DIR, self.baseDir, self.extractDir, self.filename)
222
223                 if checksum != self.checksum:
224                         raise Exception("Checksum mismatch for %s, expected %s, got %s" % (self.filename, self.checksum, checksum))
225
226                 if not os.path.exists(os.path.dirname(dstPath)):
227                         os.mkdir(os.path.dirname(dstPath))
228
229                 writeBinaryFile(dstPath, data)
230
231 class GitRepo (Source):
232         def __init__(self, httpsUrl, sshUrl, revision, baseDir, extractDir = "src", removeTags = [], patch = ""):
233                 Source.__init__(self, baseDir, extractDir)
234                 self.httpsUrl   = httpsUrl
235                 self.sshUrl             = sshUrl
236                 self.revision   = revision
237                 self.removeTags = removeTags
238                 self.patch              = patch
239
240         def checkout(self, url, fullDstPath, force):
241                 if not os.path.exists(os.path.join(fullDstPath, '.git')):
242                         execute(["git", "clone", "--no-checkout", url, fullDstPath])
243
244                 pushWorkingDir(fullDstPath)
245                 try:
246                         for tag in self.removeTags:
247                                 proc = subprocess.Popen(['git', 'tag', '-l', tag], stdout=subprocess.PIPE)
248                                 (stdout, stderr) = proc.communicate()
249                                 if len(stdout) > 0:
250                                         execute(["git", "tag", "-d",tag])
251                         force_arg = ['--force'] if force else []
252                         execute(["git", "fetch"] + force_arg + ["--tags", url, "+refs/heads/*:refs/remotes/origin/*"])
253                         execute(["git", "checkout"] + force_arg + [self.revision])
254
255                         if(self.patch != ""):
256                                 patchFile = os.path.join(EXTERNAL_DIR, self.patch)
257                                 execute(["git", "reset", "--hard", "HEAD"])
258                                 execute(["git", "apply", patchFile])
259                 finally:
260                         popWorkingDir()
261
262         def update (self, cmdProtocol, force = False):
263                 fullDstPath = os.path.join(EXTERNAL_DIR, self.baseDir, self.extractDir)
264                 url         = self.httpsUrl
265                 backupUrl   = self.sshUrl
266
267                 # If url is none then start with ssh
268                 if cmdProtocol == 'ssh' or url == None:
269                         url       = self.sshUrl
270                         backupUrl = self.httpsUrl
271
272                 try:
273                         self.checkout(url, fullDstPath, force)
274                 except:
275                         if backupUrl != None:
276                                 self.checkout(backupUrl, fullDstPath, force)
277
278 def postExtractLibpng (path):
279         shutil.copy(os.path.join(path, "scripts", "pnglibconf.h.prebuilt"),
280                                 os.path.join(path, "pnglibconf.h"))
281
282 PACKAGES = [
283         SourcePackage(
284                 "http://zlib.net/zlib-1.2.12.tar.gz",
285                 "zlib-1.2.12.tar.gz",
286                 "91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9",
287                 "zlib"),
288         SourcePackage(
289                 "http://prdownloads.sourceforge.net/libpng/libpng-1.6.27.tar.gz",
290                 "libpng-1.6.27.tar.gz",
291                 "c9d164ec247f426a525a7b89936694aefbc91fb7a50182b198898b8fc91174b4",
292                 "libpng",
293                 postExtract = postExtractLibpng),
294         SourceFile(
295                 "https://raw.githubusercontent.com/baldurk/renderdoc/v1.1/renderdoc/api/app/renderdoc_app.h",
296                 "renderdoc_app.h",
297                 "e7b5f0aa5b1b0eadc63a1c624c0ca7f5af133aa857d6a4271b0ef3d0bdb6868e",
298                 "renderdoc"),
299         GitRepo(
300                 "https://github.com/KhronosGroup/SPIRV-Tools.git",
301                 "git@github.com:KhronosGroup/SPIRV-Tools.git",
302                 "b930e734ea198b7aabbbf04ee1562cf6f57962f0",
303                 "spirv-tools"),
304         GitRepo(
305                 "https://github.com/KhronosGroup/glslang.git",
306                 "git@github.com:KhronosGroup/glslang.git",
307                 "7dda6a6347b0bd550e202942adee475956ef462a",
308                 "glslang",
309                 removeTags = ["master-tot"]),
310         GitRepo(
311                 "https://github.com/KhronosGroup/SPIRV-Headers.git",
312                 "git@github.com:KhronosGroup/SPIRV-Headers.git",
313                 "b765c355f488837ca4c77980ba69484f3ff277f5",
314                 "spirv-headers"),
315         GitRepo(
316                 "https://github.com/KhronosGroup/Vulkan-Docs.git",
317                 "git@github.com:KhronosGroup/Vulkan-Docs.git",
318                 "9b5562187a8ad72c171410b036ceedbc450153ba",
319                 "vulkan-docs"),
320         GitRepo(
321                 "https://github.com/google/amber.git",
322                 "git@github.com:google/amber.git",
323                 "8b145a6c89dcdb4ec28173339dd176fb7b6f43ed",
324                 "amber"),
325         GitRepo(
326                 "https://github.com/open-source-parsers/jsoncpp.git",
327                 "git@github.com:open-source-parsers/jsoncpp.git",
328                 "9059f5cad030ba11d37818847443a53918c327b1",
329                 "jsoncpp"),
330 ]
331
332 def parseArgs ():
333         versionsForInsecure = ((2,7,9), (3,4,3))
334         versionsForInsecureStr = ' or '.join(('.'.join(str(x) for x in v)) for v in versionsForInsecure)
335
336         parser = argparse.ArgumentParser(description = "Fetch external sources")
337         parser.add_argument('--clean', dest='clean', action='store_true', default=False,
338                                                 help='Remove sources instead of fetching')
339         parser.add_argument('--insecure', dest='insecure', action='store_true', default=False,
340                                                 help="Disable certificate check for external sources."
341                                                 " Minimum python version required " + versionsForInsecureStr)
342         parser.add_argument('--protocol', dest='protocol', default='https', choices=['ssh', 'https'],
343                                                 help="Select protocol to checkout git repositories.")
344         parser.add_argument('--force', dest='force', action='store_true', default=False,
345                                                 help="Pass --force to git fetch and checkout commands")
346
347         args = parser.parse_args()
348
349         if args.insecure:
350                 for versionItem in versionsForInsecure:
351                         if (sys.version_info.major == versionItem[0]):
352                                 if sys.version_info < versionItem:
353                                         parser.error("For --insecure minimum required python version is " +
354                                                                 versionsForInsecureStr)
355                                 break;
356
357         return args
358
359 if __name__ == "__main__":
360         args = parseArgs()
361
362         for pkg in PACKAGES:
363                 if args.clean:
364                         pkg.clean()
365                 else:
366                         pkg.update(args.protocol, args.force)