In view of the 2023 Google Summer of Code program, I decided to pick an organisation and project to work on. My interests are mainly in programming language semantics and compiler construction, so I decided to look for a compiler project. Since I'm a Lin - (erm) - GNU/Linux user, seeing GCC as an organisation immediately piqued my interest. Looking at their recommended projects for GSoC, I saw that many of the projects were concerned with the new Rust frontend for GCC, gccrs. Having wasted an alarming part of my life writing C++ and Rust, I decided that contributing to gccrs would be a fun project.

This is a walkthrough of how I got started with contributing to gccrs, as well as a guide for anyone that wants to join in, but doesn't know where to start.

⚠️ This is not an official guide. It is just a collection of my personal experiences and tips of how to solve the problems I encountered. If you have any questions, please ask them on the gccrs zulip chat.


Getting started

Introducing yourself to the community

The first step to contributing to gccrs is to introduce yourself to the community. The recommended way to do this is to join the gccrs zulip chat. Everyone I have interacted with on the zulip chat has been very friendly, kind and helpful, so I strongly recommend joining the chat, even if you don't have any technical questions at first.

Setting up your environment

The next step is to set up your environment, in order to build and test gccrs.

First thing to do, is to fork the gccrs repository on GitHub, and clone your fork to your local machine

git clone https://github.com/<your-username>/gccrs

You should consider creating a branch for your changes, so that you can keep your master branch in sync with the upstream master branch, but in this guide we will be working on the master branch for the sake of simplicity.

Next, you need to install the dependencies required to build gccrs. If you are on a debian based distribution, you can install the dependencies by running

apt install build-essential libgmp3-dev libmpfr-dev libmpc-dev flex bison autogen gcc-multilib dejagnu

Now, you are ready to build gccrs 🎉. The GNU toolchain projects are designed to be built outside of the source tree, so we must create a build directory on the same level as the gccrs directory, and run the configure script from there.

mkdir gccrs-build
cd gccrs-build
../gccrs/configure --prefix=$HOME/gccrs-install --disable-bootstrap --enable-multilib --enable-languages=rust

Building gccrs

The next step is to run make to build gccrs, but not quite yet. We have 2 small issues to discuss first:

  • This is a make based build, which means that, even if we are successful in building gccrs, when we try to edit the code, our preffered editor will not be able to provide us with auto completion, or any other IDE features. To fix this, we will use the bear tool, a tool that generates a compilation database from a make based project, so you can have the full IDE experience with VSCode, Vim, Emacs, etc.

  • The build takes a long time, so we will use the -j flag to run multiple jobs in parallel. Usually, the way we pass this argument is -j=$(nproc), which means to run as many parallel jobs as the number of processors on your machine, but since it takes a long time, I prefer to use -j=$(( $(nproc) - 2 )) so I can watch some Family Guy while the build is running and not have to worry about my computer freezing.

So now, finally, you can build gccrs

bear -- make -j=$(( $(nproc) - 2 ))

Testing and installing gccrs

So, 2 days have passed, your computer is now a furnace, and you have a working gccrs compiler. The next step is to test it

make check-rust

[Edit] The make check-rust can also be run in parallel, so you can use the -j flag here as well (thanks Marc!).

and install it

make install

This will install gccrs in $HOME/gccrs-install/bin. You can now use gccrs to compile rust code, by running

$HOME/gccrs-install/bin/gccrs -c test.rs -o test.o -frust-incomplete-and-experimental-compiler-do-not-use

⚠️ You may be tempted to add $HOME/gccrs-install/bin to your path, and you certainly can, but it takes some caution. The bin directory contains a lot of executables, and if you add it to your path, you may end up using the wrong executable. For example, the directory also includes a gcc executable, that you most probably don't want to use. So, if you do decide to add it to your path, make sure to add it at the end of your path, so that the executables in the directory are only used if the executables in your other directories are not found.

Contributing to gccrs

If you have been able to follow this guide up to here without any issues, you are now ready to start looking at the gccrs code.

Browsing the code

You don't need to know the structure of the whole project; the code that we are mainly interested in is located at the gcc/rust directory, for the rust frontend, and the gcc/testsuite/rust directory, for the rust tests.

The most popular code editor nowadays, especially to younger developers, is VSCode, so I will describe the process of setting up VSCode to work with gccrs.

Go ahead and open VSCode in the gccrs directory. You will need 2 extensions to be able to work with the code:

  • C/C++: This extension is the basic VSCode extension for C/C++ development, and provides many useful features, of which we won't use everything, as we will be using the clangd extension for the C++ language server.
  • clangd: We will be using this extension for the C++ language server, because it provides better and faster indexing, completion etc, and also because it can understand the compilation database that we generated with bear when building to provide these.

⚠️ When you install the clangd extension, you will be prompted to disable some of the features of the C/C++ extension, as they are provided by the clangd extension. You should do this, as the clangd extension provides better and faster support.

Lastly, we need to tell clangd where to find the compilation database we generated previously. The 'compilation database' is the compile_commands.json file that was generated in the gccrs-build directory. Usually, clangd expects to find this file at the project root, so we can just create a symlink to it in the gccrs directory.

ln -s ../gccrs-build/compile_commands.json .

Now, you should be able to open any file in the gccrs directory, and VSCode should be able to provide you with auto completion, go to definition, etc.

Finding a task

Now, it's time we started looking at some issues at the gccrs GitHub page. The good people of gccrs mark some issues with the good-first-pr label, which means that they are good issues for new contributors to start with. You can find all the issues with this label here. Really, the first issue you should be looking for shouldn't be about you fixing a bug, but rather about getting accustomed with the workflow of contributing.

Changing the code

So, you have found an issue that you want to work on, and you have a general idea of how you want to fix it. We wil assume that we are making a change that modifies at least some code, and that we need to write at least one test to go along with our change.

Formatting the code

  • The tool we use to format code is clang-format. clang-format expects a .clang-format file to be present in the project root directory. The clang format file gccrs uses is located at the contrib directory, so we can create a symlink to it in the project root.

    ln -s contrib/clang-format ./.clang-format
                # ^              ^ but we put a . in front of it in the project root, because it is the default name
                # | notice this file here doesn't have a . in front of it
    

    Then, you can install the clang format VSCode extension, and now you will be able to format the gccrs code correctly, using the Format Document VSCode command.

  • You may notice that VSCode messes up the way the indentation is rendered on some lines. This can be fixed by

    • clicking the Spaces button in the bottom right corner of the VSCode window, and selecting Indent Using Spaces with a tab size of 2.
    • clicking the Spaces button again, and selecting Change Tab Display Size with a tab size of 8.

Building your changes

Now, you have made your changes, and you want to build them. You can do this by running

cd ../gccrs-build
bear --append -- make all-gcc -j$(nproc)

Here we notice 2 things:

  • This time we used the --append flag, which means that we are appending to the existing compilation database, instead of overwriting it. If we didn't use this flag, we would have to make a clean build every time we wanted to build our changes.
  • We don't build the whole project, but only the all-gcc target. This is essentially rebuilding only the parts that we are interested in, so we don't have to wait for the whole project to build.

We don't have to do anything else, since we already have a symlink to the compilation database in the gccrs directory.

And don't forget to make install your changes, so you can test them.

Writing tests

Now that you have made your changes, it is time to write one or more tests that reflect the changes you made. The tests are located in the gcc/testsuite/rust directory. The 2 main subdirectories that we are interested in are compile and execute. The compile directory contains tests that are expected to compile (or error out at compile time), and the execute directory contains tests that are expected to compile and run successfully (or error out at run time). There, you can find many examples of tests that you can use as a reference. The tests are written in Rust, but have some special comments that are used by the test framework dejagnu. You can find more information about the syntax of these tests here, but it is easy enough to parse the existing tests to figure out what is going on.

Pushing the changes

By now you should have:

  • Made some changes
  • Compiled gccrs with these changes
  • Written tests showcasing the changes
  • Ran and passed the tests

So now it's time to commit your changes.

Staging

First, we need to stage our changes. We can do this by running

git add .

[Edit] Using git add . is not recommended, as it may add files that you don't want to commit. Instead, you can use git add <file1> <file2> ... or git add -i to stage your changes interactively (thanks Marc!).

You don't have to worry about the .clang-format and the compile_commands.json you created, as they are already included in the .gitignore file.

Now it's a good time to run git clang-format to make sure everything is formatted correctly.

Committing

The commit message you write must have a specific format:

  • The top of the commit message must have a brief summary of the changes as well as a reference to the issue you are working on, of the format Rust-GCC#<issue number>

  • The body of the commit message must be a changelog entry. This can be generated for you by running

    git diff --cached | contrib/mklog.py
    

    Go ahead and copy the output of this command into the body of your commit message, and write a short description of the changes you made to each file after the : character after each file name.

    ⚠️ Be careful to keep each line of the commit message at most 100 characters long, or else the commit message will be rejected by the CI.

  • The commit message should end with a Signed-off-by line, which is of the format Signed-off-by: <your name> <your email>. This is used to verify that you are the author of the commit, and that you have the right to contribute the changes you made.

    [Edit] You can also use git commit -s to automatically add the Signed-off-by line to your commit message (thanks Marc!).

Pushing and creating a pull request

Now that you have staged and committed your changes, you can push them to your fork. You can do this by running

git push

This will push your changes to the master branch of your fork. You can now open a pull request on the gccrs GitHub page, and wait for someone to review your changes.

Reviewing

Now that you have opened a pull request, you will have to wait for someone to review your changes. Your changes may be accepted as is, or you may be asked to make some small changes, in which case you will have to repeat the process of staging, committing, and pushing your changes. If you are asked to make a lot of changes, you may also be asked to squash your commits into a single commit (look here for more information about squashing commits).

Be patient! The gccrs maintainers are all volunteers who are doing this in their free time, so it may take a while for them to review your changes.

Conclusion

That was it! You can now have some tea and relax, because you have just contributed to GCC!

What an honour! 🥳