ba74fd73774179fdc9d8970f1d89c6553b072e28
[WebKit-https.git] / Tools / BuildSlaveSupport / built-product-archive
1 #!/usr/bin/python
2
3 # Copyright (C) 2009 Apple Inc.  All rights reserved.
4 # Copyright (C) 2012 Google Inc. All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # 1.  Redistributions of source code must retain the above copyright
11 #     notice, this list of conditions and the following disclaimer. 
12 # 2.  Redistributions in binary form must reproduce the above copyright
13 #     notice, this list of conditions and the following disclaimer in the
14 #     documentation and/or other materials provided with the distribution. 
15 #
16 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
17 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
20 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 import optparse
28 import os
29 import shutil
30 import subprocess
31 import sys
32 import zipfile
33
34 _buildDirectory = None
35
36
37 def main():
38     parser = optparse.OptionParser("usage: %prog [options] [action]")
39     parser.add_option("--platform", dest="platform")
40     parser.add_option("--debug", action="store_const", const="debug", dest="configuration")
41     parser.add_option("--release", action="store_const", const="release", dest="configuration")
42
43     options, (action, ) = parser.parse_args()
44     if not options.platform:
45         parser.error("Platform is required")
46         return 1
47     if not options.configuration:
48         parser.error("Configuration is required")
49         return 1
50     if action not in ('archive', 'extract'):
51         parser.error("Action is required")
52         return 1
53
54     genericPlatform = options.platform.split('-', 1)[0]
55     if not determineWebKitBuildDirectory(genericPlatform, options.configuration):
56         print >> sys.stderr, "Could not determine build directory"
57         return 1
58
59     if action == 'archive':
60         return archiveBuiltProduct(options.configuration, genericPlatform, options.platform)
61     else:
62         return extractBuiltProduct(options.configuration, genericPlatform)
63
64
65 def determineWebKitBuildDirectory(platform, configuration):
66     global _buildDirectory
67     _buildDirectory = subprocess.Popen(['perl', os.path.join(os.path.dirname(__file__), "..", "Scripts", "webkit-build-directory"),
68         "--" + platform, "--" + configuration, '--top-level'], stdout=subprocess.PIPE).communicate()[0].strip()
69     return _buildDirectory
70
71
72 def removeDirectoryIfExists(thinDirectory):
73     if os.path.isdir(thinDirectory):
74         shutil.rmtree(thinDirectory)
75
76
77 def copyBuildFiles(source, destination, patterns):
78     shutil.copytree(source, destination, ignore=shutil.ignore_patterns(*patterns))
79
80
81 def createZipManually(directoryToZip, archiveFile):
82     archiveZip = zipfile.ZipFile(archiveFile, "w")
83
84     for path, dirNames, fileNames in os.walk(directoryToZip):
85         relativePath = os.path.relpath(path, directoryToZip)
86         for fileName in fileNames:
87             archiveZip.write(os.path.join(path, fileName), os.path.join(relativePath, fileName))
88
89     archiveZip.close()
90
91
92 def createZip(directoryToZip, configuration, archiveConfigurationOnMac=False):
93     archiveDir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "WebKitBuild"))
94
95     # Chromium bots may not have this directory
96     if not os.path.isdir(archiveDir):
97         os.mkdir(archiveDir)
98
99     archiveFile = os.path.join(archiveDir, configuration + ".zip")
100
101     try:
102         os.unlink(archiveFile)
103     except OSError, e:
104         if e.errno != 2:
105             raise
106
107     if sys.platform == 'darwin':
108         if archiveConfigurationOnMac:
109             return subprocess.call(["ditto", "-c", "-k", "--keepParent", "--sequesterRsrc", directoryToZip, archiveFile])
110         return subprocess.call(["ditto", "-c", "-k", "--sequesterRsrc", directoryToZip, archiveFile])
111     elif sys.platform == 'cygwin':
112         return subprocess.call(["zip", "-r", archiveFile, "bin"], cwd=directoryToZip)
113     elif sys.platform == 'win32':
114         createZipManually(directoryToZip, archiveFile)
115         return 0
116     elif sys.platform.startswith('linux'):
117         return subprocess.call(["zip", "-y", "-r", archiveFile, "."], cwd=directoryToZip)
118
119
120 def archiveBuiltProduct(configuration, platform, fullPlatform):
121     assert platform in ('mac', 'win', 'qt', 'gtk', 'efl', 'chromium')
122
123     configurationBuildDirectory = os.path.join(_buildDirectory, configuration.title())
124
125     if platform == 'mac':
126         return createZip(configurationBuildDirectory, configuration, archiveConfigurationOnMac=True)
127     elif platform == 'win':
128         binDirectory = os.path.join(configurationBuildDirectory, "bin")
129         thinDirectory = os.path.join(configurationBuildDirectory, "thin")
130         thinBinDirectory = os.path.join(thinDirectory, "bin")
131
132         removeDirectoryIfExists(thinDirectory)
133         copyBuildFiles(binDirectory, thinBinDirectory, ['*.ilk'])
134         if createZip(thinDirectory, configuration):
135             return 1
136
137         shutil.rmtree(thinDirectory)
138
139     elif platform == 'qt' or platform == 'gtk' or platform == 'efl':
140         thinDirectory = os.path.join(configurationBuildDirectory, "thin")
141
142         removeDirectoryIfExists(thinDirectory)
143         os.mkdir(thinDirectory)
144
145         if platform == 'qt' or platform == 'efl':
146             neededDirectories = ["bin", "lib"]
147         elif platform == 'gtk':
148             neededDirectories = ["Programs", ".libs", "Libraries", "TestNetscapePlugin"]
149
150         for dirname in neededDirectories:
151             fromDir = os.path.join(configurationBuildDirectory, dirname, ".")
152             toDir = os.path.join(thinDirectory, dirname)
153             os.makedirs(toDir)
154             if subprocess.call('cp -R %s %s' % (fromDir, toDir), shell=True):
155                 return 1
156
157         for root, dirs, files in os.walk(thinDirectory, topdown=False):
158             for name in files:
159                 if name.endswith(".o"):
160                     os.remove(os.path.join(root, name))
161
162         if createZip(thinDirectory, configuration):
163             return 1
164
165     elif platform == 'chromium':
166         print "Archiving", configurationBuildDirectory
167         thinDirectory = os.path.join(configurationBuildDirectory, "thin")
168
169         # The scripts use the existence of out/Release/build.ninja to decide
170         # if this is a ninja build, so don't exclude build.ninja from the
171         # archive.
172         ignorePatterns = ['.svn', '*.a', '*.d', '*.dSYM', '*.o', '*.ilk', '*.lib', '*.idb',
173                           'BuildLog.htm', '*.obj', '*.pdb', '*.pch', '*.tlog', '*.lastbuildstate',
174                           '*.h', '*.c', '*.cc', '*.cpp', '*.stamp']
175
176         if fullPlatform and fullPlatform == 'chromium-android':
177             ignorePatterns.extend(['*.so', '*-unaligned.apk', '*-unsigned.apk'])
178
179         removeDirectoryIfExists(thinDirectory)
180         copyBuildFiles(configurationBuildDirectory, thinDirectory, ignorePatterns)
181         if createZip(thinDirectory, configuration):
182             return 1
183
184
185 def unzipArchive(directoryToExtractTo, configuration):
186     archiveDir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "WebKitBuild"))
187     assert os.path.isdir(archiveDir)
188     archiveFile = os.path.join(archiveDir, configuration + ".zip")
189
190     if sys.platform == 'darwin':
191         if subprocess.call(["ditto", "-x", "-k", archiveFile, directoryToExtractTo]):
192             return 1
193     elif sys.platform == 'cygwin' or sys.platform.startswith('linux'):
194         if subprocess.call(["unzip", "-o", archiveFile], cwd=directoryToExtractTo):
195             return 1
196     elif sys.platform == 'win32':
197         archive = zipfile.ZipFile(archiveFile, "r")
198         archive.extractall(directoryToExtractTo)
199         archive.close()
200
201     os.unlink(archiveFile)
202
203
204 def extractBuiltProduct(configuration, platform):
205     assert platform in ('mac', 'win', 'qt', 'gtk', 'efl', 'chromium')
206
207     archiveFile = os.path.join(_buildDirectory, configuration + ".zip")
208     configurationBuildDirectory = os.path.join(_buildDirectory, configuration.title())
209
210     removeDirectoryIfExists(configurationBuildDirectory)
211     os.makedirs(configurationBuildDirectory)
212
213     if platform == 'mac':
214         return unzipArchive(_buildDirectory, configuration)
215     elif platform == 'win':
216         binDirectory = os.path.join(configurationBuildDirectory, "bin")
217         os.makedirs(binDirectory)
218
219         safariPath = subprocess.Popen('cygpath -w "$PROGRAMFILES"/Safari',
220                                       shell=True, stdout=subprocess.PIPE).communicate()[0].strip()
221
222         if subprocess.call('cp -R "%s"/*.dll "%s"/*.resources %s' % (safariPath, safariPath, binDirectory), shell=True):
223             return 1
224
225         return unzipArchive(configurationBuildDirectory, configuration)
226     elif platform == 'qt' or platform == 'gtk' or platform == 'efl' or platform == 'chromium':
227         print "Extracting", configurationBuildDirectory
228         return unzipArchive(configurationBuildDirectory, configuration)
229
230
231 if __name__ == '__main__':
232     sys.exit(main())