Contributing to cmake-format

General Rules

  1. Please use pylint and flake8 to check your code for lint. There are config files in the repo.
  2. There is a python unittest suite in cmakelangtests.py. Run with python -Bm cmakelang.tests (ensure modified code is on the python path). Alternatively you can use cmake (make test, ninja test, etc) or bazel (bazel test ...).
  3. There’s an autopep8 config file in the repo as well. Feel free to use that to format the code. Note that autopep8 and pylint disagree in a few places so using autopep8 may require some manual edits afterward.

Build Systems

You don’t need to “build” cmake-format, but there are cmake and bazel build rules that you can use if you find it convenient. For the most part your contribution is in good shape if the test and lint (cmake) build targets pass or, equivalently, if bazel test ... passes. If you add new files to the repository, please update the corresponding CMakeLists.txt and BUILD files to keep the lint and test configurations up to date.

Sidecar Tests

Whether or not to write your tests in cmake or in python will depend largely on the test. If you simply want to assert that a particular snippet of code is formatted in a specific way, writing the test in cmake is most natural. If any logic is needed, then writing in the body of a python function makes the most sense.

Test written in cmake are stored in .cmake sidecar files associated with python unittest cases (see command_tests/*).

The format of these tests are very simple. The test begins with a line comment in the form of:

# test: <test-name>

And ends with the start of the next test, the end of the file, or a line comment in the form of:

# end-test

If non-default configuration options are required, or if you would like to assert a lex, parse, or layout result, then you can specify that in an optional bracket comment immediately following the # test: sentinel. The bracket comment must begin and end on it’s own line, such as:

#[=[
   line_width = 10
]=]

The content of the comment is parsed as python and variables are interpreted as configuration variables, except for the following:

  • expect_lex - a list of lexer tokens indicated the expected lex result
  • expect_parse - a tree of parse node names indicated the expected parse tree structure
  • expect_layout - a tree of layout node names and geometries indicated the expected layout result.

A test called test_<test-name> will be generated at runtime. The test will involve cmake-format formatting the code snippet using the default configuration, and asserting that the output matches the input.

Debugging

Formatting

For the most part, cmake-format ignores white-space when it parses the list file into the parse tree so most of the formatting tests can be restricted to “idempotency” tests. In other words, you write down the listfile code in the format you expect and if cmake-format doesn’t change it, the test passes.

Lexing, Parsing, Layout

If you suspect a problem with the lexing, parsing, or layout algorithms, try to create a minimial working example of the problem, and use the --dump [lex|parse|layout] command line option to inspect the output of the different phases.

Once you’ve identified the problem, the best thing to do is create a test asserting an expected output at the problematic phase. You can start by asserting an empty output. For example, something like:

# test: debug_my_case
#[=[
  expect_lex = []
]=]
if(something)
  your_cmake_code_here(foo bar baz)
else()

The test will fail, but the error message will print out the actual result. In the example above:

Second list contains 19 additional elements.
First extra element 0:
TokenType.WORD

- []
+ [TokenType.WORD,
+  TokenType.LEFT_PAREN,
+  TokenType.WORD,
+  TokenType.RIGHT_PAREN,
+  TokenType.NEWLINE,
+  TokenType.WHITESPACE,
+  TokenType.WORD,
+  TokenType.LEFT_PAREN,
+  TokenType.WORD,
+  TokenType.WHITESPACE,
+  TokenType.WORD,
+  TokenType.WHITESPACE,
+  TokenType.WORD,
+  TokenType.RIGHT_PAREN,
+  TokenType.NEWLINE,
+  TokenType.WORD,
+  TokenType.LEFT_PAREN,
+  TokenType.RIGHT_PAREN,
+  TokenType.NEWLINE]

You can copy-paste the actual output as the expected output as a starting point for your test. You can modify the “expected” specification to match what the output should be. Then, as you iterate, you can use the test to know when you’ve fixed the problem.

Pull Requests

Feel free to make a pull request on github, though please take note of the following rules and guidelines. These rules are enforced through the travis CI builds so if travis passes your submission is probably in good shape.

Squash your feature

Please squash your changes when issuing a pull request so that the request is for a single commit. This helps us move the patch from the public github mirror into the upstream respository. When updating your request, please squash additional commits (you will likely need to force-push to your feature branch).

Rebase before submit

Please rebase your patch on the current HEAD before submitting the pull request. This helps us to keep a tidy history. When we merge your commit we don’t want to create graph connections across long regions of the git history. Travis will fail any pull request which is more than 10 commits behind the HEAD.

Sign your commit

When making a pull request, please sign the commit (use git commit -S).

Un-Assigned contributions

In general, copyright for contributions should be assigned to the project. This should keep everything on the level should we find the need to offer cmake-format though additional licenses in the future.

If you’d like to make a significant contribution to cmake-format but don’t agree to the terms of the copyright assignment please contact us to set up an alternate agreement. Otherwise, please consider filing a feature-request for changes you would like to see implemented.