1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#!/usr/bin/env python
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Copyright (c) 2013 The Chromium Authors. All rights reserved.
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)# found in the LICENSE file.
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
6558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdochimport json
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import optparse
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import os
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import re
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import sys
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)if sys.version_info < (2, 6, 0):
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sys.stderr.write("python 2.6 or later is required run this script\n")
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sys.exit(1)
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import buildbot_common
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import build_projects
1890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)import build_version
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import easy_template
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import parse_dsc
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)from build_paths import SDK_SRC_DIR, OUT_DIR, SDK_RESOURCE_DIR
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import getos
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)import oshelpers
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def RemoveBuildCruft(outdir):
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for root, _, files in os.walk(outdir):
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for f in files:
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      path = os.path.join(root, f)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      ext = os.path.splitext(path)[1]
341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      # Remove unwanted files from the package. Also remove manifest.json files
351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      # (which we usually want). These ones are the manifests of the invidual
361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      # examples, though, which CWS complains about. The master manifest.json
371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      # is generated after we call RemoveBuildCruft.
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (ext in ('.d', '.o') or
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          f == 'dir.stamp' or
401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          f == 'manifest.json' or
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          re.search(r'_unstripped_.*?\.nexe', f)):
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        buildbot_common.RemoveFile(path)
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def StripNexes(outdir, platform, pepperdir):
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for root, _, files in os.walk(outdir):
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    for f in files:
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      path = os.path.join(root, f)
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      m = re.search(r'lib(32|64).*\.so', path)
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      arch = None
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if m:
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        # System .so file. Must be x86, because ARM doesn't support glibc yet.
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        arch = 'x86_' + m.group(1)
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      else:
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        basename, ext = os.path.splitext(f)
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if ext in ('.nexe', '.so'):
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          # We can get the arch from the filename...
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          valid_arches = ('x86_64', 'x86_32', 'arm')
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          for a in valid_arches:
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            if basename.endswith(a):
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              arch = a
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              break
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if not arch:
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        continue
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      strip = GetStrip(pepperdir, platform, arch, 'newlib')
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      buildbot_common.Run([strip, path])
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def GetStrip(pepperdir, platform, arch, toolchain):
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base_arch = {'x86_32': 'x86', 'x86_64': 'x86', 'arm': 'arm'}[arch]
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bin_dir = os.path.join(pepperdir, 'toolchain',
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                         '%s_%s_%s' % (platform, base_arch, toolchain), 'bin')
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  strip_prefix = {'x86_32': 'i686', 'x86_64': 'x86_64', 'arm': 'arm'}[arch]
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  strip_name = '%s-nacl-strip' % strip_prefix
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return os.path.join(bin_dir, strip_name)
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)def main(args):
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parser = optparse.OptionParser()
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  parser.add_option('-c', '--channel',
821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      help='Channel to display in the name of the package.')
830f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
840f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  # To setup bash completion for this command first install optcomplete
850f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  # and then add this line to your .bashrc:
860f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  #  complete -F _optcomplete build_app.py
870f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  try:
880f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    import optcomplete
890f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    optcomplete.autocomplete(parser)
900f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  except ImportError:
910f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    pass
920f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  options, args = parser.parse_args(args[1:])
941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if options.channel:
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if options.channel not in ('Dev', 'Beta'):
971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      parser.error('Unknown channel: %s' % options.channel)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  toolchains = ['newlib', 'glibc']
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
10190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  pepper_ver = str(int(build_version.ChromeMajorVersion()))
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  app_dir = os.path.join(OUT_DIR, 'naclsdk_app')
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  app_examples_dir = os.path.join(app_dir, 'examples')
10558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  sdk_resources_dir = SDK_RESOURCE_DIR
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  platform = getos.GetPlatform()
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  buildbot_common.RemoveDir(app_dir)
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  buildbot_common.MakeDir(app_dir)
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  # Add some dummy directories so build_projects doesn't complain...
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  buildbot_common.MakeDir(os.path.join(app_dir, 'tools'))
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  buildbot_common.MakeDir(os.path.join(app_dir, 'toolchain'))
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  config = 'Release'
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  filters = {}
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  filters['DISABLE_PACKAGE'] = False
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  filters['EXPERIMENTAL'] = False
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  filters['TOOLS'] = toolchains
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  filters['DEST'] = ['examples/api', 'examples/getting_started',
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                     'examples/demo', 'examples/tutorial']
123ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
124558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  build_projects.UpdateHelpers(app_dir, clobber=True)
125558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  build_projects.UpdateProjects(app_dir, tree, clobber=False,
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                toolchains=toolchains, configs=[config],
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                first_toolchain=True)
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
12990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  # Collect permissions from each example, and aggregate them.
130558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  def MergeLists(list1, list2):
131558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    return list1 + [x for x in list2 if x not in list1]
13290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  all_permissions = []
133558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  all_socket_permissions = []
13490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for _, project in parse_dsc.GenerateProjects(tree):
135558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    permissions = project.get('PERMISSIONS', [])
136558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    all_permissions = MergeLists(all_permissions, permissions)
137558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    socket_permissions = project.get('SOCKET_PERMISSIONS', [])
138558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    all_socket_permissions = MergeLists(all_socket_permissions,
139558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch                                        socket_permissions)
140558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  if all_socket_permissions:
141558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch    all_permissions.append({'socket': all_socket_permissions})
142558790d6acca3451cf3a6b497803a5f07d0bec58Ben Murdoch  pretty_permissions = json.dumps(all_permissions, sort_keys=True, indent=4)
14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for filename in ['background.js', 'icon128.png']:
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    buildbot_common.CopyFile(os.path.join(sdk_resources_dir, filename),
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                             os.path.join(app_examples_dir, filename))
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  os.environ['NACL_SDK_ROOT'] = pepperdir
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
15058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  build_projects.BuildProjects(app_dir, tree, deps=False, clean=False,
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                               config=config)
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RemoveBuildCruft(app_dir)
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  StripNexes(app_dir, platform, pepperdir)
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  # Add manifest.json after RemoveBuildCruft... that function removes the
1571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  # manifest.json files for the individual examples.
1581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  name = 'Native Client SDK'
1591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if options.channel:
1601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    name += ' (%s)' % options.channel
1611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  template_dict = {
1621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'name': name,
1631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'channel': options.channel,
1641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'description':
1651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        'Native Client SDK examples, showing API use and key concepts.',
1661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'key': False,  # manifests with "key" are rejected when uploading to CWS.
1671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'permissions': pretty_permissions,
1681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    'version': build_version.ChromeVersionNoTrunk()
1691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
1701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  easy_template.RunTemplateFile(
1711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      os.path.join(sdk_resources_dir, 'manifest.json.template'),
1721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      os.path.join(app_examples_dir, 'manifest.json'),
1731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      template_dict)
1741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  app_zip = os.path.join(app_dir, 'examples.zip')
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  os.chdir(app_examples_dir)
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  oshelpers.Zip([app_zip, '-r', '*'])
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return 0
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)if __name__ == '__main__':
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  sys.exit(main(sys.argv))
184