Home | History | Annotate | Download | only in build_tools
      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 
      6 import multiprocessing
      7 import optparse
      8 import os
      9 import sys
     10 
     11 import buildbot_common
     12 import build_version
     13 import generate_make
     14 import parse_dsc
     15 
     16 from build_paths import NACL_DIR, SDK_SRC_DIR, OUT_DIR, SDK_EXAMPLE_DIR
     17 from build_paths import GSTORE
     18 from generate_index import LandingPage
     19 
     20 sys.path.append(os.path.join(SDK_SRC_DIR, 'tools'))
     21 sys.path.append(os.path.join(NACL_DIR, 'build'))
     22 import getos
     23 import http_download
     24 
     25 
     26 MAKE = 'nacl_sdk/make_3.99.90-26-gf80222c/make.exe'
     27 LIB_DICT = {
     28   'linux': [],
     29   'mac': [],
     30   'win': ['x86_32']
     31 }
     32 VALID_TOOLCHAINS = ['newlib', 'glibc', 'pnacl', 'win', 'linux', 'mac']
     33 
     34 # Global verbosity setting.
     35 # If set to try (normally via a command line arg) then build_projects will
     36 # add V=1 to all calls to 'make'
     37 verbose = False
     38 
     39 
     40 def CopyFilesFromTo(filelist, srcdir, dstdir):
     41   for filename in filelist:
     42     srcpath = os.path.join(srcdir, filename)
     43     dstpath = os.path.join(dstdir, filename)
     44     buildbot_common.CopyFile(srcpath, dstpath)
     45 
     46 
     47 def UpdateHelpers(pepperdir, clobber=False):
     48   tools_dir = os.path.join(pepperdir, 'tools')
     49   if not os.path.exists(tools_dir):
     50     buildbot_common.ErrorExit('SDK tools dir is missing: %s' % tools_dir)
     51 
     52   exampledir = os.path.join(pepperdir, 'examples')
     53   if clobber:
     54     buildbot_common.RemoveDir(exampledir)
     55   buildbot_common.MakeDir(exampledir)
     56 
     57   # Copy files for individual bild and landing page
     58   files = ['favicon.ico', 'httpd.cmd']
     59   CopyFilesFromTo(files, SDK_EXAMPLE_DIR, exampledir)
     60 
     61   resourcesdir = os.path.join(SDK_EXAMPLE_DIR, 'resources')
     62   files = ['index.css', 'index.js', 'button_close.png',
     63       'button_close_hover.png']
     64   CopyFilesFromTo(files, resourcesdir, exampledir)
     65 
     66   # Copy tools scripts and make includes
     67   buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.py'),
     68       tools_dir)
     69   buildbot_common.CopyDir(os.path.join(SDK_SRC_DIR, 'tools', '*.mk'),
     70       tools_dir)
     71 
     72   # On Windows add a prebuilt make
     73   if getos.GetPlatform() == 'win':
     74     buildbot_common.BuildStep('Add MAKE')
     75     http_download.HttpDownload(GSTORE + MAKE,
     76                                os.path.join(tools_dir, 'make.exe'))
     77 
     78 
     79 def ValidateToolchains(toolchains):
     80   invalid_toolchains = set(toolchains) - set(VALID_TOOLCHAINS)
     81   if invalid_toolchains:
     82     buildbot_common.ErrorExit('Invalid toolchain(s): %s' % (
     83         ', '.join(invalid_toolchains)))
     84 
     85 
     86 def UpdateProjects(pepperdir, project_tree, toolchains,
     87                    clobber=False, configs=None, first_toolchain=False):
     88   if configs is None:
     89     configs = ['Debug', 'Release']
     90   if not os.path.exists(os.path.join(pepperdir, 'tools')):
     91     buildbot_common.ErrorExit('Examples depend on missing tools.')
     92   if not os.path.exists(os.path.join(pepperdir, 'toolchain')):
     93     buildbot_common.ErrorExit('Examples depend on missing toolchains.')
     94 
     95   ValidateToolchains(toolchains)
     96 
     97   # Create the library output directories
     98   libdir = os.path.join(pepperdir, 'lib')
     99   platform = getos.GetPlatform()
    100   for config in configs:
    101     for arch in LIB_DICT[platform]:
    102       dirpath = os.path.join(libdir, '%s_%s_host' % (platform, arch), config)
    103       if clobber:
    104         buildbot_common.RemoveDir(dirpath)
    105       buildbot_common.MakeDir(dirpath)
    106 
    107   landing_page = None
    108   for branch, projects in project_tree.iteritems():
    109     dirpath = os.path.join(pepperdir, branch)
    110     if clobber:
    111       buildbot_common.RemoveDir(dirpath)
    112     buildbot_common.MakeDir(dirpath)
    113     targets = [desc['NAME'] for desc in projects]
    114 
    115     # Generate master make for this branch of projects
    116     generate_make.GenerateMasterMakefile(pepperdir,
    117                                          os.path.join(pepperdir, branch),
    118                                          targets)
    119 
    120     if branch.startswith('examples') and not landing_page:
    121       landing_page = LandingPage()
    122 
    123     # Generate individual projects
    124     for desc in projects:
    125       srcroot = os.path.dirname(desc['FILEPATH'])
    126       generate_make.ProcessProject(pepperdir, srcroot, pepperdir, desc,
    127                                    toolchains, configs=configs,
    128                                    first_toolchain=first_toolchain)
    129 
    130       if branch.startswith('examples'):
    131         landing_page.AddDesc(desc)
    132 
    133   if landing_page:
    134     # Generate the landing page text file.
    135     index_html = os.path.join(pepperdir, 'examples', 'index.html')
    136     example_resources_dir = os.path.join(SDK_EXAMPLE_DIR, 'resources')
    137     index_template = os.path.join(example_resources_dir,
    138                                   'index.html.template')
    139     with open(index_html, 'w') as fh:
    140       out = landing_page.GeneratePage(index_template)
    141       fh.write(out)
    142 
    143   # Generate top Make for examples
    144   targets = ['api', 'demo', 'getting_started', 'tutorial']
    145   targets = [x for x in targets if 'examples/'+x in project_tree]
    146   branch_name = 'examples'
    147   generate_make.GenerateMasterMakefile(pepperdir,
    148                                        os.path.join(pepperdir, branch_name),
    149                                        targets)
    150 
    151 
    152 def BuildProjectsBranch(pepperdir, branch, deps, clean, config, args=None):
    153   make_dir = os.path.join(pepperdir, branch)
    154   print "\nMake: " + make_dir
    155 
    156   if getos.GetPlatform() == 'win':
    157     # We need to modify the environment to build host on Windows.
    158     make = os.path.join(make_dir, 'make.bat')
    159   else:
    160     make = 'make'
    161 
    162   env = None
    163   if os.environ.get('USE_GOMA') == '1':
    164     env = dict(os.environ)
    165     env['NACL_COMPILER_PREFIX'] = 'gomacc'
    166     # Add -m32 to the CFLAGS when building using i686-nacl-gcc
    167     # otherwise goma won't recognise it as different to the x86_64
    168     # build.
    169     env['X86_32_CFLAGS'] = '-m32'
    170     env['X86_32_CXXFLAGS'] = '-m32'
    171     jobs = '50'
    172   else:
    173     jobs = str(multiprocessing.cpu_count())
    174 
    175   make_cmd = [make, '-j', jobs]
    176 
    177   make_cmd.append('CONFIG='+config)
    178   if not deps:
    179     make_cmd.append('IGNORE_DEPS=1')
    180 
    181   if verbose:
    182     make_cmd.append('V=1')
    183 
    184   if args:
    185     make_cmd += args
    186   else:
    187     make_cmd.append('TOOLCHAIN=all')
    188 
    189   buildbot_common.Run(make_cmd, cwd=make_dir, env=env)
    190   if clean:
    191     # Clean to remove temporary files but keep the built
    192     buildbot_common.Run(make_cmd + ['clean'], cwd=make_dir, env=env)
    193 
    194 
    195 def BuildProjects(pepperdir, project_tree, deps=True,
    196                   clean=False, config='Debug'):
    197 
    198   # Make sure we build libraries (which live in 'src') before
    199   # any of the examples.
    200   build_first = [p for p in project_tree if p != 'src']
    201   build_second = [p for p in project_tree if p == 'src']
    202 
    203   for branch in build_first + build_second:
    204     BuildProjectsBranch(pepperdir, branch, deps, clean, config)
    205 
    206 
    207 def main(args):
    208   parser = optparse.OptionParser()
    209   parser.add_option('-c', '--clobber',
    210       help='Clobber project directories before copying new files',
    211       action='store_true', default=False)
    212   parser.add_option('-b', '--build',
    213       help='Build the projects.', action='store_true')
    214   parser.add_option('--config',
    215       help='Choose configuration to build (Debug or Release).  Builds both '
    216            'by default')
    217   parser.add_option('-x', '--experimental',
    218       help='Build experimental projects', action='store_true')
    219   parser.add_option('-t', '--toolchain',
    220       help='Build using toolchain. Can be passed more than once.',
    221       action='append', default=[])
    222   parser.add_option('-d', '--dest',
    223       help='Select which build destinations (project types) are valid.',
    224       action='append')
    225   parser.add_option('-p', '--project',
    226       help='Select which projects are valid.',
    227       action='append')
    228   parser.add_option('-v', '--verbose', action='store_true')
    229 
    230   options, args = parser.parse_args(args[1:])
    231   if args:
    232     parser.error('Not expecting any arguments.')
    233 
    234   if 'NACL_SDK_ROOT' in os.environ:
    235     # We don't want the currently configured NACL_SDK_ROOT to have any effect
    236     # on the build.
    237     del os.environ['NACL_SDK_ROOT']
    238 
    239   pepper_ver = str(int(build_version.ChromeMajorVersion()))
    240   pepperdir = os.path.join(OUT_DIR, 'pepper_' + pepper_ver)
    241 
    242   if not options.toolchain:
    243     options.toolchain = ['newlib', 'glibc', 'pnacl', 'host']
    244 
    245   if 'host' in options.toolchain:
    246     options.toolchain.remove('host')
    247     options.toolchain.append(getos.GetPlatform())
    248     print 'Adding platform: ' + getos.GetPlatform()
    249 
    250   ValidateToolchains(options.toolchain)
    251 
    252   filters = {}
    253   if options.toolchain:
    254     filters['TOOLS'] = options.toolchain
    255     print 'Filter by toolchain: ' + str(options.toolchain)
    256   if not options.experimental:
    257     filters['EXPERIMENTAL'] = False
    258   if options.dest:
    259     filters['DEST'] = options.dest
    260     print 'Filter by type: ' + str(options.dest)
    261   if options.project:
    262     filters['NAME'] = options.project
    263     print 'Filter by name: ' + str(options.project)
    264 
    265   try:
    266     project_tree = parse_dsc.LoadProjectTree(SDK_SRC_DIR, include=filters)
    267   except parse_dsc.ValidationError as e:
    268     buildbot_common.ErrorExit(str(e))
    269   parse_dsc.PrintProjectTree(project_tree)
    270 
    271   UpdateHelpers(pepperdir, clobber=options.clobber)
    272   UpdateProjects(pepperdir, project_tree, options.toolchain,
    273                  clobber=options.clobber)
    274 
    275   if options.verbose:
    276     global verbose
    277     verbose = True
    278 
    279   if options.build:
    280     if options.config:
    281       configs = [options.config]
    282     else:
    283       configs = ['Debug', 'Release']
    284     for config in configs:
    285       BuildProjects(pepperdir, project_tree, config=config)
    286 
    287   return 0
    288 
    289 
    290 if __name__ == '__main__':
    291   script_name = os.path.basename(sys.argv[0])
    292   try:
    293     sys.exit(main(sys.argv))
    294   except parse_dsc.ValidationError as e:
    295     buildbot_common.ErrorExit('%s: %s' % (script_name, e))
    296   except KeyboardInterrupt:
    297     buildbot_common.ErrorExit('%s: interrupted' % script_name)
    298