2024-12-05 December Adventure 2024: Day 5

December Adventure 2024: Day 5

MyCmd Development Log

I worked on an outline for a new post in my MyCmd development log, which is about what are essentially higher order functions and functions as first class items in Bash, if you squint. I’ll also write about another feature I use in Bash a lot, array references.

I plan on writing and publishing the post tomorrow.

MyCmd Concepts and Conventions Documentation and Cleanup

I’ve pushed some commits to document MyCmd concepts and follow naming conventions:

Cognitohazards and Side Quests: Shell Script Profiling

Since Bash doesn’t have anything syntax-wise to support namespaces or objects, I try to “simulate” them by naming conventions of my Bash functions. Bash function names can include periods, colons, and dashes. I realized that I was using colons haphazardly – both for functions around “object-like” things, like a method for a Command Group, mycmd:command_group.get_source_directory and as a general namespace in more general functions, like mycmd:pathname.is_valid_absolute_path. I wanted to analyze all of the functions I’ve written with colons in their names. For things related to “object-like” things, like the Command Group code, I wanted to imagine if this were Python, what would that function be?

I started by looking at mycmd:command_group.source_file_path_for_directory (see mycmd-command-group-lib) and I realized in order to classify it, I needed to see what calls it. I needed the call hierarchy. This brings up one of the challenges I alluded to in Why Bash?: I sometimes need to create my own tooling in order to develop Bash in the manner I want.

This made me remember I had some code lying around that allows me to generate flamegraphs for profiler visualization from the execution of a shell script. I cleaned this up and committed it in bec9db54a18b8e66df2f8f71df19dbac2098d2f4. This code uses flamegraph.pl to generate a SVG.

I intend on writing a separate Development Log entry describing this in detail, but briefly:

First, this code snippet:

    if [[ -n "${MYCMD_SHELL_TRACE-}" ]]; then
        if [[ -n "${MYCMD_TRACE_LOG_FILE-}" ]]; then
            PS4='+[${EPOCHREALTIME}][${BASH_SOURCE}:${LINENO}]:[${FUNCNAME[*]}]:'
            export PS4

            exec {_MYCMD_TRACE_LOG_FD}>>"${MYCMD_TRACE_LOG_FILE}"
            readonly _MYCMD_TRACE_LOG_FD
            export _MYCMD_TRACE_LOG_FD
            BASH_XTRACEFD="${_MYCMD_TRACE_LOG_FD}"
        fi

        set -o xtrace
    fi

I then have support/process-trace-logs.py to convert the output of this xtrace output into the perf format required by the flamegraph.pl tool.

As an example, this is from the execution of the current bin/mycmd, which is hard-coded to execute mycmd project list-tasks, using the mycmd-devel task. I then use the create-flamegraph-from-shell-trace task to generate the SVG.

Executing create-flamegraph-from-shell-trace

This results in the following flamegraph:

mycmd project list-task flamegraph

Even as cool as this visualization is, the astute among you might be asking, what does this have to do with my original question of call hierarchy? I can take the code in support/process-trace-logs.py and use it to output in a different format the call hierarchy of all of the functions being called. I think I will use this tomorrow to generate a tool to query the relationship among my code as I am figuring out my naming of functions.

Janet Learning: Aspirations Project

I did a small amount of work on my aspirations project to learn the Janet Language.

I published commit f8085753ba2291080aed8ea8f2c62b3cd6d0ee90 to add some programming quotes as test data, borrowed from this page, as I would rather keep my personal aspirations private.

I attempted to add some code to handle the command line arguments in my script, pushed in commit a421e35db692797396c7475516ca6dd91010d47f. However, I am running into some challenges.

I have been attempting to use a user local install using the --local parameter to jpm (mentioned in the documentation here) so I could install dependencies into the project local directory ./jpm_tree instead of a system wide location.

However, when I attempt to use a module from the Janet standard library, such as os in the above commit, I get the following error when attempting to do a build:

generating executable c source build/aspirations.c from main.janet...
error: could not find module os:
    /Users/travisbhartwell/Developer/Personal/aspirations/jpm_tree/lib/os.jimage
    /Users/travisbhartwell/Developer/Personal/aspirations/jpm_tree/lib/os.janet
    /Users/travisbhartwell/Developer/Personal/aspirations/jpm_tree/lib/os/init.janet
    /Users/travisbhartwell/Developer/Personal/aspirations/jpm_tree/lib/os.so

I am sure there is a way around this. I’ve done a lot today, so I will try to research this problem tomorrow.

What It Means To Be Open

Someone shared this amazing talk by Lu Wilson from Heart of Clojure 2023, “What it means to be open”:

Lu can be found on TodePond.

I found the talk to be very inspiring. I’ve enjoyed being more open about even the little changes to my dot files on my work log here for December Adventure. This talk and my experience so far motivates me to want to continue this practice even beyond December Adventure. I may not set the goal of a post every day beyond December Adventure, but rather posting whenever I have done something, even a tiny little experience. To quote Lu, “Normalise sharing scrappy fiddles!”

Tasks for Tomorrow


All of my December Adventure 2024 posts will be linked from here.