blob: 783f299d29b524a3611e251b65045e04d9e25261 [file] [log] [blame] [view]
# Bootstrapping infra.git
[TOC]
The [infra.git](/) repo uses python [wheel files][wheel files],
[virtualenv][virtualenv] and [pip][pip] to manage dependencies. The process for
bootstrapping these is contained entirely within [bootstrap/ directory](.).
See [dockerbuild](/infra/tools/dockerbuild/README.md) for a tool that tries to
make a lot of this easier. If you use dockerbuild to build a wheel for
bootstrap, you will need to upload it manually to the
[wheelhouse](https://pantheon.corp.google.com/storage/browser/chrome-python-wheelhouse/wheels).
(TODO: We should enable Dockerbuild to upload to wheelhouse too.) Check the
`.dockerbuild/wheels` directory for the local wheel.
> **Note:** The tooling outlined here supports building a python virtualenv for
> use *only* in the infra.git repo. `vpython` is a more distributed tool that
> accomplishes a similar thing, but is *not* described in this file. See
> [here][vypy doc] for its documentation.
## TL;DR - Workflows
### Setting up the env with already-built-deps
Just run:
gclient sync
# OR
gclient runhooks
### Adding a new dep
Say we want to add a stock my\_pkg python package at version 1.2.3:
#### Tarball
If it comes from a tarball:
$ ./bootstrap/ingest_source.py <tarball>
...
deadbeefdeadbeefdeadbeefdeadbeef.tar.gz
#### Another repo
If it comes from a repo, file a ticket to have it mirrored (no matter what VCS!)
to `chromium.googlesource.com/external/<repo_url>`
Grab the git commit hash of the commit to build:
badc0ffeebadc0ffeebadc0ffeebadc0
Then add the actual dep:
$ edit bootstrap/deps.pyl # add a new entry (see the 'deps.pyl' section)
...
'my_pkg' : {
'version': '1.2.3',
'build': 0, # This is the first build
'gs': 'deadbeefdeadbeefdeadbeefdeadbeef.tar.gz', # if tarball
'rev': 'badc0ffeebadc0ffeebadc0ffeebadc0', # if repo
}
...
Then build it:
$ ./bootstrap/build_deps.py --upload
# builds and uploads my_pkg-1.2.3-0_deadbeef...-....whl to google storage
*** note
If your dep is not pure-python, you will have to run `build_deps.py`
for each platform.
***
*** note
If you're running an unsupported platform (the main symptom being that `gclient
sync` fails with `__main__.NoWheelException: No matching wheel found for`. See
http://crbug.com/520285 for more details), then you have to run `build_deps.py`
to rebuild the missing packages. See also 'rolling deps' below. If you don't
have permission to upload the generated packages to Cloud Storage (e.g. if
you're not a Googler), then drop the `--upload` option and all packages will be
stored locally only.
***
### If your dep needs special treatment
Do everything in the [Adding a new dep](#Adding-a-new-dep) section, but before
running `build_deps.py`, add a file
`custom_builds/{wheel package name}.py`. This file is expected to
implement:
def Build(source_path, wheelhouse_path)
See [custom builds](#Custom-builds) below for more detail.
## bootstrap.py (a.k.a. "I just want a working infra repo!")
Run `gclient runhooks`. Under the hood, this runs:
./bootstrap/bootstrap.py --deps_file bootstrap/deps.pyl ENV
This creates a virtualenv called `{repo_root}/ENV` with all the deps
contained in `bootstrap/deps.pyl`. You must be online, or must already
have the wheels for your system in cache.
If you already have an `ENV` directory, [bootstrap.py](bootstrap.py) will check
the manifest in `ENV` to see if it matches [deps.pyl](#deps_pyl) (i.e. the diff
is zero). If it's not, then `ENV` directory will be re-created *from scratch*.
[run.py](../run.py) will automatically use the environment `ENV`. It is
an error to use `run.py` without first setting up `ENV`.
## [deps.pyl](deps.pyl)
This file is a python dictionary containing the exact versions of all
Python module dependencies. These versions are the standard upstream
package versions (e.g. '0.8.0'), plus the commit hash or sha1.{ext} of
an [ingested source bundle](injest_source.py).
The format of this file is `{'package_name': <values>}`. This file is a
Python
[ast literal](https://docs.python.org/2/library/ast.html#ast.literal_eval),
so comments are allowed and encouraged.
Note that the `package_name` key is the pip-reported name (the one set
in `setup.py`). It may be different from the name used for import, and
for the wheel.
Values are:
* version: The pip version of the module
* build: An integer representing which build of this version/hash. If you
modify the _way_ that a requirement is built, but not the source
hash, you can bump the build number to get a new pinned dependency.
And either:
* `rev`: The revision or sha1 of the source for this module. The repo
is
`git+https://chromium.googlesource.com/infra/third_party/{package_name}`
* `gs`: `{sha1}.{ext}` indicates file
`gs://chrome-python-wheelhouse/sources/{sha1}.{ext}`. The sha1 will
be checked against the content of the file.
And optionally:
* `implicit`: A boolean indicating that this dep should only be
installed as a dependency of some other dep. For example, you want
package A, which depends on package Z, but you don't really care
about Z. You should mark Z as `implicit` to allow it to be pinned
correctly, but not to deliberately install it.
## [ingest_source.py](ingest_source.py)
Some python modules don't have functional python repos (i.e. ones that
pip can natively clone+build), and thus ship their source in tarballs.
To ingest such a tarball into the infra google storage bucket, use
`ingest_source.py /path/to/archive`. This will print the value for the
'gs' key for a deps.pyl entry.
## build_deps.py / rolling deps
Any time a new dependency/version is introduced into `deps.pyl`, you
must run `build_deps.py --upload`. If the dependency is a pure-Python
dependency (i.e. no compiled extensions), you only need to run it once on
CPython 2.7. You can tell that it's a pure python module by looking at the name
of the wheel file. For example:
requests-2.3.0-py2.py3-none-any.whl
Is compatible with Python 2 and Python 3 (py2.py3) any python ABI
(none), and any OS platform (any).
Running [build_deps.py](build_deps.py) will only attempt to build dependencies
which are missing for the current platform.
If the module does contain compiled extensions, you must run
[build_deps.py](build_deps.py) on the following systems (all with CPython 2.7):
* OS X 10.9 - `x86_64`
* Windows 7 - `x86_64`
* Linux - `x86_64`
TODO(iannucci): Add job to build wheels on all appropriate systems.
Once a wheel is sucessfully built, it is uploaded to
`gs://chrome-python-wheelhouse/wheels` if it is not there already.
Only Googlers have access to that bucket. Make sure to run the following
command to authenticate first:
depot_tools/gsutil.py config
[build_deps.py](build_deps.py) assumes that it can find `gsutil` on `PATH`, so
go ahead and install it appropriately for whichever platform you're on.
## Custom builds
Sometimes building a wheel is a bit trickier than
`pip wheel {repo}@{hash}`. In order to support this, add a script named
`custom_builds/{name}.py`. This module should have a function defined
like:
```python
def Build(source_path, wheelhouse_path)
```
Where `source_path` is a string path to the checked-out / unpacked
source code, and `wheelhouse_path` is a string path where
`build_deps.py` expects to find a `.whl` file after Build completes.
Note that your Build function will actually need to invoke pip manually.
Currently you can get the path for pip by doing:
`os.path.join(sys.prefix, 'bin', 'pip')`, and you can invoke it with
subprocess
([example](https://code.google.com/p/chromium/codesearch#chromium/infra/bootstrap/custom)).
## Rolling the version of wheel
Since wheel is a package needed to build the wheels, it has a slightly
different treatment. To roll a wheel, bump the version in deps.pyl, and
then run `build_deps.py --upload` to build and upload the wheel for
`wheel` pinned at the version in `deps.pyl`.
Once you do that, `build_deps.py` will continue working as expected.
If you ran into an authentication error, make sure you ran
`depot_tools/gsutil.py config` and if that didn't work, you may need to
contact http://go/chops-security.
## Building deps on Windows
TODO(iannucci): actually implement this
Windows builds require a slightly more care when building, due to the
complexities of getting a compile environment. To this effect,
`build_deps.py` relies on the `depot_tools/win_toolchain` functionality
to get a hermetic windows compiler toolchain. This should not be an
issue for chromium devs working on windows, since they should already
have this installed by compiling chromium, but it's something to be
aware of.
## Modified (non-upstream) deps
If it is necessary to roll a patched version of a library, we should
branch it in the infra googlesource mirror. This branch should be named
`{version}-cr`, and will build packages whose version is
`{version}.{cr_version}` (e.g. modify `setup.py` on this branch to add
an additional component to the version field).
For example, given the package `jane` at version `2.1.3`, we would
create a branch `2.1.3-cr`. On this branch we would commit any changes
necessary to `2.1.3`, and would adjust the version number in the builds
to be e.g. `2.1.3.0`.
[wheel files]: https://www.python.org/dev/peps/pep-0427/
[virtualenv]: https://github.com/pypa/virtualenv
[pip]: https://github.com/pypa/pip
[vypy doc]: https://chromium.googlesource.com/infra/infra/+/master/go/src/infra/tools/vpython/README.md
## Platform Problems
### NoWheelExecption
A builder is failing with NoWheelException, but you feel strongly that the
entry in [deps.pyl](deps.pyl) matches what is in the
[wheelhouse](https://pantheon.corp.google.com/storage/browser/chrome-python-wheelhouse/wheels).
One thing that might be happening is that pip doesn't think that wheel, based on
its filename, is supported for that platform. You might log on to that bot and
start a python interpreter:
```
>>> import pip
>>> pip.pep425tags.supported_tags
```
will list all of the [supported tags](https://www.python.org/dev/peps/pep-0425/)
for that platform. What to do next depends on your particular situation, but
hopefully this information is helpful for giving you ideas.