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 8It is done by first building it manually in a temporary directory, then building 9it with its own BUILD.gn to the final destination. 10""" 11 12import contextlib 13import errno 14import logging 15import optparse 16import os 17import shutil 18import subprocess 19import sys 20import tempfile 21 22BOOTSTRAP_DIR = os.path.dirname(os.path.abspath(__file__)) 23GN_ROOT = os.path.dirname(BOOTSTRAP_DIR) 24SRC_ROOT = os.path.dirname(os.path.dirname(GN_ROOT)) 25 26is_linux = sys.platform.startswith('linux') 27is_mac = sys.platform.startswith('darwin') 28is_posix = is_linux or is_mac 29 30def check_call(cmd, **kwargs): 31 logging.debug('Running: %s', ' '.join(cmd)) 32 subprocess.check_call(cmd, cwd=GN_ROOT, **kwargs) 33 34def 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 43def scoped_tempdir(): 44 path = tempfile.mkdtemp() 45 try: 46 yield path 47 finally: 48 shutil.rmtree(path) 49 50 51def 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 97def 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 105def 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 396def 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 412if __name__ == '__main__': 413 sys.exit(main(sys.argv[1:])) 414