feat(linux): the Go half of OpsLog builds for Linux

Measured rather than guessed: the whole repository was cross-compiled for
linux/amd64 and the gaps closed one by one. There were fewer than expected.

Flex and TCI were never Windows-specific — they carried //go:build windows by
inheritance and import nothing but net and gorilla/websocket. Untagged, no code
change. The two backends a Linux operator is most likely to own were already
portable.

Audio was 560 lines, not 2287: only devices.go and engine.go touch WASAPI, while
manager.go, recorder.go, wav.go and mp3.go were pure Go wearing the tag by
association. The whole platform surface is seven functions, now implemented a
second time on PulseAudio through github.com/jfreymuth/pulse — pure Go over the
server socket, so the no-cgo rule survives, and PipeWire answers the same
protocol. The fixed 16 kHz mono format and the server-side resampling mirror
what AUTOCONVERTPCM does on Windows, for the same reason.

OmniRig is the only real loss, and its backend still EXISTS off Windows rather
than being compiled out of app.go: a settings database is portable, so an
operator moving a profile across keeps "omnirig" saved and must be told to pick
a native backend instead of meeting a nil one.

The parts where Linux is not Windows, and where a compile-only stub would have
been a silent bug:

  - data dir: still beside the binary, but ~/.local/share/OpsLog/data when that
    folder belongs to the system — decided by trying the write, because /opt and
    /usr/local are writable on some stations and not others.
  - single instance: an flock, not a pid file. The kernel drops it however the
    process dies, so a crash leaves nothing to delete by hand. This is the guard
    that stops two instances fighting over the rig frequency.
  - update: simpler here. Unix renames over a running binary, so the deferred
    swap the Windows path needs a detached helper for is unreachable.
  - tasklist/taskkill become /proc and SIGTERM; the boot log moves out of /tmp,
    which is wiped exactly when the evidence is wanted.
  - serial ports sorted naturally: /dev/ttyUSB10 was landing between USB1 and
    USB2, the same trap COM10 fell into.

release.ps1 now cross-builds and vets for linux before it builds the exe, and
refuses the release if that fails — a port rots one unguarded x/sys/windows call
at a time.

Nothing has been executed on Linux yet: Wails needs webkit2gtk and cgo there, so
the binary must be built on Linux. scripts/linux-setup.sh checks the machine and
does it; BUILDING-LINUX.md is the manual version.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
2026-09-09 10:21:27 +02:00
co-authored by Claude Opus 5
parent f05e6290df
commit 8b1dff581b
48 changed files with 1456 additions and 251 deletions
+112
View File
@@ -0,0 +1,112 @@
# Building OpsLog on Linux
OpsLog is developed on Windows. The Linux build shares every line of the
frontend and all but a handful of Go files; what differs is listed at the bottom
of this page.
**It cannot be cross-compiled from Windows.** Wails links against the system
WebKit on Linux, which needs cgo and the GTK/WebKit headers, so the binary has
to be produced on a Linux machine (or a container). What *can* be checked from
Windows — and is, at every release — is that the Go half still compiles:
```bash
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./...
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go vet ./...
```
## The short way
```bash
./scripts/linux-setup.sh
```
It checks everything below, prints the one install command your distribution
needs if something is missing, and builds when nothing is. The rest of this page
is what it checks, for when you would rather do it by hand.
## Dependencies
```bash
# Debian / Ubuntu
sudo apt install build-essential pkg-config libgtk-3-dev libwebkit2gtk-4.1-dev nodejs npm
# Fedora
sudo dnf install gcc-c++ pkgconf-pkg-config gtk3-devel webkit2gtk4.1-devel nodejs npm
# Arch
sudo pacman -S base-devel pkgconf gtk3 webkit2gtk-4.1 nodejs npm
```
**Go and node do not come from the package manager.** No current distribution
ships a Go new enough for `go.mod` (Ubuntu 24.04 / Mint 22 have 1.22, Ubuntu
22.04 / Mint 21 have 1.18), and Ubuntu 22.04 / Mint 21 ship node 12 where Vite
needs 18. Both are the usual reason a first build fails with an error that
points somewhere else entirely:
```bash
# Go, from go.dev
wget https://go.dev/dl/go1.25.1.linux-amd64.tar.gz
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.25.1.linux-amd64.tar.gz
echo 'export PATH=/usr/local/go/bin:$HOME/go/bin:$PATH' >> ~/.profile # log out and back in
# node 20, only if `node -v` is below 18
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt install nodejs
```
Then the Wails CLI:
```bash
go install github.com/wailsapp/wails/v2/cmd/[email protected]
wails doctor # says what is still missing
```
`libwebkit2gtk-4.0` also works; pass `-tags webkit2_40` to `wails build` if your
distribution only has the older one.
## Build
```bash
wails build # → build/bin/OpsLog
./build/bin/OpsLog
```
`wails dev` works the same as on Windows.
## Runtime requirements
- **PulseAudio or PipeWire** for the voice keyer, the QSO recorder and the CW
decoder. PipeWire is fine — OpsLog speaks the PulseAudio protocol, which
`pipewire-pulse` answers. Without a sound server those three features report
"cannot reach the sound server" and everything else works normally.
- **Serial port access** for CAT, keyers, rotators and amplifiers. Ports appear
as `/dev/ttyUSB0`, `/dev/ttyACM0`… and on most distributions belong to the
`dialout` group:
```bash
sudo usermod -aG dialout $USER # log out and back in
```
This is the single most common reason a rig that works in WSJT-X shows
"permission denied" in OpsLog.
- **TrustedQSL** (`tqsl`) for LoTW uploads, from your package manager. OpsLog
finds it on `PATH`.
## Where OpsLog keeps its data
Next to the binary, in `data/` — the same portable layout as on Windows, so a
folder in your home directory carries the logbook with it.
If the binary sits somewhere you cannot write (`/usr/bin`, `/opt`), OpsLog uses
`~/.local/share/OpsLog/data` instead and says so in `startup.log`. The startup
log itself lives in `~/.cache/OpsLog/startup.log`.
## What is different from the Windows build
| | |
|---|---|
| **OmniRig** | Not available — it is Windows COM automation. Use a native backend instead: Icom CI-V (USB and network), Yaesu, Kenwood/Elecraft, FlexRadio, TCI, Xiegu. |
| **Denkovi USB relay** | Not available — it needs FTDI's `ftd2xx.dll`. The other relay backends work. |
| **Audio** | PulseAudio/PipeWire instead of WASAPI. Same devices, same fixed 16 kHz mono format. |
| **Auto-update** | Works, and is simpler: Linux lets a running binary be replaced, so none of the Windows deferred-swap machinery is needed. |
| **Window placement** | OpsLog cannot read the monitor layout, so a saved window position is always trusted rather than clamped onto a visible screen. |
| **Single instance** | An `flock` on `$XDG_RUNTIME_DIR/OpsLog/instance.lock` instead of a named mutex. It cannot raise the existing window, only refuse to start a second one. |