1#!/usr/bin/env python
2# Copyright (c) 2013 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
6import json
7import optparse
8import os
9import re
10import sys
11
12if sys.version_info < (2, 6, 0):
13  sys.stderr.write("python 2.6 or later is required run this script\n")
14  sys.exit(1)
15
16import buildbot_common
17import build_projects
18import build_version
19import easy_template
20import parse_dsc
21
22from build_paths import SDK_SRC_DIR, OUT_DIR, SDK_RESOURCE_DIR
23
24sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
25import getos
26import oshelpers
27
28
29def RemoveBuildCruft(outdir):
30  for root, _, files in os.walk(outdir):
31    for f in files:
32      path = os.path.join(root, f)
33      ext = os.path.splitext(path)[1]
34      # Remove unwanted files from the package. Also remove manifest.json files
35      # (which we usually want). These ones are the manifests of the invidual
36      # examples, though, which CWS complains about. The master manifest.json
37      # is generated after we call RemoveBuildCruft.
38      if (ext in ('.d', '.o') or
39          f == 'dir.stamp' or
40          f == 'manifest.json' or
41          re.search(r'_unstripped_.*?\.nexe', f)):
42        buildbot_common.RemoveFile(path)
43
44
45def StripNexes(outdir, platform, pepperdir):
46  for root, _, files in os.walk(outdir):
47    for f in files:
48      path = os.path.join(root, f)
49      m = re.search(r'lib(32|64).*\.so', path)
50      arch = None
51      if m:
52        # System .so file. Must be x86, because ARM doesn't support glibc yet.
53        arch = 'x86_' + m.group(1)
54      else:
55        basename, ext = os.path.splitext(f)
56        if ext in ('.nexe', '.so'):
57          # We can get the arch from the filename...
58          valid_arches = ('x86_64', 'x86_32', 'arm')
59          for a in valid_arches:
60            if basename.endswith(a):
61              arch = a
62              break
63      if not arch:
64        continue
65
66      strip = GetStrip(pepperdir, platform, arch, 'newlib')
67      buildbot_common.Run([strip, path])
68
69
70def GetStrip(pepperdir, platform, arch, toolchain):
71  base_arch = {'x86_32': 'x86', 'x86_64': 'x86', 'arm': 'arm'}[arch]
72  bin_dir = os.path.join(pepperdir, 'toolchain',
73                         '%s_%s_%s' % (platform, base_arch, toolchain), 'bin')
74  strip_prefix = {'x86_32': 'i686', 'x86_64': 'x86_64', 'arm': 'arm'}[arch]
75  strip_name = '%s-nacl-strip' % strip_prefix
76  return os.path.join(bin_dir, strip_name)
77
78
79def main(args):
80  parser = optparse.OptionParser()
81  parser.add_option('-c', '--channel',
82      help='Channel to display in the name of the package.')
83
84  # To setup bash completion for this command first install optcomplete
85  # and then add this line to your .bashrc:
86  #  complete -F _optcomplete build_app.py
87  try:
88    import optcomplete
89    optcomplete.autocomplete(parser)
90  except ImportError:
91    pass
92
93  options, args = parser.parse_args(args[1:])
94
95  if options.channel:
96    if options.channel not in ('Dev', 'Beta'):
97      parser.error('Unknown channel: %s' % options.channel)
98
99  toolchains = ['newlib', 'glibc']
100
101  pepper_ver = str(int(build_version.ChromeMajorVersion()))
102  pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
103  app_dir = os.path.join(OUT_DIR, 'naclsdk_app')
104  app_examples_dir = os.path.join(app_dir, 'examples')
105  sdk_resources_dir = SDK_RESOURCE_DIR
106  platform = getos.GetPlatform()
107
108  buildbot_common.RemoveDir(app_dir)
109  buildbot_common.MakeDir(app_dir)
110
111  # Add some dummy directories so build_projects doesn't complain...
112  buildbot_common.MakeDir(os.path.join(app_dir, 'tools'))
113  buildbot_common.MakeDir(os.path.join(app_dir, 'toolchain'))
114
115  config = 'Release'
116
117  filters = {}
118  filters['DISABLE_PACKAGE'] = False
119  filters['EXPERIMENTAL'] = False
120  filters['TOOLS'] = toolchains
121  filters['DEST'] = ['examples/api', 'examples/getting_started',
122                     'examples/demo', 'examples/tutorial']
123  tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
124  build_projects.UpdateHelpers(app_dir, clobber=True)
125  build_projects.UpdateProjects(app_dir, tree, clobber=False,
126                                toolchains=toolchains, configs=[config],
127                                first_toolchain=True)
128
129  # Collect permissions from each example, and aggregate them.
130  def MergeLists(list1, list2):
131    return list1 + [x for x in list2 if x not in list1]
132  all_permissions = []
133  all_socket_permissions = []
134  for _, project in parse_dsc.GenerateProjects(tree):
135    permissions = project.get('PERMISSIONS', [])
136    all_permissions = MergeLists(all_permissions, permissions)
137    socket_permissions = project.get('SOCKET_PERMISSIONS', [])
138    all_socket_permissions = MergeLists(all_socket_permissions,
139                                        socket_permissions)
140  if all_socket_permissions:
141    all_permissions.append({'socket': all_socket_permissions})
142  pretty_permissions = json.dumps(all_permissions, sort_keys=True, indent=4)
143
144  for filename in ['background.js', 'icon128.png']:
145    buildbot_common.CopyFile(os.path.join(sdk_resources_dir, filename),
146                             os.path.join(app_examples_dir, filename))
147
148  os.environ['NACL_SDK_ROOT'] = pepperdir
149
150  build_projects.BuildProjects(app_dir, tree, deps=False, clean=False,
151                               config=config)
152
153  RemoveBuildCruft(app_dir)
154  StripNexes(app_dir, platform, pepperdir)
155
156  # Add manifest.json after RemoveBuildCruft... that function removes the
157  # manifest.json files for the individual examples.
158  name = 'Native Client SDK'
159  if options.channel:
160    name += ' (%s)' % options.channel
161  template_dict = {
162    'name': name,
163    'channel': options.channel,
164    'description':
165        'Native Client SDK examples, showing API use and key concepts.',
166    'key': False,  # manifests with "key" are rejected when uploading to CWS.
167    'permissions': pretty_permissions,
168    'version': build_version.ChromeVersionNoTrunk()
169  }
170  easy_template.RunTemplateFile(
171      os.path.join(sdk_resources_dir, 'manifest.json.template'),
172      os.path.join(app_examples_dir, 'manifest.json'),
173      template_dict)
174
175  app_zip = os.path.join(app_dir, 'examples.zip')
176  os.chdir(app_examples_dir)
177  oshelpers.Zip([app_zip, '-r', '*'])
178
179  return 0
180
181
182if __name__ == '__main__':
183  sys.exit(main(sys.argv))
184