0525a63f4ef1eda05386da6296a27a7325c447e6
[WebKit-https.git] / Tools / BuildSlaveSupport / built-product-archive
1 #!/usr/bin/python
2
3 # Copyright (C) 2009, 2015 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 _configurationBuildDirectory = None
35 _topLevelBuildDirectory = None
36
37
38 def main():
39     parser = optparse.OptionParser("usage: %prog [options] [action]")
40     parser.add_option("--platform", dest="platform")
41     parser.add_option("--debug", action="store_const", const="debug", dest="configuration")
42     parser.add_option("--release", action="store_const", const="release", dest="configuration")
43
44     options, (action, ) = parser.parse_args()
45     if not options.platform:
46         parser.error("Platform is required")
47         return 1
48     if not options.configuration:
49         parser.error("Configuration is required")
50         return 1
51     if action not in ('archive', 'extract'):
52         parser.error("Action is required")
53         return 1
54
55     genericPlatform = options.platform.split('-', 1)[0]
56     determineWebKitBuildDirectories(genericPlatform, options.platform, options.configuration)
57     if not _topLevelBuildDirectory:
58         print >> sys.stderr, 'Could not determine top-level build directory'
59         return 1
60     if not _configurationBuildDirectory:
61         print >> sys.stderr, 'Could not determine configuration-specific build directory'
62         return 1
63
64     if action == 'archive':
65         return archiveBuiltProduct(options.configuration, genericPlatform, options.platform)
66     else:
67         return extractBuiltProduct(options.configuration, genericPlatform)
68
69
70 def webkitBuildDirectoryForConfigurationAndPlatform(configuration, platform, fullPlatform='', returnTopLevelDirectory=False):
71     if fullPlatform.startswith('ios-simulator'):
72         platform = 'ios-simulator'
73     elif platform == 'ios':
74         platform = 'device'
75     command = ['perl', os.path.join(os.path.dirname(__file__), '..', 'Scripts', 'webkit-build-directory'), '--' + platform, '--' + configuration]
76     if returnTopLevelDirectory:
77         command += ['--top-level']
78     else:
79         command += ['--configuration']
80     return subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0].strip()
81
82
83 def determineWebKitBuildDirectories(platform, fullPlatform, configuration):
84     global _configurationBuildDirectory
85     global _topLevelBuildDirectory
86     _configurationBuildDirectory = webkitBuildDirectoryForConfigurationAndPlatform(configuration, platform, fullPlatform)
87     _topLevelBuildDirectory = webkitBuildDirectoryForConfigurationAndPlatform(configuration, platform, fullPlatform, returnTopLevelDirectory=True)
88     return _topLevelBuildDirectory
89
90
91 def removeDirectoryIfExists(thinDirectory):
92     if os.path.isdir(thinDirectory):
93         shutil.rmtree(thinDirectory)
94
95
96 def copyBuildFiles(source, destination, patterns):
97     shutil.copytree(source, destination, ignore=shutil.ignore_patterns(*patterns))
98
99
100 def createZipManually(directoryToZip, archiveFile):
101     archiveZip = zipfile.ZipFile(archiveFile, "w")
102
103     for path, dirNames, fileNames in os.walk(directoryToZip):
104         relativePath = os.path.relpath(path, directoryToZip)
105         for fileName in fileNames:
106             archiveZip.write(os.path.join(path, fileName), os.path.join(relativePath, fileName))
107
108     archiveZip.close()
109
110
111 def createZip(directoryToZip, configuration, embedParentDirectoryNameOnDarwin=False):
112     archiveDir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "WebKitBuild"))
113     archiveFile = os.path.join(archiveDir, configuration + ".zip")
114
115     try:
116         os.unlink(archiveFile)
117     except OSError, e:
118         if e.errno != 2:
119             raise
120
121     if sys.platform == 'darwin':
122         command = ['ditto', '-c', '-k', '--sequesterRsrc']
123         if embedParentDirectoryNameOnDarwin:
124             command += ['--keepParent']
125         command += [directoryToZip, archiveFile]
126         return subprocess.call(command)
127     elif sys.platform == 'cygwin':
128         return subprocess.call(["zip", "-r", archiveFile, "bin32"], cwd=directoryToZip)
129     elif sys.platform == 'win32':
130         createZipManually(directoryToZip, archiveFile)
131         return 0
132     elif sys.platform.startswith('linux'):
133         return subprocess.call(["zip", "-y", "-r", archiveFile, "."], cwd=directoryToZip)
134
135
136 def dirContainsdwo(directory):
137     sourcedir = os.path.join(_configurationBuildDirectory, directory)
138     for root, dirs, files in os.walk(sourcedir, topdown=False):
139         for name in files:
140             if name.endswith(".dwo"):
141                 return True
142     return False
143
144
145 def archiveBuiltProduct(configuration, platform, fullPlatform):
146     assert platform in ('mac', 'win', 'gtk', 'efl', 'ios')
147
148     if fullPlatform.startswith('ios-simulator'):
149         # We need to include in the archive the Mac tool, LayoutTestRelay, to run layout tests in the iOS simulator.
150         combinedDirectory = os.path.join(_topLevelBuildDirectory, 'combined-mac-and-ios')
151         removeDirectoryIfExists(combinedDirectory)
152         os.makedirs(combinedDirectory)
153
154         if subprocess.call(['/bin/cp', '-pR', _configurationBuildDirectory, combinedDirectory]):
155             return 1
156
157         macBuildDirectory = webkitBuildDirectoryForConfigurationAndPlatform(configuration, 'mac')
158         destinationDirectory = os.path.join(combinedDirectory, os.path.relpath(macBuildDirectory, _topLevelBuildDirectory))
159         os.makedirs(destinationDirectory)
160         for filename in ['LayoutTestRelay', 'LayoutTestRelay.dSYM']:
161             sourceFile = os.path.join(macBuildDirectory, filename)
162             if not os.path.exists(sourceFile):
163                 continue
164             if subprocess.call(['/bin/cp', '-pR', sourceFile, destinationDirectory]):
165                 return 1
166
167         if createZip(combinedDirectory, configuration):
168             return 1
169         shutil.rmtree(combinedDirectory)
170     elif platform in ('mac', 'ios'):
171         return createZip(_configurationBuildDirectory, configuration, embedParentDirectoryNameOnDarwin=True)
172     elif platform == 'win':
173         # FIXME: We shouldn't hardcode the assumption of a 32-bit build. See <https://bugs.webkit.org/show_bug.cgi?id=149715>.
174         binDirectory = os.path.join(_configurationBuildDirectory, 'bin32')
175         thinDirectory = os.path.join(_configurationBuildDirectory, 'thin')
176         thinBinDirectory = os.path.join(thinDirectory, "bin32")
177
178         removeDirectoryIfExists(thinDirectory)
179         copyBuildFiles(binDirectory, thinBinDirectory, ['*.ilk'])
180         if createZip(thinDirectory, configuration):
181             return 1
182
183         shutil.rmtree(thinDirectory)
184
185     elif platform == 'gtk' or platform == 'efl':
186         thinDirectory = os.path.join(_configurationBuildDirectory, 'thin')
187
188         removeDirectoryIfExists(thinDirectory)
189         os.mkdir(thinDirectory)
190
191         neededDirectories = ["bin", "lib"]
192
193         # When debug fission is enabled the directories below contain dwo files
194         # with the debug information needed to generate backtraces with GDB.
195         for objectDir in ["Tools", "Source"]:
196             if dirContainsdwo(objectDir):
197                 neededDirectories.append(objectDir)
198
199         for dirname in neededDirectories:
200             fromDir = os.path.join(_configurationBuildDirectory, dirname, '.')
201             toDir = os.path.join(thinDirectory, dirname)
202             os.makedirs(toDir)
203             if subprocess.call('cp -R %s %s' % (fromDir, toDir), shell=True):
204                 return 1
205
206         for root, dirs, files in os.walk(thinDirectory, topdown=False):
207             for name in files:
208                 if name.endswith(".o"):
209                     os.remove(os.path.join(root, name))
210
211         if createZip(thinDirectory, configuration):
212             return 1
213
214 def unzipArchive(directoryToExtractTo, configuration):
215     archiveDir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "WebKitBuild"))
216     assert os.path.isdir(archiveDir)
217     archiveFile = os.path.join(archiveDir, configuration + ".zip")
218
219     if sys.platform == 'darwin':
220         if subprocess.call(["ditto", "-x", "-k", archiveFile, directoryToExtractTo]):
221             return 1
222     elif sys.platform == 'cygwin' or sys.platform.startswith('linux'):
223         if subprocess.call(["unzip", "-o", archiveFile], cwd=directoryToExtractTo):
224             return 1
225     elif sys.platform == 'win32':
226         archive = zipfile.ZipFile(archiveFile, "r")
227         archive.extractall(directoryToExtractTo)
228         archive.close()
229
230     os.unlink(archiveFile)
231
232
233 def extractBuiltProduct(configuration, platform):
234     assert platform in ('mac', 'win', 'gtk', 'efl', 'ios')
235
236     archiveFile = os.path.join(_topLevelBuildDirectory, configuration + '.zip')
237
238     removeDirectoryIfExists(_configurationBuildDirectory)
239     os.makedirs(_configurationBuildDirectory)
240
241     if platform in ('mac', 'ios'):
242         return unzipArchive(_topLevelBuildDirectory, configuration)
243     elif platform == 'win' or platform == 'gtk' or platform == 'efl':
244         print 'Extracting', _configurationBuildDirectory
245         return unzipArchive(_configurationBuildDirectory, configuration)
246
247
248 if __name__ == '__main__':
249     sys.exit(main())