Posts tagged dev


My current gitmessage template

:: dev

By: Maciej Barć

It is possible to configure git to use a extra helpful git message when inside the git commit editor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#
# When committing follow those rules:
#   1. Use the Conventional Commits style when committing!
#      Follow this template:
#        > <type>[optional scope]: <description>
#        >
#        > [optional body]
#        >
#        > [optional footer(s)]
#      Allowed commit types are:
#        fix, feat, build,
#        chore, ci, docs,
#        style, refactor, perf,
#        test.
#      See also: https://www.conventionalcommits.org/en/v1.0.0/
#   2. Remember to add a "Signed-off-by"!
#      For example:
#        > Signed-off-by: John Smith <js@js.org>
#

Save that as .gitmessage on repo root.

Then wire it to local git config with:

1
git config --local commit.template .gitmessage

See also:

Scribble hacks: insert module code

:: dev, lisp

By: Maciej Barć

get-module-content for showing module implementation

Normally Scribble, the Racket’s documentation tool, does not provide a way to see how a function/object/module is actually defined in the code. Just recently I wound a good-enough way to insert the whole content of the module we are documenting with Scribble.

Here’s my implementation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

The input is a module name in the form that you would use in absolute require form, except that it is a string, not a bare syntax.

get-module-content will reach out to Your Racket package database and find a module that you gave. It will read it and remove all comments. It returns a Racket string.

The string? returned by get-module-content can later be used to either extract some more info but it can be effectively used to insert the full module implementation block.

get-module-content usage inside Scribble

In Scribble I insert the module source code like so:

1
2
3
The source code of this module:

@codeblock[@get-module-content{mypkg/utils/path}]

See also

Common Project Layout, Version 1… A Rough Draft #1

:: dev

By: Maciej Barć

For CPL version 0 see common-project-layout-version–0. A template project for CPL v1 can be found at gitlab.com/xgqt/xgqt-misc-template-cplv1.

To solve the repository layout problem we propose this hierarchy:

  • separate the code from text/prose by clear distinction on top-level — thus the code dir
  • for code we create this structure:
    • authoring — authoring scripts for developers
    • copyright — copyright documents for the code
    • integration — integration scripts, ie CI/CD scripts or Groovy sources for Jenkins
    • source — versioned application/library sources, each major version is assigned a v<MAJOR> directory; this allows us to keep and maintain multiple versions, it is very reminiscent to having multiple package versions in a Ports (FreeBSD/Gentoo) repository
      • for the v<MAJOR> dirs we separate each bag of sources or scripts into its own directory, for example:
        • 3rd-party
        • admin
        • build-support
        • run
        • <APPNAME>-app
        • <APPNAME>-lib

Tutorial: PowerShell Modules in FSharp

:: dev, dotnet, fsharp, powershell, tutorial

By: Maciej Barć

F# is a amazing functional language bridging C#/.NET ecosystem with OCaml and interactive programming. It has recently became by favorite language to create personal projects in. It both has additional security of strong typing, unlike Python or Ruby while keeping the functional and interactive properties of weak-typed languages. F# is truly a engineering marvel.

PowerShell 7 is the Open-Source implementation of the old Windows PowerShell that is also cross-platform and can be used for scripting and automation. It’s Object-orientation makes an amazing extension capability compared to Bash.

You can use the F# language to create PowerShell modules. Normally PS Modules are written in C# but since the interop between .NET languages is insanely fluent one can just swap C# for F#.

Creating new project

1
2
mkdir -p ~/source/temporary/powershell-modules/fs-example
cd ~/source/temporary/powershell-modules/fs-example

Dotnet ClassLib

Init new F# Class project:

1
dotnet new classlib --language "F#"

Dependencies

Add dependency on Powershell interaction library:

1
dotnet add package PowerShellStandard.Library --version 5.1.1

F# interaction with PowerShell

Let’s rewrite Library.fs to contain:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace FsExample.PowerShell

open System.Management.Automation

[<Cmdlet(VerbsCommon.Format, "FsExample")>]
type FsExample() =
    inherit PSCmdlet()

    [<Parameter>]
    [<ValidateNotNullOrEmpty>]
    member val Name: string = "Me" with get, set

    override this.BeginProcessing() =
        printfn "Hello %s" this.Name

Then, change RootNamespace to the main module name, that is FsExample.PowerShell.

We need to copy all assemblies to output. Add <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <RootNamespace>FsExample.PowerShell</RootNamespace>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>

    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>

  <!-- Snip ... -->

</Project>

PowerShell library

Create PSModuleAssets directory:

1
2
mkdir -p PSModuleAssets
cd PSModuleAssets

The main module file will in turn load the DLL compiled form above F# code.

Create main module file - fs-example.psm1:

1
2
3
#!/usr/bin/env -S pwsh -NoLogo -NoProfile -NonInteractive

Import-Module "$PSScriptRoot/fs-example.dll"

We then need to create the main manifest file fs-example.psd1.

  • Path - relative path to output the manifest,
  • RootModule - relative module path to load on module import,
  • ModuleVersion, Description, Author and Copyright are some of standard metadata fields,
  • AliasesToExport, CmdletsToExport, DscResourcesToExport, FunctionsToExport and VariablesToExport are either globs or lists that tell what functions will be available on module load, for simplicity we will just specify a glob expression '*'.
1
2
3
4
5
6
New-ModuleManifest -Path ./fs-example.psd1 -RootModule ./fs-example.psm1 `
    -ModuleVersion "1.0.0" `
    -Description "FSharp example module" `
    -Author "Me" -Copyright "Copyright (c) 2025, Me" `
    -AliasesToExport '*' -CmdletsToExport '*' `
    -DscResourcesToExport '*' -FunctionsToExport '*' -VariablesToExport '*'

Copying PowerShell assets

Following ItemGroup will copy all files from PSModuleAssets to the output directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<Project Sdk="Microsoft.NET.Sdk">

  <!-- Snip ... -->

  <ItemGroup>
    <None Include="PSModuleAssets/*.*">
      <Link>%(Filename)%(Extension)</Link>
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

  <!-- Snip ... -->

</Project>

Building the F# module

Clean up the old builds:

1
2
rm -fr ./psrepository
rm -fr ./out-module/fs-example

Dotnet-Build

And then build the module:

1
2
dotnet restore
dotnet build --configuration Release --output ./out-module/fs-example

Language locality bug

You have to use the en-US locale when publishing PowerShell modules.

1
2
3
$env:LANG = "en_US.UTF-8"
$env:LANGUAGE = "en_US.UTF-8"
$env:LC_MESSAGES = "en_US.utf8"

Installation

Remove old registered PowerShell repository:

1
try { Unregister-PSRepository -Name fs-example } catch { }

Register-PSRepository and Publish-Module

This quite complicated script will:

  • set up a PowerShell repository in current location,
  • register the PowerShell repository,
  • publish the module into the PowerShell repository,
  • install that published module from the PowerShell repository.
1
2
3
4
5
6
7
8
$repo = "fs-example"
$repoPath = "$pwd/psrepository/fs-example"

New-Item -ItemType Directory -Path $repoPath | Out-Null
Register-PSRepository -InstallationPolicy Trusted -Name $repo -SourceLocation $repoPath

Publish-Module -Verbose -Path $pwd/out-module/fs-example -Repository $repo
Install-Module -Scope CurrentUser -Force -Verbose -Name fs-example -Repository $repo

Removal

Uninstall-Module

Just call Uninstall-Module to remove any PowerShell module.

1
Uninstall-Module -Name fs-example

Usage in the REPL

Import-Module

Import it with Import-Module.

1
Import-Module -Verbose fs-example -Force

Finally - use Format-FsExample

Remember the above F# function that was defined in the class?

1
2
[<Cmdlet(VerbsCommon.Format, "FsExample")>]
type FsExample()

It will be now available under the name composed of the “common verb” and a secondary part, so Format-FsExample.

Try it:

1
Format-FsExample -Name Maciej

GHC 9.8 and Cabal 3.10.3 on Gentoo Linux

:: dev, gentoo, haskell, tutorial

By: Maciej Barć

The official ::gentoo repository currently contains only GHC on version 9.2.8. To install newer GHC one has to either download/build themselves or use the ::haskell overlay (https://github.com/gentoo-haskell/gentoo-haskell).

Enable the ::haskell overlay

Enable:

1
eselect repository enable haskell

Sync:

1
2
3
emerge --sync haskell
egencache --update --repo haskell --jobs 12 --load 6
eix-update

Unmask needed packages

Add to /etc/portage/package.unmask/0000_hs.conf

1
2
3
4
5
6
7
8
<dev-lang/ghc-9.9

<dev-haskell/cabal-3.11
<dev-haskell/cabal-install-3.11
<dev-haskell/cabal-install-solver-3.11
<dev-haskell/cabal-syntax-3.11
<dev-haskell/text-2.2
<dev-haskell/parsec-3.1.18

Add to /etc/portage/package.accept_keywords/0000_hs.conf

1
2
3
app-admin/haskell-updater
dev-haskell/*
dev-lang/ghc

Install

1
emerge --ask --verbose ">=dev-lang/ghc-9.8" ">=dev-haskell/cabal-install-3.10"

Build of GHC 9.8 takes around ~2 hours on a 8-core laptop-grade CPU.

Bonus: masking packages from ::haskell

If you want to exclude a given version from the ::haskell overly from being installed/updated, then you can add a similar line(s) to /etc/portage/package.mask/0000_hs.conf:

1
app-emacs/haskell-mode::haskell

Tag the releases

:: dev

By: Maciej Barć

Tag your releases!

There were so many times when I wanted to use some package, it has a version on repology, but when I open the project’s repo there are no tags.

Nowadays it is extremely easy to bump software versions according to semantic versioning and also to tag them, that it should go without saying that most OSS and proprietary projects could also follow that workflow.

There are many projects both language-specific and agnostic to manage the release (and tagging) process. My favorite agnostic one is tbump and I highly recommend it.

Install Bazel ZSH completion

:: bazel, dev

By: Maciej Barć

1
2
3
4
5
6
7
8
(
    repo="bazelbuild/bazel";
    raw="https://raw.githubusercontent.com/${repo}";
    url="${raw}/master/scripts/zsh_completion/_bazel";
    site_functions="/usr/share/zsh/site-functions";

    wget "${url}" -O ${site_functions}/_bazel
)

Afterwards, restart your ZSH shell session.

This also works if you have bazelisk installed but bazel has to be symlinked to the bazelisk executable path.

Change location of intermediate objects in .NET

:: dev, dotnet

By: Maciej Barć

.NET creates the so-called intermediate objects while building .NET projects, those are located in the “bin” and “obj” directories. The default is not very satisfying, primarily because if a program from a different machine or a container modifies those, then any cached file system paths that are encoded in the objects will be broken. But also it is probably mostly a legacy behavior to have them split between “bin” and “obj” directories.

I prefer for them to say in one - ".cache", because that’s that they are - cache. With the following configuration objects will be stored inside the ".cache" directory. Furthermore, the objects produced by the native machine in the “native” subdirectory and the ones produced by container software in “container” subdirectory.

1
2
3
4
5
6
7
<PropertyGroup>
  <CachePath>.\.cache\native</CachePath>
  <CachePath Condition="'$(DOTNET_RUNNING_IN_CONTAINER)' == 'true'">.\.cache\container</CachePath>
  <MSBUildProjectExtensionsPath>$(CachePath)\obj\</MSBUildProjectExtensionsPath>
  <BaseIntermediateOutputPath>$(CachePath)\obj\</BaseIntermediateOutputPath>
  <BaseOutputPath>$(CachePath)\bin\</BaseOutputPath>
</PropertyGroup>

If anybody want to go hardcore and cache the intermediate objects based on the RID or architecture triplet, then this can also be done, for example, by adding environment variables to the path.

Common Project Layout, version 0

:: dev

By: Maciej Barć

This is a tongue-in-cheek “draft” for Common Project Layout, version 0. It will probably never become any sort of adopted standard but I think it is good to share some ideas that I had while working on this.

Definition

Common Project Layout (CPL) is a set of good practices for structuring medium-to-large monorepo-like software repositories.

Benefits

CPL helps with code organization. It can be a good “framework” (in a very loose meaning of this word) to modularize product components.

It can make large repositories easier to work with and understand.

Upfront limitations

CPL is strictly designed for “hosting” software and all the non-code assets are left up to the engineers to decide their location.

For example branding assets could be put into the Branding top-level directory, but on the other hand are we sure they will stay the same with major version?

Since we can agree that we consider documentation “producers” (not the produced artifacts) to be code we could also acknowledge that some assets could have their own versioned subproject.

Requirements

Versioning

CPL requires that the software is versioned inside directories whose names include the version. Recommended pattern is to name directories vMAJOR where MAJOR is either the current tagged major version or one that will be if no tags exist. It is also recommended to group the vMAJOR directories under one common directory, for example Source.

Subprojects

The vMAJOR could theoretically contain all the source code mixed together but it should be grouped and organized by their purpose.

Subproject is defined as a directory inside a versioned (vMAJOR) directory. “Versioned subproject” and “subproject” are synonymous to CPL.

To mark the purpose of a subproject, whether it is to be used as a helper or as a “container” for source that is actually exposed (or binaries created from it), it should be adequately named.

For helpers name does not matter but for source subproject it should be prefixed by project name.

For example we could have this layout:

1
2
3
4
5
6
7
8
Source/
└── v1/
    ├── Makefile
    ├── VERSION
    ├── admin/
    ├── make/
    ├── my-project-app/
    └── my-project-util/

In the above example my-project-app and my-project-lib are the source subproject and admin and make are subproject that are there only to help in building, managing and deploying the actual source subprojects.

At the and it is up to the engineer to choose if something is considered a source subproject. For example: If we have a helper subproject that all it does is hold Docker / Podman files for creating a development container what should we name it? As of now I had named them PROJECT-dev-container.

Recommendations

Make and admin

I think it is a good practice for each vMAJOR to have a Makefile, or equivalent in other build system, that will call scripts inside vMAJOR/admin directory that each take care of some small / specific task.

For example the vMAJOR/Makefile recipe for build can call admin/build_my_project_app.py and admin/build_my_project_lib.py. Each those scripts would call the “real” build system specific to the subproject they act upon.

VERSION file

It is nice to have a VERSION file in the vMAJOR directory. It can be reused by build tools and also to show what was the last version worked upon inside vMAJOR, the latest git tag can either be put on different major version or simply not be there yet.

References

See those repositories for referencing the CPL layout:

Using gzexe for shipping Racket executables

:: dev, lisp, tutorial

By: Maciej Barć

Racket executables made by raco exe are known to be quite large. One of tools that can be used to help reduce the size of produced binaries is the gzexe program.

gzexe is a tool that can compress a executable binary. It can be acquired by installing gzip on most Linux distributions (included in the app-arch/gzip package on Gentoo).

Creating a hello-world executable with Racket

Write following contents to hello-world.rkt file:

1
2
3
4
5
6
7
#lang racket/base

(define (main)
  (displayln "It REPLs. Ship it!"))

(module+ main
  (main))

To make a binary run:

1
raco exe --orig-exe -v -o hello-world hello-world.rkt

The file hello-world will be produced.

This is what file hello-world says about it:

1
2
3
hello-world: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 3.2.0, stripped

This “small” executable weights 46 MB!

In comparison busybox weights around 2 MB.

Compressing with gzexe

Keep in mind that gzexe will overwrite the compressed file and create a backup with appended "~".

1
gzexe hello-world

And this gives us only 8,5 MB. Nice!

In comparison bazel, which is a single-binary build system written in JAVA, executable takes 33 MB on my Gentoo machine. I tried compressing it with gzexe and it reduces it only by 10%, to around 29 MB.

gzexeis not a silver bullet but with Racket exes it works very nicely.

Shell script setup

:: dev

By: Maciej Barć

Good practices

Use sh

If you do not need bash features, then use sh, it is installed on every UNIX-like system.

1
#!/bin/sh

Exit on failure

Shell scripts continue even if a command returns error. To fail right away use:

1
set -e

Trap C-c

Catch Control-c and exit.

1
trap "exit 130" INT

Use script directory

Assume we are executing a script from directory /Admin, where / is the root of a given project directory.

1
2
script_path="${0}"
script_root="$(dirname "${script_path}")"

We can use ${script_root} to call other scripts form the Admin directory, but we can also use it to relate to the /.

1
2
3
project_root="$(realpath "${script_root}/../")"
echo "[INFO] Entering directory: ${project_root}"
cd "${project_root}"

So with above we can run commands form / (repository root). Like for example make and other:

1
2
make
python3 ./Admin/serve_1.py

Even better

Use Python for repository maintenance scripts.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from os import chdir
from os import path

from subprocess import run
from sys import argv

script_path = path.realpath(__file__)
script_root = path.dirname(script_path)
project_root = path.realpath(path.join(script_root, ".."))

print(f" * Entering directory: {project_root}")
chdir(project_root)

leftover_args = argv[1::]
command_arguments = ["make"] + leftover_args

cmd_string = " ".join(command_arguments)
print(f" * Executing command: {cmd_string}")
run(command_arguments, check=True)

Debugging Frog blog with syntax macros

:: blog, lisp, dev, tutorial

By: Maciej Barć

Constructing debugging syntax

I wanted to echo parameter values when I set them in my blog’s frog.rkt config file.

Nothing simpler in Racket!

First I create this macro for echoing a single parameter value when it is set:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(define-syntax-rule (verbose-set-parameter parameter-id parameter-value)
  (begin
    ;; Set the parameter.
    (parameter-id parameter-value)

    ;; Then call the parameter and print it's value.
    ;; The "'parameter-id" is special syntax
    ;; for turning a "parameter-id" identifier to a symbol.
    ;; We can also write it like:
    ;; > (quote parameter-id)
    ;; to be less confusing.
    (printf "[DEBUG] (~a ~v)\n" 'parameter-id (parameter-id))))

then, I create a wrapper for above macro that can take multiple parameter pairs:

1
2
3
4
5
(define-syntax-rule (verbose-set-parameters (parameter-id parameter-value) ...)
  (begin
    ;; Unpack a chain of "(parameter-id parameter-value)" pairs
    ;; using the "..." syntax.
    (verbose-set-parameter parameter-id parameter-value) ...))

Using the macro

Afterwards we can call it like so:

1
2
3
(verbose-set-parameters
 (current-title "XGQT's blog")
 (current-author "Maciej Barć"))

Notice that even the form of setting a parameter, that is (parameter-procedure "value"), remains the same, but in reality it is just similar to how the syntax macro pattern-matches on it.

Inspecting macro expansion

In racket-mode inside GNU Emacs we can inspect the macro expansion with racket-expand-region. Stepping through the expansion provided this result:

1
2
3
4
5
6
7
(begin
  (begin
    (current-title "XGQT's blog")
    (printf "[DEBUG] (~a ~v)\n" 'current-title (current-title)))
  (begin
    (current-author "Maciej Barć")
    (printf "[DEBUG] (~a ~v)\n" 'current-author (current-author))))