My C++ Setup
Prelude
I have been trying to write some C++ for the past few days. I was just exploring the standard library and modern C++ features. It has been going good so far, as I have been using C++ just to solve some puzzle questions on some sites. Now, I want to level up the game. I would like to write and understand some real world code with C++.
There are some factors that need to be in check while writing OSS or some real-world project in any programming language like formatting, testing, building and packaging etc. JavaScript taught me this well. It is filled with a lot of things like npm, grunt, gulp, webpack, babel, yarn, standard, es-lint, js-hint etc. to help programmers.
Most of the modern languages are starting to understand these needs and are coming up with official tools. This leads to lesser time spent on making decisions about the development environment. Example: go gives go-fmt for formatting your code, rust comes with cargo for dependency management. (Reminds me of Zuckerberg’s answer to why he wears the same colored T-shirt everyday)
Unfortunately if you are writing C++ you need to sit down and create a build environment that works for you. This involves time and in my guess, it is the reason why not-so-many projects are bootstrapped with C++. Example: npm init
is all I need for convincing me to bootstrap a project in JavaScript and from there I know that I could easily pull in all the tools I need to achieve the development environment I need.
What do I need?
- Format code
- Produce executable
- Add dependency
- Run test
- Produce library
CMake
CMake is build system generator, which is popular and being recommended by a lot of people in 2018.
- Effective CMake
- Introduction to CMake
- How to create slides about CMake with CMake? : This one is short and interesting.
I have problems with CMake and I am not going to be secretive about it.
Learning curve is high for me as I feel that getting started guides and documentation of it is not sufficient enough. When you are trying to read the documentation for cmake, you will end up hitting the man page of it.
The man page, seriously? come on.
So, how do I learn CMake properly? “Buy a book that just costs 59.00$”
No, thanks.
I could feel that CMake is a powerful tool - power being in generators. It is even Open Sourced.
Another point is you need to learn its DSL to write CMake configuration file.
Day 6 of #100DaysOfCode
— Vishnu Bharathi (@scriptnull) November 21, 2018
Learning about CMake. Coming from a place wherenpm ...
does wonders, CMake feels complex. My first impression is learning a language for writing another language adds more barriers.
I just wish we had something simple.
My take on this is “I don’t need CMake now, but I might need it in future.”
Bazel
I met Bazel while exploring envoy.
{Fast, Correct} - Choose two
Build and test software of any size, quickly and reliably
It is open sourced and has good OPEN documentation and getting started guides. Another interesting feature for me is “One tool, multiple languages”.
Check out this cool blog post from envoy talking about how they use bazel.
I am going to give it a try.
Install
I am on macOS. So going with brew.
1 | brew tap bazelbuild/tap |
WORKSPACE and BUILD
A bazel project makes use of two files WORKSPACE and BUILD.
To start a bazel workspace,
1 | touch WORKSPACE |
Empty file, huh? not much use? nope. To my surprise this WORKSPACE file could be super powerful. I just realized it after seeing the workspace rules
The project could have one or more BUILD files inside, helping bazel to figure out how to build the particular directory.
Each instance of a build rule in the BUILD file is called a target
1 | cc_binary( |
cc_binary
is the rule and hello-world
is the target. For the full list of available rules check out this bazel docs page.
To run the hello-world
target,1
2# run from WORKSPACE root
bazel build //main:hello-world
This outputs the binary in bazel-bin
folder.
I recommended following this tutorial about bazel if you are interested in learning more about it.
Format code
After investigating, I came across clang-format
- a tool to format C++ code. There are also other interesting tools and ideas for tools on this Clang documentation page.
Some open source projects that use clang-format
are envoy and electron. In fact, electron even has this nice documentation on getting started with it.
1 | npm install -g clang-format |
Produce executable
Since we are using bazel, this is as simple as adding a new package called main and using cc_binary
rule in it.
1 | . |
Add the following rule to main/BUILD
1 | cc_binary( |
To build the package and run the app1
2
3bazel build //main:app
./bazel-bin/main/app
Add dependency
This page about managing external dependencies with bazel is quite useful. My best guess is that I will be trying to add non-bazel projects as third party-dependencies most of the time.
Let me try adding a testing library called catch2. First step is adding the git repository as needed artifact in the WORKSPACE file.
1 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") |
Next step is adding catch2 as dependency in the tests target.
1 | cc_test( |
And our test file would look like
1 |
|
woof! I spend quite few hours trying to figure this out! But finally, I got it working.
My first iteration made me to specify the header file like this
1 |
copts
to the rescue and now the header is accessible just as
1 |
Now that we have added a library as dependency for cc_test
, the process would be same for cc_binary
and cc_library
.
Run test
bazel test
command is used to run tests.
1 | # Run tests |
Produce library
By this time, we should have encountered cc_library
rule a couple of times. Yep! It could be used to produce libraries that are consumable by other projects.
An example straight from the docs
1 | cc_library( |
Conclusion
Finally, I am kind of peaceful about what I got done here. But I am on the look out for other options too. For example, in the middle of this I gave up on bazel once and checked out ninja for sometime (haha). But then I already invested in some amount of complexity already that is too hard to ignore and move on.
I also hope that C++20 modules would remove away a lot of complexity and make things easier.