# 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)
