Difference between revisions of "LDC contributor's guide"

From D Wiki
Jump to: navigation, search
(APIs and idioms)
m (APIs and idioms)
 
(21 intermediate revisions by 4 users not shown)
Line 2: Line 2:
  
 
== Continuous Integration ==
 
== Continuous Integration ==
The LDC project uses two different CI systems: [https://travis-ci.org/ldc-developers/ldc Travis CI], which is Ubuntu x86 only but also tests pull requests, and [http://ci.lycus.org/job/LDC/ ci.lycus.org], which has three different x86_64 Linux slaves.
+
The LDC project uses multiple CI services, see the badges in [https://github.com/ldc-developers/ldc/blob/master/README.md README.md]. Azure Pipelines and Shippable take care of fully automated package generation + deployment to GitHub release.
  
Any efforts to get regular builds running on Windows and OS X would be very much appreciated.
+
Any efforts to get regular builds+tests on other platforms would be very much appreciated (especially non-x86 platforms, like ARM, PowerPC, ...).
  
 
== Style guidelines ==
 
== Style guidelines ==
Line 10: Line 10:
  
 
=== Formatting, etc. ===
 
=== Formatting, etc. ===
* Hard rules: 4 spaces for indentation, LF newlines.
+
* Use ''clang-format'' with the ''LLVM'' style for formatting.
* Use include guards consistently, in the format ''LDC_<dir>_<filename>'', e.g. ''LDC_GEN_DVALUE_H''.
 
 
* Includes are placed at the top of the file, sorted lexicographically and grouped in the following order:
 
* Includes are placed at the top of the file, sorted lexicographically and grouped in the following order:
 
*# Main include corresponding to the .cpp file, if any.
 
*# Main include corresponding to the .cpp file, if any.
Line 19: Line 18:
  
 
=== APIs and idioms ===
 
=== APIs and idioms ===
* Avoid making changes to the front end sources (''dmd'' and ''dmd2'') as much as reasonably possible. If modifications are necessary, be sure to enclose the deviations from the upstream source <tt>#if IN_LLVM</tt> blocks, and keep the original source code around for easier comparison. This makes it much easier to resolve any merge conflicts when folding in new frontend releases.
+
* Avoid making changes to the front end sources (''dmd'' directory) as much as reasonably possible. If modifications are necessary, be sure to enclose the deviations from the upstream source <tt>#if IN_LLVM</tt> blocks, and keep the original source code around for easier comparison. Add a comment referring to the relevant issue on our tracker, or the upstream DMD commit in case of a straight backport. This makes it much easier to resolve any merge conflicts when folding in new frontend releases.
 
* Use [http://llvm.org/docs/doxygen/html/ErrorHandling_8h.html#ace243f5c25697a1107cce46626b3dc94 ''llvm_unreachable("message")''] instead of ''assert(0 && "message")'' (from ''llvm/Support/ErrorHandling.h''). Not only might it lead to slightly better codegen, it also prevents »variable might be used uninitialized« and similar compiler warnings.
 
* Use [http://llvm.org/docs/doxygen/html/ErrorHandling_8h.html#ace243f5c25697a1107cce46626b3dc94 ''llvm_unreachable("message")''] instead of ''assert(0 && "message")'' (from ''llvm/Support/ErrorHandling.h''). Not only might it lead to slightly better codegen, it also prevents »variable might be used uninitialized« and similar compiler warnings.
  
 +
== Test suite ==
 +
 +
Tests are very important to prevent regressions. Upon fixing a bug, a test case should be added so that the bug stays fixed and doesn't come back by accident. Upon adding a new feature, extensive tests should be added that test all functionalities of that feature.
 +
* Bug fixing often starts from a minimal test case, which is a good basis for the eventual test case that is added to the testsuite.
 +
* Instead of creating tests <i>after</i> feature development, consider adding tests <i>during</i> feature development: it is a good way to solidify the feature implementation because often you will discover tricky cases that you didn't think of and that may steer the direction of development. The "Lit-based" testsuite makes it easy to run individual tests (run <tt>python runlit.py -v [testname]</tt> in your <tt>build/tests/</tt> directory) so there is no excuse! ;-)
 +
 +
LDC is tested by the [[LDC Lit-based testsuite]] and DMD's dmd-testsuite with LDC specific modifications and additions. The dmd-testsuite is put in the <tt>tests/d2/</tt> directory. The Lit-based testsuite is found in the subdirectories of <tt>tests/</tt>, excluding the <tt>d2</tt> subfolder. You can check the previously mentioned wiki page dedicated to the Lit-based test-suite for more info.
 +
 +
To <b>run the whole DMD test-suite</b>, you need to be in the <tt>/build</tt> directory of LDC and issue:
 +
 +
<tt>$ DMD_TESTSUITE_MAKE_ARGS=-j$(nproc) ctest -V -R dmd-testsuite</tt>
 +
 +
where <tt>$(nproc)</tt> is the number of processes to run in parallel. This will run the <i>whole</i> suite.
 +
 +
Running <b>a specific test</b> is a little more complicated than the Lit-based suite.
 +
Basically, you should figure out what the specific test you want needs. For example, tests in <tt>/compilable</tt> should be able to be compiled. Hence, you can test them by just compiling them
 +
(possibly with <tt>-c</tt>). Tests in <tt>/runnable</tt> should be able to run successfully and so you can compile (and run them) with <tt>-run</tt>.
 +
Some tests need extra work which is usually commented at the top of the file.
 +
 +
In all cases, you should use your LDC built binary for compilation.
 +
 +
== Merging a new DMD release ==
 +
 +
=== Updating druntime and Phobos ===
 +
 +
For each of the 2 submodules:
 +
 +
* Make sure you have both ldc and dlang git remotes. Otherwise add via <tt>git remote add dlang https://github.com/dlang/druntime.git</tt>.
 +
* Sync: <tt>git fetch --all</tt>
 +
* Create a new branch from current ldc head, e.g.: <tt>git checkout -b ldc-merge-2.079 origin/ldc</tt>
 +
* Merge the upstream tag, e.g.: <tt>git merge v2.079.0</tt>. Resolve any merge conflicts and make sure to include the conflicting files list in the merge commit message.
 +
* When confident, push it to the official repo: <tt>git push -u origin ldc-merge-2.079</tt>
 +
 +
=== Updating the dmd-testsuite repository ===
 +
 +
The [https://github.com/ldc-developers/dmd-testsuite dmd-testsuite] repository is derived from the upstream DMD repository by the [https://github.com/ldc-developers/ldc-scripts/blob/master/dmd-testsuite/update-dmd-testsuite.sh update-dmd-testsuite.sh] script in the [https://github.com/ldc-developers/ldc-scripts ldc-scripts] repository. Just run it without arguments (on Posix, not recommended on Windows), and it should update the repository with the latest commits/tags (given that you have Git installed and an SSH key configured for github.com). The changes should apply to the remote repository in a fast-forward fashion, never force a push.
 +
 +
After this is done, just merge the <tt>v2.<xyz></tt> tag into a new <tt>ldc-merge-2.<xyz></tt> branch derived from current <tt>ldc</tt> (see above), manually resolving any merge conflicts as necessary (most changes should definitely be upstreamed).
 +
 +
=== Updating the front-end ===
 +
 +
There are basically 2 approaches to this: either re-applying the LDC-specific mods onto the new upstream files, or applying the upstream patch onto the current LDC front-end. I tend to the latter with a workflow like this:
 +
 +
* Generate the DMD patch from the previous front-end version to the next one, e.g., in the [https://github.com/dlang/dmd DMD src tree]: <tt>git diff v2.078.3..v2.079.0 > ../2.079.0.diff</tt>
 +
* Edit the .diff file manually, preferrably with an editor supporting that format (collapsible sections per file diff etc.):
 +
** Remove obsolete file diffs. As of 2.079, we only care about files in the <tt>res</tt>, <tt>src/dmd</tt> and <tt>src/dmd/root</tt> directories, other directories can be ignored, incl. <tt>src/dmd/backend</tt>.
 +
** DMD's top-level <tt>res</tt> directory maps to LDC's top-level one, unlike the front-end code (<tt>src/dmd</tt> vs. <tt>dmd</tt>). If there are diffs in the <tt>res</tt> dir, apply them separately and remove the diffs from the edited .diff file.
 +
** Start virtually applying the edited patch onto LDC, e.g., in the ldc src tree: <tt>patch -p 2 -u --dry-run --merge -i ../2.079.0-patched.diff</tt>
 +
** It's most likely going to complain about missing files. That's because we don't use all files in the <tt>src/dmd</tt> directory. You can edit the .diff file in parallel while skipping the missing files, e.g., by looking up the diff for each missing file. If it's an incremental diff, LDC most likely still doesn't need that file, and you can remove the diff. If it's a full diff, i.e., a newly added file, the new front-end may require it, so keep it in mind.
 +
* When you feel ready, it's time to remove the <tt>--dry-run</tt> switch from the patch command, thereby really applying your edited patch.
 +
* Now on to the (potentially) hard work - fixing the merge conflicts (I usually grep for <tt>&lt;&lt;&lt;&lt;</tt>). This can be especially annoying if a code block with LDC-specifics (<tt>version (IN_LLVM)</tt>) has been removed or, worse, moved to another file. So if a conflict is pretty big & messy, it's often easier to check the LDC-specific diff for the old front-end file, and re-applying that onto the new file. If a block with LDC specifics has been removed, be sure to check whether it's just been moved/refactored, as the LDC specifics will need to be moved too. I usually look for a seemingly distinct code piece in that block and grep for it in LDC's <tt>dmd</tt> subdir.
 +
* Update the front-end version (and probably LDC version too) in <tt>CMakeLists.txt</tt> (<tt>DMDFE_*</tt>).
 +
* Update the git submodule hashes to the respective <tt>ldc-merge-2.079</tt> heads.
 +
* The first goal should be getting LDC to compile. So try building with <tt>ninja ldc2</tt> and fix all errors along the way.
 +
* Then try getting druntime and Phobos to build with that new LDC: <tt>ninja all</tt>
 +
** In case of nasty crashes, a likely culprit is a divergence of the D code with the C++ headers, as the latter are only minimally covered by DMD CI. Unfortunately, mismatching struct and vtable layouts can be hard to track down.
 +
* As soon as LDC can build druntime and Phobos, feel free to commit and push to a <tt>merge-2.079</tt> branch in the official LDC repo. Time to let CI do some testing on a multitude of platforms!
 +
* From now on, it's all about investigating and fixing failures, implementing new upstream features, disabling unfitting tests etc.
 +
* Special care needs to be taken for DMD's <tt>src/dmd/mars.d</tt>, containing its <tt>main()</tt>; LDC uses substantial parts too. Among other things, new DMD command line options show up there. Make sure to add them to LDMD's help output when/if implementing them.
 +
 +
== Releasing a new version of LDC ==
 +
 +
There is a separate page describing the LDC release process: [[How to release LDC]].
  
 
[[Category:LDC]]
 
[[Category:LDC]]

Latest revision as of 02:30, 15 February 2020

This page is a work-in-progress collection of tips for LDC contributors and development guidelines.

Continuous Integration

The LDC project uses multiple CI services, see the badges in README.md. Azure Pipelines and Shippable take care of fully automated package generation + deployment to GitHub release.

Any efforts to get regular builds+tests on other platforms would be very much appreciated (especially non-x86 platforms, like ARM, PowerPC, ...).

Style guidelines

Generally, use your good taste and try to use a style similar to the surrounding code. Many parts of the LLVM Coding Standards are directly applicable to LDC as well.

Formatting, etc.

  • Use clang-format with the LLVM style for formatting.
  • Includes are placed at the top of the file, sorted lexicographically and grouped in the following order:
    1. Main include corresponding to the .cpp file, if any.
    2. DMD and other LDC includes.
    3. LLVM/libconfig includes.
    4. System includes.

APIs and idioms

  • Avoid making changes to the front end sources (dmd directory) as much as reasonably possible. If modifications are necessary, be sure to enclose the deviations from the upstream source #if IN_LLVM blocks, and keep the original source code around for easier comparison. Add a comment referring to the relevant issue on our tracker, or the upstream DMD commit in case of a straight backport. This makes it much easier to resolve any merge conflicts when folding in new frontend releases.
  • Use llvm_unreachable("message") instead of assert(0 && "message") (from llvm/Support/ErrorHandling.h). Not only might it lead to slightly better codegen, it also prevents »variable might be used uninitialized« and similar compiler warnings.

Test suite

Tests are very important to prevent regressions. Upon fixing a bug, a test case should be added so that the bug stays fixed and doesn't come back by accident. Upon adding a new feature, extensive tests should be added that test all functionalities of that feature.

  • Bug fixing often starts from a minimal test case, which is a good basis for the eventual test case that is added to the testsuite.
  • Instead of creating tests after feature development, consider adding tests during feature development: it is a good way to solidify the feature implementation because often you will discover tricky cases that you didn't think of and that may steer the direction of development. The "Lit-based" testsuite makes it easy to run individual tests (run python runlit.py -v [testname] in your build/tests/ directory) so there is no excuse! ;-)

LDC is tested by the LDC Lit-based testsuite and DMD's dmd-testsuite with LDC specific modifications and additions. The dmd-testsuite is put in the tests/d2/ directory. The Lit-based testsuite is found in the subdirectories of tests/, excluding the d2 subfolder. You can check the previously mentioned wiki page dedicated to the Lit-based test-suite for more info.

To run the whole DMD test-suite, you need to be in the /build directory of LDC and issue:

$ DMD_TESTSUITE_MAKE_ARGS=-j$(nproc) ctest -V -R dmd-testsuite

where $(nproc) is the number of processes to run in parallel. This will run the whole suite.

Running a specific test is a little more complicated than the Lit-based suite. Basically, you should figure out what the specific test you want needs. For example, tests in /compilable should be able to be compiled. Hence, you can test them by just compiling them (possibly with -c). Tests in /runnable should be able to run successfully and so you can compile (and run them) with -run. Some tests need extra work which is usually commented at the top of the file.

In all cases, you should use your LDC built binary for compilation.

Merging a new DMD release

Updating druntime and Phobos

For each of the 2 submodules:

  • Make sure you have both ldc and dlang git remotes. Otherwise add via git remote add dlang https://github.com/dlang/druntime.git.
  • Sync: git fetch --all
  • Create a new branch from current ldc head, e.g.: git checkout -b ldc-merge-2.079 origin/ldc
  • Merge the upstream tag, e.g.: git merge v2.079.0. Resolve any merge conflicts and make sure to include the conflicting files list in the merge commit message.
  • When confident, push it to the official repo: git push -u origin ldc-merge-2.079

Updating the dmd-testsuite repository

The dmd-testsuite repository is derived from the upstream DMD repository by the update-dmd-testsuite.sh script in the ldc-scripts repository. Just run it without arguments (on Posix, not recommended on Windows), and it should update the repository with the latest commits/tags (given that you have Git installed and an SSH key configured for github.com). The changes should apply to the remote repository in a fast-forward fashion, never force a push.

After this is done, just merge the v2.<xyz> tag into a new ldc-merge-2.<xyz> branch derived from current ldc (see above), manually resolving any merge conflicts as necessary (most changes should definitely be upstreamed).

Updating the front-end

There are basically 2 approaches to this: either re-applying the LDC-specific mods onto the new upstream files, or applying the upstream patch onto the current LDC front-end. I tend to the latter with a workflow like this:

  • Generate the DMD patch from the previous front-end version to the next one, e.g., in the DMD src tree: git diff v2.078.3..v2.079.0 > ../2.079.0.diff
  • Edit the .diff file manually, preferrably with an editor supporting that format (collapsible sections per file diff etc.):
    • Remove obsolete file diffs. As of 2.079, we only care about files in the res, src/dmd and src/dmd/root directories, other directories can be ignored, incl. src/dmd/backend.
    • DMD's top-level res directory maps to LDC's top-level one, unlike the front-end code (src/dmd vs. dmd). If there are diffs in the res dir, apply them separately and remove the diffs from the edited .diff file.
    • Start virtually applying the edited patch onto LDC, e.g., in the ldc src tree: patch -p 2 -u --dry-run --merge -i ../2.079.0-patched.diff
    • It's most likely going to complain about missing files. That's because we don't use all files in the src/dmd directory. You can edit the .diff file in parallel while skipping the missing files, e.g., by looking up the diff for each missing file. If it's an incremental diff, LDC most likely still doesn't need that file, and you can remove the diff. If it's a full diff, i.e., a newly added file, the new front-end may require it, so keep it in mind.
  • When you feel ready, it's time to remove the --dry-run switch from the patch command, thereby really applying your edited patch.
  • Now on to the (potentially) hard work - fixing the merge conflicts (I usually grep for <<<<). This can be especially annoying if a code block with LDC-specifics (version (IN_LLVM)) has been removed or, worse, moved to another file. So if a conflict is pretty big & messy, it's often easier to check the LDC-specific diff for the old front-end file, and re-applying that onto the new file. If a block with LDC specifics has been removed, be sure to check whether it's just been moved/refactored, as the LDC specifics will need to be moved too. I usually look for a seemingly distinct code piece in that block and grep for it in LDC's dmd subdir.
  • Update the front-end version (and probably LDC version too) in CMakeLists.txt (DMDFE_*).
  • Update the git submodule hashes to the respective ldc-merge-2.079 heads.
  • The first goal should be getting LDC to compile. So try building with ninja ldc2 and fix all errors along the way.
  • Then try getting druntime and Phobos to build with that new LDC: ninja all
    • In case of nasty crashes, a likely culprit is a divergence of the D code with the C++ headers, as the latter are only minimally covered by DMD CI. Unfortunately, mismatching struct and vtable layouts can be hard to track down.
  • As soon as LDC can build druntime and Phobos, feel free to commit and push to a merge-2.079 branch in the official LDC repo. Time to let CI do some testing on a multitude of platforms!
  • From now on, it's all about investigating and fixing failures, implementing new upstream features, disabling unfitting tests etc.
  • Special care needs to be taken for DMD's src/dmd/mars.d, containing its main(); LDC uses substantial parts too. Among other things, new DMD command line options show up there. Make sure to add them to LDMD's help output when/if implementing them.

Releasing a new version of LDC

There is a separate page describing the LDC release process: How to release LDC.