Add recipe module for goma

Change-Id: Ie4066378b1c9c65102000bdb3bc070760379d9af
diff --git a/recipe_modules/goma/__init__.py b/recipe_modules/goma/__init__.py
new file mode 100644
index 0000000..7d9ffa4
--- /dev/null
+++ b/recipe_modules/goma/__init__.py
@@ -0,0 +1,9 @@
+DEPS = [
+    'cipd',
+    'recipe_engine/path',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/python',
+    'recipe_engine/raw_io',
+    'recipe_engine/step',
+]
diff --git a/recipe_modules/goma/api.py b/recipe_modules/goma/api.py
new file mode 100644
index 0000000..d1b0f0a
--- /dev/null
+++ b/recipe_modules/goma/api.py
@@ -0,0 +1,170 @@
+# 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)
+        self._goma_dir = None
+        self._goma_started = False
+
+        self._goma_ctl_env = {}
+        self._goma_jobs = None
+
+    @property
+    def service_account_json_path(self):
+        return '/creds/service_accounts/service-account-goma-client.json'
+
+    @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
+
+    def ensure_goma(self, canary=False):
+        with self.m.step.nest('ensure_goma'):
+            with self.m.step.context({'infra_step': True}):
+                try:
+                    self.m.cipd.set_service_account_credentials(
+                        self.service_account_json_path)
+
+                    self.m.cipd.install_client()
+                    goma_package = ('infra_internal/goma/client/%s' %
+                        self.m.cipd.platform_suffix())
+                    ref='release'
+                    if canary:
+                        ref='candidate'
+                    self._goma_dir = self.m.path['cache'].join('cipd', 'goma')
+
+                    self.m.cipd.ensure(self._goma_dir, {goma_package: ref})
+
+                    return self._goma_dir
+                except self.m.step.StepFailure: # pragma: no cover
+                    return None
+
+    @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 start(self, env=None, **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
+
+        self._goma_ctl_env['GOMA_SERVICE_ACCOUNT_JSON_FILE'] = (
+            self.service_account_json_path)
+
+        # GLOG_log_dir should not be set.
+        assert env is None or 'GLOG_log_dir' not in env
+
+        goma_ctl_start_env = self._goma_ctl_env.copy()
+        if env is not None:
+            goma_ctl_start_env.update(env)
+
+        try:
+            self.m.python(
+                name='start_goma',
+                script=self.goma_ctl,
+                args=['restart'], env=goma_ctl_start_env, 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'], env=self._goma_ctl_env, **kwargs)
+            except self.m.step.StepFailure:
+                pass
+            raise e
+
+    def stop(self, **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():
+            self.m.python(name='goma_jsonstatus', script=self.goma_ctl,
+                          args=['jsonstatus', self.json_path],
+                          env=self._goma_ctl_env, **kwargs)
+            self.m.python(name='goma_stat', script=self.goma_ctl,
+                          args=['stat'],
+                          env=self._goma_ctl_env, **kwargs)
+            self.m.python(name='stop_goma', script=self.goma_ctl,
+                          args=['stop'], env=self._goma_ctl_env, **kwargs)
+
+        self._goma_started = False
+        self._goma_ctl_env = {}
+
+    @contextmanager
+    def build_with_goma(self, env=None):
+        """Make context wrapping goma start/stop.
+
+        Raises:
+            StepFailure or InfraFailure if it fails to build.
+        """
+
+        self.start(env)
+        try:
+            yield
+        finally:
+            self.stop()
diff --git a/recipe_modules/goma/example.expected/linux.json b/recipe_modules/goma/example.expected/linux.json
new file mode 100644
index 0000000..23992d3
--- /dev/null
+++ b/recipe_modules/goma/example.expected/linux.json
@@ -0,0 +1,214 @@
+[
+  {
+    "cmd": [],
+    "name": "ensure_goma"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[infra::cipd]/resources/bootstrap.py",
+      "--platform",
+      "linux-amd64",
+      "--dest-directory",
+      "[SLAVE_BUILD]/cipd",
+      "--json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_goma.install cipd",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@cipd instance_id: 40-chars-fake-of-the-package-instance_id@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"executable\": \"[SLAVE_BUILD]/cipd/cipd\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"instance_id\": \"40-chars-fake-of-the-package-instance_id\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[SLAVE_BUILD]/cipd/cipd",
+      "ensure",
+      "--root",
+      "[CACHE]/cipd/goma",
+      "--list",
+      "infra_internal/goma/client/linux-amd64 release",
+      "--json-output",
+      "/path/to/tmp/json",
+      "--service-account-json",
+      "/creds/service_accounts/service-account-goma-client.json"
+    ],
+    "name": "ensure_goma.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-release---------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"infra_internal/goma/client/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  ]@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "ensure_goma (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[infra::cipd]/resources/bootstrap.py",
+      "--platform",
+      "linux-amd64",
+      "--dest-directory",
+      "[SLAVE_BUILD]/cipd",
+      "--json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_goma.install cipd (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@cipd instance_id: 40-chars-fake-of-the-package-instance_id@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"executable\": \"[SLAVE_BUILD]/cipd/cipd\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"instance_id\": \"40-chars-fake-of-the-package-instance_id\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[SLAVE_BUILD]/cipd/cipd",
+      "ensure",
+      "--root",
+      "[CACHE]/cipd/goma",
+      "--list",
+      "infra_internal/goma/client/linux-amd64 candidate",
+      "--json-output",
+      "/path/to/tmp/json",
+      "--service-account-json",
+      "/creds/service_accounts/service-account-goma-client.json"
+    ],
+    "name": "ensure_goma.ensure_installed (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-candidate-------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"infra_internal/goma/client/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  ]@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/Release",
+      "--args=use_goma=true goma_dir=[CACHE]/cipd/goma"
+    ],
+    "name": "gn"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "restart"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "start_goma"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport multiprocessing\nimport sys\n\njob_limit = 200\nif sys.platform.startswith('linux'):\n  # Use 80 for linux not to load goma backend.\n  job_limit = 80\n\ntry:\n  jobs = min(job_limit, multiprocessing.cpu_count() * 10)\nexcept NotImplementedError:\n  jobs = 50\n\nprint jobs\n"
+    ],
+    "name": "calculate the number of recommended jobs",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import multiprocessing@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@job_limit = 200@@@",
+      "@@@STEP_LOG_LINE@python.inline@if sys.platform.startswith('linux'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  # Use 80 for linux not to load goma backend.@@@",
+      "@@@STEP_LOG_LINE@python.inline@  job_limit = 80@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  jobs = min(job_limit, multiprocessing.cpu_count() * 10)@@@",
+      "@@@STEP_LOG_LINE@python.inline@except NotImplementedError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  jobs = 50@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@print jobs@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "50"
+    ],
+    "name": "echo goma jobs"
+  },
+  {
+    "cmd": [
+      "echo",
+      "50"
+    ],
+    "name": "echo goma jobs second"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "jsonstatus",
+      "[CACHE]/cipd/goma/jsonstatus"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "goma_jsonstatus"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "stat"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "goma_stat"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "stop"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "stop_goma"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/goma/example.expected/mac.json b/recipe_modules/goma/example.expected/mac.json
new file mode 100644
index 0000000..90032d1
--- /dev/null
+++ b/recipe_modules/goma/example.expected/mac.json
@@ -0,0 +1,214 @@
+[
+  {
+    "cmd": [],
+    "name": "ensure_goma"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[infra::cipd]/resources/bootstrap.py",
+      "--platform",
+      "mac-amd64",
+      "--dest-directory",
+      "[SLAVE_BUILD]/cipd",
+      "--json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_goma.install cipd",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@cipd instance_id: 40-chars-fake-of-the-package-instance_id@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"executable\": \"[SLAVE_BUILD]/cipd/cipd\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"instance_id\": \"40-chars-fake-of-the-package-instance_id\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[SLAVE_BUILD]/cipd/cipd",
+      "ensure",
+      "--root",
+      "[CACHE]/cipd/goma",
+      "--list",
+      "infra_internal/goma/client/mac-amd64 release",
+      "--json-output",
+      "/path/to/tmp/json",
+      "--service-account-json",
+      "/creds/service_accounts/service-account-goma-client.json"
+    ],
+    "name": "ensure_goma.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-release---------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"infra_internal/goma/client/mac-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  ]@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "ensure_goma (2)"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[infra::cipd]/resources/bootstrap.py",
+      "--platform",
+      "mac-amd64",
+      "--dest-directory",
+      "[SLAVE_BUILD]/cipd",
+      "--json-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "ensure_goma.install cipd (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@cipd instance_id: 40-chars-fake-of-the-package-instance_id@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"executable\": \"[SLAVE_BUILD]/cipd/cipd\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"instance_id\": \"40-chars-fake-of-the-package-instance_id\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[SLAVE_BUILD]/cipd/cipd",
+      "ensure",
+      "--root",
+      "[CACHE]/cipd/goma",
+      "--list",
+      "infra_internal/goma/client/mac-amd64 candidate",
+      "--json-output",
+      "/path/to/tmp/json",
+      "--service-account-json",
+      "/creds/service_accounts/service-account-goma-client.json"
+    ],
+    "name": "ensure_goma.ensure_installed (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-candidate-------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"infra_internal/goma/client/mac-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  ]@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "gn",
+      "gen",
+      "out/Release",
+      "--args=use_goma=true goma_dir=[CACHE]/cipd/goma"
+    ],
+    "name": "gn"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "restart"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "start_goma"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport multiprocessing\nimport sys\n\njob_limit = 200\nif sys.platform.startswith('linux'):\n  # Use 80 for linux not to load goma backend.\n  job_limit = 80\n\ntry:\n  jobs = min(job_limit, multiprocessing.cpu_count() * 10)\nexcept NotImplementedError:\n  jobs = 50\n\nprint jobs\n"
+    ],
+    "name": "calculate the number of recommended jobs",
+    "stdout": "/path/to/tmp/",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@import multiprocessing@@@",
+      "@@@STEP_LOG_LINE@python.inline@import sys@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@job_limit = 200@@@",
+      "@@@STEP_LOG_LINE@python.inline@if sys.platform.startswith('linux'):@@@",
+      "@@@STEP_LOG_LINE@python.inline@  # Use 80 for linux not to load goma backend.@@@",
+      "@@@STEP_LOG_LINE@python.inline@  job_limit = 80@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@try:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  jobs = min(job_limit, multiprocessing.cpu_count() * 10)@@@",
+      "@@@STEP_LOG_LINE@python.inline@except NotImplementedError:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  jobs = 50@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@print jobs@@@",
+      "@@@STEP_LOG_END@python.inline@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "echo",
+      "50"
+    ],
+    "name": "echo goma jobs"
+  },
+  {
+    "cmd": [
+      "echo",
+      "50"
+    ],
+    "name": "echo goma jobs second"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "jsonstatus",
+      "[CACHE]/cipd/goma/jsonstatus"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "goma_jsonstatus"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "stat"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "goma_stat"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "[CACHE]/cipd/goma/goma_ctl.py",
+      "stop"
+    ],
+    "env": {
+      "GOMA_SERVICE_ACCOUNT_JSON_FILE": "/creds/service_accounts/service-account-goma-client.json"
+    },
+    "name": "stop_goma"
+  },
+  {
+    "name": "$result",
+    "recipe_result": null,
+    "status_code": 0
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/goma/example.py b/recipe_modules/goma/example.py
new file mode 100644
index 0000000..090bbe5
--- /dev/null
+++ b/recipe_modules/goma/example.py
@@ -0,0 +1,35 @@
+# Copyright 2016 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.
+
+DEPS = [
+    'goma',
+    'recipe_engine/platform',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+    api.goma.ensure_goma()
+    api.goma.ensure_goma(canary=True)
+    api.step('gn', ['gn', 'gen', 'out/Release',
+                    '--args=use_goma=true goma_dir=%s' % api.goma.goma_dir])
+
+    with api.goma.build_with_goma(env={}):
+        # build something using goma.
+        api.step('echo goma jobs',
+                 ['echo', str(api.goma.recommended_goma_jobs)])
+        api.step('echo goma jobs second',
+                 ['echo', str(api.goma.recommended_goma_jobs)])
+
+
+def GenTests(api):
+    for platform in ('linux', 'mac'):
+        properties = {
+            'buildername': 'test_builder',
+            'path_config': 'swarmbucket',
+        }
+
+        yield (api.test(platform) + api.platform.name(platform) +
+               api.properties.generic(**properties))