Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / build_tools / build_sdk.py
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 """Entry point for both build and try bots.
7
8 This script is invoked from XXX, usually without arguments
9 to package an SDK. It automatically determines whether
10 this SDK is for mac, win, linux.
11
12 The script inspects the following environment variables:
13
14 BUILDBOT_BUILDERNAME to determine whether the script is run locally
15 and whether it should upload an SDK to file storage (GSTORE)
16 """
17
18 # pylint: disable=W0621
19
20 # std python includes
21 import datetime
22 import glob
23 import optparse
24 import os
25 import re
26 import sys
27
28 if sys.version_info < (2, 6, 0):
29   sys.stderr.write("python 2.6 or later is required run this script\n")
30   sys.exit(1)
31
32 # local includes
33 import buildbot_common
34 import build_projects
35 import build_updater
36 import build_version
37 import generate_notice
38 import manifest_util
39 import parse_dsc
40 import verify_filelist
41
42 from build_paths import SCRIPT_DIR, SDK_SRC_DIR, SRC_DIR, NACL_DIR, OUT_DIR
43 from build_paths import NACLPORTS_DIR, GSTORE, GONACL_APPENGINE_SRC_DIR
44
45 # Add SDK make tools scripts to the python path.
46 sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
47 sys.path.append(os.path.join(NACL_DIR, 'build'))
48
49 import getos
50 import oshelpers
51
52 BUILD_DIR = os.path.join(NACL_DIR, 'build')
53 NACL_TOOLCHAIN_DIR = os.path.join(NACL_DIR, 'toolchain')
54 NACL_TOOLCHAINTARS_DIR = os.path.join(NACL_TOOLCHAIN_DIR, '.tars')
55
56 CYGTAR = os.path.join(BUILD_DIR, 'cygtar.py')
57 PKGVER = os.path.join(BUILD_DIR, 'package_version', 'package_version.py')
58
59 NACLPORTS_URL = 'https://naclports.googlecode.com/svn/trunk/src'
60 NACLPORTS_REV = 1293
61
62 GYPBUILD_DIR = 'gypbuild'
63
64 options = None
65
66 # Map of: ToolchainName: (PackageName, SDKDir).
67 TOOLCHAIN_PACKAGE_MAP = {
68     'newlib': ('nacl_x86_newlib', '%(platform)s_x86_newlib'),
69     'bionic': ('nacl_arm_bionic', '%(platform)s_arm_bionic'),
70     'arm': ('nacl_arm_newlib', '%(platform)s_arm_newlib'),
71     'glibc': ('nacl_x86_glibc', '%(platform)s_x86_glibc'),
72     'pnacl': ('pnacl_newlib', '%(platform)s_pnacl')
73     }
74
75
76 def GetToolchainNaClInclude(tcname, tcpath, arch):
77   if arch == 'x86':
78     if tcname == 'pnacl':
79       return os.path.join(tcpath, 'sdk', 'include')
80     return os.path.join(tcpath, 'x86_64-nacl', 'include')
81   elif arch == 'arm':
82     return os.path.join(tcpath, 'arm-nacl', 'include')
83   else:
84     buildbot_common.ErrorExit('Unknown architecture: %s' % arch)
85
86
87 def GetGypGenDir(xarch):
88   if xarch == 'arm':
89     build_dir = GYPBUILD_DIR + '-arm'
90   else:
91     build_dir = GYPBUILD_DIR
92   return os.path.join(OUT_DIR, build_dir, 'Release', 'gen')
93
94
95 def GetGypBuiltLib(tcname, xarch=None):
96   if tcname == 'pnacl':
97     tcname = 'pnacl_newlib'
98   if not xarch:
99     xarch = ''
100   return os.path.join(GetGypGenDir(xarch), 'tc_' + tcname, 'lib' + xarch)
101
102
103 def GetToolchainNaClLib(tcname, tcpath, xarch):
104   if tcname == 'pnacl':
105     return os.path.join(tcpath, 'sdk', 'lib')
106   elif xarch == '32':
107     return os.path.join(tcpath, 'x86_64-nacl', 'lib32')
108   elif xarch == '64':
109     return os.path.join(tcpath, 'x86_64-nacl', 'lib')
110   elif xarch == 'arm':
111     return os.path.join(tcpath, 'arm-nacl', 'lib')
112
113
114 def GetToolchainDirName(tcname, xarch):
115   if tcname == 'pnacl':
116     return '%s_%s' % (getos.GetPlatform(), tcname)
117   elif xarch == 'arm':
118     return '%s_arm_%s' % (getos.GetPlatform(), tcname)
119   else:
120     return '%s_x86_%s' % (getos.GetPlatform(), tcname)
121
122
123 def GetGypToolchainLib(tcname, xarch):
124   if xarch == 'arm':
125     toolchain = xarch
126   else:
127     toolchain = tcname
128
129   tcpath = os.path.join(GetGypGenDir(xarch), 'sdk',
130                         '%s_x86' % getos.GetPlatform(),
131                         TOOLCHAIN_PACKAGE_MAP[toolchain][0])
132   return GetToolchainNaClLib(tcname, tcpath, xarch)
133
134
135 def GetOutputToolchainLib(pepperdir, tcname, xarch):
136   tcpath = os.path.join(pepperdir, 'toolchain',
137                         GetToolchainDirName(tcname, xarch))
138   return GetToolchainNaClLib(tcname, tcpath, xarch)
139
140
141 def GetPNaClNativeLib(tcpath, arch):
142   if arch not in ['arm', 'x86-32', 'x86-64']:
143     buildbot_common.ErrorExit('Unknown architecture %s.' % arch)
144   return os.path.join(tcpath, 'lib-' + arch)
145
146
147 def BuildStepDownloadToolchains(toolchains):
148   buildbot_common.BuildStep('Running package_version.py')
149   args = [sys.executable, PKGVER, '--exclude', 'arm_trusted']
150   if 'bionic' in toolchains:
151     build_platform = '%s_x86' % getos.GetPlatform()
152     args.extend(['--append', os.path.join(build_platform, 'nacl_arm_bionic')])
153   args.append('sync')
154   buildbot_common.Run(args, cwd=NACL_DIR)
155
156
157 def BuildStepCleanPepperDirs(pepperdir, pepperdir_old):
158   buildbot_common.BuildStep('Clean Pepper Dirs')
159   buildbot_common.RemoveDir(pepperdir_old)
160   buildbot_common.RemoveDir(pepperdir)
161   buildbot_common.MakeDir(pepperdir)
162
163
164 def BuildStepMakePepperDirs(pepperdir, subdirs):
165   for subdir in subdirs:
166     buildbot_common.MakeDir(os.path.join(pepperdir, subdir))
167
168 TEXT_FILES = [
169   'AUTHORS',
170   'COPYING',
171   'LICENSE',
172   'README.Makefiles',
173   'getting_started/README',
174 ]
175
176 def BuildStepCopyTextFiles(pepperdir, pepper_ver, chrome_revision,
177                            nacl_revision):
178   buildbot_common.BuildStep('Add Text Files')
179   InstallFiles(SDK_SRC_DIR, pepperdir, TEXT_FILES)
180
181   # Replace a few placeholders in README
182   readme_text = open(os.path.join(SDK_SRC_DIR, 'README')).read()
183   readme_text = readme_text.replace('${VERSION}', pepper_ver)
184   readme_text = readme_text.replace('${CHROME_REVISION}', chrome_revision)
185   readme_text = readme_text.replace('${CHROME_COMMIT_POSITION}',
186                                     build_version.ChromeCommitPosition())
187   readme_text = readme_text.replace('${NACL_REVISION}', nacl_revision)
188
189   # Year/Month/Day Hour:Minute:Second
190   time_format = '%Y/%m/%d %H:%M:%S'
191   readme_text = readme_text.replace('${DATE}',
192       datetime.datetime.now().strftime(time_format))
193
194   open(os.path.join(pepperdir, 'README'), 'w').write(readme_text)
195
196
197 def BuildStepUntarToolchains(pepperdir, toolchains):
198   buildbot_common.BuildStep('Untar Toolchains')
199   platform = getos.GetPlatform()
200   build_platform = '%s_x86' % platform
201   tmpdir = os.path.join(OUT_DIR, 'tc_temp')
202   buildbot_common.RemoveDir(tmpdir)
203   buildbot_common.MakeDir(tmpdir)
204
205   # Create a list of extract packages tuples, the first part should be
206   # "$PACKAGE_TARGET/$PACKAGE". The second part should be the destination
207   # directory relative to pepperdir/toolchain.
208   extract_packages = []
209   for toolchain in toolchains:
210     toolchain_map = TOOLCHAIN_PACKAGE_MAP.get(toolchain, None)
211     if toolchain_map:
212       package_name, tcname = toolchain_map
213       package_tuple = (os.path.join(build_platform, package_name),
214                        tcname % {'platform': platform})
215       extract_packages.append(package_tuple)
216
217   if extract_packages:
218     # Extract all of the packages into the temp directory.
219     package_names = [package_tuple[0] for package_tuple in extract_packages]
220     buildbot_common.Run([sys.executable, PKGVER,
221                            '--packages', ','.join(package_names),
222                            '--tar-dir', NACL_TOOLCHAINTARS_DIR,
223                            '--dest-dir', tmpdir,
224                            'extract'])
225
226     # Move all the packages we extracted to the correct destination.
227     for package_name, dest_dir in extract_packages:
228       full_src_dir = os.path.join(tmpdir, package_name)
229       full_dst_dir = os.path.join(pepperdir, 'toolchain', dest_dir)
230       buildbot_common.Move(full_src_dir, full_dst_dir)
231
232   # Cleanup the temporary directory we are no longer using.
233   buildbot_common.RemoveDir(tmpdir)
234
235
236 # List of toolchain headers to install.
237 # Source is relative to top of Chromium tree, destination is relative
238 # to the toolchain header directory.
239 NACL_HEADER_MAP = {
240   'newlib': [
241       ('native_client/src/include/nacl/nacl_exception.h', 'nacl/'),
242       ('native_client/src/include/nacl/nacl_minidump.h', 'nacl/'),
243       ('native_client/src/untrusted/irt/irt.h', ''),
244       ('native_client/src/untrusted/irt/irt_dev.h', ''),
245       ('native_client/src/untrusted/nacl/nacl_dyncode.h', 'nacl/'),
246       ('native_client/src/untrusted/nacl/nacl_startup.h', 'nacl/'),
247       ('native_client/src/untrusted/pthread/pthread.h', ''),
248       ('native_client/src/untrusted/pthread/semaphore.h', ''),
249       ('native_client/src/untrusted/valgrind/dynamic_annotations.h', 'nacl/'),
250       ('ppapi/nacl_irt/public/irt_ppapi.h', ''),
251   ],
252   'glibc': [
253       ('native_client/src/include/nacl/nacl_exception.h', 'nacl/'),
254       ('native_client/src/include/nacl/nacl_minidump.h', 'nacl/'),
255       ('native_client/src/untrusted/irt/irt.h', ''),
256       ('native_client/src/untrusted/irt/irt_dev.h', ''),
257       ('native_client/src/untrusted/nacl/nacl_dyncode.h', 'nacl/'),
258       ('native_client/src/untrusted/nacl/nacl_startup.h', 'nacl/'),
259       ('native_client/src/untrusted/valgrind/dynamic_annotations.h', 'nacl/'),
260       ('ppapi/nacl_irt/public/irt_ppapi.h', ''),
261   ],
262   'host': []
263 }
264
265 def InstallFiles(src_root, dest_root, file_list):
266   """Copy a set of files from src_root to dest_root according
267   to the given mapping.  This allows files to be copied from
268   to a location in the destination tree that is different to the
269   location in the source tree.
270
271   If the destination mapping ends with a '/' then the destination
272   basename is inherited from the the source file.
273
274   Wildcards can be used in the source list but it is not recommended
275   as this can end up adding things to the SDK unintentionally.
276   """
277   for file_spec in file_list:
278     # The list of files to install can be a simple list of
279     # strings or a list of pairs, where each pair corresponds
280     # to a mapping from source to destination names.
281     if type(file_spec) == str:
282       src_file = dest_file = file_spec
283     else:
284       src_file, dest_file = file_spec
285
286     src_file = os.path.join(src_root, src_file)
287
288     # Expand sources files using glob.
289     sources = glob.glob(src_file)
290     if not sources:
291       sources = [src_file]
292
293     if len(sources) > 1 and not dest_file.endswith('/'):
294       buildbot_common.ErrorExit("Target file must end in '/' when "
295                                 "using globbing to install multiple files")
296
297     for source in sources:
298       if dest_file.endswith('/'):
299         dest = os.path.join(dest_file, os.path.basename(source))
300       else:
301         dest = dest_file
302       dest = os.path.join(dest_root, dest)
303       if not os.path.isdir(os.path.dirname(dest)):
304         buildbot_common.MakeDir(os.path.dirname(dest))
305       buildbot_common.CopyFile(source, dest)
306
307
308 def InstallNaClHeaders(tc_dst_inc, tc_name):
309   """Copies NaCl headers to expected locations in the toolchain."""
310   if tc_name == 'arm':
311     # arm toolchain header should be the same as the x86 newlib
312     # ones
313     tc_name = 'newlib'
314
315   InstallFiles(SRC_DIR, tc_dst_inc, NACL_HEADER_MAP[tc_name])
316
317
318 def MakeNinjaRelPath(path):
319   return os.path.join(os.path.relpath(OUT_DIR, SRC_DIR), path)
320
321
322 TOOLCHAIN_LIBS = {
323   'bionic' : [
324     'libminidump_generator.a',
325     'libnacl_dyncode.a',
326     'libnacl_exception.a',
327     'libnacl_list_mappings.a',
328     'libppapi.a',
329   ],
330   'newlib' : [
331     'crti.o',
332     'crtn.o',
333     'libminidump_generator.a',
334     'libnacl.a',
335     'libnacl_dyncode.a',
336     'libnacl_exception.a',
337     'libnacl_list_mappings.a',
338     'libnosys.a',
339     'libppapi.a',
340     'libppapi_stub.a',
341     'libpthread.a',
342   ],
343   'glibc': [
344     'libminidump_generator.a',
345     'libminidump_generator.so',
346     'libnacl.a',
347     'libnacl_dyncode.a',
348     'libnacl_dyncode.so',
349     'libnacl_exception.a',
350     'libnacl_exception.so',
351     'libnacl_list_mappings.a',
352     'libnacl_list_mappings.so',
353     'libppapi.a',
354     'libppapi.so',
355     'libppapi_stub.a',
356   ],
357   'pnacl': [
358     'libminidump_generator.a',
359     'libnacl.a',
360     'libnacl_dyncode.a',
361     'libnacl_exception.a',
362     'libnacl_list_mappings.a',
363     'libnosys.a',
364     'libppapi.a',
365     'libppapi_stub.a',
366     'libpthread.a',
367   ]
368 }
369
370
371 def GypNinjaInstall(pepperdir, toolchains):
372   build_dir = GYPBUILD_DIR
373   ninja_out_dir = os.path.join(OUT_DIR, build_dir, 'Release')
374   tools_files = [
375     ['sel_ldr', 'sel_ldr_x86_32'],
376     ['ncval_new', 'ncval'],
377     ['irt_core_newlib_x32.nexe', 'irt_core_x86_32.nexe'],
378     ['irt_core_newlib_x64.nexe', 'irt_core_x86_64.nexe'],
379   ]
380
381   platform = getos.GetPlatform()
382
383   # TODO(binji): dump_syms doesn't currently build on Windows. See
384   # http://crbug.com/245456
385   if platform != 'win':
386     tools_files += [
387       ['dump_syms', 'dump_syms'],
388       ['minidump_dump', 'minidump_dump'],
389       ['minidump_stackwalk', 'minidump_stackwalk']
390     ]
391
392   tools_files.append(['sel_ldr64', 'sel_ldr_x86_64'])
393
394   if platform == 'linux':
395     tools_files.append(['nacl_helper_bootstrap',
396                         'nacl_helper_bootstrap_x86_32'])
397     tools_files.append(['nacl_helper_bootstrap64',
398                         'nacl_helper_bootstrap_x86_64'])
399
400   buildbot_common.MakeDir(os.path.join(pepperdir, 'tools'))
401
402   # Add .exe extensions to all windows tools
403   for pair in tools_files:
404     if platform == 'win' and not pair[0].endswith('.nexe'):
405       pair[0] += '.exe'
406       pair[1] += '.exe'
407
408   InstallFiles(ninja_out_dir, os.path.join(pepperdir, 'tools'), tools_files)
409
410   # Add ARM binaries
411   if platform == 'linux' and not options.no_arm_trusted:
412     tools_files = [
413       ['irt_core_newlib_arm.nexe', 'irt_core_arm.nexe'],
414       ['irt_core_newlib_arm.nexe', 'irt_core_arm.nexe'],
415       ['sel_ldr', 'sel_ldr_arm'],
416       ['nacl_helper_bootstrap', 'nacl_helper_bootstrap_arm']
417     ]
418     ninja_out_dir = os.path.join(OUT_DIR, build_dir + '-arm', 'Release')
419     InstallFiles(ninja_out_dir, os.path.join(pepperdir, 'tools'), tools_files)
420
421   for tc in set(toolchains) & set(['newlib', 'glibc', 'pnacl']):
422     if tc == 'pnacl':
423       xarches = (None,)
424     else:
425       xarches = ('arm', '32', '64')
426
427     for xarch in xarches:
428       if tc == 'glibc' and xarch == 'arm':
429         continue
430
431       src_dir = GetGypBuiltLib(tc, xarch)
432       dst_dir = GetOutputToolchainLib(pepperdir, tc, xarch)
433       InstallFiles(src_dir, dst_dir, TOOLCHAIN_LIBS[tc])
434
435       # Copy ARM newlib components to bionic
436       if tc == 'newlib' and xarch == 'arm' and 'bionic' in toolchains:
437         bionic_dir = GetOutputToolchainLib(pepperdir, 'bionic', xarch)
438         InstallFiles(src_dir, bionic_dir, TOOLCHAIN_LIBS['bionic'])
439
440       if tc != 'pnacl':
441         src_dir = GetGypToolchainLib(tc, xarch)
442         InstallFiles(src_dir, dst_dir, ['crt1.o'])
443
444
445 def GypNinjaBuild_NaCl(rel_out_dir):
446   gyp_py = os.path.join(NACL_DIR, 'build', 'gyp_nacl')
447   nacl_core_sdk_gyp = os.path.join(NACL_DIR, 'build', 'nacl_core_sdk.gyp')
448   all_gyp = os.path.join(NACL_DIR, 'build', 'all.gyp')
449
450   out_dir = MakeNinjaRelPath(rel_out_dir)
451   out_dir_arm = MakeNinjaRelPath(rel_out_dir + '-arm')
452   GypNinjaBuild('ia32', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir)
453   GypNinjaBuild('arm', gyp_py, nacl_core_sdk_gyp, 'nacl_core_sdk', out_dir_arm)
454   GypNinjaBuild('ia32', gyp_py, all_gyp, 'ncval_new', out_dir)
455
456   platform = getos.GetPlatform()
457   if platform == 'win':
458     NinjaBuild('sel_ldr64', out_dir)
459   else:
460     out_dir_64 = MakeNinjaRelPath(rel_out_dir + '-64')
461     GypNinjaBuild('x64', gyp_py, nacl_core_sdk_gyp, 'sel_ldr', out_dir_64)
462
463     # We only need sel_ldr from the 64-bit out directory.
464     # sel_ldr needs to be renamed, so we'll call it sel_ldr64.
465     files_to_copy = [('sel_ldr', 'sel_ldr64')]
466     if platform == 'linux':
467       files_to_copy.append(('nacl_helper_bootstrap', 'nacl_helper_bootstrap64'))
468
469     for src, dst in files_to_copy:
470       buildbot_common.CopyFile(
471           os.path.join(SRC_DIR, out_dir_64, 'Release', src),
472           os.path.join(SRC_DIR, out_dir, 'Release', dst))
473
474
475 def GypNinjaBuild_Breakpad(rel_out_dir):
476   # TODO(binji): dump_syms doesn't currently build on Windows. See
477   # http://crbug.com/245456
478   if getos.GetPlatform() == 'win':
479     return
480
481   gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium')
482   out_dir = MakeNinjaRelPath(rel_out_dir)
483   gyp_file = os.path.join(SRC_DIR, 'breakpad', 'breakpad.gyp')
484   build_list = ['dump_syms', 'minidump_dump', 'minidump_stackwalk']
485   GypNinjaBuild('ia32', gyp_py, gyp_file, build_list, out_dir)
486
487
488 def GypNinjaBuild_PPAPI(arch, rel_out_dir):
489   gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium')
490   out_dir = MakeNinjaRelPath(rel_out_dir)
491   gyp_file = os.path.join(SRC_DIR, 'ppapi', 'native_client',
492                           'native_client.gyp')
493   GypNinjaBuild(arch, gyp_py, gyp_file, 'ppapi_lib', out_dir)
494
495
496 def GypNinjaBuild_Pnacl(rel_out_dir, target_arch):
497   # TODO(binji): This will build the pnacl_irt_shim twice; once as part of the
498   # Chromium build, and once here. When we move more of the SDK build process
499   # to gyp, we can remove this.
500   gyp_py = os.path.join(SRC_DIR, 'build', 'gyp_chromium')
501
502   out_dir = MakeNinjaRelPath(rel_out_dir)
503   gyp_file = os.path.join(SRC_DIR, 'ppapi', 'native_client', 'src',
504                           'untrusted', 'pnacl_irt_shim', 'pnacl_irt_shim.gyp')
505   targets = ['aot']
506   GypNinjaBuild(target_arch, gyp_py, gyp_file, targets, out_dir, False)
507
508
509 def GypNinjaBuild(arch, gyp_py_script, gyp_file, targets,
510                   out_dir, force_arm_gcc=True):
511   gyp_env = dict(os.environ)
512   gyp_env['GYP_GENERATORS'] = 'ninja'
513   gyp_defines = []
514   if options.mac_sdk:
515     gyp_defines.append('mac_sdk=%s' % options.mac_sdk)
516   if arch:
517     gyp_defines.append('target_arch=%s' % arch)
518     if arch == 'arm':
519       if getos.GetPlatform() == 'linux':
520         gyp_env['CC'] = 'arm-linux-gnueabihf-gcc'
521         gyp_env['CXX'] = 'arm-linux-gnueabihf-g++'
522         gyp_env['AR'] = 'arm-linux-gnueabihf-ar'
523         gyp_env['AS'] = 'arm-linux-gnueabihf-as'
524         gyp_env['CC_host'] = 'cc'
525         gyp_env['CXX_host'] = 'c++'
526       gyp_defines += ['armv7=1', 'arm_thumb=0', 'arm_neon=1',
527           'arm_float_abi=hard']
528       if force_arm_gcc:
529         gyp_defines.append('nacl_enable_arm_gcc=1')
530       if options.no_arm_trusted:
531         gyp_defines.append('disable_cross_trusted=1')
532   if getos.GetPlatform() == 'mac':
533     gyp_defines.append('clang=1')
534
535   gyp_env['GYP_DEFINES'] = ' '.join(gyp_defines)
536   for key in ['GYP_GENERATORS', 'GYP_DEFINES', 'CC']:
537     value = gyp_env.get(key)
538     if value is not None:
539       print '%s="%s"' % (key, value)
540   gyp_generator_flags = ['-G', 'output_dir=%s' % (out_dir,)]
541   gyp_depth = '--depth=.'
542   buildbot_common.Run(
543       [sys.executable, gyp_py_script, gyp_file, gyp_depth] + \
544           gyp_generator_flags,
545       cwd=SRC_DIR,
546       env=gyp_env)
547   NinjaBuild(targets, out_dir)
548
549
550 def NinjaBuild(targets, out_dir):
551   if type(targets) is not list:
552     targets = [targets]
553   out_config_dir = os.path.join(out_dir, 'Release')
554   buildbot_common.Run(['ninja', '-C', out_config_dir] + targets, cwd=SRC_DIR)
555
556
557 def BuildStepBuildToolchains(pepperdir, toolchains):
558   buildbot_common.BuildStep('SDK Items')
559
560   GypNinjaBuild_NaCl(GYPBUILD_DIR)
561   GypNinjaBuild_Breakpad(GYPBUILD_DIR)
562
563   platform = getos.GetPlatform()
564   newlibdir = os.path.join(pepperdir, 'toolchain', platform + '_x86_newlib')
565   glibcdir = os.path.join(pepperdir, 'toolchain', platform + '_x86_glibc')
566   armdir = os.path.join(pepperdir, 'toolchain', platform + '_arm_newlib')
567   pnacldir = os.path.join(pepperdir, 'toolchain', platform + '_pnacl')
568
569   if set(toolchains) & set(['glibc', 'newlib']):
570     GypNinjaBuild_PPAPI('ia32', GYPBUILD_DIR)
571
572   if 'arm' in toolchains:
573     GypNinjaBuild_PPAPI('arm', GYPBUILD_DIR + '-arm')
574
575   GypNinjaInstall(pepperdir, toolchains)
576
577   if 'newlib' in toolchains:
578     InstallNaClHeaders(GetToolchainNaClInclude('newlib', newlibdir, 'x86'),
579                        'newlib')
580
581   if 'glibc' in toolchains:
582     InstallNaClHeaders(GetToolchainNaClInclude('glibc', glibcdir, 'x86'),
583                        'glibc')
584
585   if 'arm' in toolchains:
586     InstallNaClHeaders(GetToolchainNaClInclude('newlib', armdir, 'arm'),
587                        'arm')
588
589   if 'pnacl' in toolchains:
590     # NOTE: For ia32, gyp builds both x86-32 and x86-64 by default.
591     for arch in ('ia32', 'arm'):
592       # Fill in the latest native pnacl shim library from the chrome build.
593       build_dir = GYPBUILD_DIR + '-pnacl-' + arch
594       GypNinjaBuild_Pnacl(build_dir, arch)
595       if arch == 'ia32':
596         nacl_arches = ['x86-32', 'x86-64']
597       elif arch == 'arm':
598         nacl_arches = ['arm']
599       else:
600         buildbot_common.ErrorExit('Unknown architecture: %s' % arch)
601       for nacl_arch in nacl_arches:
602         release_build_dir = os.path.join(OUT_DIR, build_dir, 'Release',
603                                          'gen', 'tc_pnacl_translate',
604                                          'lib-' + nacl_arch)
605
606         buildbot_common.CopyFile(
607             os.path.join(release_build_dir, 'libpnacl_irt_shim.a'),
608             GetPNaClNativeLib(pnacldir, nacl_arch))
609
610     InstallNaClHeaders(GetToolchainNaClInclude('pnacl', pnacldir, 'x86'),
611                        'newlib')
612
613
614 def MakeDirectoryOrClobber(pepperdir, dirname, clobber):
615   dirpath = os.path.join(pepperdir, dirname)
616   if clobber:
617     buildbot_common.RemoveDir(dirpath)
618   buildbot_common.MakeDir(dirpath)
619
620   return dirpath
621
622
623 def BuildStepUpdateHelpers(pepperdir, clobber):
624   buildbot_common.BuildStep('Update project helpers')
625   build_projects.UpdateHelpers(pepperdir, clobber=clobber)
626
627
628 def BuildStepUpdateUserProjects(pepperdir, toolchains,
629                                 build_experimental, clobber):
630   buildbot_common.BuildStep('Update examples and libraries')
631
632   filters = {}
633   if not build_experimental:
634     filters['EXPERIMENTAL'] = False
635   if toolchains:
636     toolchains = toolchains[:]
637
638     # arm isn't a valid toolchain for build_projects
639     if 'arm' in toolchains:
640       toolchains.remove('arm')
641
642     if 'host' in toolchains:
643       toolchains.remove('host')
644       toolchains.append(getos.GetPlatform())
645
646     filters['TOOLS'] = toolchains
647
648   # Update examples and libraries
649   filters['DEST'] = [
650     'getting_started',
651     'examples/api',
652     'examples/demo',
653     'examples/tutorial',
654     'src'
655   ]
656
657   tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
658   build_projects.UpdateProjects(pepperdir, tree, clobber=clobber,
659                                 toolchains=toolchains)
660
661
662 def BuildStepMakeAll(pepperdir, directory, step_name,
663                      deps=True, clean=False, config='Debug', args=None):
664   buildbot_common.BuildStep(step_name)
665   build_projects.BuildProjectsBranch(pepperdir, directory, clean,
666                                      deps, config, args)
667
668
669 def BuildStepBuildLibraries(pepperdir, directory):
670   BuildStepMakeAll(pepperdir, directory, 'Build Libraries Debug',
671       clean=True, config='Debug')
672   BuildStepMakeAll(pepperdir, directory, 'Build Libraries Release',
673       clean=True, config='Release')
674
675   # Cleanup .pyc file generated while building libraries.  Without
676   # this we would end up shipping the pyc in the SDK tarball.
677   buildbot_common.RemoveFile(os.path.join(pepperdir, 'tools', '*.pyc'))
678
679
680 def GenerateNotice(fileroot, output_filename='NOTICE', extra_files=None):
681   # Look for LICENSE files
682   license_filenames_re = re.compile('LICENSE|COPYING|COPYRIGHT')
683
684   license_files = []
685   for root, _, files in os.walk(fileroot):
686     for filename in files:
687       if license_filenames_re.match(filename):
688         path = os.path.join(root, filename)
689         license_files.append(path)
690
691   if extra_files:
692     license_files += [os.path.join(fileroot, f) for f in extra_files]
693   print '\n'.join(license_files)
694
695   if not os.path.isabs(output_filename):
696     output_filename = os.path.join(fileroot, output_filename)
697   generate_notice.Generate(output_filename, fileroot, license_files)
698
699
700 def BuildStepVerifyFilelist(pepperdir):
701   buildbot_common.BuildStep('Verify SDK Files')
702   file_list_path = os.path.join(SCRIPT_DIR, 'sdk_files.list')
703   try:
704     verify_filelist.Verify(file_list_path, pepperdir)
705     print 'OK'
706   except verify_filelist.ParseException, e:
707     buildbot_common.ErrorExit('Parsing sdk_files.list failed:\n\n%s' % e)
708   except verify_filelist.VerifyException, e:
709     file_list_rel = os.path.relpath(file_list_path)
710     verify_filelist_py = os.path.splitext(verify_filelist.__file__)[0] + '.py'
711     verify_filelist_py = os.path.relpath(verify_filelist_py)
712     pepperdir_rel = os.path.relpath(pepperdir)
713
714     msg = """\
715 SDK verification failed:
716
717 %s
718 Add/remove files from %s to fix.
719
720 Run:
721     ./%s %s %s
722 to test.""" % (e, file_list_rel, verify_filelist_py, file_list_rel,
723                pepperdir_rel)
724     buildbot_common.ErrorExit(msg)
725
726
727 def BuildStepTarBundle(pepper_ver, tarfile):
728   buildbot_common.BuildStep('Tar Pepper Bundle')
729   buildbot_common.MakeDir(os.path.dirname(tarfile))
730   buildbot_common.Run([sys.executable, CYGTAR, '-C', OUT_DIR, '-cjf', tarfile,
731        'pepper_' + pepper_ver], cwd=NACL_DIR)
732
733
734 def GetManifestBundle(pepper_ver, chrome_revision, nacl_revision, tarfile,
735                       archive_url):
736   with open(tarfile, 'rb') as tarfile_stream:
737     archive_sha1, archive_size = manifest_util.DownloadAndComputeHash(
738         tarfile_stream)
739
740   archive = manifest_util.Archive(manifest_util.GetHostOS())
741   archive.url = archive_url
742   archive.size = archive_size
743   archive.checksum = archive_sha1
744
745   bundle = manifest_util.Bundle('pepper_' + pepper_ver)
746   bundle.revision = int(chrome_revision)
747   bundle.repath = 'pepper_' + pepper_ver
748   bundle.version = int(pepper_ver)
749   bundle.description = (
750       'Chrome %s bundle. Chrome revision: %s. NaCl revision: %s' % (
751             pepper_ver, chrome_revision, nacl_revision))
752   bundle.stability = 'dev'
753   bundle.recommended = 'no'
754   bundle.archives = [archive]
755   return bundle
756
757
758 def BuildStepArchiveBundle(name, pepper_ver, chrome_revision, nacl_revision,
759                            tarfile):
760   buildbot_common.BuildStep('Archive %s' % name)
761   bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/%s' % (
762       build_version.ChromeVersion(),)
763   tarname = os.path.basename(tarfile)
764   tarfile_dir = os.path.dirname(tarfile)
765   buildbot_common.Archive(tarname, bucket_path, tarfile_dir)
766
767   # generate "manifest snippet" for this archive.
768   archive_url = GSTORE + 'nacl_sdk/%s/%s' % (
769       build_version.ChromeVersion(), tarname)
770   bundle = GetManifestBundle(pepper_ver, chrome_revision, nacl_revision,
771                              tarfile, archive_url)
772
773   manifest_snippet_file = os.path.join(OUT_DIR, tarname + '.json')
774   with open(manifest_snippet_file, 'wb') as manifest_snippet_stream:
775     manifest_snippet_stream.write(bundle.GetDataAsString())
776
777   buildbot_common.Archive(tarname + '.json', bucket_path, OUT_DIR,
778                           step_link=False)
779
780
781 def BuildStepArchiveSDKTools():
782   # Only push up sdk_tools.tgz and nacl_sdk.zip on the linux buildbot.
783   builder_name = os.getenv('BUILDBOT_BUILDERNAME', '')
784   if builder_name == 'linux-sdk-multi':
785     buildbot_common.BuildStep('Build SDK Tools')
786     build_updater.BuildUpdater(OUT_DIR)
787
788     buildbot_common.BuildStep('Archive SDK Tools')
789     bucket_path = 'nativeclient-mirror/nacl/nacl_sdk/%s' % (
790         build_version.ChromeVersion(),)
791     buildbot_common.Archive('sdk_tools.tgz', bucket_path, OUT_DIR,
792                             step_link=False)
793     buildbot_common.Archive('nacl_sdk.zip', bucket_path, OUT_DIR,
794                             step_link=False)
795
796
797 def BuildStepSyncNaClPorts():
798   """Pull the pinned revision of naclports from SVN."""
799   buildbot_common.BuildStep('Sync naclports')
800   if not os.path.exists(NACLPORTS_DIR):
801     # checkout new copy of naclports
802     cmd = ['svn', 'checkout', '-q', '-r', str(NACLPORTS_REV), NACLPORTS_URL,
803            'naclports']
804     buildbot_common.Run(cmd, cwd=os.path.dirname(NACLPORTS_DIR))
805   else:
806     # sync existing copy to pinned revision.
807     cmd = ['svn', 'update', '-r', str(NACLPORTS_REV)]
808     buildbot_common.Run(cmd, cwd=NACLPORTS_DIR)
809
810
811 def BuildStepBuildNaClPorts(pepper_ver, pepperdir):
812   """Build selected naclports in all configurations."""
813   # TODO(sbc): currently naclports doesn't know anything about
814   # Debug builds so the Debug subfolders are all empty.
815
816   env = dict(os.environ)
817   env['NACL_SDK_ROOT'] = pepperdir
818   env['PEPPER_DIR'] = os.path.basename(pepperdir)  # pepper_NN
819   env['NACLPORTS_NO_ANNOTATE'] = "1"
820   env['NACLPORTS_NO_UPLOAD'] = "1"
821   env['BUILDBOT_GOT_REVISION'] = str(NACLPORTS_REV)
822
823   build_script = 'build_tools/buildbot_sdk_bundle.sh'
824   buildbot_common.BuildStep('Build naclports')
825
826   bundle_dir = os.path.join(NACLPORTS_DIR, 'out', 'sdk_bundle')
827   out_dir = os.path.join(bundle_dir, 'pepper_%s' % pepper_ver)
828
829   # Remove the sdk_bundle directory to remove stale files from previous builds.
830   buildbot_common.RemoveDir(bundle_dir)
831
832   buildbot_common.Run([build_script], env=env, cwd=NACLPORTS_DIR)
833
834   # Some naclports do not include a standalone LICENSE/COPYING file
835   # so we explicitly list those here for inclusion.
836   extra_licenses = ('tinyxml/readme.txt',
837                     'jpeg-8d/README',
838                     'zlib-1.2.3/README')
839   src_root = os.path.join(NACLPORTS_DIR, 'out', 'build')
840   output_license = os.path.join(out_dir, 'ports', 'LICENSE')
841   GenerateNotice(src_root , output_license, extra_licenses)
842   readme = os.path.join(out_dir, 'ports', 'README')
843   oshelpers.Copy(['-v', os.path.join(SDK_SRC_DIR, 'README.naclports'), readme])
844
845
846 def BuildStepTarNaClPorts(pepper_ver, tarfile):
847   """Create tar archive containing headers and libs from naclports build."""
848   buildbot_common.BuildStep('Tar naclports Bundle')
849   buildbot_common.MakeDir(os.path.dirname(tarfile))
850   pepper_dir = 'pepper_%s' % pepper_ver
851   archive_dirs = [os.path.join(pepper_dir, 'ports')]
852
853   ports_out = os.path.join(NACLPORTS_DIR, 'out', 'sdk_bundle')
854   cmd = [sys.executable, CYGTAR, '-C', ports_out, '-cjf', tarfile]
855   cmd += archive_dirs
856   buildbot_common.Run(cmd, cwd=NACL_DIR)
857
858
859 def BuildStepBuildAppEngine(pepperdir, chrome_revision):
860   """Build the projects found in src/gonacl_appengine/src"""
861   buildbot_common.BuildStep('Build GoNaCl AppEngine Projects')
862   cmd = ['make', 'upload', 'REVISION=%s' % chrome_revision]
863   env = dict(os.environ)
864   env['NACL_SDK_ROOT'] = pepperdir
865   env['NACLPORTS_NO_ANNOTATE'] = "1"
866   buildbot_common.Run(cmd, env=env, cwd=GONACL_APPENGINE_SRC_DIR)
867
868
869 def main(args):
870   parser = optparse.OptionParser(description=__doc__)
871   parser.add_option('--nacl-tree-path',
872       help='Path to native client tree for bionic build.',
873       dest='nacl_tree_path')
874   parser.add_option('--qemu', help='Add qemu for ARM.',
875       action='store_true')
876   parser.add_option('--bionic', help='Add bionic build.',
877       action='store_true')
878   parser.add_option('--tar', help='Force the tar step.',
879       action='store_true')
880   parser.add_option('--archive', help='Force the archive step.',
881       action='store_true')
882   parser.add_option('--release', help='PPAPI release version.',
883       dest='release', default=None)
884   parser.add_option('--build-ports',
885       help='Build naclport bundle.', action='store_true')
886   parser.add_option('--build-app-engine',
887       help='Build AppEngine demos.', action='store_true')
888   parser.add_option('--experimental',
889       help='build experimental examples and libraries', action='store_true',
890       dest='build_experimental')
891   parser.add_option('--skip-toolchain', help='Skip toolchain untar',
892       action='store_true')
893   parser.add_option('--mac-sdk',
894       help='Set the mac-sdk (e.g. 10.6) to use when building with ninja.')
895   parser.add_option('--no-arm-trusted', action='store_true',
896       help='Disable building of ARM trusted components (sel_ldr, etc).')
897
898   # To setup bash completion for this command first install optcomplete
899   # and then add this line to your .bashrc:
900   #  complete -F _optcomplete build_sdk.py
901   try:
902     import optcomplete
903     optcomplete.autocomplete(parser)
904   except ImportError:
905     pass
906
907   global options
908   options, args = parser.parse_args(args[1:])
909   if args:
910     parser.error("Unexpected arguments: %s" % str(args))
911
912   if options.nacl_tree_path:
913     options.bionic = True
914     toolchain_build = os.path.join(options.nacl_tree_path, 'toolchain_build')
915     print 'WARNING: Building bionic toolchain from NaCl checkout.'
916     print 'This option builds bionic from the sources currently in the'
917     print 'provided NativeClient checkout, and the results instead of '
918     print 'downloading a toolchain from the builder. This may result in a'
919     print 'NaCl SDK that can not run on ToT chrome.'
920     print 'NOTE:  To clobber you will need to run toolchain_build_bionic.py'
921     print 'directly from the NativeClient checkout.'
922     print ''
923     response = raw_input("Type 'y' and hit enter to continue.\n")
924     if response != 'y' and response != 'Y':
925       print 'Aborting.'
926       return 1
927
928     # Get head version of NativeClient tree
929     buildbot_common.BuildStep('Build bionic toolchain.')
930     buildbot_common.Run([sys.executable, 'toolchain_build_bionic.py', '-f'],
931                         cwd=toolchain_build)
932   else:
933     toolchain_build = None
934
935   if buildbot_common.IsSDKBuilder():
936     options.archive = True
937     options.build_ports = True
938     options.build_app_engine = True
939     options.tar = True
940
941   toolchains = ['newlib', 'glibc', 'arm', 'pnacl', 'host']
942
943   # Changes for experimental bionic builder
944   if options.bionic:
945     toolchains.append('bionic')
946     options.build_ports = False
947     options.build_app_engine = False
948
949   print 'Building: ' + ' '.join(toolchains)
950
951   if options.archive and not options.tar:
952     parser.error('Incompatible arguments with archive.')
953
954   chrome_version = int(build_version.ChromeMajorVersion())
955   chrome_revision = build_version.ChromeRevision()
956   nacl_revision = build_version.NaClRevision()
957   pepper_ver = str(chrome_version)
958   pepper_old = str(chrome_version - 1)
959   pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
960   pepperdir_old = os.path.join(OUT_DIR, 'pepper_' + pepper_old)
961   if options.bionic:
962     tarname = 'naclsdk_bionic.tar.bz2'
963   else:
964     tarname = 'naclsdk_' + getos.GetPlatform() + '.tar.bz2'
965   tarfile = os.path.join(OUT_DIR, tarname)
966
967   if options.release:
968     pepper_ver = options.release
969   print 'Building PEPPER %s at %s' % (pepper_ver, chrome_revision)
970
971   if 'NACL_SDK_ROOT' in os.environ:
972     # We don't want the currently configured NACL_SDK_ROOT to have any effect
973     # of the build.
974     del os.environ['NACL_SDK_ROOT']
975
976   if not options.skip_toolchain:
977     BuildStepCleanPepperDirs(pepperdir, pepperdir_old)
978     BuildStepMakePepperDirs(pepperdir, ['include', 'toolchain', 'tools'])
979     BuildStepDownloadToolchains(toolchains)
980     if options.nacl_tree_path:
981       # Instead of untarring, copy the raw bionic toolchain
982       not_bionic = [i for i in toolchains if i != 'bionic']
983       BuildStepUntarToolchains(pepperdir, not_bionic)
984       tcname = GetToolchainDirName('bionic', 'arm')
985       srcdir = os.path.join(toolchain_build, 'out', tcname)
986       bionicdir = os.path.join(pepperdir, 'toolchain', tcname)
987       oshelpers.Copy(['-r', srcdir, bionicdir])
988     else:
989       BuildStepUntarToolchains(pepperdir, toolchains)
990
991     BuildStepBuildToolchains(pepperdir, toolchains)
992
993   BuildStepUpdateHelpers(pepperdir, True)
994   BuildStepUpdateUserProjects(pepperdir, toolchains,
995                               options.build_experimental, True)
996
997   BuildStepCopyTextFiles(pepperdir, pepper_ver, chrome_revision, nacl_revision)
998
999   # Ship with libraries prebuilt, so run that first.
1000   BuildStepBuildLibraries(pepperdir, 'src')
1001   GenerateNotice(pepperdir)
1002
1003   # Verify the SDK contains what we expect.
1004   if not options.bionic:
1005     BuildStepVerifyFilelist(pepperdir)
1006
1007   if options.tar:
1008     BuildStepTarBundle(pepper_ver, tarfile)
1009
1010   if options.build_ports and getos.GetPlatform() == 'linux':
1011     ports_tarfile = os.path.join(OUT_DIR, 'naclports.tar.bz2')
1012     BuildStepSyncNaClPorts()
1013     BuildStepBuildNaClPorts(pepper_ver, pepperdir)
1014     if options.tar:
1015       BuildStepTarNaClPorts(pepper_ver, ports_tarfile)
1016
1017   if options.build_app_engine and getos.GetPlatform() == 'linux':
1018     BuildStepBuildAppEngine(pepperdir, chrome_revision)
1019
1020   if options.qemu:
1021     qemudir = os.path.join(NACL_DIR, 'toolchain', 'linux_arm-trusted')
1022     oshelpers.Copy(['-r', qemudir, pepperdir])
1023
1024   # Archive on non-trybots.
1025   if options.archive:
1026     BuildStepArchiveBundle('build', pepper_ver, chrome_revision, nacl_revision,
1027                            tarfile)
1028     if options.build_ports and getos.GetPlatform() == 'linux':
1029       BuildStepArchiveBundle('naclports', pepper_ver, chrome_revision,
1030                              nacl_revision, ports_tarfile)
1031     BuildStepArchiveSDKTools()
1032
1033   return 0
1034
1035
1036 if __name__ == '__main__':
1037   try:
1038     sys.exit(main(sys.argv))
1039   except KeyboardInterrupt:
1040     buildbot_common.ErrorExit('build_sdk: interrupted')