2 # Copyright (c) 2012 The Native Client 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.
6 """Recipes for NativeClient toolchain packages.
8 The real entry plumbing is in toolchain_main.py.
20 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
21 import pynacl.gsd_storage
22 import pynacl.hashing_tools
23 import pynacl.platform
24 import pynacl.repo_tools
26 BUILD_SCRIPT = os.path.abspath(__file__)
27 TOOLCHAIN_BUILD = os.path.dirname(BUILD_SCRIPT)
28 NATIVE_CLIENT = os.path.dirname(TOOLCHAIN_BUILD)
29 PKG_VERSION = os.path.join(NATIVE_CLIENT, 'build', 'package_version')
30 sys.path.append(PKG_VERSION)
34 import toolchain_build
37 from file_update import Mkdir, Rmdir, Symlink
38 from file_update import NeedsUpdate, UpdateFromTo, UpdateText
41 BIONIC_VERSION = '17f8f59d2fbdcd72a57e94ffaeff465601d68450'
43 TOOLCHAIN_BUILD_SRC = os.path.join(TOOLCHAIN_BUILD, 'src')
44 TOOLCHAIN_BUILD_OUT = os.path.join(TOOLCHAIN_BUILD, 'out')
46 BIONIC_SRC = os.path.join(TOOLCHAIN_BUILD_SRC, 'bionic')
47 TOOLCHAIN = os.path.join(NATIVE_CLIENT, 'toolchain')
50 def GetToolchainPath(target_arch, libc, *extra_paths):
51 os_name = pynacl.platform.GetOS()
52 host_arch = pynacl.platform.GetArch()
53 return os.path.join(TOOLCHAIN,
54 '%s_%s' % (os_name, host_arch),
55 'nacl_%s_%s' % (target_arch, libc),
59 def GetBionicBuildPath(target_arch, *extra_paths):
60 os_name = pynacl.platform.GetOS()
61 return os.path.join(TOOLCHAIN_BUILD_OUT,
62 "%s_%s_bionic" % (os_name, target_arch),
66 def ReplaceText(text, maplist):
69 text = text.replace(key, m[key])
73 def ReplaceArch(text, arch, subarch=None):
94 '$NACL': NACL_ARCHES[arch],
95 '$GCC': GCC_ARCHES[arch],
96 '$CPU': CPU_ARCHES[arch],
97 '$SUB': subarch or '',
99 '$VER': VERSION_MAP[arch]
101 return ReplaceText(text, [REPLACE_MAP])
104 def Clobber(fast=False):
106 Rmdir(os.path.join(TOOLCHAIN_BUILD, 'cache'))
107 Rmdir(os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_arm_work'))
115 Rmdir(GetToolchainPath(arch, 'bionic'))
116 for workdir in BUILD_DIRS:
117 Rmdir(os.path.join(TOOLCHAIN_BUILD_OUT, workdir % arch))
120 def FetchAndBuild_gcc_libs():
121 tc_args = ['-y', '--no-use-remote-cache', 'gcc_libs_arm']
122 # Call toolchain_build to build the gcc libs. We do not need to fill in
123 # any package targets since we are using toolchain_build as an
125 toolchain_main.PackageBuilder(toolchain_build.PACKAGES, {}, tc_args).Main()
128 def FetchBionicSources():
130 url = '%s/nacl-%s.git' % (toolchain_build.GIT_BASE_URL, project)
131 pynacl.repo_tools.SyncGitRepo(url,
132 os.path.join(TOOLCHAIN_BUILD_SRC, project),
136 def MungeIRT(src, dst):
139 'native_client/src/untrusted/irt/' : '',
142 with open(src, 'r') as srcf:
144 text = ReplaceText(text, [replace_map])
145 with open(dst, 'w') as dstf:
149 def CreateBasicToolchain():
150 # Create a toolchain directory containing only the toolchain binaries and
151 # basic files line nacl_arm_macros.s.
153 UpdateFromTo(GetToolchainPath(arch, 'newlib'),
154 GetBionicBuildPath(arch),
155 filters=['*arm-nacl/include*', '*arm-nacl/lib*','*.a', '*.o'])
156 UpdateFromTo(GetToolchainPath(arch, 'newlib'),
157 GetBionicBuildPath(arch),
161 # crt1.o crti.o 4.8.2/crtbeginT.o ... 4.8.2/crtend.o crtn.o
162 # -shared build uses:
163 # crti.o 4.8.2/crtbeginS.o ... 4.8.2/crtendS.o crtn.o crtn.o
164 # However we only provide a crtbegin(S) and crtend(S)
166 * This is a dummy linker script.
167 * libnacl.a, libcrt_common.a, crt0.o crt1.o crti.o and crtn.o are all
168 * empty. Instead all the work is done by crtbegin(S).o and crtend(S).o and
169 * the bionic libc. These are provided for compatability with the newlib
170 * toolchain binaries.
172 EMPTY_FILES = ['crt0.o', 'crt1.o', 'crti.o', 'crtn.o',
173 'libnacl.a', 'libcrt_common.a', 'libpthread.a']
175 # Bionic uses the following include paths
177 ('bionic/libc/arch-nacl/syscalls/irt_poll.h',
178 '$NACL-nacl/include/irt_poll.h'),
179 ('bionic/libc/arch-nacl/syscalls/irt_socket.h',
180 '$NACL-nacl/include/irt_socket.h'),
181 ('bionic/libc/include', '$NACL-nacl/include'),
182 ('bionic/libc/arch-nacl/syscalls/nacl_socket.h',
183 '$NACL-nacl/include/nacl_socket.h'),
184 ('bionic/libc/arch-nacl/syscalls/nacl_stat.h',
185 '$NACL-nacl/include/nacl_stat.h'),
186 ('bionic/libc/arch-$ARCH/include/machine',
187 '$NACL-nacl/include/machine'),
188 ('bionic/libc/kernel/common', '$NACL-nacl/include'),
189 ('bionic/libc/kernel/arch-$ARCH/asm', '$NACL-nacl/include/asm'),
190 ('bionic/libm/include', '$NACL-nacl/include'),
191 ('bionic/libm/$CPU', '$NACL-nacl/include'),
192 ('bionic/safe-iop/include', '$NACL-nacl/include'),
193 ('bionic/libstdc++/nacl',
194 '$NACL-nacl/include/c++/4.8.2/$NACL-nacl'),
195 ('bionic/nacl/$ARCH', '.'),
200 for name in ['irt.h', 'irt_dev.h']:
201 src = os.path.join(NATIVE_CLIENT, 'src', 'untrusted', 'irt', name)
202 dst = GetBionicBuildPath(arch, '$NACL-nacl', 'include', name)
203 MungeIRT(src, ReplaceArch(dst, arch))
205 inspath = GetBionicBuildPath(arch)
207 # Create empty objects and libraries
208 libpath = ReplaceArch(os.path.join(inspath, '$NACL-nacl', 'lib'), arch)
209 for name in EMPTY_FILES:
210 UpdateText(os.path.join(libpath, name), EMPTY)
212 # Copy BIONIC files to toolchain
213 for src, dst in BIONIC_PAIRS:
214 srcpath = ReplaceArch(os.path.join(TOOLCHAIN_BUILD_SRC, src), arch)
215 dstpath = ReplaceArch(os.path.join(inspath, dst), arch)
216 UpdateFromTo(srcpath, dstpath)
219 gcc = ReplaceArch(os.path.join(inspath, 'bin', '$NACL-nacl-gcc'), arch)
220 lib = ReplaceArch(os.path.join(inspath, 'lib/gcc/$NACL-nacl/$VER'), arch)
221 specs = os.path.join(lib, 'specs')
222 with open(specs, 'w') as specfile:
223 process.Run([gcc, '-dumpspecs'], cwd=None, shell=False,
224 outfile=specfile, verbose=False)
225 text = open(specs, 'r').read()
227 # Replace items in the spec file
228 text = ReplaceText(text, [{
229 '-lgcc': '-lgcc --as-needed %{!static: -lgcc_s} --no-as-needed %{!shared: -lgcc_eh}',
230 '--hash-style=gnu': '--hash-style=sysv',
232 open(specs, 'w').write(text)
235 def ConfigureAndBuild_libc():
237 inspath = GetBionicBuildPath(arch)
238 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'bionic_$ARCH_work')
239 workpath = ReplaceArch(workpath, arch)
240 ConfigureAndBuild(arch, 'bionic/libc', workpath, inspath, )
243 def ConfigureAndBuild_libc():
245 inspath = GetBionicBuildPath(arch)
246 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'bionic_$ARCH_work')
247 workpath = ReplaceArch(workpath, arch)
248 ConfigureAndBuild(arch, 'bionic/libc', workpath, inspath)
249 ConfigureAndBuild(arch, 'bionic/libm', workpath, inspath)
252 def ConfigureAndBuildLinker():
254 inspath = GetBionicBuildPath(arch)
255 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'bionic_$ARCH_work')
256 workpath = ReplaceArch(workpath, arch)
257 ConfigureAndBuild(arch, 'bionic/linker', workpath, inspath)
260 def ConfigureGCCProject(arch, project, cfg, workpath, inspath):
261 # configure does not always have +x
262 filepath = os.path.abspath(os.path.join(workpath, cfg[0]))
263 st_info = os.stat(filepath)
264 os.chmod(filepath, st_info.st_mode | stat.S_IEXEC)
267 newpath = GetBionicBuildPath(arch, 'bin') + ':' + env['PATH']
269 proj = '%s %s' % (project, arch)
270 setpath = ['/usr/bin/env', 'PATH=' + newpath]
272 # Check if config changed or script is new
273 config_path = os.path.join(workpath, 'config.info')
274 updated = UpdateText(config_path, ' '.join(cfg))
275 updated |= NeedsUpdate(config_path, BUILD_SCRIPT)
278 print 'Configure ' + proj
279 if process.Run(setpath + cfg, cwd=workpath, env=env, outfile=sys.stdout):
280 raise RuntimeError('Failed to configure %s.\n' % proj)
282 print 'Reusing config for %s.' % proj
285 def MakeGCCProject(arch, project, workpath, targets=[]):
287 newpath = GetBionicBuildPath(arch, 'bin') + ':' + env['PATH']
288 proj = '%s %s' % (project, arch)
289 setpath = ['/usr/bin/env', 'PATH=' + newpath]
292 proj = project = ': ' + ' '.join(targets)
297 if process.Run(setpath + ['make', '-j16', 'V=1'] + targets,
298 cwd=workpath, outfile=sys.stdout):
299 raise RuntimeError('Failed to build %s.\n' % proj)
303 def ConfigureAndBuild_libgcc(skip_build=False):
306 tcpath = GetBionicBuildPath(arch)
309 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_$GCC_bionic_work')
310 workpath = ReplaceArch(workpath, arch)
314 Symlink('../gcc_libs_arm_work/gcc' , os.path.join(workpath, 'gcc'))
317 inspath = os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_$GCC_bionic_install')
318 inspath = ReplaceArch(inspath, arch)
320 dstpath = ReplaceArch(os.path.join(workpath, '$NACL-nacl/libgcc'), arch)
322 '../../../../src/gcc_libs/libgcc/configure',
324 '--build=i686-linux',
327 '--enable-shared-libgcc',
330 '--prefix=' + inspath,
331 'CFLAGS=-I../../../gcc_lib_arm_work'
335 ConfigureGCCProject(arch, project, cfg, dstpath, inspath)
336 MakeGCCProject(arch, project, dstpath, ['libgcc.a'])
338 # Copy temp version of libgcc.a for linking libc.so
339 UpdateFromTo(os.path.join(dstpath, 'libgcc.a'),
340 os.path.join(tcpath, 'arm-nacl', 'lib', 'libgcc.a'))
343 def BuildAndInstall_libgcc_s(skip_build=False):
346 tcpath = GetBionicBuildPath(arch)
348 # Remove temp copy of libgcc.a, it get's installed at the end
349 os.remove(os.path.join(tcpath, 'arm-nacl', 'lib', 'libgcc.a'))
352 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_$GCC_bionic_work')
353 workpath = ReplaceArch(workpath, arch)
354 dstpath = ReplaceArch(os.path.join(workpath, '$NACL-nacl/libgcc'), arch)
357 inspath = os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_$GCC_bionic_install')
358 inspath = ReplaceArch(inspath, arch)
361 MakeGCCProject(arch, project, dstpath)
362 MakeGCCProject(arch, project, dstpath, ['install'])
364 UpdateFromTo(os.path.join(inspath, 'lib', 'gcc'),
365 os.path.join(tcpath, 'lib', 'gcc'),
367 UpdateFromTo(os.path.join(inspath, 'lib', 'libgcc_s.so.1'),
368 os.path.join(tcpath, 'arm-nacl', 'lib', 'libgcc_s.so.1'))
369 UpdateFromTo(os.path.join(inspath, 'lib', 'libgcc_s.so'),
370 os.path.join(tcpath, 'arm-nacl', 'lib', 'libgcc_s.so'))
373 def ConfigureAndBuild_libstdcpp():
375 project = 'libstdc++'
376 tcpath = GetBionicBuildPath(arch)
379 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_$GCC_bionic_work')
380 workpath = ReplaceArch(workpath, arch)
383 inspath = os.path.join(TOOLCHAIN_BUILD_OUT, 'gcc_$GCC_bionic_install')
384 inspath = ReplaceArch(inspath, arch)
386 dstpath = ReplaceArch(os.path.join(workpath, '$NACL-nacl/libstdc++-v3'), arch)
389 '../../../../src/gcc_libs/libstdc++-v3/configure',
391 '--build=i686-linux',
395 '--disable-libstdcxx-pch',
396 '--enable-shared-libgcc',
398 '--prefix=' + inspath,
399 'CFLAGS=-I../../../gcc_lib_arm_work'
402 ConfigureGCCProject(arch, project, cfg, dstpath, inspath)
403 MakeGCCProject(arch, project, dstpath)
404 MakeGCCProject(arch, project, dstpath, ['install'])
411 'libstdc++.so.6.0.18',
412 'libstdc++.so.6.0.18-gdb.py',
416 for filename in filelist:
417 UpdateFromTo(os.path.join(inspath, 'lib', filename),
418 os.path.join(tcpath, 'arm-nacl', 'lib', filename))
420 UpdateFromTo(os.path.join(inspath, 'include'),
421 os.path.join(tcpath, 'arm-nacl', 'include'))
424 def GetProjectPaths(arch, project):
425 srcpath = os.path.join(BIONIC_SRC, project)
426 workpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'bionic_$ARCH_work')
427 instpath = os.path.join(TOOLCHAIN_BUILD_OUT, 'bionic_$ARCH_install')
429 toolpath = GetBionicBuildPath(arch)
430 workpath = ReplaceArch(os.path.join(workpath, 'bionic', project), arch)
431 instpath = ReplaceArch(os.path.join(toolpath, '$NACL-nacl', 'lib'), arch)
441 def CreateProject(arch, project, clobber=False):
442 paths = GetProjectPaths(arch, project)
444 MAKEFILE_TEMPLATE = """
445 # Copyright (c) 2014 The Chromium Authors. All rights reserved.
446 # Use of this source code is governed by a BSD-style license that can be
447 # found in the LICENSE file.
449 # GNU Makefile based on shared rules provided by the Native Client SDK.
450 # See README.Makefiles for more details.
452 NATIVE_CLIENT_PATH?=$(nacl_path)
453 TOOLCHAIN_PATH?=$(tc_path)
454 TOOLCHAIN_PREFIX:=$(TOOLCHAIN_PATH)/bin/$GCC-nacl-
456 CC:=$(TOOLCHAIN_PREFIX)gcc
457 CXX:=$(TOOLCHAIN_PREFIX)g++
458 AR:=$(TOOLCHAIN_PREFIX)ar
467 MAKEFILE_DEPS:=$(build_tc_path)/tc_bionic.mk
468 MAKEFILE_DEPS+=$(src_path)/Makefile
470 include $(build_tc_path)/tc_bionic.mk
471 include $(src_path)/Makefile
474 '$(src_path)': paths['src'],
475 '$(dst_path)': paths['work'],
476 '$(ins_path)': paths['ins'],
477 '$(tc_path)': GetBionicBuildPath(arch),
478 '$(build_tc_path)': TOOLCHAIN_BUILD,
479 '$(nacl_path)': NATIVE_CLIENT,
481 text = ReplaceText(MAKEFILE_TEMPLATE, [remap])
482 text = ReplaceArch(text, arch)
485 print 'Clobbering Bionic project directory: ' + paths['work']
490 UpdateText(os.path.join(paths['work'], 'Makefile'), text)
493 def ConfigureBionicProjects(clobber=False):
494 PROJECTS = ['libc', 'libm', 'linker', 'tests', 'newlinker', 'newtests']
496 for project in PROJECTS:
497 print 'Configure %s for %s.' % (project, arch)
498 CreateProject(arch, project, clobber)
501 def MakeBionicProject(project, targets=[], clobber=False):
503 paths = GetProjectPaths(arch, project)
504 workpath = paths['work']
505 inspath = paths['ins']
506 targetlist = ' '.join(targets)
508 print 'Building %s for %s at %s %s.' % (project, arch, workpath, targetlist)
510 args = ['make', '-j12', 'V=1', 'clean']
511 if process.Run(args, cwd=workpath, outfile=sys.stdout):
512 raise RuntimeError('Failed to clean %s for %s.\n' % (project, arch))
514 args = ['make', '-j12', 'V=1'] + targets
515 if process.Run(args, cwd=workpath, outfile=sys.stdout):
516 raise RuntimeError('Failed to build %s for %s.\n' % (project, arch))
518 print 'Done with %s for %s.\n' % (project, arch)
521 def ArchiveAndUpload(version, zipname, zippath, packages_file):
523 print >>sys.stderr, '@@@BUILD_STEP archive_and_upload@@@'
525 bucket_path = 'nativeclient-archive2/toolchain/%s' % version
526 gsd_store = pynacl.gsd_storage.GSDStorage(bucket_path, [bucket_path])
528 zipname = os.path.join(TOOLCHAIN_BUILD_OUT, zipname)
534 # Archive the zippath to the zipname.
535 if process.Run(['tar', '-czf', zipname, zippath],
536 cwd=TOOLCHAIN_BUILD_OUT,
538 raise RuntimeError('Failed to zip %s from %s.\n' % (zipname, zippath))
540 # Create Zip Hash file using the hash of the zip file.
541 hashzipname = zipname + '.sha1hash'
542 hashval = pynacl.hashing_tools.HashFileContents(zipname)
543 with open(hashzipname, 'w') as f:
546 # Upload the Zip file.
547 zipurl = gsd_store.PutFile(zipname, os.path.basename(zipname))
549 print >>sys.stderr, ('@@@STEP_LINK@download (%s)@%s@@@' %
550 (os.path.basename(zipname), zipurl))
552 # Upload the Zip Hash file.
553 hashurl = gsd_store.PutFile(hashzipname, os.path.basename(hashzipname))
555 print >>sys.stderr, ('@@@STEP_LINK@download (%s)@%s@@@' %
556 (os.path.basename(hashzipname), hashurl))
558 # Create a package info file for the nacl_arm_bionic package.
559 archive_desc = archive_info.ArchiveInfo(name=os.path.basename(zipname),
560 archive_hash=hashval,
561 tar_src_dir='linux_arm_bionic',
563 package_desc = package_info.PackageInfo()
564 package_desc.AppendArchive(archive_desc)
566 os_name = pynacl.platform.GetOS()
567 arch_name = pynacl.platform.GetArch()
568 package_info_file = os.path.join(TOOLCHAIN_BUILD_OUT,
570 '%s_%s' % (os_name, arch_name),
571 'nacl_arm_bionic.json')
572 package_desc.SavePackageFile(package_info_file)
574 # If packages_file is specified, write out our packages file of 1 package.
576 with open(packages_file, 'wt') as f:
577 f.write(package_info_file)
581 parser = argparse.ArgumentParser(add_help=False)
583 '-v', '--verbose', dest='verbose',
584 default=False, action='store_true',
585 help='Produce more output.')
588 '-c', '--clobber', dest='clobber',
589 default=False, action='store_true',
590 help='Clobber working directories before building.')
593 '-f', '--fast-clobber', dest='fast_clobber',
594 default=False, action='store_true',
595 help='Clobber bionic working directories before building.')
598 '-s', '--sync', dest='sync',
599 default=False, action='store_true',
600 help='Sync sources first.')
603 '-b', '--buildbot', dest='buildbot',
604 default=False, action='store_true',
605 help='Running on the buildbot.')
608 '-l', '--llvm', dest='llvm',
609 default=False, action='store_true',
610 help='Enable building via llvm.')
613 '-u', '--upload', dest='upload',
614 default=False, action='store_true',
615 help='Upload build artifacts.')
618 '--packages-file', dest='packages_file',
620 help='Output packages file describing list of package files built.')
623 '--skip-gcc', dest='skip_gcc',
624 default=False, action='store_true',
625 help='Skip building GCC components.')
627 options, leftover_args = parser.parse_known_args()
628 if '-h' in leftover_args or '--help' in leftover_args:
629 print 'The following arguments are specific to toolchain_build_bionic.py:'
631 print 'The rest of the arguments are generic, in toolchain_main.py'
635 ARCHES.append('pnacl')
637 if options.buildbot or options.upload:
638 version = os.environ['BUILDBOT_REVISION']
640 if options.clobber or options.fast_clobber:
641 Clobber(fast=options.fast_clobber)
643 if options.sync or options.buildbot:
646 # Copy headers and compiler tools
647 CreateBasicToolchain()
649 # Configure Bionic Projects, libc, libm, linker, tests, ...
650 ConfigureBionicProjects(clobber=options.buildbot)
652 # Build and install IRT header before building GCC
653 MakeBionicProject('libc', ['irt'])
655 if not options.skip_gcc:
656 # Build newlib gcc_libs for use by bionic
657 FetchAndBuild_gcc_libs()
659 # Configure and build libgcc.a
660 ConfigureAndBuild_libgcc(skip_build=options.skip_gcc)
662 # With libgcc.a, we can now build libc.so
663 MakeBionicProject('libc')
665 # With libc.so, we can build libgcc_s.so
666 BuildAndInstall_libgcc_s(skip_build=options.skip_gcc)
668 # With libc and libgcc_s, we can now build libm
669 MakeBionicProject('libm')
671 # With libc, libgcc, and libm, we can now build libstdc++
672 ConfigureAndBuild_libstdcpp()
674 # Now we can build the linker
675 #MakeBionicProject('linker')
676 MakeBionicProject('newlinker')
678 # Now we have a full toolchain, so test it
679 #MakeBionicProject('tests')
680 MakeBionicProject('newtests')
682 # We can run only off buildbots
683 if not options.buildbot:
684 process.Run(['./scons', 'platform=arm', '--mode=nacl,dbg-linux', '-j20'],
686 MakeBionicProject('tests', ['run'])
687 MakeBionicProject('newtests', ['run'])
689 dst = os.path.join(TOOLCHAIN_BUILD_OUT, 'linux_arm_bionic', 'log.txt')
690 with open(dst, 'w') as dstf:
691 process.Run(['git', 'log', '-n', '1'],
692 cwd=os.path.join(TOOLCHAIN_BUILD_SRC, 'bionic'),
696 if options.buildbot or options.upload:
697 zipname = 'naclsdk_linux_arm_bionic.tgz'
698 ArchiveAndUpload(version, zipname, 'linux_arm_bionic',
699 options.packages_file)
702 if __name__ == '__main__':
703 sys.exit(main(sys.argv))