blob: e1ff2af98184e72f358756b2bdf727b9d5ae4158 [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A module to add ninja support to cr."""
import multiprocessing
import os
import cr
_PHONY_SUFFIX = ': phony'
_LINK_SUFFIX = ': link'
DEFAULT = cr.Config.From(
GOMA_DIR=os.path.expanduser('~/goma'),
)
class NinjaBuilder(cr.Builder):
"""An implementation of Builder that uses ninja to do the actual build."""
# Some basic configuration installed if we are enabled.
EXTRA_FOR_IO_BOUND_JOBS = 2
ENABLED = cr.Config.From(
NINJA_BINARY=os.path.join('{DEPOT_TOOLS}', 'ninja'),
NINJA_JOBS=multiprocessing.cpu_count() + EXTRA_FOR_IO_BOUND_JOBS,
NINJA_PROCESSORS=multiprocessing.cpu_count(),
NINJA_BUILD_FILE=os.path.join('{CR_BUILD_DIR}', 'build.ninja'),
# Don't rename to GOMA_* or Goma will complain: "unknown GOMA_ parameter".
NINJA_GOMA_LINE='cc = {CR_GOMA_CC} $',
)
# A config block only included if goma is detected.
GOMA = cr.Config.From(
CR_GOMA_CC=os.path.join('{GOMA_DIR}', 'gomacc'),
CR_GOMA_CTL=os.path.join('{GOMA_DIR}', 'goma_ctl.py'),
GOMA_DIR='{CR_GOMA_DIR}',
NINJA_JOBS=multiprocessing.cpu_count() * 10,
)
# A placeholder for the system detected configuration
DETECTED = cr.Config('DETECTED')
def __init__(self):
super(NinjaBuilder, self).__init__()
self._targets = []
def Build(self, targets, arguments):
# Make sure Goma is started if Ninja is set to use it.
# This may be redundant, but it currently improves reliability.
try:
with open(cr.context.Get('NINJA_BUILD_FILE'), 'r') as f:
if f.readline().rstrip('\n') == cr.context.Get('NINJA_GOMA_LINE'):
# Goma is active, so make sure it's started.
cr.Host.ExecuteSilently(
'{CR_GOMA_CTL}',
'ensure_start'
)
except IOError:
pass
build_arguments = [target.build_target for target in targets]
build_arguments.extend(arguments)
cr.Host.Execute(
'{NINJA_BINARY}',
'-C{CR_BUILD_DIR}',
'-j{NINJA_JOBS}',
'-l{NINJA_PROCESSORS}',
*build_arguments
)
def Clean(self, targets, arguments):
build_arguments = [target.build_target for target in targets]
build_arguments.extend(arguments)
cr.Host.Execute(
'{NINJA_BINARY}',
'-C{CR_BUILD_DIR}',
'-tclean',
*build_arguments
)
def GetTargets(self):
"""Overridden from Builder.GetTargets."""
if not self._targets:
try:
cr.context.Get('CR_BUILD_DIR', raise_errors=True)
except KeyError:
return self._targets
output = cr.Host.Capture(
'{NINJA_BINARY}',
'-C{CR_BUILD_DIR}',
'-ttargets',
'all'
)
for line in output.split('\n'):
line = line.strip()
if line.endswith(_PHONY_SUFFIX):
target = line[:-len(_PHONY_SUFFIX)].strip()
self._targets.append(target)
elif line.endswith(_LINK_SUFFIX):
target = line[:-len(_LINK_SUFFIX)].strip()
self._targets.append(target)
return self._targets
@classmethod
def ClassInit(cls):
# TODO(iancottrell): If we can't detect ninja, we should be disabled.
ninja_binaries = cr.Host.SearchPath('ninja')
if ninja_binaries:
cls.DETECTED.Set(NINJA_BINARY=ninja_binaries[0])
goma_binaries = cr.Host.SearchPath('gomacc', [
'{GOMA_DIR}',
'/usr/local/google/code/goma',
os.path.expanduser('~/goma')
])
if goma_binaries:
cls.DETECTED.Set(CR_GOMA_DIR=os.path.dirname(goma_binaries[0]))
cls.DETECTED.AddChildren(cls.GOMA)