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 logging
14import optparse
15import os
16import shutil
17import subprocess
18import sys
19import tempfile
20
21BOOTSTRAP_DIR = os.path.dirname(os.path.abspath(__file__))
22GN_ROOT = os.path.dirname(BOOTSTRAP_DIR)
23SRC_ROOT = os.path.dirname(os.path.dirname(GN_ROOT))
24
25
26def is_linux():
27  return sys.platform.startswith('linux')
28
29
30def check_call(cmd, **kwargs):
31  logging.debug('Running: %s', ' '.join(cmd))
32  subprocess.check_call(cmd, cwd=GN_ROOT, **kwargs)
33
34
35@contextlib.contextmanager
36def scoped_tempdir():
37  path = tempfile.mkdtemp()
38  try:
39    yield path
40  finally:
41    shutil.rmtree(path)
42
43
44def main(argv):
45  parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
46  parser.add_option('-d', '--debug', action='store_true',
47                    help='Do a debug build. Defaults to release build.')
48  parser.add_option('-o', '--output',
49                    help='place output in PATH', metavar='PATH')
50  parser.add_option('-v', '--verbose', action='store_true',
51                    help='Log more details')
52  options, args = parser.parse_args(argv)
53
54  if args:
55    parser.error('Unrecognized command line arguments: %s.' % ', '.join(args))
56
57  logging.basicConfig(level=logging.DEBUG if options.verbose else logging.ERROR)
58
59  if options.debug:
60    build_rel = os.path.join('out', 'Debug')
61  else:
62    build_rel = os.path.join('out', 'Release')
63  build_root = os.path.join(SRC_ROOT, build_rel)
64
65  try:
66    with scoped_tempdir() as tempdir:
67      print 'Building gn manually in a temporary directory for bootstrapping...'
68      build_gn_with_ninja_manually(tempdir)
69
70      print 'Building gn using itself to %s...' % build_rel
71      build_gn_with_gn(os.path.join(tempdir, 'gn'), build_rel, options.debug)
72
73      if options.output:
74        # Preserve the executable permission bit.
75        shutil.copy2(os.path.join(build_root, 'gn'), options.output)
76  except subprocess.CalledProcessError as e:
77    print >> sys.stderr, str(e)
78    return 1
79  return 0
80
81
82def build_gn_with_ninja_manually(tempdir):
83  write_ninja(os.path.join(tempdir, 'build.ninja'))
84  check_call(['ninja', '-C', tempdir, 'gn'])
85
86
87def write_ninja(path):
88  cflags = os.environ.get('CFLAGS', '').split()
89  ldflags = os.environ.get('LDFLAGS', '').split()
90  include_dirs = [SRC_ROOT]
91  libs = []
92
93  static_libraries = {
94      'base': {'sources': [], 'tool': 'cxx'},
95      'dynamic_annotations': {'sources': [], 'tool': 'cc'},
96      'gn': {'sources': [], 'tool': 'cxx'},
97  }
98
99  for name in os.listdir(GN_ROOT):
100    if not name.endswith('.cc'):
101      continue
102    if name.endswith('_unittest.cc'):
103      continue
104    if name in ['generate_test_gn_data.cc']:
105      continue
106    full_path = os.path.join(GN_ROOT, name)
107    static_libraries['gn']['sources'].append(
108        os.path.relpath(full_path, SRC_ROOT))
109
110  static_libraries['dynamic_annotations']['sources'].extend([
111      'base/third_party/dynamic_annotations/dynamic_annotations.c',
112  ])
113  static_libraries['base']['sources'].extend([
114      'base/at_exit.cc',
115      'base/atomicops_internals_x86_gcc.cc',
116      'base/base_paths.cc',
117      'base/base_switches.cc',
118      'base/callback_internal.cc',
119      'base/command_line.cc',
120      'base/debug/alias.cc',
121      'base/debug/stack_trace.cc',
122      'base/debug/trace_event_impl.cc',
123      'base/debug/trace_event_impl_constants.cc',
124      'base/debug/trace_event_memory.cc',
125      'base/debug/trace_event_synthetic_delay.cc',
126      'base/environment.cc',
127      'base/file_util.cc',
128      'base/files/file.cc',
129      'base/files/file_enumerator.cc',
130      'base/files/file_path.cc',
131      'base/files/file_path_constants.cc',
132      'base/files/scoped_file.cc',
133      'base/json/json_parser.cc',
134      'base/json/json_reader.cc',
135      'base/json/json_string_value_serializer.cc',
136      'base/json/json_writer.cc',
137      'base/json/string_escape.cc',
138      'base/lazy_instance.cc',
139      'base/location.cc',
140      'base/logging.cc',
141      'base/memory/ref_counted.cc',
142      'base/memory/ref_counted_memory.cc',
143      'base/memory/singleton.cc',
144      'base/memory/weak_ptr.cc',
145      'base/message_loop/incoming_task_queue.cc',
146      'base/message_loop/message_loop.cc',
147      'base/message_loop/message_loop_proxy.cc',
148      'base/message_loop/message_loop_proxy_impl.cc',
149      'base/message_loop/message_pump.cc',
150      'base/message_loop/message_pump_default.cc',
151      'base/metrics/bucket_ranges.cc',
152      'base/metrics/histogram.cc',
153      'base/metrics/histogram_base.cc',
154      'base/metrics/histogram_samples.cc',
155      'base/metrics/sample_map.cc',
156      'base/metrics/sample_vector.cc',
157      'base/metrics/sparse_histogram.cc',
158      'base/metrics/statistics_recorder.cc',
159      'base/path_service.cc',
160      'base/pending_task.cc',
161      'base/pickle.cc',
162      'base/process/kill.cc',
163      'base/process/process_iterator.cc',
164      'base/process/process_metrics.cc',
165      'base/profiler/alternate_timer.cc',
166      'base/profiler/tracked_time.cc',
167      'base/run_loop.cc',
168      'base/sequence_checker_impl.cc',
169      'base/sequenced_task_runner.cc',
170      'base/strings/string16.cc',
171      'base/strings/string_number_conversions.cc',
172      'base/strings/string_piece.cc',
173      'base/strings/string_split.cc',
174      'base/strings/string_util.cc',
175      'base/strings/string_util_constants.cc',
176      'base/strings/stringprintf.cc',
177      'base/strings/utf_string_conversion_utils.cc',
178      'base/strings/utf_string_conversions.cc',
179      'base/synchronization/cancellation_flag.cc',
180      'base/synchronization/lock.cc',
181      'base/sys_info.cc',
182      'base/task_runner.cc',
183      'base/third_party/dmg_fp/dtoa_wrapper.cc',
184      'base/third_party/dmg_fp/g_fmt.cc',
185      'base/third_party/icu/icu_utf.cc',
186      'base/third_party/nspr/prtime.cc',
187      'base/thread_task_runner_handle.cc',
188      'base/threading/non_thread_safe_impl.cc',
189      'base/threading/post_task_and_reply_impl.cc',
190      'base/threading/sequenced_worker_pool.cc',
191      'base/threading/simple_thread.cc',
192      'base/threading/thread_checker_impl.cc',
193      'base/threading/thread_collision_warner.cc',
194      'base/threading/thread_id_name_manager.cc',
195      'base/threading/thread_local_storage.cc',
196      'base/threading/thread_restrictions.cc',
197      'base/time/time.cc',
198      'base/timer/elapsed_timer.cc',
199      'base/timer/timer.cc',
200      'base/tracked_objects.cc',
201      'base/tracking_info.cc',
202      'base/values.cc',
203      'base/vlog.cc',
204  ])
205
206  if is_linux():
207    static_libraries['libevent'] = {
208        'sources': [
209            'third_party/libevent/buffer.c',
210            'third_party/libevent/epoll.c',
211            'third_party/libevent/evbuffer.c',
212            'third_party/libevent/evdns.c',
213            'third_party/libevent/event.c',
214            'third_party/libevent/event_tagging.c',
215            'third_party/libevent/evrpc.c',
216            'third_party/libevent/evutil.c',
217            'third_party/libevent/http.c',
218            'third_party/libevent/log.c',
219            'third_party/libevent/poll.c',
220            'third_party/libevent/select.c',
221            'third_party/libevent/signal.c',
222            'third_party/libevent/strlcpy.c',
223        ],
224        'tool': 'cc',
225        'include_dirs': [
226            os.path.join(SRC_ROOT, 'third_party', 'libevent', 'linux')
227         ],
228        'cflags': cflags + ['-DHAVE_CONFIG_H'],
229    }
230    static_libraries['xdg_user_dirs'] = {
231        'sources': [
232            'base/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
233        ],
234        'tool': 'cxx',
235    }
236    static_libraries['base']['sources'].extend([
237        'base/base_paths_posix.cc',
238        'base/debug/debugger_posix.cc',
239        'base/debug/stack_trace_posix.cc',
240        'base/file_util_posix.cc',
241        'base/files/file_enumerator_posix.cc',
242        'base/files/file_posix.cc',
243        'base/message_loop/message_pump_glib.cc',
244        'base/message_loop/message_pump_libevent.cc',
245        'base/nix/xdg_util.cc',
246        'base/posix/file_descriptor_shuffle.cc',
247        'base/process/internal_linux.cc',
248        'base/process/kill_posix.cc',
249        'base/process/process_handle_linux.cc',
250        'base/process/process_handle_posix.cc',
251        'base/process/process_iterator_linux.cc',
252        'base/process/process_linux.cc',
253        'base/process/process_metrics_linux.cc',
254        'base/process/process_metrics_posix.cc',
255        'base/process/process_posix.cc',
256        'base/safe_strerror_posix.cc',
257        'base/strings/sys_string_conversions_posix.cc',
258        'base/synchronization/condition_variable_posix.cc',
259        'base/synchronization/lock_impl_posix.cc',
260        'base/synchronization/waitable_event_posix.cc',
261        'base/sys_info_linux.cc',
262        'base/sys_info_posix.cc',
263        'base/threading/platform_thread_linux.cc',
264        'base/threading/platform_thread_posix.cc',
265        'base/threading/thread_local_posix.cc',
266        'base/threading/thread_local_storage_posix.cc',
267        'base/time/time_posix.cc',
268    ])
269
270    cflags.extend(['-O2', '-pthread', '-pipe'])
271
272    static_libraries['base'].setdefault('cflags', []).extend(
273        subprocess.check_output(
274            ['pkg-config', 'gtk+-2.0', 'x11', '--cflags']).split())
275    ldflags.extend(['-pthread'])
276    ldflags.extend(subprocess.check_output(
277        ['pkg-config', 'gtk+-2.0', 'x11',
278         '--libs-only-L', '--libs-only-other']).split())
279    libs.extend(subprocess.check_output(
280        ['pkg-config', 'gtk+-2.0', 'x11', '--libs-only-l']).split())
281
282  with open(os.path.join(GN_ROOT, 'bootstrap', 'build.ninja.template')) as f:
283    ninja_template = f.read()
284
285  def src_to_obj(path):
286    return '%s' % os.path.splitext(path)[0] + '.o'
287
288  ninja_lines = []
289  for library, settings in static_libraries.iteritems():
290    for src_file in settings['sources']:
291      ninja_lines.extend([
292          'build %s: %s %s' % (src_to_obj(src_file),
293                               settings['tool'],
294                               os.path.join(SRC_ROOT, src_file)),
295          '  includes = %s' % ' '.join(
296              ['-I' + dirname for dirname in
297               include_dirs + settings.get('include_dirs', [])]),
298          '  cflags = %s' % ' '.join(cflags + settings.get('cflags', [])),
299      ])
300
301    ninja_lines.append('build %s.a: alink_thin %s' % (
302        library,
303        ' '.join([src_to_obj(src_file) for src_file in settings['sources']])))
304
305  ninja_lines.extend([
306      'build gn: link %s' % (
307          ' '.join(['%s.a' % library for library in static_libraries])),
308      '  ld = $ldxx',
309      '  ldflags = %s' % ' '.join(ldflags),
310      '  libs = %s' % ' '.join(libs),
311      '',  # Make sure the file ends with a newline.
312  ])
313
314  with open(path, 'w') as f:
315    f.write(ninja_template + '\n'.join(ninja_lines))
316
317
318def build_gn_with_gn(temp_gn, build_dir, debug):
319  cmd = [temp_gn, 'gen', build_dir]
320  if not debug:
321    cmd.append('--args=is_debug=false')
322  check_call(cmd)
323  check_call(['ninja', '-C', build_dir, 'gn'])
324  if not debug:
325    check_call(['strip', os.path.join(build_dir, 'gn')])
326
327
328if __name__ == '__main__':
329  sys.exit(main(sys.argv[1:]))
330