ninja backend for coremake

C and C++ is known for its abysmal dependency and build system situation. For historic reasons a build system has not been standardized thus low level build tools such as make and ninja as well high-level build tool generators such as Autotools, CMake, meson and entire build frameworks like bazel have come and gone throughout the years.

A lesser known build generator is coremake. And lets be honest: it is a horrible, underdocumented piece of shit written in hard-to-follow spaghetti C. I would not even call it a build system generator it is more a static generator tool that reads its own undocumented domain-specific language (which is actually not that bad because like meson it can only declare dependencies) written in .proj files and a so-called “platform” file that, amusingly, ends in .build. That platform file is does not just specify a certain tool chain and maybe CFLAGS but is a hodge podge of that and instructions how to generate output. As you can tell from the official repo, insane people have done the insane and written generator code for Android, make, Visual Studio and whatnot.

Since, the make backend defaults to recursive Makefiles (booo), I was tempted to add one more to the whatnots and that is ninja. Some may know it as the default backend of meson and an optional backend of CMake. Anyhow, on a scale of 1 (make) to 10 (bazel) in terms of stuff, it ranges somewhere between 0.01 and 0.02 thus all logic must come from a meta build system. And to spare you the work of figuring this out, here is something for the second advent which allows you doing this

$ coremake ninja-clang-linux_86_64
$ ninja
$ ninja -t compdb > compile_commands.json

Adding basic ninja support to coremake was a small adventure. Although ninja’s description language is super easy to follow, figuring out coremake’s quirks concerning scoping and path makes you want to puke and I am certain I have only scratched 5% of the disgusting surface.


Canon EOS RP under Linux

Canon shipped their newest and cheapest full frame camera, the Canon EOS RP, end of February. In their infinite wisdom, they decided that they had to to develop a new CR3 format for this as well as the previously released EOS R and M50 cameras. And of course, so far no one was able to reverse-engineer the raw format entirely, so it will take a good while until those cameras will be supported by all major open source raw programs out of the box. The last option is running the Adobe DNG Converter in a Windows VM or under Wine to convert the proprietary CR3 format into their (intermediate) DNG format. After a good month, Adobe released a new version that supported the RP … but it stopped working under Wine 🤦. So, whoever stumbles upon

Unhandled exception: unimplemented function api-ms-win-core-winrt-error-l1-.GetRestrictedErrorInfo called in 64-bit code

open winecfg, go to Libraries, add two entries for api-ms-win-core-winrt-error-l1-1-0 and deactivate them. The DNG converter will complain but it will work (much faster than in a Windows 10 VM!) nevertheless.


Time tracking with Ledger

Whoever is reading this blog knows that I use Ledger-likes to track my finances. Some of you may also know that the currency is just some arbitrary name for any kind of unit. And a minority of you may also know there is special support for tracking time in the original Ledger program. This post explains how I use Ledger and a few Bash aliases to track different activities during a normal work day.

In a “regular” Ledger file you will find transactions that describe the flow of a commodity from one (or more) to another account at a certain time. There is however special support for timelog entries in the ledger program. They look similar but have special syntax to describe the start and end of a “transaction”

i 2019/03/02 00:30:20 Entertainment:Netflix
o 2019/03/02 00:35:20

which basically state what to account from i to o. Suppose you have a timelog file foo.ledger then ledger -f time.ledger bal would give you something like this:

               9.07h Work
               32.3m    Mail
                6.9m    Admin
               8.40h    Development
               1.69h Entertainment:Netflix
               14.2m Drinking
               11.9m Meetings
--------------------
               11.19h

Of course, you can customize the date range and hierarchy depth. Let’s alias that to

alias wasted='ledger -f ${TIMELOG} bal -b $(date -dlast-monday +%m/%d) --depth 2'

All good. Now, adding new entries by “punching in” new lines like that above is more than just cumbersome, it’s something a simple alias could do as well. I have defined something like

alias clock-in='echo i $(date +"%Y/%m/%d %H:%M:%S") >> ${TIMELOG}'
alias clock-out='echo o $(date +"%Y/%m/%d %H:%M:%S") >> ${TIMELOG}'

where TIMELOG points to the time.ledger file. Once you type

$ clock-in Work:Mail

an appropriate transaction will be made. To check what’s currently going on, this alias might help:

alias clock-status='[[ $(tail -1 ${TIMELOG} | cut -c 1) == "i" ]] && { echo "Clocked IN to $(tail -1 ${TIMELOG} | cut -d " " -f 4)"; wasted; } || { echo "Clocked OUT"; wasted;}' 

That all looks nice and dandy until you realize you don’t remember a particular activity you want to account you current time on. If you have used any CLI program you probably hit Tab more than twice. Wait no more, just define


function _clock_in ()
{
    local cur prev
    _get_comp_words_by_ref -n : cur

    local words="$(cut -d ' ' -s -f 4 ${TIMELOG} | sed '/^$/d' | sort | uniq)"
    COMPREPLY=($(compgen -W "${words}" -- ${cur}))
    __ltrim_colon_completions "${cur}"
}

complete -F _clock_in clock-in

and you are all set to complete the timelog entry while you clock in. You could write more elaborate logic in an appropriate scripting language but that’s an exercise for the reader.


meson and Google Test

I wrote about meson the awesome build system before. For C-based projects with many test executables there is nice infrastructure, however many C++ projects probably use Google Test or Catch and a single binary which runs the entire test suite. This is all nice and dandy with Google Test if you compile and link the test executable straight from the unit test source files. If however you build intermediate static libraries for organizational reasons you will quickly notice that Google Test won’t run anything at all because the symbols from Google Test itself won’t end up in the final binary without specifying the --whole-archive flag. Luckily, meson got the link_whole parameter since version 0.46, so instead of declaring your static test library as

test_lib = static_library('testlib',
  sources: test_sources,
  dependencies: [gtest_dep] + build_deps,
)

test_dep = declare_dependency(
  link_with: test_lib,
  dependencies: other_deps,
)

test_binary = executable('testfoo',
  sources: ['main.cpp'],
  dependencies: [test_dep],
)

you would change test_dep to

test_dep = declare_dependency(
  link_whole: test_lib,
  dependencies: other_deps,
)

and run your tests as usual.