blob: d4b7ec8c37beec7a8eadea815480a5e9f36cf595 [file] [log] [blame]
# Copyright 2015 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.
from contextlib import contextmanager
from recipe_engine import recipe_api
class GomaApi(recipe_api.RecipeApi):
"""GomaApi contains helper functions for using goma."""
def __init__(self, **kwargs):
super(GomaApi, self).__init__(**kwargs)
# Flag to represent local goma module running.
self._is_local = False
self._goma_dir = None
self._goma_started = False
self._goma_jobs = None
@property
def service_account_json_path(self):
return self.m.service_account.get_json_path('goma-client')
@property
def json_path(self):
assert self._goma_dir
return self.m.path.join(self._goma_dir, 'jsonstatus')
@property
def recommended_goma_jobs(self):
"""
Return the recommended number of jobs for parallel build using Goma.
This function caches the _goma_jobs.
"""
if self._goma_jobs:
return self._goma_jobs
# We need to use python.inline not to change behavior of recipes.
step_result = self.m.python.inline(
'calculate the number of recommended jobs',
"""
import multiprocessing
import sys
job_limit = 200
if sys.platform.startswith('linux'):
# Use 80 for linux not to load goma backend.
job_limit = 80
try:
jobs = min(job_limit, multiprocessing.cpu_count() * 10)
except NotImplementedError:
jobs = 50
print jobs
""",
stdout=self.m.raw_io.output(),
step_test_data=(
lambda: self.m.raw_io.test_api.stream_output('50\n'))
)
self._goma_jobs = int(step_result.stdout)
return self._goma_jobs
@property
def goma_ctl(self):
return self.m.path.join(self._goma_dir, 'goma_ctl.py')
@property
def goma_dir(self):
assert self._goma_dir
return self._goma_dir
def set_goma_dir(self, goma_dir): # pragma: no cover
""" This function is for local recipe test only."""
self._goma_dir = goma_dir
self._is_local = True
def ensure_goma(self, canary=False):
if self._is_local: # pragma: no cover
return self._goma_dir
with self.m.step.nest('ensure_goma'):
with self.m.context(infra_steps=True):
self.m.cipd.set_service_account_credentials(
self.service_account_json_path)
goma_package = ('infra_internal/goma/client/%s' %
self.m.cipd.platform_suffix())
ref='release'
if canary:
ref='candidate'
self._goma_dir = self.m.path['start_dir'].join('cipd', 'goma')
self.m.cipd.ensure(self._goma_dir, {goma_package: ref})
return self._goma_dir
@contextmanager
def goma_env(self, env):
if 'GOMA_DEPS_CACHE_FILE' not in env:
env['GOMA_DEPS_CACHE_FILE'] = 'goma_deps_cache'
if 'GOMA_CACHE_DIR' not in env:
env['GOMA_CACHE_DIR'] = self.m.path['cache'].join('goma')
if not self._is_local:
env['GOMA_SERVICE_ACCOUNT_JSON_FILE'] = self.service_account_json_path
with self.m.context(env=env):
yield
def start(self, env={}, **kwargs):
"""Start goma compiler_proxy.
A user MUST execute ensure_goma beforehand.
It is user's responsibility to handle failure of starting compiler_proxy.
"""
assert self._goma_dir
assert not self._goma_started
# GLOG_log_dir should not be set.
assert 'GLOG_log_dir' not in self.m.context.env, (
'GLOG_log_dir must not be set in env during goma.start()')
with self.goma_env(env):
try:
self.m.python(
name='start_goma',
script=self.goma_ctl,
args=['restart'], infra_step=True, **kwargs)
self._goma_started = True
except self.m.step.InfraFailure as e: # pragma: no cover
try:
with self.m.step.defer_results():
self.m.python(
name='stop_goma (start failure)',
script=self.goma_ctl,
args=['stop'], **kwargs)
except self.m.step.StepFailure:
pass
raise e
def stop(self, env={}, **kwargs):
"""Stop goma compiler_proxy.
A user MUST execute start beforehand.
It is user's responsibility to handle failure of stopping compiler_proxy.
Raises:
StepFailure if it fails to stop goma.
"""
assert self._goma_dir
assert self._goma_started
with self.m.step.defer_results():
with self.goma_env(env):
self.m.python(name='goma_jsonstatus', script=self.goma_ctl,
args=['jsonstatus', self.json_path],
**kwargs)
self.m.python(name='goma_stat', script=self.goma_ctl,
args=['stat'],
**kwargs)
self.m.python(name='stop_goma', script=self.goma_ctl,
args=['stop'], **kwargs)
self._goma_started = False
@contextmanager
def build_with_goma(self, env={}):
"""Make context wrapping goma start/stop.
Raises:
StepFailure or InfraFailure if it fails to build.
"""
self.start(env)
try:
yield
finally:
self.stop(env)