Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(615)

Side by Side Diff: standalone/standalone_installer.py

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright 2009 Google Inc.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 # ========================================================================
16
17 """Builds standalone installers and MSI wrappers around them.
18
19 This is very close to the logic within installers\build.scons. The difference
20 is that we have an additional file standalone_installers.txt. This file
21 contains a list of standalone installers to create along with necessary values.
22 For each entry in standalone_installers.txt, we create a corresponding
23 standalone installer, which is the meta-installer, app installer binaries, and
24 update response tarred together.
25 MSI installers that wrap the standalone installer may also be created.
26 """
27
28 import array
29 import base64
30 import codecs
31 import os
32 import sha
33
34 from enterprise.installer import build_enterprise_installer
35 from installers import build_metainstaller
36 from installers import tag_meta_installers
37 from installers import tagged_installer
38
39
40 class OfflineInstaller(object):
41 """Represents the information for a bundle."""
42
43 def __init__(self,
44 friendly_product_name,
45 exe_base_name,
46 binaries,
47 msi_base_name,
48 custom_tag_params,
49 silent_uninstall_args,
50 should_build_enterprise_msi,
51 msi_installer_data,
52 installers_txt_filename):
53 self.friendly_product_name = friendly_product_name
54 self.exe_base_name = exe_base_name
55 self.binaries = binaries
56 self.msi_base_name = msi_base_name
57 self.custom_tag_params = custom_tag_params
58 self.silent_uninstall_args = silent_uninstall_args
59 self.should_build_enterprise_msi = should_build_enterprise_msi
60 self.msi_installer_data = msi_installer_data
61 self.installers_txt_filename = installers_txt_filename
62
63
64 def ReadOfflineInstallersFile(env, offline_installers_file_path):
65 """Enumerates the entries in the offline installers file.
66
67 Args:
68 env: Environment.
69 offline_installers_file_path: Path to file specifying installers to build.
70
71 Returns:
72 Returns a list of structures used for creating the prestamped binaries.
73 """
74
75 offline_installers = []
76 offline_abs_path = env.File(offline_installers_file_path).abspath
77 installer_file = codecs.open(offline_abs_path, 'r')
78 for line in installer_file:
79 line = line.strip()
80 if len(line) and not line.startswith('#'):
81 (friendly_product_name,
82 exe_base_name,
83 binaries,
84 msi_base_name,
85 custom_tag_params,
86 silent_uninstall_args,
87 should_build_enterprise_msi,
88 msi_installer_data,
89 installers_txt_filename) = eval(line)
90 installer = OfflineInstaller(friendly_product_name,
91 exe_base_name,
92 binaries,
93 msi_base_name,
94 custom_tag_params,
95 silent_uninstall_args,
96 should_build_enterprise_msi,
97 msi_installer_data,
98 installers_txt_filename)
99 offline_installers.append(installer)
100 return offline_installers
101
102
103 def BuildOfflineInstallersVersion(env,
104 omaha_version_info,
105 omaha_files_path,
106 empty_metainstaller_path,
107 offline_installers_file_path,
108 manifest_files_path,
109 prefix='',
110 is_official=False):
111 """Builds all standalone installers specified in offline_installers_file_path.
112
113 Args:
114 env: Environment.
115 omaha_version_info: info about the version of the Omaha files
116 omaha_files_path: Path to the directory containing the Omaha binaries.
117 empty_metainstaller_path: Path to empty (no tarball) metainstaller binary.
118 offline_installers_file_path: Path to file specifying installers to build.
119 manifest_files_path: Path to the directory containing the manifests for the
120 apps specified in offline_installers_file_path.
121 prefix: Optional prefix for the resulting installer.
122 is_official: Whether to build official (vs. test) standalone installers.
123 """
124
125 offline_installers = ReadOfflineInstallersFile(env,
126 offline_installers_file_path)
127
128 for offline_installer in offline_installers:
129 BuildOfflineInstaller(
130 env,
131 offline_installer,
132 omaha_version_info,
133 omaha_files_path,
134 empty_metainstaller_path,
135 offline_installers_file_path,
136 manifest_files_path,
137 prefix,
138 is_official
139 )
140
141
142 def _GenerateUpdateResponseFile(target, source, env):
143 """Generate GUP file based on a list of sources.
144
145 Don't call function directly from this script. source may be
146 generated as part of build. Use function as action in env.Command.
147
148 Args:
149 target: Target GUP file name.
150 source: A list of source files. Source files should be listed as manifest1,
151 binary1, manifest2, binary2 and so on. Order is important so that
152 manifests and installers can be differentiated and 'INSTALLER_VERSIONS'
153 can be applied properly.
154 env: Construct environment. This environment must contain environment
155 variable 'INSTALLER_VERSIONS', which contains a list of versions for
156 corresponding binaries in source and should be in same order.
157
158 Raises:
159 Exception: When build encounters error.
160 """
161 xml_header = '<?xml version="1.0" encoding="UTF-8"?>\n'
162 response_header = '<response protocol="3.0">'
163 response_footer = '</response>'
164
165 local_env = env.Clone()
166
167 version_list = local_env['INSTALLER_VERSIONS']
168 if not version_list:
169 raise Exception('INSTALLER_VERSIONS is missing from environment.')
170
171 manifest_content_list = [xml_header, response_header]
172 for file_index in xrange(0, len(source), 2):
173 source_manifest_path = source[file_index]
174 binary_path = source[file_index + 1]
175 size = os.stat(binary_path.abspath).st_size
176 installer_file = open(binary_path.abspath, mode='rb')
177 data = array.array('B')
178 data.fromfile(installer_file, size)
179 installer_file.close()
180 s = sha.new(data)
181 hash_value = base64.b64encode(s.digest())
182
183 manifest_file = open(source_manifest_path.abspath)
184 manifest_content = manifest_file.read()
185 response_body_start_index = manifest_content.find('<response')
186 if response_body_start_index < 0:
187 raise Exception('GUP file does not contain response element.')
188 # + 1 to include the closing > in header
189 response_body_start_index = manifest_content.find(
190 '>', response_body_start_index)
191 if response_body_start_index < 0:
192 raise Exception('GUP file does not contain response element.')
193 response_body_start_index += 1
194 response_body_end_index = manifest_content.find(
195 '</response>', response_body_start_index)
196 if response_body_end_index < 0:
197 raise Exception('GUP file is not in valid response format.')
198 local_env['INSTALLER_SIZE'] = str(size)
199 local_env['INSTALLER_HASH'] = hash_value
200 local_env['INSTALLER_VERSION'] = version_list[file_index/2]
201 manifest_content_list.append(local_env.subst(
202 manifest_content[response_body_start_index:response_body_end_index],
203 raw=1))
204 manifest_file.close()
205 manifest_content_list.append(response_footer)
206
207 manifest_content_str = ''.join(manifest_content_list)
208 output_file = open(target[0].abspath, 'w')
209 output_file.write(manifest_content_str)
210 output_file.close()
211
212
213 def BuildOfflineInstaller(
214 env,
215 offline_installer,
216 omaha_version_info,
217 omaha_files_path,
218 empty_metainstaller_path,
219 offline_installers_file_path,
220 manifest_files_path,
221 prefix='',
222 is_official=False,
223 installers_sources_path='$MAIN_DIR/installers',
224 enterprise_installers_sources_path='$MAIN_DIR/enterprise/installer',
225 lzma_path='$MAIN_DIR/third_party/lzma/v4_65/files/lzma.exe',
226 resmerge_path='$MAIN_DIR/tools/resmerge'):
227 """Builds the standalone installers specified by offline_installer.
228
229 Args:
230 env: Environment.
231 offline_installer: OfflineInstaller containing the information about the
232 standalone installer to build.
233 omaha_version_info: info about the version of the Omaha files
234 omaha_files_path: Path to the directory containing the Omaha binaries.
235 empty_metainstaller_path: Path to empty (no tarball) metainstaller binary.
236 offline_installers_file_path: Path to file specifying installers to build.
237 manifest_files_path: Path to the directory containing the manifests for the
238 apps specified in offline_installers_file_path.
239 prefix: Optional prefix for the resulting installer.
240 is_official: Whether to build official (vs. test) standalone installers.
241 installers_sources_path: path to the directory containing the source files
242 for building the metainstaller
243 enterprise_installers_sources_path: path to the directory containing the
244 source files for building enterprise installers
245 lzma_path: path to lzma.exe
246 resmerge_path: path to resmerge.exe
247
248 Returns:
249 Target nodes.
250
251 Raises:
252 Exception: Missing or invalid data specified in offline_installer.
253 """
254 standalone_installer_base_name = offline_installer.exe_base_name
255 if not standalone_installer_base_name:
256 raise Exception('Product name not specified.')
257
258 output_dir = '$STAGING_DIR'
259 if not is_official:
260 standalone_installer_base_name = ('UNOFFICIAL_' +
261 standalone_installer_base_name)
262 output_dir = '$TARGET_ROOT/Test_Installers'
263
264 target_base = prefix + standalone_installer_base_name
265 target_name = target_base + '.exe'
266 log_name = target_base + '_Contents.txt'
267
268 # Write Omaha's version.
269 log_text = '*** Omaha Version ***\n\n'
270 log_text += omaha_version_info.GetVersionString() + '\n'
271
272 # Rename the checked in binaries by adding the application guid as the
273 # extension. This is needed as the meta-installer expects the
274 # extension.
275 # Also, log information about each app.
276 additional_payload_contents = []
277 if not offline_installer.binaries:
278 raise Exception('No binaries specified.')
279
280 manifest_target = ''
281 manifest_source = []
282 version_list = []
283 for binary in offline_installer.binaries:
284 (version, installer_path, guid) = binary
285 if not installer_path or not guid or not version:
286 raise Exception('Application specification is incomplete.')
287
288 installer_path_modified = os.path.basename(installer_path) + '.' + guid
289 # Have to use Command('copy') here instead of replicate, as the
290 # file is being renamed in the process.
291 env.Command(
292 target=installer_path_modified,
293 source=installer_path,
294 action='@copy /y $SOURCES $TARGET'
295 )
296
297 manifest_source.extend([
298 manifest_files_path + '/' + guid + '.gup', installer_path_modified])
299 version_list.append(version)
300 additional_payload_contents.append(installer_path_modified)
301
302 # TODO(omaha): Use full guid and version to generate unique string, use
303 # hash of the unique string as target directory name.
304 manifest_target += guid[0:4] + version
305
306 # Log info about the app.
307 log_text += '\n\n*** App: ' + guid + ' ***\n'
308 log_text += '\nVersion:' + version + '\n'
309 log_text += '\nINSTALLER:\n' + installer_path + '\n'
310
311 # Place the generated manifests in a subdirectory. This allows a single
312 # build to generate installers for multiple versions of the same app.
313 manifest_target += '/OfflineManifest.gup'
314 manifest_file_path = env.Command(
315 target=manifest_target,
316 source=manifest_source,
317 action=[_GenerateUpdateResponseFile],
318 INSTALLER_VERSIONS=version_list
319 )
320
321 # Use the BCJ2 tool from the official build we're using to generate this
322 # metainstaller, not the current build directory.
323 bcj2_path = omaha_files_path + '/bcj2.exe'
324
325 additional_payload_contents.append(manifest_file_path)
326
327 def WriteLog(target, source, env):
328 """Writes the log of what is being built."""
329 dump_data = ''
330 for f in source:
331 file_to_dump = open(env.File(f).abspath, 'r', -1)
332 content = file_to_dump.read()
333 file_to_dump.close()
334 dump_data += '\nMANIFEST:\n'
335 dump_data += str(f)
336 dump_data += '\n'
337 dump_data += content
338 source = source # Avoid PyLint warning.
339 f = open(env.File(target[0]).abspath, 'w')
340 f.write(env['write_data'])
341 f.write(dump_data)
342 f.close()
343 return 0
344
345 env.Command(
346 target='%s/%s' % (output_dir, log_name),
347 source=manifest_file_path,
348 action=WriteLog,
349 write_data=log_text
350 )
351
352 results = []
353 results += build_metainstaller.BuildMetaInstaller(
354 env=env,
355 target_name=target_name,
356 omaha_version_info=omaha_version_info,
357 empty_metainstaller_path=empty_metainstaller_path,
358 omaha_files_path=omaha_files_path,
359 prefix=prefix,
360 suffix='_' + standalone_installer_base_name,
361 additional_payload_contents=additional_payload_contents,
362 additional_payload_contents_dependencies=offline_installers_file_path,
363 output_dir=output_dir,
364 installers_sources_path=installers_sources_path,
365 lzma_path=lzma_path,
366 resmerge_path=resmerge_path,
367 bcj2_path=bcj2_path
368 )
369
370 standalone_installer_path = '%s/%s' % (output_dir, target_name)
371
372 # Build an enterprise installer.
373 if offline_installer.should_build_enterprise_msi:
374 # TODO(omaha): Add support for bundles here and to
375 # BuildEnterpriseInstallerFromStandaloneInstaller().
376 # TODO(omaha): Determine how product_version should be decided for MSI in
377 # bundle scenarios.
378 # TODO(omaha): custom tag, silent uninstall args, distribution data may need
379 # to be made per-app.
380 if 1 < len(offline_installer.binaries):
381 raise Exception('Enterprise installers do not currently support bundles.')
382 (product_version, installer_path, product_guid) = offline_installer.binaries [0]
383
384 # Note: msi_base_name should not include version info and cannot change!
385 friendly_product_name = offline_installer.friendly_product_name
386 msi_base_name = offline_installer.msi_base_name
387 custom_tag_params = offline_installer.custom_tag_params
388 silent_uninstall_args = offline_installer.silent_uninstall_args
389 msi_installer_data = offline_installer.msi_installer_data
390
391 # custom_tag_params and msi_installer_data are optional.
392 if (not product_version or not friendly_product_name or not msi_base_name or
393 not silent_uninstall_args):
394 raise Exception('Field required to build enterprise MSI is missing.')
395
396 if not is_official:
397 msi_base_name = ('UNOFFICIAL_' + msi_base_name)
398
399 results += (build_enterprise_installer.
400 BuildEnterpriseInstallerFromStandaloneInstaller(
401 env,
402 friendly_product_name,
403 product_version,
404 product_guid,
405 custom_tag_params,
406 silent_uninstall_args,
407 msi_installer_data,
408 standalone_installer_path,
409 omaha_files_path + '/show_error_action.dll',
410 prefix + msi_base_name,
411 enterprise_installers_sources_path,
412 output_dir=output_dir
413 ))
414
415 # Tag the meta-installer if an installers.txt file was specified.
416 if offline_installer.installers_txt_filename:
417 installers_txt_path = env.File(
418 offline_installer.installers_txt_filename).abspath
419 app_bundles = tag_meta_installers.ReadBundleInstallerFile(
420 installers_txt_path)
421
422 bundles = {}
423 for (key, bundle_list) in app_bundles.items():
424 if not bundle_list or not key:
425 continue
426 if not key in bundles:
427 bundles[key] = bundle_list
428 else:
429 new_bundles_list = bundles[key] + bundle_list
430 bundles[key] = new_bundles_list
431
432 tag_meta_installers.SetOutputFileNames(target_name, bundles, '')
433 for bundles_lang in bundles.itervalues():
434 for bundle in bundles_lang:
435 results += tagged_installer.TagOneBundle(
436 env=env,
437 bundle=bundle,
438 untagged_binary_path=standalone_installer_path,
439 output_dir='$TARGET_ROOT/Tagged_Offline_Installers',
440 )
441
442 return results
OLDNEW
« no previous file with comments | « standalone/manifests/{D6B08267-B440-4C85-9F79-E195E80D9937}.gup ('k') | standalone/standalone_installers.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698