Home | History | Annotate | Download | only in bootstrap
      1 #!/usr/bin/env python
      2 # Copyright 2014 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 """Bootstraps gn.
      7 
      8 It is done by first building it manually in a temporary directory, then building
      9 it with its own BUILD.gn to the final destination.
     10 """
     11 
     12 import contextlib
     13 import errno
     14 import logging
     15 import optparse
     16 import os
     17 import shutil
     18 import subprocess
     19 import sys
     20 import tempfile
     21 
     22 BOOTSTRAP_DIR = os.path.dirname(os.path.abspath(__file__))
     23 GN_ROOT = os.path.dirname(BOOTSTRAP_DIR)
     24 SRC_ROOT = os.path.dirname(os.path.dirname(GN_ROOT))
     25 
     26 is_linux = sys.platform.startswith('linux')
     27 is_mac = sys.platform.startswith('darwin')
     28 is_posix = is_linux or is_mac
     29 
     30 def check_call(cmd, **kwargs):
     31   logging.debug('Running: %s', ' '.join(cmd))
     32   subprocess.check_call(cmd, cwd=GN_ROOT, **kwargs)
     33 
     34 def mkdir_p(path):
     35   try:
     36     os.makedirs(path)
     37   except OSError as e:
     38     if e.errno == errno.EEXIST and os.path.isdir(path):
     39       pass
     40     else: raise
     41 
     42 @contextlib.contextmanager
     43 def scoped_tempdir():
     44   path = tempfile.mkdtemp()
     45   try:
     46     yield path
     47   finally:
     48     shutil.rmtree(path)
     49 
     50 
     51 def main(argv):
     52   parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
     53   parser.add_option('-d', '--debug', action='store_true',
     54                     help='Do a debug build. Defaults to release build.')
     55   parser.add_option('-o', '--output',
     56                     help='place output in PATH', metavar='PATH')
     57   parser.add_option('-s', '--no-rebuild', action='store_true',
     58                     help='Do not rebuild GN with GN.')
     59   parser.add_option('-v', '--verbose', action='store_true',
     60                     help='Log more details')
     61   options, args = parser.parse_args(argv)
     62 
     63   if args:
     64     parser.error('Unrecognized command line arguments: %s.' % ', '.join(args))
     65 
     66   logging.basicConfig(level=logging.DEBUG if options.verbose else logging.ERROR)
     67 
     68   if options.debug:
     69     build_rel = os.path.join('out', 'Debug')
     70   else:
     71     build_rel = os.path.join('out', 'Release')
     72   build_root = os.path.join(SRC_ROOT, build_rel)
     73 
     74   try:
     75     with scoped_tempdir() as tempdir:
     76       print 'Building gn manually in a temporary directory for bootstrapping...'
     77       build_gn_with_ninja_manually(tempdir, options)
     78       temp_gn = os.path.join(tempdir, 'gn')
     79       out_gn = os.path.join(build_root, 'gn')
     80 
     81       if options.no_rebuild:
     82         mkdir_p(build_root)
     83         shutil.copy2(temp_gn, out_gn)
     84       else:
     85         print 'Building gn using itself to %s...' % build_rel
     86         build_gn_with_gn(temp_gn, build_rel, options)
     87 
     88       if options.output:
     89         # Preserve the executable permission bit.
     90         shutil.copy2(out_gn, options.output)
     91   except subprocess.CalledProcessError as e:
     92     print >> sys.stderr, str(e)
     93     return 1
     94   return 0
     95 
     96 
     97 def build_gn_with_ninja_manually(tempdir, options):
     98   write_ninja(os.path.join(tempdir, 'build.ninja'), options)
     99   cmd = ['ninja', '-C', tempdir]
    100   if options.verbose:
    101     cmd.append('-v')
    102   cmd.append('gn')
    103   check_call(cmd)
    104 
    105 def write_ninja(path, options):
    106   cc = os.environ.get('CC', '')
    107   cxx = os.environ.get('CXX', '')
    108   cflags = os.environ.get('CFLAGS', '').split()
    109   cflags_cc = os.environ.get('CXXFLAGS', '').split()
    110   ld = os.environ.get('LD', cxx)
    111   ldflags = os.environ.get('LDFLAGS', '').split()
    112   include_dirs = [SRC_ROOT]
    113   libs = []
    114 
    115   if is_posix:
    116     if options.debug:
    117       cflags.extend(['-O0', '-g'])
    118     else:
    119       cflags.extend(['-O2', '-g0'])
    120 
    121     cflags.extend(['-D_FILE_OFFSET_BITS=64', '-pthread', '-pipe'])
    122     cflags_cc.extend(['-std=gnu++11', '-Wno-c++11-narrowing'])
    123 
    124   static_libraries = {
    125       'base': {'sources': [], 'tool': 'cxx'},
    126       'dynamic_annotations': {'sources': [], 'tool': 'cc'},
    127       'gn': {'sources': [], 'tool': 'cxx'},
    128   }
    129 
    130   for name in os.listdir(GN_ROOT):
    131     if not name.endswith('.cc'):
    132       continue
    133     if name.endswith('_unittest.cc'):
    134       continue
    135     if name in ['generate_test_gn_data.cc', 'run_all_unittests.cc']:
    136       continue
    137     full_path = os.path.join(GN_ROOT, name)
    138     static_libraries['gn']['sources'].append(
    139         os.path.relpath(full_path, SRC_ROOT))
    140 
    141   static_libraries['dynamic_annotations']['sources'].extend([
    142       'base/third_party/dynamic_annotations/dynamic_annotations.c',
    143   ])
    144   static_libraries['base']['sources'].extend([
    145       'base/at_exit.cc',
    146       'base/atomicops_internals_x86_gcc.cc',
    147       'base/base_paths.cc',
    148       'base/base_switches.cc',
    149       'base/callback_internal.cc',
    150       'base/command_line.cc',
    151       'base/debug/alias.cc',
    152       'base/debug/stack_trace.cc',
    153       'base/debug/task_annotator.cc',
    154       'base/debug/trace_event_impl.cc',
    155       'base/debug/trace_event_impl_constants.cc',
    156       'base/debug/trace_event_memory.cc',
    157       'base/debug/trace_event_synthetic_delay.cc',
    158       'base/environment.cc',
    159       'base/files/file.cc',
    160       'base/files/file_enumerator.cc',
    161       'base/files/file_path.cc',
    162       'base/files/file_path_constants.cc',
    163       'base/files/file_util.cc',
    164       'base/files/scoped_file.cc',
    165       'base/json/json_parser.cc',
    166       'base/json/json_reader.cc',
    167       'base/json/json_string_value_serializer.cc',
    168       'base/json/json_writer.cc',
    169       'base/json/string_escape.cc',
    170       'base/lazy_instance.cc',
    171       'base/location.cc',
    172       'base/logging.cc',
    173       'base/memory/ref_counted.cc',
    174       'base/memory/ref_counted_memory.cc',
    175       'base/memory/singleton.cc',
    176       'base/memory/weak_ptr.cc',
    177       'base/message_loop/incoming_task_queue.cc',
    178       'base/message_loop/message_loop.cc',
    179       'base/message_loop/message_loop_proxy.cc',
    180       'base/message_loop/message_loop_proxy_impl.cc',
    181       'base/message_loop/message_pump.cc',
    182       'base/message_loop/message_pump_default.cc',
    183       'base/metrics/bucket_ranges.cc',
    184       'base/metrics/histogram.cc',
    185       'base/metrics/histogram_base.cc',
    186       'base/metrics/histogram_samples.cc',
    187       'base/metrics/sample_map.cc',
    188       'base/metrics/sample_vector.cc',
    189       'base/metrics/sparse_histogram.cc',
    190       'base/metrics/statistics_recorder.cc',
    191       'base/path_service.cc',
    192       'base/pending_task.cc',
    193       'base/pickle.cc',
    194       'base/process/kill.cc',
    195       'base/process/process_iterator.cc',
    196       'base/process/process_metrics.cc',
    197       'base/profiler/alternate_timer.cc',
    198       'base/profiler/tracked_time.cc',
    199       'base/run_loop.cc',
    200       'base/sequence_checker_impl.cc',
    201       'base/sequenced_task_runner.cc',
    202       'base/strings/string16.cc',
    203       'base/strings/string_number_conversions.cc',
    204       'base/strings/string_piece.cc',
    205       'base/strings/string_split.cc',
    206       'base/strings/string_util.cc',
    207       'base/strings/string_util_constants.cc',
    208       'base/strings/stringprintf.cc',
    209       'base/strings/utf_string_conversion_utils.cc',
    210       'base/strings/utf_string_conversions.cc',
    211       'base/synchronization/cancellation_flag.cc',
    212       'base/synchronization/lock.cc',
    213       'base/sys_info.cc',
    214       'base/task_runner.cc',
    215       'base/third_party/dmg_fp/dtoa_wrapper.cc',
    216       'base/third_party/dmg_fp/g_fmt.cc',
    217       'base/third_party/icu/icu_utf.cc',
    218       'base/third_party/nspr/prtime.cc',
    219       'base/thread_task_runner_handle.cc',
    220       'base/threading/non_thread_safe_impl.cc',
    221       'base/threading/post_task_and_reply_impl.cc',
    222       'base/threading/sequenced_worker_pool.cc',
    223       'base/threading/simple_thread.cc',
    224       'base/threading/thread_checker_impl.cc',
    225       'base/threading/thread_collision_warner.cc',
    226       'base/threading/thread_id_name_manager.cc',
    227       'base/threading/thread_local_storage.cc',
    228       'base/threading/thread_restrictions.cc',
    229       'base/time/time.cc',
    230       'base/timer/elapsed_timer.cc',
    231       'base/timer/timer.cc',
    232       'base/tracked_objects.cc',
    233       'base/tracking_info.cc',
    234       'base/values.cc',
    235       'base/vlog.cc',
    236   ])
    237 
    238   if is_posix:
    239     static_libraries['base']['sources'].extend([
    240         'base/base_paths_posix.cc',
    241         'base/debug/debugger_posix.cc',
    242         'base/debug/stack_trace_posix.cc',
    243         'base/files/file_enumerator_posix.cc',
    244         'base/files/file_posix.cc',
    245         'base/files/file_util_posix.cc',
    246         'base/message_loop/message_pump_libevent.cc',
    247         'base/posix/file_descriptor_shuffle.cc',
    248         'base/process/kill_posix.cc',
    249         'base/process/process_handle_posix.cc',
    250         'base/process/process_metrics_posix.cc',
    251         'base/process/process_posix.cc',
    252         'base/safe_strerror_posix.cc',
    253         'base/synchronization/condition_variable_posix.cc',
    254         'base/synchronization/lock_impl_posix.cc',
    255         'base/synchronization/waitable_event_posix.cc',
    256         'base/sys_info_posix.cc',
    257         'base/threading/platform_thread_posix.cc',
    258         'base/threading/thread_local_posix.cc',
    259         'base/threading/thread_local_storage_posix.cc',
    260         'base/time/time_posix.cc',
    261     ])
    262     static_libraries['libevent'] = {
    263         'sources': [
    264             'third_party/libevent/buffer.c',
    265             'third_party/libevent/evbuffer.c',
    266             'third_party/libevent/evdns.c',
    267             'third_party/libevent/event.c',
    268             'third_party/libevent/event_tagging.c',
    269             'third_party/libevent/evrpc.c',
    270             'third_party/libevent/evutil.c',
    271             'third_party/libevent/http.c',
    272             'third_party/libevent/log.c',
    273             'third_party/libevent/poll.c',
    274             'third_party/libevent/select.c',
    275             'third_party/libevent/signal.c',
    276             'third_party/libevent/strlcpy.c',
    277         ],
    278         'tool': 'cc',
    279         'include_dirs': [],
    280         'cflags': cflags + ['-DHAVE_CONFIG_H'],
    281     }
    282 
    283 
    284   if is_linux:
    285     libs.extend(['-lrt'])
    286     ldflags.extend(['-pthread'])
    287 
    288     static_libraries['xdg_user_dirs'] = {
    289         'sources': [
    290             'base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
    291         ],
    292         'tool': 'cxx',
    293     }
    294     static_libraries['base']['sources'].extend([
    295         'base/nix/xdg_util.cc',
    296         'base/process/internal_linux.cc',
    297         'base/process/process_handle_linux.cc',
    298         'base/process/process_iterator_linux.cc',
    299         'base/process/process_linux.cc',
    300         'base/process/process_metrics_linux.cc',
    301         'base/strings/sys_string_conversions_posix.cc',
    302         'base/sys_info_linux.cc',
    303         'base/threading/platform_thread_linux.cc',
    304     ])
    305     static_libraries['libevent']['include_dirs'].extend([
    306         os.path.join(SRC_ROOT, 'third_party', 'libevent', 'linux')
    307     ])
    308     static_libraries['libevent']['sources'].extend([
    309         'third_party/libevent/epoll.c',
    310     ])
    311 
    312 
    313   if is_mac:
    314     static_libraries['base']['sources'].extend([
    315         'base/base_paths_mac.mm',
    316         'base/files/file_util_mac.mm',
    317         'base/mac/bundle_locations.mm',
    318         'base/mac/foundation_util.mm',
    319         'base/mac/mach_logging.cc',
    320         'base/mac/scoped_mach_port.cc',
    321         'base/mac/scoped_nsautorelease_pool.mm',
    322         'base/message_loop/message_pump_mac.mm',
    323         'base/process/process_handle_mac.cc',
    324         'base/process/process_iterator_mac.cc',
    325         'base/strings/sys_string_conversions_mac.mm',
    326         'base/time/time_mac.cc',
    327         'base/threading/platform_thread_mac.mm',
    328     ])
    329     static_libraries['libevent']['include_dirs'].extend([
    330         os.path.join(SRC_ROOT, 'third_party', 'libevent', 'mac')
    331     ])
    332     static_libraries['libevent']['sources'].extend([
    333         'third_party/libevent/kqueue.c',
    334     ])
    335 
    336 
    337   if is_mac:
    338     template_filename = 'build_mac.ninja.template'
    339   else:
    340     template_filename = 'build.ninja.template'
    341 
    342   with open(os.path.join(GN_ROOT, 'bootstrap', template_filename)) as f:
    343     ninja_template = f.read()
    344 
    345   def src_to_obj(path):
    346     return '%s' % os.path.splitext(path)[0] + '.o'
    347 
    348   ninja_lines = []
    349   for library, settings in static_libraries.iteritems():
    350     for src_file in settings['sources']:
    351       ninja_lines.extend([
    352           'build %s: %s %s' % (src_to_obj(src_file),
    353                                settings['tool'],
    354                                os.path.join(SRC_ROOT, src_file)),
    355           '  includes = %s' % ' '.join(
    356               ['-I' + dirname for dirname in
    357                include_dirs + settings.get('include_dirs', [])]),
    358           '  cflags = %s' % ' '.join(cflags + settings.get('cflags', [])),
    359           '  cflags_cc = %s' %
    360               ' '.join(cflags_cc + settings.get('cflags_cc', [])),
    361       ])
    362       if cc:
    363         ninja_lines.append('  cc = %s' % cc)
    364       if cxx:
    365         ninja_lines.append('  cxx = %s' % cxx)
    366 
    367     ninja_lines.append('build %s.a: alink_thin %s' % (
    368         library,
    369         ' '.join([src_to_obj(src_file) for src_file in settings['sources']])))
    370 
    371   if is_mac:
    372     libs.extend([
    373         '-framework', 'AppKit',
    374         '-framework', 'CoreFoundation',
    375         '-framework', 'Foundation',
    376         '-framework', 'Security',
    377     ]);
    378 
    379   ninja_lines.extend([
    380       'build gn: link %s' % (
    381           ' '.join(['%s.a' % library for library in static_libraries])),
    382       '  ldflags = %s' % ' '.join(ldflags),
    383       '  libs = %s' % ' '.join(libs),
    384   ])
    385   if ld:
    386     ninja_lines.append('  ld = %s' % ld)
    387   else:
    388     ninja_lines.append('  ld = $ldxx')
    389 
    390   ninja_lines.append('')  # Make sure the file ends with a newline.
    391 
    392   with open(path, 'w') as f:
    393     f.write(ninja_template + '\n'.join(ninja_lines))
    394 
    395 
    396 def build_gn_with_gn(temp_gn, build_dir, options):
    397   cmd = [temp_gn, 'gen', build_dir]
    398   if not options.debug:
    399     cmd.append('--args=is_debug=false')
    400   check_call(cmd)
    401 
    402   cmd = ['ninja', '-C', build_dir]
    403   if options.verbose:
    404     cmd.append('-v')
    405   cmd.append('gn')
    406   check_call(cmd)
    407 
    408   if not debug:
    409     check_call(['strip', os.path.join(build_dir, 'gn')])
    410 
    411 
    412 if __name__ == '__main__':
    413   sys.exit(main(sys.argv[1:]))
    414