1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Build script to generate a new sdk_tools bundle.
7
8This script packages the files necessary to generate the SDK updater -- the
9tool users run to download new bundles, update existing bundles, etc.
10"""
11
12import buildbot_common
13import build_version
14import glob
15import optparse
16import os
17import sys
18
19SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
20SDK_SRC_DIR = os.path.dirname(SCRIPT_DIR)
21SDK_DIR = os.path.dirname(SDK_SRC_DIR)
22SRC_DIR = os.path.dirname(SDK_DIR)
23NACL_DIR = os.path.join(SRC_DIR, 'native_client')
24CYGTAR = os.path.join(NACL_DIR, 'build', 'cygtar.py')
25
26sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
27
28import oshelpers
29
30
31UPDATER_FILES = [
32  # launch scripts
33  ('build_tools/naclsdk', 'nacl_sdk/naclsdk'),
34  ('build_tools/naclsdk.bat', 'nacl_sdk/naclsdk.bat'),
35
36  # base manifest
37  ('build_tools/json/naclsdk_manifest0.json',
38      'nacl_sdk/sdk_cache/naclsdk_manifest2.json'),
39
40  # SDK tools
41  ('build_tools/sdk_tools/cacerts.txt', 'nacl_sdk/sdk_tools/cacerts.txt'),
42  ('build_tools/sdk_tools/*.py', 'nacl_sdk/sdk_tools/'),
43  ('build_tools/sdk_tools/command/*.py', 'nacl_sdk/sdk_tools/command/'),
44  ('build_tools/sdk_tools/third_party/*.py', 'nacl_sdk/sdk_tools/third_party/'),
45  ('build_tools/sdk_tools/third_party/fancy_urllib/*.py',
46      'nacl_sdk/sdk_tools/third_party/fancy_urllib/'),
47  ('build_tools/sdk_tools/third_party/fancy_urllib/README',
48      'nacl_sdk/sdk_tools/third_party/fancy_urllib/README'),
49  ('build_tools/manifest_util.py', 'nacl_sdk/sdk_tools/manifest_util.py'),
50  ('LICENSE', 'nacl_sdk/sdk_tools/LICENSE'),
51  (CYGTAR, 'nacl_sdk/sdk_tools/cygtar.py'),
52]
53
54
55def MakeUpdaterFilesAbsolute(out_dir):
56  """Return the result of changing all relative paths in UPDATER_FILES to
57  absolute paths.
58
59  Args:
60    out_dir: The output directory.
61  Returns:
62    A list of 2-tuples. The first element in each tuple is the source path and
63    the second is the destination path.
64  """
65  assert os.path.isabs(out_dir)
66
67  result = []
68  for in_file, out_file in UPDATER_FILES:
69    if not os.path.isabs(in_file):
70      in_file = os.path.join(SDK_SRC_DIR, in_file)
71    out_file = os.path.join(out_dir, out_file)
72    result.append((in_file, out_file))
73  return result
74
75
76def GlobFiles(files):
77  """Expand wildcards for 2-tuples of sources/destinations.
78
79  This function also will convert destinations from directories into filenames.
80  For example:
81    ('foo/*.py', 'bar/') => [('foo/a.py', 'bar/a.py'), ('foo/b.py', 'bar/b.py')]
82
83  Args:
84    files: A list of 2-tuples of (source, dest) paths.
85  Returns:
86    A new list of 2-tuples, after the sources have been wildcard-expanded, and
87    the destinations have been changed from directories to filenames.
88  """
89  result = []
90  for in_file_glob, out_file in files:
91    if out_file.endswith('/'):
92      for in_file in glob.glob(in_file_glob):
93        result.append((in_file,
94                      os.path.join(out_file, os.path.basename(in_file))))
95    else:
96      result.append((in_file_glob, out_file))
97  return result
98
99
100def CopyFiles(files):
101  """Given a list of 2-tuples (source, dest), copy each source file to a dest
102  file.
103
104  Args:
105    files: A list of 2-tuples."""
106  for in_file, out_file in files:
107    buildbot_common.MakeDir(os.path.dirname(out_file))
108    buildbot_common.CopyFile(in_file, out_file)
109
110
111def UpdateRevisionNumber(out_dir, revision_number):
112  """Update the sdk_tools bundle to have the given revision number.
113
114  This function finds all occurrences of the string "{REVISION}" in
115  sdk_update_main.py and replaces them with |revision_number|. The only
116  observable effect of this change should be that running:
117
118    naclsdk -v
119
120  will contain the new |revision_number|.
121
122  Args:
123    out_dir: The output directory containing the scripts to update.
124    revision_number: The revision number as an integer, or None to use the
125        current Chrome revision (as retrieved through svn/git).
126  """
127  if revision_number is None:
128    revision_number = build_version.ChromeRevision()
129
130  SDK_UPDATE_MAIN = os.path.join(out_dir,
131      'nacl_sdk/sdk_tools/sdk_update_main.py')
132
133  contents = open(SDK_UPDATE_MAIN, 'r').read().replace(
134      '{REVISION}', str(revision_number))
135  open(SDK_UPDATE_MAIN, 'w').write(contents)
136
137
138def BuildUpdater(out_dir, revision_number=None):
139  """Build naclsdk.zip and sdk_tools.tgz in |out_dir|.
140
141  Args:
142    out_dir: The output directory.
143    revision_number: The revision number of this updater, as an integer. Or
144        None, to use the current Chrome revision."""
145  buildbot_common.BuildStep('Create Updater')
146
147  out_dir = os.path.abspath(out_dir)
148
149  # Build SDK directory
150  buildbot_common.RemoveDir(os.path.join(out_dir, 'nacl_sdk'))
151
152  updater_files = MakeUpdaterFilesAbsolute(out_dir)
153  updater_files = GlobFiles(updater_files)
154
155  CopyFiles(updater_files)
156  UpdateRevisionNumber(out_dir, revision_number)
157
158  out_files = [os.path.relpath(out_file, out_dir)
159               for _, out_file in updater_files]
160
161  # Make zip
162  buildbot_common.RemoveFile(os.path.join(out_dir, 'nacl_sdk.zip'))
163  buildbot_common.Run([sys.executable, oshelpers.__file__, 'zip',
164                      'nacl_sdk.zip'] + out_files,
165                      cwd=out_dir)
166
167  # Tar of all files under nacl_sdk/sdk_tools
168  sdktoolsdir = os.path.join('nacl_sdk', 'sdk_tools')
169  tarname = os.path.join(out_dir, 'sdk_tools.tgz')
170  files_to_tar = [os.path.relpath(out_file, sdktoolsdir)
171      for out_file in out_files if out_file.startswith(sdktoolsdir)]
172  buildbot_common.RemoveFile(tarname)
173  buildbot_common.Run([sys.executable, CYGTAR, '-C',
174      os.path.join(out_dir, sdktoolsdir), '-czf', tarname] + files_to_tar)
175  sys.stdout.write('\n')
176
177
178def main(args):
179  parser = optparse.OptionParser()
180  parser.add_option('-o', '--out', help='output directory',
181      dest='out_dir', default=os.path.join(SRC_DIR, 'out'))
182  parser.add_option('-r', '--revision', help='revision number of this updater',
183      dest='revision', default=None)
184  options, args = parser.parse_args(args[1:])
185
186  if options.revision:
187    options.revision = int(options.revision)
188  BuildUpdater(options.out_dir, options.revision)
189
190
191if __name__ == '__main__':
192  sys.exit(main(sys.argv))
193