diff options
Diffstat (limited to 'meson/docs/markdown/Contributing.md')
-rw-r--r-- | meson/docs/markdown/Contributing.md | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/meson/docs/markdown/Contributing.md b/meson/docs/markdown/Contributing.md new file mode 100644 index 000000000..bb2f60488 --- /dev/null +++ b/meson/docs/markdown/Contributing.md @@ -0,0 +1,568 @@ +--- +short-description: Contributing to Meson +... + +# Contributing to Meson + +A large fraction of Meson is contributed by people outside the core +team. This documentation explains some of the design rationales of +Meson as well as how to create and submit your patches for inclusion +to Meson. + +Thank you for your interest in participating to the development. + +## Submitting patches + +All changes must be submitted as [pull requests to +Github](https://github.com/mesonbuild/meson/pulls). This causes them +to be run through the CI system. All submissions must pass a full CI +test run before they are even considered for submission. + +## Keeping pull requests up to date + +It is possible that while your pull request is being reviewed, other +changes are committed to master that cause merge conflicts that must +be resolved. The basic rule for this is very simple: keep your pull +request up to date using rebase _only_. + +Do not merge head back to your branch. Any merge commits in your pull +request make it not acceptable for merging into master and you must +remove them. + +## Special procedure for new features + +Every new feature requires some extra steps, namely: + +- Must include a project test under `test cases/`, or if that's not + possible or if the test requires a special environment, it must go + into `run_unittests.py`. +- Must be registered with the [FeatureChecks + framework](Release-notes-for-0.47.0.md#feature-detection-based-on-meson_version-in-project) + that will warn the user if they try to use a new feature while + targeting an older Meson version. +- Needs a release note snippet inside `docs/markdown/snippets/` with + a heading and a brief paragraph explaining what the feature does + with an example. + +## Acceptance and merging + +The kind of review and acceptance any merge proposal gets depends on +the changes it contains. All pull requests must be reviewed and +accepted by someone with commit rights who is not the original +submitter. Merge requests can be roughly split into three different +categories. + +The first one consists of MRs that only change the markdown +documentation under `docs/markdown`. Anyone with access rights can +push changes to these directly to master. For major changes it is +still recommended to create a MR so other people can comment on it. + +The second group consists of merges that don't change any +functionality, fixes to the CI system and bug fixes that have added +regression tests (see below) and don't change existing +functionality. Once successfully reviewed anyone with merge rights can +merge these to master. + +The final kind of merges are those that add new functionality or +change existing functionality in a backwards incompatible way. These +require the approval of the project lead. + +In a simplified list form the split would look like the following: + +- members with commit access can do: + - documentation changes (directly to master if warranted) + - bug fixes that don't change functionality + - refactorings + - new dependency types + - new tool support (e.g. a new Doxygen-kind of tool) + - support for new compilers to existing languages +- project leader decision is needed for: + - new modules + - new functions in the Meson language + - syntax changes for Meson files + - changes breaking backwards compatibility + - support for new languages + +## Strategy for merging pull requests to trunk + +Meson's merge strategy should fulfill the following guidelines: + +- preserve as much history as possible + +- have as little junk in the repo as possible + +- everything in the "master lineage" should always pass all tests + +These goals are slightly contradictory so the correct thing to do +often requires some judgement on part of the person doing the +merge. Github provides three different merge options, The rules of +thumb for choosing between them goes like this: + +- single commit pull requests should always be rebased + +- a pull request with one commit and one "fixup" commit (such as + testing something to see if it passes CI) should be squashed + +- large branches with many commits should be merged with a merge + commit, especially if one of the commits does not pass all tests + (which happens in e.g. large and difficult refactorings) + +If in doubt, ask for guidance on IRC. + +## Tests + +All new features must come with automatic tests that thoroughly prove +that the feature is working as expected. Similarly bug fixes must come +with a unit test that demonstrates the bug, proves that it has been +fixed and prevents the feature from breaking in the future. + +Sometimes it is difficult to create a unit test for a given bug. If +this is the case, note this in your pull request. We may permit bug +fix merge requests in these cases. This is done on a case by case +basis. Sometimes it may be easier to write the test than convince the +maintainers that one is not needed. Exercise judgment and ask for help +in problematic cases. + +The tests are split into two different parts: unit tests and full +project tests. To run all tests, execute `./run_tests.py`. Unit tests +can be run with `./run_unittests.py` and project tests with +`./run_project_tests.py`. + +### Project tests + +Subsets of project tests can be selected with +`./run_project_tests.py --only` option. This can save a great deal of +time when only a certain part of Meson is being tested. +For example, a useful and easy contribution to Meson is making +sure the full set of compilers is supported. One could for example test +various Fortran compilers by setting `FC=ifort` or `FC=flang` or similar +with `./run_project_test.py --only fortran`. +Some families of tests require a particular backend to run. +For example, all the CUDA project tests run and pass on Windows via +`./run_project_tests.py --only cuda --backend ninja` + +Each project test is a standalone project that can be compiled on its +own. They are all in the `test cases` subdirectory. The simplest way to +run a single project test is to do something like `./meson.py test\ +cases/common/1\ trivial builddir`. The one exception to this is `test +cases/unit` directory discussed below. + +The test cases in the `common` subdirectory are meant to be run always +for all backends. They should only depend on C and C++, without any +external dependencies such as libraries. Tests that require those are +in the `test cases/frameworks` directory. If there is a need for an +external program in the common directory, such as a code generator, it +should be implemented as a Python script. The goal of test projects is +also to provide sample projects that end users can use as a base for +their own projects. + +All project tests follow the same pattern: they are configured, +compiled, tests are run and finally install is run. Passing means that +configuring, building and tests succeed and that installed files match +those expected. + +Any tests that require more thorough analysis, such as checking that +certain compiler arguments can be found in the command line or that +the generated pkg-config files actually work should be done with a +unit test. + +Additionally: + +* `crossfile.ini` and `nativefile.ini` are passed to the configure step with +`--cross-file` and `--native-file` options, respectively. + +* `mlog.cmd_ci_include()` can be called from anywhere inside Meson to +capture the contents of an additional file into the CI log on failure. + +Projects needed by unit tests are in the `test cases/unit` +subdirectory. They are not run as part of `./run_project_tests.py`. + +### Configuring project tests + +The (optional) `test.json` file, in the root of a test case, is used +for configuring the test. All of the following root entries in the `test.json` +are independent of each other and can be combined as needed. + +Exanple `test.json`: + +```json +{ + "env": { + "VAR": "VAL" + }, + "installed": [ + { "type": "exe", "file": "usr/bin/testexe" }, + { "type": "pdb", "file": "usr/bin/testexe" }, + { "type": "shared_lib", "file": "usr/lib/z", "version": "1.2.3" }, + ], + "matrix": { + "options": { + "opt1": [ + { "val": "abc" }, + { "val": "qwert" }, + { "val": "bad" } + ], + "opt2": [ + { "val": null }, + { "val": "true" }, + { "val": "false" }, + ] + }, + "exclude": [ + { "opt1": "qwert", "opt2": "false" }, + { "opt1": "bad" } + ] + }, + "tools": { + "cmake": ">=3.11" + } +} +``` + +#### env + +The `env` key contains a dictionary which specifies additional +environment variables to be set during the configure step of the test. + +There is some basic support for configuring the string with the `@<VAR>@` syntax: + +- `@ROOT@`: absolute path of the source directory +- `@PATH@`: current value of the `PATH` env variable + +#### installed + +The `installed` dict contains a list of dicts, describing which files are expected +to be installed. Each dict contains the following keys: + +- `file` +- `type` +- `platform` (optional) +- `version` (optional) +- `language` (optional) + +The `file` entry contains the relative path (from the install root) to the +actually installed file. + +The `type` entry specifies how the `file` path should be interpreted based on the +current platform. The following values are currently supported: + +| type | Description | +| ------------- | ------------------------------------------------------------------------------------------------------- | +| `file` | No postprocessing, just use the provided path | +| `python_file` | Use the provided path while replacing the python directory. | +| `dir` | To include all files inside the directory (for generated docs, etc). The path must be a valid directory | +| `exe` | For executables. On Windows the `.exe` suffix is added to the path in `file` | +| `shared_lib` | For shared libraries, always written as `name`. The appropriate suffix and prefix are added by platform | +| `python_lib` | For python libraries, while replacing the python directory. The appropriate suffix is added by platform | +| `pdb` | For Windows PDB files. PDB entries are ignored on non Windows platforms | +| `implib` | For Windows import libraries. These entries are ignored on non Windows platforms | +| `py_implib` | For Windows import libraries. These entries are ignored on non Windows platforms | +| `implibempty` | Like `implib`, but no symbols are exported in the library | +| `expr` | `file` is an expression. This type should be avoided and removed if possible | + +Except for the `file`, `python_file` and `expr` types, all paths should be provided *without* a suffix. + +| Argument | Applies to | Description | +| -----------|----------------------------|-------------------------------------------------------------------------------| +| `version` | `shared_lib`, `pdb` | Sets the version to look for appropriately per-platform | +| `language` | `pdb` | Determines which compiler/linker determines the existence of this file | + +The `shared_lib` and `pdb` types takes an optional additional +parameter, `version`, this is us a string in `X.Y.Z` format that will +be applied to the library. Each version to be tested must have a +single version. The harness will apply this correctly per platform: + +The `python_file`, `python_lib`, and `py_implib` types have basic support for configuring the string with the `@<VAR>@` syntax: + +- `@PYTHON_PLATLIB@`: python `get_install_dir` directory relative to prefix +- `@PYTHON_PURELIB@`: python `get_install_dir(pure: true)` directory relative to prefix + +`pdb` takes an optional `language` argument. This determines which +compiler/linker should generate the pdb file. Because it's possible to +mix compilers that do and don't generate pdb files (dmd's optlink +doesn't). Currently this is only needed when mixing D and C code. + +```json +{ + "type": "shared_lib", "file": "usr/lib/lib", + "type": "shared_lib", "file": "usr/lib/lib", "version": "1", + "type": "shared_lib", "file": "usr/lib/lib", "version": "1.2.3.", +} +``` + +This will be applied appropriately per platform. On windows this +expects `lib.dll` and `lib-1.dll`. on MacOS it expects `liblib.dylib` +and `liblib.1.dylib`. On other Unices it expects `liblib.so`, +`liblib.so.1`, and `liblib.so.1.2.3`. + +If the `platform` key is present, the installed file entry is only +considered if the platform matches. The following values for +`platform` are currently supported: + +| platform | Description | +| ---------- | -------------------------------------------------------------------- | +| `msvc` | Matches when a msvc like compiler is used (`msvc`, `clang-cl`, etc.) | +| `gcc` | Not `msvc` | +| `cygwin` | Matches when the platform is cygwin | +| `!cygwin` | Not `cygwin` | + +#### matrix + +The `matrix` section can be used to define a test matrix to run +project tests with different Meson options. + +In the `options` dict, all possible options and their values are +specified. Each key in the `options` dict is a Meson option. It stores +a list of all potential values in a dict format. + +Each value must contain the `val` key for the value of the option. +`null` can be used for adding matrix entries without the current +option. + +The `skip_on_env`, `skip_on_jobname` and `skip_on_os` keys (as described below) +may be used in the value to skip that matrix entry, based on the current +environment. + +Similarly, the `compilers` key can be used to define a mapping of +compilers to languages that are required for this value. + +```json +{ + "compilers": { + "c": "gcc", + "cpp": "gcc", + "d": "gdc" + } +} +``` + +Specific option combinations can be excluded with the `exclude` +section. It should be noted that `exclude` does not require exact +matches. Instead, any matrix entry containing all option value +combinations in `exclude` will be excluded. Thus an empty dict (`{}`) +to will match **all** elements in the test matrix. + +The above example will produce the following matrix entries: +- `opt1=abc` +- `opt1=abc opt2=true` +- `opt1=abc opt2=false` +- `opt1=qwert` +- `opt1=qwert opt2=true` + +#### do_not_set_opts + +Currently supported values are: +- `prefix` +- `libdir` + +#### tools + +This section specifies a dict of tool requirements in a simple +key-value format. If a tool is specified, it has to be present in the +environment, and the version requirement must be fulfilled. Otherwise, +the entire test is skipped (including every element in the test +matrix). + +#### stdout + +The `stdout` key contains a list of dicts, describing the expected +stdout. + +Each dict contains the following keys: + +- `line` +- `match` (optional) + +Each item in the list is matched, in order, against the remaining +actual stdout lines, after any previous matches. If the actual stdout +is exhausted before every item in the list is matched, the expected +output has not been seen, and the test has failed. + +The `match` element of the dict determines how the `line` element is +matched: + +| Type | Description | +| -------- | ----------------------- | +| `literal` | Literal match (default) | +| `re` | regex match | + +#### skip_on_env + +The `skip_on_env` key can be used to specify a list of environment variables. If +at least one environment variable in the `skip_on_env` list is present, the test +is skipped. + +#### skip_on_jobname + +The `skip_on_jobname` key contains a list of strings. If the `MESON_CI_JOBNAME` +environment variable is set, and any of them are a sub-string of it, the test is +expected to be skipped (that is, it is expected that the test will output +`MESON_SKIP_TEST`, because the CI environment is not one in which it can run, +for whatever reason). + +The test is failed if it skips or runs unexpectedly. + +#### skip_on_os + +The `skip_on_os` key can be used to specify a list of OS names (or their +negations, prefixed with a `!`). If at least one item in the `skip_on_os` list +is matched, the test is expected to be skipped. + +The test is failed if it skips or runs unexpectedly. + +### Skipping integration tests + +Meson uses several continuous integration testing systems that have +slightly different interfaces for indicating a commit should be +skipped. + +Continuous integration systems currently used: +- [Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/git-commands?view=vsts&tabs=yaml#how-do-i-avoid-triggering-a-ci-build-when-the-script-pushes) + allows `***NO_CI***` in the commit message. +- [Sider](https://sider.review) + runs Flake8 ([see below](#python-coding-style)) + +To promote consistent naming policy, use: + +- `[skip ci]` in the commit title if you want to disable all + integration tests + +## Documentation + +The `docs` directory contains the full documentation that will be used +to generate [the Meson web site](http://mesonbuild.com). Every change +in functionality must change the documentation pages. In most cases +this means updating the reference documentation page but bigger +changes might need changes in other documentation, too. + +All new functionality needs to have a mention in the release +notes. These features should be written in standalone files in the +`docs/markdown/snippets` directory. The release manager will combine +them into one page when doing the release. + +[Integration tests should be disabled](#skipping-integration-tests) for +documentation-only commits by putting `[skip ci]` into commit title. +Reviewers should ask contributors to put `[skip ci]` into the title because +tests are run again after merge for `master`. + +## Python Coding style + +Meson follows the basic Python coding style. Additional rules are the +following: + +- indent 4 spaces, no tabs ever +- indent meson.build files with two spaces +- try to keep the code as simple as possible +- contact the mailing list before embarking on large scale projects + to avoid wasted effort + +Meson uses Flake8 for style guide enforcement. The Flake8 options for +the project are contained in .flake8. + +To run Flake8 on your local clone of Meson: + +```console +$ python3 -m pip install flake8 +$ cd meson +$ flake8 +``` + +To run it automatically before committing: + +```console +$ flake8 --install-hook=git +$ git config --bool flake8.strict true +``` + +## C/C++ coding style + +Meson has a bunch of test code in several languages. The rules for +those are simple. + +- indent 4 spaces, no tabs ever +- brace always on the same line as if/for/else/function definition + +## External dependencies + +The goal of Meson is to be as easily usable as possible. The user +experience should be "get Python3 and Ninja, run", even on +Windows. Unfortunately this means that we can't have dependencies on +projects outside of Python's standard library. This applies only to +core functionality, though. For additional helper programs etc the use +of external dependencies may be ok. If you feel that you are dealing +with this kind of case, please contact the developers first with your +use case. + +## Turing completeness + +The main design principle of Meson is that the definition language is +not Turing complete. Any change that would make Meson Turing complete +is automatically rejected. In practice this means that defining your +own functions inside `meson.build` files and generalised loops will +not be added to the language. + +## Do I need to sign a CLA in order to contribute? + +No you don't. All contributions are welcome. + +## No lingering state + +Meson operates in much the same way as functional programming +languages. It has inputs, which include `meson.build` files, values of +options, compilers and so on. These are passed to a function, which +generates output build definition. This function is pure, which means that: + +- for any given input the output is always the same +- running Meson twice in a row _always_ produce the same output in both runs + +The latter one is important, because it enforces that there is no way +for "secret state" to pass between consecutive invocations of +Meson. This is the reason why, for example, there is no `set_option` +function even though there is a `get_option` one. + +If this were not the case, we could never know if the build output is +"stable". For example suppose there were a `set_option` function and a +boolean variable `flipflop`. Then you could do this: + +```meson +set_option('flipflop', not get_option('flipflop')) +``` + +This piece of code would never converge. Every Meson run would change +the value of the option and thus the output you get out of this build +definition would be random. + +Meson does not permit this by forbidding these sorts of covert +channels. + +There is one exception to this rule. Users can call into external +commands with `run_command`. If the output of that command does not +behave like a pure function, this problem arises. Meson does not try +to guard against this case, it is the responsibility of the user to +make sure the commands they run behave like pure functions. + +## Environment variables + +Environment variables are like global variables, except that they are +also hidden by default. Envvars should be avoided whenever possible, +all functionality should be exposed in better ways such as command +line switches. + +## Random design points that fit nowhere else + +- All features should follow the 90/9/1 rule. 90% of all use cases + should be easy, 9% should be possible and it is totally fine to not + support the final 1% if it would make things too complicated. + +- Any build directory will have at most two toolchains: one native and + one cross. + +- Prefer specific solutions to generic frameworks. Solve the end + user's problems rather than providing them tools to do it + themselves. + +- Never use features of the Unix shell (or Windows shell for that + matter). Doing things like forwarding output with `>` or invoking + multiple commands with `&&` are not permitted. Whenever these sorts + of requirements show up, write an internal Python script with the + desired functionality and use that instead. |