ninja.py revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1# Copyright 2013 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""A module to add ninja support to cr."""
6
7import os
8
9import cr
10
11_PHONY_SUFFIX = ': phony'
12_LINK_SUFFIX = ': link'
13
14
15class NinjaBuilder(cr.Builder):
16  """An implementation of Builder that uses ninja to do the actual build."""
17
18  # Some basic configuration installed if we are enabled.
19  ENABLED = cr.Config.From(
20      NINJA_BINARY=os.path.join('{DEPOT_TOOLS}', 'ninja'),
21      NINJA_JOBS=200,
22      NINJA_PROCESSORS=12,
23      NINJA_BUILD_FILE=os.path.join('{CR_BUILD_DIR}', 'build.ninja'),
24      GOMA_DIR=os.path.join('{GOOGLE_CODE}', 'goma'),
25      # Don't rename to GOMA_* or Goma will complain: "unkown GOMA_ parameter".
26      NINJA_GOMA_CTL=os.path.join('{GOMA_DIR}', 'goma_ctl.py'),
27      NINJA_GOMA_CC=os.path.join('{GOMA_DIR}', 'gomacc'),
28      NINJA_GOMA_LINE='cc = {NINJA_GOMA_CC} $',
29  )
30  # A placeholder for the system detected configuration
31  DETECTED = cr.Config('DETECTED')
32
33  def __init__(self):
34    super(NinjaBuilder, self).__init__()
35    self._targets = []
36
37  def Build(self, context, targets, arguments):
38    # Make sure Goma is started if Ninja is set to use it.
39    # This may be redundant, but it currently improves reliability.
40    try:
41      with open(context.Get('NINJA_BUILD_FILE'), 'r') as f:
42        if f.readline().rstrip('\n') == context.Get('NINJA_GOMA_LINE'):
43          # Goma is active, so make sure it's started.
44          cr.Host.ExecuteSilently(
45              context,
46              '{NINJA_GOMA_CTL}',
47              'ensure_start'
48          )
49    except IOError:
50      pass
51
52    build_arguments = [target.build_target for target in targets]
53    build_arguments.extend(arguments)
54    cr.Host.Execute(
55        context,
56        '{NINJA_BINARY}',
57        '-C{CR_BUILD_DIR}',
58        '-j{NINJA_JOBS}',
59        '-l{NINJA_PROCESSORS}',
60        *build_arguments
61    )
62
63  def Clean(self, context, targets, arguments):
64    build_arguments = [target.build_target for target in targets]
65    build_arguments.extend(arguments)
66    cr.Host.Execute(
67        context,
68        '{NINJA_BINARY}',
69        '-C{CR_BUILD_DIR}',
70        '-tclean',
71        *build_arguments
72    )
73
74  def GetTargets(self, context):
75    """Overridden from Builder.GetTargets."""
76    if not self._targets:
77      try:
78        context.Get('CR_BUILD_DIR', raise_errors=True)
79      except KeyError:
80        return self._targets
81      output = cr.Host.Capture(
82          context,
83          '{NINJA_BINARY}',
84          '-C{CR_BUILD_DIR}',
85          '-ttargets',
86          'all'
87      )
88      for line in output.split('\n'):
89        line = line.strip()
90        if line.endswith(_PHONY_SUFFIX):
91          target = line[:-len(_PHONY_SUFFIX)].strip()
92          self._targets.append(target)
93        elif line.endswith(_LINK_SUFFIX):
94          target = line[:-len(_LINK_SUFFIX)].strip()
95          self._targets.append(target)
96    return self._targets
97
98  @classmethod
99  def ClassInit(cls):
100    # TODO(iancottrell): If we can't detect ninja, we should be disabled.
101    ninja_binaries = cr.Host.SearchPath('ninja')
102    if ninja_binaries:
103      cls.DETECTED.Set(NINJA_BINARY=ninja_binaries[0])
104