summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src/build_tools/build_projects.py
blob: 4758460ebaff65afaffdc8571515aaf19969c83c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import multiprocessing
import optparse
import os
import sys

import buildbot_common
import build_version
import generate_make
import parse_dsc

from build_paths import NACL_DIR, SDK_SRC_DIR, OUT_DIR, SDK_RESOURCE_DIR
from build_paths import GSTORE
from generate_index import LandingPage

sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
sys.path.append(os.path.join(NACL_DIR, 'build'))
import getos
import http_download


MAKE = 'nacl_sdk/make_3.99.90-26-gf80222c/make.exe'
LIB_DICT = {
  'linux': [],
  'mac': [],
  'win': ['x86_32']
}
VALID_TOOLCHAINS = ['newlib', 'glibc', 'pnacl', 'win', 'linux', 'mac']

# Global verbosity setting.
# If set to try (normally via a command line arg) then build_projects will
# add V=1 to all calls to 'make'
verbose = False


def CopyFilesFromTo(filelist, srcdir, dstdir):
  for filename in filelist:
    srcpath = os.path.join(srcdir, filename)
    dstpath = os.path.join(dstdir, filename)
    buildbot_common.CopyFile(srcpath, dstpath)


def UpdateHelpers(pepperdir, clobber=False):
  tools_dir = os.path.join(pepperdir, 'tools')
  if not os.path.exists(tools_dir):
    buildbot_common.ErrorExit('SDK tools dir is missing: %s' % tools_dir)

  exampledir = os.path.join(pepperdir, 'examples')
  if clobber:
    buildbot_common.RemoveDir(exampledir)
  buildbot_common.MakeDir(exampledir)

  # Copy files for individual build and landing page
  files = ['favicon.ico', 'httpd.cmd', 'index.css', 'index.js',
      'button_close.png', 'button_close_hover.png']
  CopyFilesFromTo(files, SDK_RESOURCE_DIR, exampledir)

  # Copy tools scripts and make includes
  buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.py'),
      tools_dir)
  buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.mk'),
      tools_dir)

  # On Windows add a prebuilt make
  if getos.GetPlatform() == 'win':
    buildbot_common.BuildStep('Add MAKE')
    http_download.HttpDownload(GSTORE + MAKE,
                               os.path.join(tools_dir, 'make.exe'))


def ValidateToolchains(toolchains):
  invalid_toolchains = set(toolchains) - set(VALID_TOOLCHAINS)
  if invalid_toolchains:
    buildbot_common.ErrorExit('Invalid toolchain(s): %s' % (
        ', '.join(invalid_toolchains)))


def UpdateProjects(pepperdir, project_tree, toolchains,
                   clobber=False, configs=None, first_toolchain=False):
  if configs is None:
    configs = ['Debug', 'Release']
  if not os.path.exists(os.path.join(pepperdir, 'tools')):
    buildbot_common.ErrorExit('Examples depend on missing tools.')
  if not os.path.exists(os.path.join(pepperdir, 'toolchain')):
    buildbot_common.ErrorExit('Examples depend on missing toolchains.')

  ValidateToolchains(toolchains)

  # Create the library output directories
  libdir = os.path.join(pepperdir, 'lib')
  platform = getos.GetPlatform()
  for config in configs:
    for arch in LIB_DICT[platform]:
      dirpath = os.path.join(libdir, '%s_%s_host' % (platform, arch), config)
      if clobber:
        buildbot_common.RemoveDir(dirpath)
      buildbot_common.MakeDir(dirpath)

  landing_page = None
  for branch, projects in project_tree.iteritems():
    dirpath = os.path.join(pepperdir, branch)
    if clobber:
      buildbot_common.RemoveDir(dirpath)
    buildbot_common.MakeDir(dirpath)
    targets = [desc['NAME'] for desc in projects]

    # Generate master make for this branch of projects
    generate_make.GenerateMasterMakefile(pepperdir,
                                         os.path.join(pepperdir, branch),
                                         targets)

    if branch.startswith('examples') and not landing_page:
      landing_page = LandingPage()

    # Generate individual projects
    for desc in projects:
      srcroot = os.path.dirname(desc['FILEPATH'])
      generate_make.ProcessProject(pepperdir, srcroot, pepperdir, desc,
                                   toolchains, configs=configs,
                                   first_toolchain=first_toolchain)

      if branch.startswith('examples'):
        landing_page.AddDesc(desc)

  if landing_page:
    # Generate the landing page text file.
    index_html = os.path.join(pepperdir, 'examples', 'index.html')
    index_template = os.path.join(SDK_RESOURCE_DIR, 'index.html.template')
    with open(index_html, 'w') as fh:
      out = landing_page.GeneratePage(index_template)
      fh.write(out)

  # Generate top Make for examples
  targets = ['api', 'demo', 'getting_started', 'tutorial']
  targets = [x for x in targets if 'examples/'+x in project_tree]
  branch_name = 'examples'
  generate_make.GenerateMasterMakefile(pepperdir,
                                       os.path.join(pepperdir, branch_name),
                                       targets)


def BuildProjectsBranch(pepperdir, branch, deps, clean, config, args=None):
  make_dir = os.path.join(pepperdir, branch)
  print "\nMake: " + make_dir

  if getos.GetPlatform() == 'win':
    # We need to modify the environment to build host on Windows.
    make = os.path.join(make_dir, 'make.bat')
  else:
    make = 'make'

  env = None
  if os.environ.get('USE_GOMA') == '1':
    env = dict(os.environ)
    env['NACL_COMPILER_PREFIX'] = 'gomacc'
    # Add -m32 to the CFLAGS when building using i686-nacl-gcc
    # otherwise goma won't recognise it as different to the x86_64
    # build.
    env['X86_32_CFLAGS'] = '-m32'
    env['X86_32_CXXFLAGS'] = '-m32'
    jobs = '50'
  else:
    jobs = str(multiprocessing.cpu_count())

  make_cmd = [make, '-j', jobs]

  make_cmd.append('CONFIG='+config)
  if not deps:
    make_cmd.append('IGNORE_DEPS=1')

  if verbose:
    make_cmd.append('V=1')

  if args:
    make_cmd += args
  else:
    make_cmd.append('TOOLCHAIN=all')

  buildbot_common.Run(make_cmd, cwd=make_dir, env=env)
  if clean:
    # Clean to remove temporary files but keep the built
    buildbot_common.Run(make_cmd + ['clean'], cwd=make_dir, env=env)


def BuildProjects(pepperdir, project_tree, deps=True,
                  clean=False, config='Debug'):

  # Make sure we build libraries (which live in 'src') before
  # any of the examples.
  build_first = [p for p in project_tree if p != 'src']
  build_second = [p for p in project_tree if p == 'src']

  for branch in build_first + build_second:
    BuildProjectsBranch(pepperdir, branch, deps, clean, config)


def main(argv):
  parser = optparse.OptionParser()
  parser.add_option('-c', '--clobber',
      help='Clobber project directories before copying new files',
      action='store_true', default=False)
  parser.add_option('-b', '--build',
      help='Build the projects.', action='store_true')
  parser.add_option('--config',
      help='Choose configuration to build (Debug or Release).  Builds both '
           'by default')
  parser.add_option('-x', '--experimental',
      help='Build experimental projects', action='store_true')
  parser.add_option('-t', '--toolchain',
      help='Build using toolchain. Can be passed more than once.',
      action='append', default=[])
  parser.add_option('-d', '--dest',
      help='Select which build destinations (project types) are valid.',
      action='append')
  parser.add_option('-p', '--project',
      help='Select which projects are valid.',
      action='append')
  parser.add_option('-v', '--verbose', action='store_true')

  options, args = parser.parse_args(argv[1:])
  if options.project:
    parser.error('The -p/--project option is deprecated.\n'
                 'Just use positional paramaters instead.')

  if 'NACL_SDK_ROOT' in os.environ:
    # We don't want the currently configured NACL_SDK_ROOT to have any effect
    # on the build.
    del os.environ['NACL_SDK_ROOT']

  pepper_ver = str(int(build_version.ChromeMajorVersion()))
  pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)

  if not options.toolchain:
    options.toolchain = ['newlib', 'glibc', 'pnacl', 'host']

  if 'host' in options.toolchain:
    options.toolchain.remove('host')
    options.toolchain.append(getos.GetPlatform())
    print 'Adding platform: ' + getos.GetPlatform()

  ValidateToolchains(options.toolchain)

  filters = {}
  if options.toolchain:
    filters['TOOLS'] = options.toolchain
    print 'Filter by toolchain: ' + str(options.toolchain)
  if not options.experimental:
    filters['EXPERIMENTAL'] = False
  if options.dest:
    filters['DEST'] = options.dest
    print 'Filter by type: ' + str(options.dest)
  if args:
    filters['NAME'] = args
    print 'Filter by name: ' + str(args)

  try:
    project_tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
  except parse_dsc.ValidationError as e:
    buildbot_common.ErrorExit(str(e))
  parse_dsc.PrintProjectTree(project_tree)

  UpdateHelpers(pepperdir, clobber=options.clobber)
  UpdateProjects(pepperdir, project_tree, options.toolchain,
                 clobber=options.clobber)

  if options.verbose:
    global verbose
    verbose = True

  if options.build:
    if options.config:
      configs = [options.config]
    else:
      configs = ['Debug', 'Release']
    for config in configs:
      BuildProjects(pepperdir, project_tree, config=config)

  return 0


if __name__ == '__main__':
  script_name = os.path.basename(sys.argv[0])
  try:
    sys.exit(main(sys.argv))
  except parse_dsc.ValidationError as e:
    buildbot_common.ErrorExit('%s: %s' % (script_name, e))
  except KeyboardInterrupt:
    buildbot_common.ErrorExit('%s: interrupted' % script_name)