Home | History | Annotate | Download | only in actions
      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 
      7 import multiprocessing
      8 import os
      9 
     10 import cr
     11 
     12 _PHONY_SUFFIX = ': phony'
     13 _LINK_SUFFIX = ': link'
     14 
     15 
     16 DEFAULT = cr.Config.From(
     17     GOMA_DIR=os.path.expanduser('~/goma'),
     18 )
     19 
     20 class NinjaBuilder(cr.Builder):
     21   """An implementation of Builder that uses ninja to do the actual build."""
     22 
     23   # Some basic configuration installed if we are enabled.
     24   EXTRA_FOR_IO_BOUND_JOBS = 2
     25   ENABLED = cr.Config.From(
     26       NINJA_BINARY=os.path.join('{DEPOT_TOOLS}', 'ninja'),
     27       NINJA_JOBS=multiprocessing.cpu_count() + EXTRA_FOR_IO_BOUND_JOBS,
     28       NINJA_PROCESSORS=multiprocessing.cpu_count(),
     29       NINJA_BUILD_FILE=os.path.join('{CR_BUILD_DIR}', 'build.ninja'),
     30       # Don't rename to GOMA_* or Goma will complain: "unkown GOMA_ parameter".
     31       NINJA_GOMA_LINE='cc = {CR_GOMA_CC} $',
     32   )
     33   # A config block only included if goma is detected.
     34   GOMA = cr.Config.From(
     35       CR_GOMA_CC=os.path.join('{GOMA_DIR}', 'gomacc'),
     36       CR_GOMA_CTL=os.path.join('{GOMA_DIR}', 'goma_ctl.py'),
     37       GOMA_DIR='{CR_GOMA_DIR}',
     38       GYP_DEF_gomadir='{CR_GOMA_DIR}',
     39       GYP_DEF_use_goma=1,
     40       NINJA_JOBS=multiprocessing.cpu_count() * 10,
     41   )
     42   # A placeholder for the system detected configuration
     43   DETECTED = cr.Config('DETECTED')
     44 
     45   def __init__(self):
     46     super(NinjaBuilder, self).__init__()
     47     self._targets = []
     48 
     49   def Build(self, targets, arguments):
     50     # Make sure Goma is started if Ninja is set to use it.
     51     # This may be redundant, but it currently improves reliability.
     52     try:
     53       with open(cr.context.Get('NINJA_BUILD_FILE'), 'r') as f:
     54         if f.readline().rstrip('\n') == cr.context.Get('NINJA_GOMA_LINE'):
     55           # Goma is active, so make sure it's started.
     56           cr.Host.ExecuteSilently(
     57               '{CR_GOMA_CTL}',
     58               'ensure_start'
     59           )
     60     except IOError:
     61       pass
     62 
     63     build_arguments = [target.build_target for target in targets]
     64     build_arguments.extend(arguments)
     65     cr.Host.Execute(
     66         '{NINJA_BINARY}',
     67         '-C{CR_BUILD_DIR}',
     68         '-j{NINJA_JOBS}',
     69         '-l{NINJA_PROCESSORS}',
     70         *build_arguments
     71     )
     72 
     73   def Clean(self, targets, arguments):
     74     build_arguments = [target.build_target for target in targets]
     75     build_arguments.extend(arguments)
     76     cr.Host.Execute(
     77         '{NINJA_BINARY}',
     78         '-C{CR_BUILD_DIR}',
     79         '-tclean',
     80         *build_arguments
     81     )
     82 
     83   def GetTargets(self):
     84     """Overridden from Builder.GetTargets."""
     85     if not self._targets:
     86       try:
     87         cr.context.Get('CR_BUILD_DIR', raise_errors=True)
     88       except KeyError:
     89         return self._targets
     90       output = cr.Host.Capture(
     91           '{NINJA_BINARY}',
     92           '-C{CR_BUILD_DIR}',
     93           '-ttargets',
     94           'all'
     95       )
     96       for line in output.split('\n'):
     97         line = line.strip()
     98         if line.endswith(_PHONY_SUFFIX):
     99           target = line[:-len(_PHONY_SUFFIX)].strip()
    100           self._targets.append(target)
    101         elif line.endswith(_LINK_SUFFIX):
    102           target = line[:-len(_LINK_SUFFIX)].strip()
    103           self._targets.append(target)
    104     return self._targets
    105 
    106   @classmethod
    107   def ClassInit(cls):
    108     # TODO(iancottrell): If we can't detect ninja, we should be disabled.
    109     ninja_binaries = cr.Host.SearchPath('ninja')
    110     if ninja_binaries:
    111       cls.DETECTED.Set(NINJA_BINARY=ninja_binaries[0])
    112 
    113     goma_binaries = cr.Host.SearchPath('gomacc', [
    114       '{GOMA_DIR}',
    115       '/usr/local/google/code/goma',
    116       os.path.expanduser('~/goma')
    117     ])
    118     if goma_binaries:
    119       cls.DETECTED.Set(CR_GOMA_DIR=os.path.dirname(goma_binaries[0]))
    120       cls.DETECTED.AddChildren(cls.GOMA)
    121