On Maintaining on Old Side Project

Posted on February 19, 2018 by Richard Goulter

Recently I’ve had the motivation to try and maintain one of my side-projects.
Or at least, brush it up from being “completely awful” to “not terrible”.

This is mostly just for fun.
But in a way, it’s valuable in that I learn the lessons of what it’s like to maintain a project which isn’t freshly-written. That’s the kindof learning which fresh programmers can’t easily get.

Partly this also comes from wanting to be able to display a ‘portfolio’ of ‘look, I can program’.
Albeit, it’s hard to justify it from this perspective, since: - The original post mentioned here essentially says “I had a great personal brand, but didn’t get hired anyway”. - On the one hand, this is great in terms of ‘equal opportunity’, in that what supposedly matters is ‘ability to pass a tech interview’, not ‘ability to present yourself well’. And in theory the former ought to be better than the latter at predicting ‘will do a good job’. - I saw a post on LinkedIn where a guy pointed out that, although he had applied via LinkedIn to over 900 job listings, the only times he got a job was through networking. - On the other hand, this is concerning in terms of ‘equal opportunity’, in that effects of networking aren’t going to be as fair as a more ‘systematic’ process of finding a candidate. (Or so you’d think!).

So. While I think it makes sense that ‘polished side project’ means ‘good portfolio’ and a step towards ‘good personal brand’; it seems like tech doesn’t particularly respect ‘good personal brand’.
So, side projects are just for fun. :-)

Things I Didn’t Do/Understand in C++ as an Undergraduate

Properly Setting Up the Windows Build

The project is an old C++ project which was initially done for a computational geometry course. Generally, as an undergraduate, the process for such projects is “here’s how to manually install any dependency, here’s how to configure the project”.

My experience of not-C++ projects is “here’s the project dependencies file, and here’s the command to install the dependencies”.
I don’t know which language did this first, but this is my experience with e.g. Ruby, Python, Haskell, even Java/Scala.

This recent iteration of maintaining the side-project was aimed at getting the project to work on Windows. Previous refactoring efforts (e.g. to use CMake, adding a .travis.yml file to the repository) improved the project for Linux/macOS, but broke the Windows build.

I ran into a couple of problems getting the project working on Windows.
One was getting the compiler to find the dependencies. (On Linux, this is more/less a process of “use system package manager to install dependencies).
The other was that, I’d refactored the code to use a class called”Polygon”, and one of my files included <windows.h>, which has a function called Polygon which pollutes the global namespace and etc.
Fun to figure out.

Windows C++ development is made a bit more confusing in that there are different compilers / environments. e.g. MSYS2/MinGW, and MSVC (Visual Studio’s C/C++ compiler).
– It took me way too long to figure out fundamental things about these. In the end, I figured out the easiest thing to do was write a .bat script which adds the relevant compiler to PATH.

As an example of how ‘weird’ this is, CMake’s “MinGW Makefiles” didn’t work if MSYS2’s sh.exe was on PATH. With MSYS2 and MinGW on the PATH, the correct generator to use would be MSYS Makefiles.

Getting the Thing to Run (& Maybe Deploy)

My experience with programming in other languages:
If you can build the project, you can run the project.
My experience with C++:
Just because the project built, doesn’t mean that the project would run.

The reason for this is fairly straightforward: C++ builds the executable, and the dependencies in other libraries will get ‘linked’ dynamically when the program runs. (On Windows, from Dynamically-Linked Libraries. On unix, from shared libraries). And so these need to be on the PATH when running the program. But not when building the program.
– Perhaps you’ll forgive me if this wasn’t immediately obvious to me (given the ‘can build the thing => can run the thing’ expectation).

I’m aware that one way to get around this is to ‘statically link’ the dependencies into the built executable.
– But I’m not aware of a trivial/easy way to do this on Windows. (Especially not if there’re complicated transitive dependencies!).

Unfortunately, I’m still not aware of an easy way to find all these requisite DLLs for running the executable.
– It occurs to me that for “other languages”, I never really dealt with “how to deploy the program; how to share it with someone else”. This is one BIG advantage to JavaScript / web browser programs. (Or web applications).

VCPKG is Great

I am vaguely aware that conan.io is one piece of software trying to bring the concept of ‘modern language dependency installer’ to C++.

I saw that an example of competing software is vcpkg.
I tried it, it’s amazing.

It’s only for the Visual Studio compiler, and is analogous to pacman for MSYS2’s MinGW-w64 compiler.

It Just Works.
– You use it to install certain packages (e.g. OpenCV, Qt5) and it downloads the source, builds the project.

When using vcpkg to build a project, it copies the DLLs to the build directory which makes the program easier to deploy/share. Whoa!

As far as I can tell, static-linking with vcpkg would be easy, too, since vcpkg builds the packages from source.

One downside is that not all projects support all generators. (e.g. I couldn’t use ninja with OpenCV on Windows for MSVC. But this is upstream, and not vcpkg’s fault).

Compared to conan.io?
The vcpkg FAQ argues that vcpkg targets only Windows/MSVC, so doesn’t have to solve the problems which conan.io is trying to solve (and which are solved by distribution package managers like brew, pacman).
And that since vcpkgs is a privately-maintained repository of packages, rather than a ‘public federation’ of packages, there’s less effort required to find the dependencies which work for your project.

Setting Up AppVeyor

One of the cool things about open-source projects these days is the fancy badges they have saying “hey the project is working”.

Travis-CI is one such CI service which offers free builds for “open source” projects. (i.e. free stuff for public repositories).
But Travis-CI only supports Linux/macOS. The equivalent for Windows is AppVeyor.

Despite my newfound knowledge of how to build the project reliably on Windows, I found this not-so-easy.

Using vcpkg for the build is a nice idea. But vcpkg doesn’t use binaries for its packages. So although vcpkg makes it super easy to get a development environment going, … it takes too long for an AppVeyor build, if using sufficiently heavy dependencies. (AppVeyor builds time out after an hour).

Fortunately, the machine does have common heavy dependencies like Qt5 installed already.
But it didn’t have OpenCV.

It may be that using chocolatey (Windows’ answer to macOS’ homebrew) to install OpenCV is probably ‘best’.
But I wanted to keep with vcpkg.

The main difficult I had was to cache the vcpkg directory. It took me some time to realise that I was the caching didn’t affect the init section of the AppVeyor build. (i.e. the cache only gets loaded for the build step, and cache gets saved after the build step).

Thoughts on the C++ Language

The above remarks (e.g. about not knowing how to find DLLs, & that it’s hard to install dependencies) are more about the environment C++ runs in, and the developer ecosystem.
But considering C++ as a language:

Okay to be fair, I don’t think anyone really defends C++ as a “good” language (aside from maybe the pragmatic case that “it’s what people use”, and that having an imperfect which works is better than not having anything, etc.).

C++ is monstrously complex. (e.g. a C++ parser is something which has to be hand-written, since the language is so complex). “Effective C++” describes C++ as a ‘federation of four languages’. (If I recall correctly: C, C with classes, standard-template library, and template meta-programming).
– There are competing languages for “better C” replacement: Go, Rust, and then more obscure languages like D (which I’ve heard is really nice, but seems to lack the community which e.g. Rust has gunned for).

What I’ve come to realise maintaining this C++ project is that there’s a lot of C++ stuff which I’d consider ‘fundamental’ in other languages, where with C++ “I just don’t know it”.
And I don’t think other languages suffer this problem as easily.

The details sound fairly easy, but it’s not necessarily clear how they combine.
For example: individually the words const, pointer, reference are individually easy to understand, but very quickly difficult to understand when combined.
Especially for someone who hasn’t maintained a C++ project in a long time.

e.g. Coming from other languages, it may not be clear that there’s a difference between ‘direct initialization’ and ‘copy initialization’. cf. CPP Reference.

Every language inevitably has a ‘long tail’ of obscure details that an experienced programmer will run into. I don’t even know what kindof weird stuff that’d involve for C++!
But with C++, I feel it’s more like the ‘lowest common denominator’ of being able to write simple C++ programs (e.g. “Undergrad C++”) doesn’t cover very much.

Concluding Thoughts

One way to summarize the above is I’ve gone from ‘I wrote a project in C++ while not knowing much about C++’ to ‘I had to learn a bit more about how C++ stuff works for polishing the project’.
– I kindof have mixed thoughts about these things. It’s .. nice that with programming, it’s ‘easy’ to do a lot of cool stuff without having a good understanding of what’s really going on.
But it also seems a bit uncraftsmanlike to make stuff with such a poor understanding.

I think polishing this side-project a bit has been a good project for understanding what to avoid in a project, and what’s good to have.

Newer post Older post