blob: 4d55622857e18bf65125816442c0d5ca4c792ca6 [file] [log] [blame]
Hacking on BuildStream
======================
Some tips and guidelines for developers hacking on BuildStream
Getting Started
---------------
After cloning the BuildStream module with git, you will want a development installation.
First install it with Meson as normal into a location of your choice, for
example /opt/buildstream.
sudo mkdir /opt/buildstream
sudo chown $USER:$USER /opt/buildstream
mkdir build
meson .. --prefix=/opt/buildstream -Dbash_completion=no
ninja install
You should now be able to run ``/opt/buildstream/bin/bst``. You will probably
want to add ``/opt/buildstream/bin`` to your PATH as part of your shell's
startup file.
Feel free to enable the bash_completion option, but you will need to run
`ninja install` as root in that case.
Every time you make a change to the BuildStream source code you will need
to rerun `ninja install`. This can be avoided by replacing the installed
module with a symlink to your source directory (similar to the `pip -e`
feature of Pip). Here is an example command to do this. The exact paths
will depend on your OS and Python version. Run it from the top of your
buildstream.git checkout.
rm -R /opt/buildstream/lib64/python3.6/site-packages/buildstream
ln -s `pwd`/buildstream /opt/buildstream/lib64/python3.6/site-packages/buildstream
Coding Style
------------
Coding style details for BuildStream
Style Guide
~~~~~~~~~~~
Python coding style for BuildStream is pep8, which is documented here: https://www.python.org/dev/peps/pep-0008/
We have a couple of minor exceptions to this standard, we dont want to compromise
code readability by being overly restrictive on line length for instance.
The pep8 linter will run automatically when running the test suite.
Imports
~~~~~~~
Module imports inside BuildStream are done with . notation
Good::
from .context import Context
Bad::
from buildstream.context import Context
The exception to the above rule is when authoring plugins,
plugins do not reside in the same namespace so they must
address buildstream in the imports.
An element plugin will derive from Element by importing::
from buildstream import Element
When importing utilities specifically, dont import function names
from there, instead::
from . import utils
This makes things clear when reading code that said functions
are not defined in the same file but come from utils.py for example.
One Class One Module
~~~~~~~~~~~~~~~~~~~~
BuildStream is mostly Object Oriented with a few utility files.
* Every object should go into its own file (module) inside the buildstream package
* Files should be named after their class in lower case with no underscore.
This is to say a class named FooBar will certainly reside in a file named foobar.py.
Unless FooBar is private in which case the file is of course _foobar.py.
When adding a public class, it should be imported in toplevel __init__.py
so that buildstream consumers can import it directly from the buildstream
package, without requiring knowledge of the BuildStream package structure,
which is allowed to change over time.
Private API
~~~~~~~~~~~
BuildStream strives to guarantee a minimal and comprehensive public API
surface both for embedders of the BuildStream pipeline and implementors
of custom plugin Elements and Sources.
Python does not have a real concept of private API, but as a convention
anything which is private uses an underscore prefix.
* Modules which are private have their file named _module.py
* Private classes are named _Class
* Private methods, class variables and instance variables have a leading underscore as well
Exceptions to the above rules is to follow a principle of least underscores:
* If a module is entirely private, there is no need for the classes
it contains to have a leading underscore.
* If a class is entirely private, there is no need to mark its members
as private with leading underscores.
Documenting BuildStream
-----------------------
BuildStream starts out as a documented project from day one and uses
sphinx to document itself.
Useful links:
* Sphinx documentation: http://www.sphinx-doc.org/en/1.4.8/contents.html
* rst primer: http://www.sphinx-doc.org/en/stable/rest.html
Building Docs
~~~~~~~~~~~~~
The documentation build is not integrated into the ``setup.py`` and is
difficult (or impossible) to do so, so there is a little bit of setup
you need to take care of first.
Before you can build the BuildStream documentation yourself, you need
to first install ``sphinx`` and ``sphinx-click``, using pip or some
other mechanism::
pip install --user sphinx
pip install --user sphinx-click
Furthermore, the documentation build requires that BuildStream itself
be installed first, this can be a developer installation as described
at the top of this text::
cd buildstream
pip install --user -e .
Finally, to build the current set of docs, just run the following::
cd doc
make
This will give you a build/html directory with the html docs.
Documenting Conventions
~~~~~~~~~~~~~~~~~~~~~~~
When adding a new class to the buildstream core, an entry referring to
the new module where the new class is defined should be added to
the toplevel index manually in doc/source/index.rst.
We use the sphinx.ext.napoleon extension for the purpose of having
a bit nicer docstrings than the default sphinx docstrings.
A docstring for a method, class or function should have the following
format::
"""Brief description of entity
Args:
argument1 (type): Description of arg
argument2 (type): Description of arg
Returns:
Description of returned thing indicating its type
Raises:
SomeError, SomeOtherError
A detailed description can go here if one is needed, only
after the above part documents the calling conventions.
"""
Testing BuildStream
-------------------
BuildStream uses pytest for regression tests and testing out
the behavior of newly added components.
The elaborate documentation for pytest can be found here: http://doc.pytest.org/en/latest/contents.html
Don't get lost in the docs if you don't need to, follow existing examples instead.
Running Tests
~~~~~~~~~~~~~
To run the tests, just type this in the build directory::
meson test
When debugging a test, it can be desirable to see the stdout
and stderr generated by a test, to do this use the --verbose
option:
meson test --verbose
In order to pass in extra arguments to Pytest, set the PYTEST_ARGS
environment variable. For example, to abort on the first failure
you can run::
PYTEST_ADDOPTS=-x meson test --verbose
If you want to run a specific test or a group of tests, you
can specify a prefix to match. E.g. if you want to run all of
the frontend tests you can do::
PYTEST_ADDOPTS='-k tests/frontend' meson test --verbose
Adding Tests
~~~~~~~~~~~~
Tests are found in the tests subdirectory, inside of which
there is a separarate directory for each *domain* of tests.
The list of tests in maintained in ``tests/meson.build``.
If the new test is not appropriate for the existing test domains,
then simply create a new directory for it under the tests subdirectory.
Various tests may include data files to test on, there are examples
of this in the existing tests. When adding data for a test, create
a subdirectory beside your test in which to store data.
When creating a test that needs data, use the datafiles extension
to decorate your test case (again, examples exist in the existing
tests for this), documentation on the datafiles extension can
be found here: https://pypi.python.org/pypi/pytest-datafiles