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 multiprocessing 8import os 9 10import cr 11 12_PHONY_SUFFIX = ': phony' 13_LINK_SUFFIX = ': link' 14 15 16DEFAULT = cr.Config.From( 17 GOMA_DIR=os.path.expanduser('~/goma'), 18) 19 20class 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