blob: 452d2d1e598467137133eb41331ac7ca8e86da7d [file] [log] [blame] [view] [edit]
# How to Implement Changes to Emboss
<!-- TODO(bolms): write and link to guides on the `embossc` design -->
## Getting the Code
The master Emboss repository lives at https://github.com/google/emboss — you
can `git clone` that repository directly, or make [a fork on
GitHub](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks)
and then `git clone` your fork.
## Prerequisites
In order to run Emboss, you will need [Python](https://www.python.org/).
Emboss supports all versions of Python that are [still supported by the
(C)Python codevelopers](https://devguide.python.org/versions/), but versions
older than that generally will not work.
The Emboss tests run under [Bazel](https://bazel.build/). In order to run the
tests, you will need to [install Bazel](https://bazel.build/start) on your
system.
## Running Tests
Emboss has a reasonably extensive test suite. In order to run the test suite,
`cd` into the top `emboss` directory, and run:
```sh
bazel test ...
```
Bazel will download the necessary prerequisites, compile the (C++) code, and
run all the tests. Tests will take a moment to compile and run; Bazel will
show running status, like this:
```
Starting local Bazel server and connecting to it...
[502 / 782] 22 actions, 21 running
Compiling runtime/cpp/test/emboss_memory_util_test.cc; 10s linux-sandbox
Compiling runtime/cpp/test/emboss_memory_util_test.cc; 10s linux-sandbox
Creating runfiles tree bazel-out/k8-fastbuild/bin/compiler/front_end/emboss_front_end.runfiles; 2s local
Creating runfiles tree bazel-out/k8-fastbuild/bin/compiler/front_end/synthetics_test.runfiles; 1s local
Creating runfiles tree bazel-out/k8-fastbuild/bin/compiler/front_end/make_parser_test.runfiles; 1s local
Creating runfiles tree bazel-out/k8-fastbuild/bin/compiler/back_end/cpp/header_generator_test.runfiles; 1s local
Compiling absl/strings/str_join.h; 0s linux-sandbox
Creating runfiles tree bazel-out/k8-fastbuild/bin/compiler/front_end/generate_cached_parser.runfiles; 0s local ...
```
You may see a few `WARNING` messages; these are generally harmless.
Once Bazel finishes running tests, you should see a list of all tests and their
status (all should be `PASSED` if you just cloned the main Emboss repo):
```
Starting local Bazel server and connecting to it...
INFO: Analyzed 226 targets (98 packages loaded, 4080 targets configured).
INFO: Found 116 targets and 110 test targets...
INFO: Elapsed time: 65.577s, Critical Path: 22.22s
INFO: 862 processes: 372 internal, 490 linux-sandbox.
INFO: Build completed successfully, 862 total actions
//compiler/back_end/cpp:alignments_test PASSED in 0.2s
//compiler/back_end/cpp:alignments_test_no_opts PASSED in 0.1s
//compiler/back_end/cpp:anonymous_bits_test PASSED in 0.2s
//compiler/back_end/cpp:anonymous_bits_test_no_opts PASSED in 0.1s
[... many more tests ...]
//runtime/cpp/test:emboss_prelude_test PASSED in 0.2s
//runtime/cpp/test:emboss_prelude_test_no_opts PASSED in 0.2s
//runtime/cpp/test:emboss_text_util_test PASSED in 0.2s
//runtime/cpp/test:emboss_text_util_test_no_opts PASSED in 0.2s
Executed 110 out of 110 tests: 110 tests pass.
```
If a test fails, you will see lines at the end like:
```
//compiler/back_end/cpp:alignments_test FAILED in 0.0s
/usr/local/home/bolms/.cache/bazel/_bazel_bolms/444a471ee8e028e0535394d088883276/execroot/_main/bazel-out/k8-fastbuild/testlogs/compiler/back_end/cpp/alignments_test/test.log
Executed 110 out of 110 tests: 109 tests pass and 1 fails locally.
```
You can read the `test.log` file to find out where the failure occurred.
Note that each C++ test actually runs multiple times with different Emboss
`#define` options, so a single failure may cause multiple Bazel tests to fail:
```
//compiler/back_end/cpp:alignments_test FAILED in 0.0s
/usr/local/home/bolms/.cache/bazel/_bazel_bolms/1c6e4694f903a02feef32c92ec3f1cae/execroot/_main/bazel-out/k8-fastbuild/testlogs/compiler/back_end/cpp/alignments_test/test.log
//compiler/back_end/cpp:alignments_test_no_checks FAILED in 0.0s
/usr/local/home/bolms/.cache/bazel/_bazel_bolms/1c6e4694f903a02feef32c92ec3f1cae/execroot/_main/bazel-out/k8-fastbuild/testlogs/compiler/back_end/cpp/alignments_test_no_checks/test.log
//compiler/back_end/cpp:alignments_test_no_checks_no_opts FAILED in 0.0s
/usr/local/home/bolms/.cache/bazel/_bazel_bolms/1c6e4694f903a02feef32c92ec3f1cae/execroot/_main/bazel-out/k8-fastbuild/testlogs/compiler/back_end/cpp/alignments_test_no_checks_no_opts/test.log
//compiler/back_end/cpp:alignments_test_no_opts FAILED in 0.0s
/usr/local/home/bolms/.cache/bazel/_bazel_bolms/1c6e4694f903a02feef32c92ec3f1cae/execroot/_main/bazel-out/k8-fastbuild/testlogs/compiler/back_end/cpp/alignments_test_no_opts/test.log
Executed 168 out of 168 tests: 164 tests pass and 4 fail locally.
```
(The Emboss repository goes one step further and runs each of *those* tests
under multiple compilers and optimization options.)
If you are working on fixing a failure in one particular test, you can tell
Bazel to run just that test by specifying the name of the test on the command
line:
```
bazel test //compiler/back_end/cpp:alignments_test
```
This can be quicker than re-running the entire test suite.
### `docs_are_up_to_date_test`
If you are making changes to the Emboss grammar, you can ignore failures in
`docs_are_up_to_date_test` until you have your updated grammar finalized: that
test ensures that certain generated documentation files are up to date when
code reaches the main repository. See [Checked-In Generated
Code](#checked-in-generated-code), below.
## Implementing a Feature
The the Emboss compiler is under [`compiler/`](../compiler/), with
[`front_end/`](../compiler/front_end/), [`back_end/`](../compiler/front_end/),
and [`util/`](../compiler/util/) directories for the front end, back end, and
shared utilities, respectively.
The C++ runtime library is under [`runtime/cpp/`](../runtime/cpp).
### Coding Style
For Python, Emboss uses the default style of
the [Black](https://black.readthedocs.io/en/stable/) code formatter.[^genfile]
[^genfile]: There is one, very large, generated `.py` file checked into the
Emboss repository that is intentionally excluded from code formatting
both because it can hang the formatter and because the formatted version
takes noticeably longer for CPython to load.
For C++, Emboss uses the `--style=Google` preset of
[ClangFormat](https://clang.llvm.org/docs/ClangFormat.html).
## Writing Tests
Most code changes require tests: bug fixes should have at least one test that
fails before the bug fix and passes after the fix, and new features should have
many tests that cover all aspects of how the feature might be used.
### Python
The Emboss Python tests use the Python
[`unittest`](https://docs.python.org/3/library/unittest.html) module. Most
[tests of the Emboss front end](../compiler/front_end/) are structured as:
1. Run a small `.emb` file through the front end, stopping immediately before
the step under test, and hold the result IR (intermediate representation).
2. Run the step under test on that IR, making sure that there are either no
errors, or that the errors are expected.
3. For the "no errors" tests, check various properties of the resulting IR to
ensure that the step under test did what it was supposed to.
### C++
The Emboss C++ tests use [the GoogleTest
framework](https://google.github.io/googletest/).
[Pure runtime tests](../runtime/cpp/test) `#include` the C++ runtime library
headers and manually instantiate them, then test various properties.
[Generated code tests](../compiler/back_end/cpp/testcode/), which incidentally
test the runtime library as well, work by using a header generated from a [test
`.emb`](../testdata/) and interacting with the generated Emboss code the way
that a user might do so.
## Writing Documentation
If you are adding a feature to Emboss, make sure to update [the
documentation](../doc/). In particular, the [language
reference](../doc/language-reference.md) and the [C++ code
reference](cpp-reference.md) are very likely to need to be updated.
## Checked-In Generated Code
There are several checked-in generated files in the Emboss source repository.
As a general rule, this is not a best practice, but it is necessary in order to
achieve the "zero installation" use of the Emboss compiler, where an end user
can simply `git clone` the repository and run the `embossc` executable directly
even if the cloned repository lives on a read-only filesystem.
In order to minimize the chances of any of those files becoming stale, each one
has a unit test that checks that the file in the Emboss directory matches what
its generator would currently generate.