| #!/usr/bin/env python |
| # Copyright 2014 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. |
| |
| """Bootstraps gn. |
| |
| It is done by first building it manually in a temporary directory, then building |
| it with its own BUILD.gn to the final destination. |
| """ |
| |
| import contextlib |
| import errno |
| import logging |
| import optparse |
| import os |
| import shutil |
| import subprocess |
| import sys |
| import tempfile |
| |
| BOOTSTRAP_DIR = os.path.dirname(os.path.abspath(__file__)) |
| GN_ROOT = os.path.dirname(BOOTSTRAP_DIR) |
| SRC_ROOT = os.path.dirname(os.path.dirname(GN_ROOT)) |
| |
| is_linux = sys.platform.startswith('linux') |
| is_mac = sys.platform.startswith('darwin') |
| is_posix = is_linux or is_mac |
| |
| def check_call(cmd, **kwargs): |
| logging.debug('Running: %s', ' '.join(cmd)) |
| subprocess.check_call(cmd, cwd=GN_ROOT, **kwargs) |
| |
| def mkdir_p(path): |
| try: |
| os.makedirs(path) |
| except OSError as e: |
| if e.errno == errno.EEXIST and os.path.isdir(path): |
| pass |
| else: raise |
| |
| @contextlib.contextmanager |
| def scoped_tempdir(): |
| path = tempfile.mkdtemp() |
| try: |
| yield path |
| finally: |
| shutil.rmtree(path) |
| |
| |
| def run_build(tempdir, options): |
| if options.debug: |
| build_rel = os.path.join('out', 'Debug') |
| else: |
| build_rel = os.path.join('out', 'Release') |
| build_root = os.path.join(SRC_ROOT, build_rel) |
| |
| print 'Building gn manually in a temporary directory for bootstrapping...' |
| build_gn_with_ninja_manually(tempdir, options) |
| temp_gn = os.path.join(tempdir, 'gn') |
| out_gn = os.path.join(build_root, 'gn') |
| |
| if options.no_rebuild: |
| mkdir_p(build_root) |
| shutil.copy2(temp_gn, out_gn) |
| else: |
| print 'Building gn using itself to %s...' % build_rel |
| build_gn_with_gn(temp_gn, build_rel, options) |
| |
| if options.output: |
| # Preserve the executable permission bit. |
| shutil.copy2(out_gn, options.output) |
| |
| |
| def main(argv): |
| parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
| parser.add_option('-d', '--debug', action='store_true', |
| help='Do a debug build. Defaults to release build.') |
| parser.add_option('-o', '--output', |
| help='place output in PATH', metavar='PATH') |
| parser.add_option('-s', '--no-rebuild', action='store_true', |
| help='Do not rebuild GN with GN.') |
| parser.add_option('--no-clean', action='store_true', |
| help='Re-used build directory instead of using new ' |
| 'temporary location each time') |
| parser.add_option('-v', '--verbose', action='store_true', |
| help='Log more details') |
| options, args = parser.parse_args(argv) |
| |
| if args: |
| parser.error('Unrecognized command line arguments: %s.' % ', '.join(args)) |
| |
| logging.basicConfig(level=logging.DEBUG if options.verbose else logging.ERROR) |
| |
| try: |
| if options.no_clean: |
| build_dir = os.path.join(SRC_ROOT, 'out_bootstrap') |
| if not os.path.exists(build_dir): |
| os.makedirs(build_dir) |
| return run_build(build_dir, options) |
| else: |
| with scoped_tempdir() as tempdir: |
| return run_build(tempdir, options) |
| except subprocess.CalledProcessError as e: |
| print >> sys.stderr, str(e) |
| return 1 |
| return 0 |
| |
| |
| def build_gn_with_ninja_manually(tempdir, options): |
| write_ninja(os.path.join(tempdir, 'build.ninja'), options) |
| cmd = ['ninja', '-C', tempdir] |
| if options.verbose: |
| cmd.append('-v') |
| cmd.append('gn') |
| check_call(cmd) |
| |
| def write_ninja(path, options): |
| cc = os.environ.get('CC', '') |
| cxx = os.environ.get('CXX', '') |
| cflags = os.environ.get('CFLAGS', '').split() |
| cflags_cc = os.environ.get('CXXFLAGS', '').split() |
| ld = os.environ.get('LD', cxx) |
| ldflags = os.environ.get('LDFLAGS', '').split() |
| include_dirs = [SRC_ROOT] |
| libs = [] |
| |
| if is_posix: |
| if options.debug: |
| cflags.extend(['-O0', '-g']) |
| else: |
| cflags.extend(['-O2', '-g0']) |
| |
| cflags.extend(['-D_FILE_OFFSET_BITS=64', '-pthread', '-pipe']) |
| cflags_cc.extend(['-std=c++11', '-Wno-c++11-narrowing']) |
| |
| static_libraries = { |
| 'base': {'sources': [], 'tool': 'cxx'}, |
| 'dynamic_annotations': {'sources': [], 'tool': 'cc'}, |
| 'gn': {'sources': [], 'tool': 'cxx'}, |
| } |
| |
| for name in os.listdir(GN_ROOT): |
| if not name.endswith('.cc'): |
| continue |
| if name.endswith('_unittest.cc'): |
| continue |
| if name in ['generate_test_gn_data.cc', 'run_all_unittests.cc']: |
| continue |
| full_path = os.path.join(GN_ROOT, name) |
| static_libraries['gn']['sources'].append( |
| os.path.relpath(full_path, SRC_ROOT)) |
| |
| static_libraries['dynamic_annotations']['sources'].extend([ |
| 'base/third_party/dynamic_annotations/dynamic_annotations.c', |
| 'base/third_party/superfasthash/superfasthash.c', |
| ]) |
| static_libraries['base']['sources'].extend([ |
| 'base/at_exit.cc', |
| 'base/base_paths.cc', |
| 'base/base_switches.cc', |
| 'base/callback_internal.cc', |
| 'base/command_line.cc', |
| 'base/debug/alias.cc', |
| 'base/debug/stack_trace.cc', |
| 'base/debug/task_annotator.cc', |
| 'base/environment.cc', |
| 'base/files/file.cc', |
| 'base/files/file_enumerator.cc', |
| 'base/files/file_path.cc', |
| 'base/files/file_path_constants.cc', |
| 'base/files/file_tracing.cc', |
| 'base/files/file_util.cc', |
| 'base/files/scoped_file.cc', |
| 'base/hash.cc', |
| 'base/json/json_parser.cc', |
| 'base/json/json_reader.cc', |
| 'base/json/json_string_value_serializer.cc', |
| 'base/json/json_writer.cc', |
| 'base/json/string_escape.cc', |
| 'base/lazy_instance.cc', |
| 'base/location.cc', |
| 'base/logging.cc', |
| 'base/memory/ref_counted.cc', |
| 'base/memory/ref_counted_memory.cc', |
| 'base/memory/singleton.cc', |
| 'base/memory/weak_ptr.cc', |
| 'base/message_loop/incoming_task_queue.cc', |
| 'base/message_loop/message_loop.cc', |
| 'base/message_loop/message_loop_task_runner.cc', |
| 'base/message_loop/message_pump.cc', |
| 'base/message_loop/message_pump_default.cc', |
| 'base/metrics/bucket_ranges.cc', |
| 'base/metrics/histogram.cc', |
| 'base/metrics/histogram_base.cc', |
| 'base/metrics/histogram_samples.cc', |
| 'base/metrics/sample_map.cc', |
| 'base/metrics/sample_vector.cc', |
| 'base/metrics/sparse_histogram.cc', |
| 'base/metrics/statistics_recorder.cc', |
| 'base/path_service.cc', |
| 'base/pending_task.cc', |
| 'base/pickle.cc', |
| 'base/process/kill.cc', |
| 'base/process/process_iterator.cc', |
| 'base/process/process_metrics.cc', |
| 'base/profiler/alternate_timer.cc', |
| 'base/profiler/tracked_time.cc', |
| 'base/run_loop.cc', |
| 'base/sequence_checker_impl.cc', |
| 'base/sequenced_task_runner.cc', |
| 'base/sha1_portable.cc', |
| 'base/strings/pattern.cc', |
| 'base/strings/string16.cc', |
| 'base/strings/string_number_conversions.cc', |
| 'base/strings/string_piece.cc', |
| 'base/strings/string_split.cc', |
| 'base/strings/string_util.cc', |
| 'base/strings/string_util_constants.cc', |
| 'base/strings/stringprintf.cc', |
| 'base/strings/utf_string_conversion_utils.cc', |
| 'base/strings/utf_string_conversions.cc', |
| 'base/synchronization/cancellation_flag.cc', |
| 'base/synchronization/lock.cc', |
| 'base/sys_info.cc', |
| 'base/task_runner.cc', |
| 'base/third_party/dmg_fp/dtoa_wrapper.cc', |
| 'base/third_party/dmg_fp/g_fmt.cc', |
| 'base/third_party/icu/icu_utf.cc', |
| 'base/third_party/nspr/prtime.cc', |
| 'base/thread_task_runner_handle.cc', |
| 'base/threading/non_thread_safe_impl.cc', |
| 'base/threading/post_task_and_reply_impl.cc', |
| 'base/threading/sequenced_worker_pool.cc', |
| 'base/threading/simple_thread.cc', |
| 'base/threading/thread_checker_impl.cc', |
| 'base/threading/thread_collision_warner.cc', |
| 'base/threading/thread_id_name_manager.cc', |
| 'base/threading/thread_local_storage.cc', |
| 'base/threading/thread_restrictions.cc', |
| 'base/threading/worker_pool.cc', |
| 'base/time/time.cc', |
| 'base/timer/elapsed_timer.cc', |
| 'base/timer/timer.cc', |
| 'base/trace_event/memory_allocator_dump.cc', |
| 'base/trace_event/memory_allocator_dump_guid.cc', |
| 'base/trace_event/memory_dump_manager.cc', |
| 'base/trace_event/memory_dump_request_args.cc', |
| 'base/trace_event/memory_dump_session_state.cc', |
| 'base/trace_event/process_memory_dump.cc', |
| 'base/trace_event/process_memory_maps.cc', |
| 'base/trace_event/process_memory_maps_dump_provider.cc', |
| 'base/trace_event/process_memory_totals.cc', |
| 'base/trace_event/process_memory_totals_dump_provider.cc', |
| 'base/trace_event/trace_buffer.cc', |
| 'base/trace_event/trace_config.cc', |
| 'base/trace_event/trace_event_argument.cc', |
| 'base/trace_event/trace_event_impl.cc', |
| 'base/trace_event/trace_event_memory.cc', |
| 'base/trace_event/trace_event_memory_overhead.cc', |
| 'base/trace_event/trace_event_synthetic_delay.cc', |
| 'base/trace_event/trace_log.cc', |
| 'base/trace_event/trace_log_constants.cc', |
| 'base/trace_event/trace_sampling_thread.cc', |
| 'base/tracked_objects.cc', |
| 'base/tracking_info.cc', |
| 'base/values.cc', |
| 'base/vlog.cc', |
| ]) |
| |
| if is_posix: |
| static_libraries['base']['sources'].extend([ |
| 'base/base_paths_posix.cc', |
| 'base/debug/debugger_posix.cc', |
| 'base/debug/stack_trace_posix.cc', |
| 'base/files/file_enumerator_posix.cc', |
| 'base/files/file_posix.cc', |
| 'base/files/file_util_posix.cc', |
| 'base/message_loop/message_pump_libevent.cc', |
| 'base/posix/file_descriptor_shuffle.cc', |
| 'base/posix/safe_strerror.cc', |
| 'base/process/kill_posix.cc', |
| 'base/process/process_handle_posix.cc', |
| 'base/process/process_metrics_posix.cc', |
| 'base/process/process_posix.cc', |
| 'base/synchronization/condition_variable_posix.cc', |
| 'base/synchronization/lock_impl_posix.cc', |
| 'base/synchronization/waitable_event_posix.cc', |
| 'base/sys_info_posix.cc', |
| 'base/threading/platform_thread_internal_posix.cc', |
| 'base/threading/platform_thread_posix.cc', |
| 'base/threading/thread_local_posix.cc', |
| 'base/threading/thread_local_storage_posix.cc', |
| 'base/threading/worker_pool_posix.cc', |
| 'base/time/time_posix.cc', |
| ]) |
| static_libraries['libevent'] = { |
| 'sources': [ |
| 'third_party/libevent/buffer.c', |
| 'third_party/libevent/evbuffer.c', |
| 'third_party/libevent/evdns.c', |
| 'third_party/libevent/event.c', |
| 'third_party/libevent/event_tagging.c', |
| 'third_party/libevent/evrpc.c', |
| 'third_party/libevent/evutil.c', |
| 'third_party/libevent/http.c', |
| 'third_party/libevent/log.c', |
| 'third_party/libevent/poll.c', |
| 'third_party/libevent/select.c', |
| 'third_party/libevent/signal.c', |
| 'third_party/libevent/strlcpy.c', |
| ], |
| 'tool': 'cc', |
| 'include_dirs': [], |
| 'cflags': cflags + ['-DHAVE_CONFIG_H'], |
| } |
| |
| |
| if is_linux: |
| libs.extend(['-lrt']) |
| ldflags.extend(['-pthread']) |
| |
| static_libraries['xdg_user_dirs'] = { |
| 'sources': [ |
| 'base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc', |
| ], |
| 'tool': 'cxx', |
| } |
| static_libraries['base']['sources'].extend([ |
| 'base/nix/xdg_util.cc', |
| 'base/process/internal_linux.cc', |
| 'base/process/process_handle_linux.cc', |
| 'base/process/process_iterator_linux.cc', |
| 'base/process/process_linux.cc', |
| 'base/process/process_metrics_linux.cc', |
| 'base/strings/sys_string_conversions_posix.cc', |
| 'base/sys_info_linux.cc', |
| 'base/threading/platform_thread_linux.cc', |
| 'base/trace_event/malloc_dump_provider.cc', |
| ]) |
| static_libraries['libevent']['include_dirs'].extend([ |
| os.path.join(SRC_ROOT, 'third_party', 'libevent', 'linux') |
| ]) |
| static_libraries['libevent']['sources'].extend([ |
| 'third_party/libevent/epoll.c', |
| ]) |
| |
| |
| if is_mac: |
| static_libraries['base']['sources'].extend([ |
| 'base/base_paths_mac.mm', |
| 'base/files/file_util_mac.mm', |
| 'base/mac/bundle_locations.mm', |
| 'base/mac/call_with_eh_frame.cc', |
| 'base/mac/call_with_eh_frame_asm.S', |
| 'base/mac/foundation_util.mm', |
| 'base/mac/mach_logging.cc', |
| 'base/mac/scoped_mach_port.cc', |
| 'base/mac/scoped_nsautorelease_pool.mm', |
| 'base/message_loop/message_pump_mac.mm', |
| 'base/process/process_handle_mac.cc', |
| 'base/process/process_iterator_mac.cc', |
| 'base/process/process_metrics_mac.cc', |
| 'base/strings/sys_string_conversions_mac.mm', |
| 'base/time/time_mac.cc', |
| 'base/threading/platform_thread_mac.mm', |
| ]) |
| static_libraries['libevent']['include_dirs'].extend([ |
| os.path.join(SRC_ROOT, 'third_party', 'libevent', 'mac') |
| ]) |
| static_libraries['libevent']['sources'].extend([ |
| 'third_party/libevent/kqueue.c', |
| ]) |
| |
| |
| if is_mac: |
| template_filename = 'build_mac.ninja.template' |
| else: |
| template_filename = 'build.ninja.template' |
| |
| with open(os.path.join(GN_ROOT, 'bootstrap', template_filename)) as f: |
| ninja_template = f.read() |
| |
| def src_to_obj(path): |
| return '%s' % os.path.splitext(path)[0] + '.o' |
| |
| ninja_lines = [] |
| for library, settings in static_libraries.iteritems(): |
| for src_file in settings['sources']: |
| ninja_lines.extend([ |
| 'build %s: %s %s' % (src_to_obj(src_file), |
| settings['tool'], |
| os.path.join(SRC_ROOT, src_file)), |
| ' includes = %s' % ' '.join( |
| ['-I' + dirname for dirname in |
| include_dirs + settings.get('include_dirs', [])]), |
| ' cflags = %s' % ' '.join(cflags + settings.get('cflags', [])), |
| ' cflags_cc = %s' % |
| ' '.join(cflags_cc + settings.get('cflags_cc', [])), |
| ]) |
| if cc: |
| ninja_lines.append(' cc = %s' % cc) |
| if cxx: |
| ninja_lines.append(' cxx = %s' % cxx) |
| |
| ninja_lines.append('build %s.a: alink_thin %s' % ( |
| library, |
| ' '.join([src_to_obj(src_file) for src_file in settings['sources']]))) |
| |
| if is_mac: |
| libs.extend([ |
| '-framework', 'AppKit', |
| '-framework', 'CoreFoundation', |
| '-framework', 'Foundation', |
| '-framework', 'Security', |
| ]); |
| |
| ninja_lines.extend([ |
| 'build gn: link %s' % ( |
| ' '.join(['%s.a' % library for library in static_libraries])), |
| ' ldflags = %s' % ' '.join(ldflags), |
| ' libs = %s' % ' '.join(libs), |
| ]) |
| if ld: |
| ninja_lines.append(' ld = %s' % ld) |
| else: |
| ninja_lines.append(' ld = $ldxx') |
| |
| ninja_lines.append('') # Make sure the file ends with a newline. |
| |
| with open(path, 'w') as f: |
| f.write(ninja_template + '\n'.join(ninja_lines)) |
| |
| |
| def build_gn_with_gn(temp_gn, build_dir, options): |
| cmd = [temp_gn, 'gen', build_dir] |
| if not options.debug: |
| cmd.append('--args=is_debug=false') |
| check_call(cmd) |
| |
| cmd = ['ninja', '-C', build_dir] |
| if options.verbose: |
| cmd.append('-v') |
| cmd.append('gn') |
| check_call(cmd) |
| |
| if not options.debug: |
| check_call(['strip', os.path.join(build_dir, 'gn')]) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |