On Maintaining on Old Side Project
Tags:
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.
Joy.
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).
Oops.
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.