# Klammertext source installation on Linux (Ubuntu / Pop!_OS)

This document describes how to build and install Klammertext from source on a
Linux system — Ubuntu or Pop!_OS; the steps are identical — without using the
container. A source installation gives full access to all `@eval` modes,
including `:haskell` and `:shell` commands that depend on locally installed
software.

For the container installation on Linux, see `linux_container_install.md`.


## Prerequisites

The following packages are required to build Klammertext:

```bash
sudo apt-get update
sudo apt-get install g++ make python3-dev
```

The C++ compiler must support C++20. GCC 11 or later is required (Ubuntu 22.04
and later include GCC 12+).

The SKS `@image` klammer requires OpenImageIO Python bindings. These must match
the Python version that ktext is built against (check with
`python3.XX -c "import OpenImageIO"`). For example, if ktext links against
Python 3.12:

```bash
pip3.12 install OpenImageIO
```

Verify:

```bash
g++ --version
```


## Clone the repository

```bash
git clone https://git.andykopra.com/ack/klammertext.git
cd klammertext
```


## Environment variables

Klammertext's runtime environment is provided by a single self-configuring
file. Source it from your shell profile (e.g., `~/.bashrc` or `~/.zshrc`):

```bash
source /path/to/klammertext/env/runtime.env
```

It self-locates `KLAMMERTEXT_HOME` from its own path, adds `bin/` and
`tst/` to `PATH` (plus the newest `~/external/texlive/<year>/bin/<arch>` if a
TeX Live is installed there), sets `LD_LIBRARY_PATH` so `libklammertext.so` is
found, and sets the LSan suppressions. There is no per-host or per-OS variable
to set. For a TeX Live or library in a non-standard location, add it to an
optional, gitignored `env/runtime.env.local` (sourced at the end).

After editing your shell profile, reload it:

```bash
source ~/.bashrc
```


## Configure the build

No build configuration is needed. The single `env/makefile.env` is
cross-platform: it reads `KLAMMERTEXT_HOME` from the environment (set by
`runtime.env` above), auto-detects the platform with `uname`, and auto-detects
Python with `python3-config` — no hardcoded version and no per-host file to
edit. Verify the Python development headers are present:

```bash
python3-config --includes    # prints -I.../python3.XX for your Python
```

If `python3-config` is missing, install your distribution's `python3-dev`
(Debian/Ubuntu) or `python3-devel` (Fedora/RHEL) package.


## Build

Build the shared library, the SKS components, and the three commands with a
single command: `make -C com` builds its prerequisites in `mac/` and `sks/`
first, then the commands. The build is optimized (`-O3`) by default — the build
you want to install and run:

```bash
make -C com -j        # lib/libklammertext.so + sks/*.so + bin/{ktext,kdesc,kdiag}
```

(For a slower `-O0` debug build with AddressSanitizer, intended for development,
prefix `DEBUG=1`: `DEBUG=1 make -C com -j`.)

Verify the build:

```bash
ktext -s '@eval 1 + 1 @' -d
```

This should print `2`. For a quick document smoke test, create a small file and
render it to HTML:

```bash
cat > hello.kt <<'EOF'
@document
:structure article
:title Hello
:text
@s1 Hello, Klammertext @

This document was built from source.
@
EOF
ktext hello.kt -t html          # writes hello/index.html
```


## TeX Live (for PDF output)

The Standard Klammer Set uses **XeLaTeX** for the `pdf` target. Build a complete
Klammertext TeX Live tree with the bundled script, giving it a destination
directory under `~/external/texlive/<year>` — the location `runtime.env`
auto-detects. The script needs `perl`, `xz-utils`, `fontconfig`, and either
`wget` or `curl`:

```bash
sudo apt-get install perl wget xz-utils fontconfig
bash $KLAMMERTEXT_HOME/doc/install/texlive_additional_packages.sh ~/external/texlive/2026
```

This installs `scheme-small` plus the additional packages the SKS needs and
rebuilds all formats, fetching the binaries for your architecture. It writes a
`KLAMMERTEXT_BUILD_INFO.txt` provenance file (mirror, release, package list,
date) into the tree.

Because the tree lives under `~/external/texlive/2026`, `runtime.env` finds it
automatically — open a new shell (or re-source `runtime.env`) and `xelatex`
will be on `PATH`. No manual `KLAMMERTEXT_TEXLIVE_BIN` is needed.

If you would rather reuse a TeX Live you already have, point `runtime.env` at it
from the gitignored escape hatch instead, and install the SKS's extra packages
into it yourself (the package list is in `doc/install/texlive_additional_packages.sh`):

```bash
cat >> "$KLAMMERTEXT_HOME/env/runtime.env.local" <<'EOF'
export KLAMMERTEXT_TEXLIVE_BIN=/path/to/texlive/bin/x86_64-linux
export PATH="$KLAMMERTEXT_TEXLIVE_BIN:$PATH"
EOF
```

Verify and test (reusing the `hello.kt` from the Build section):

```bash
xelatex --version
ktext hello.kt -t pdf           # writes hello.pdf
```


## Optional: Haskell (for @eval :haskell)

The `@eval :haskell` mode requires `runghc`, which is part of the Haskell
toolchain. Alternatively, the `akopra/klammertext:haskell` container image
includes GHC (see `linux_container_install.md`).

The recommended way to install Haskell on Ubuntu is via ghcup:

```bash
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
```

Follow the prompts to install GHC, cabal, and related tools. After
installation, ensure the ghcup bin directory is in your `PATH`:

```bash
export PATH=$HOME/.ghcup/bin:$PATH
```

Verify:

```bash
runghc --version
```

Test in Klammertext:

```bash
ktext -s '@eval :haskell main = putStr "Hello from Haskell" @' -d
```


## Optional: the `mdpdf` command (Markdown to PDF)

Klammertext ships a second, independent route in `sks/tns/`: `mdpdf` renders
a **Markdown** file to PDF through a headless browser, bypassing Klammertext
entirely. It is for a document that is not written in Klammertext — one you
have not converted yet, or one whose Markdown is not worth converting.

The command is a shell function that `env/runtime.env` defines, so if you
source that file (see *Environment variables* above) you already have it —
**in shells started after the installation**. In a shell that was already
running, source it again:

```bash
source $KLAMMERTEXT_HOME/env/runtime.env
type mdpdf                      # mdpdf is a shell function
```

Two things it needs that the distribution does not install:

```bash
# 1. The Markdown renderer, in a virtual environment of its own.  PEP 668
#    forbids installing it into the system Python, so this is not optional
#    and not a system package.
python3 $KLAMMERTEXT_HOME/sks/tns/md_to_pdf.py --setup

# 2. A Chromium-based browser -- Brave, Chrome or Chromium -- which is the
#    renderer.  Any one of them will do; install whichever you prefer.
sudo apt install chromium-browser
```

A browser installed as a **flatpak** works too and is found without being
told: that is what the Pop!_Shop installs on Pop!_OS, and a flatpak puts
nothing on `PATH`, so the command looks for the application by name in the
flatpak export directories as well. The document is served to the browser
over the loopback interface rather than handed to it as a file path, so a
sandboxed browser needs no filesystem permission of any kind — including
none to reach the fonts, which is what silently cost a flatpak render its
typography before this was so.

Then:

```bash
mdpdf notes.md                  # writes notes.pdf beside it
mdpdf notes                     # the .md may be left off
```

It supplies the fonts, the size matching and the code wrapping, and completes
on `*.md` at the TAB key. Each default is a variable you may set in your
shell profile — `MDPDF_SERIF`, `MDPDF_SANS`, `MDPDF_MONO`, `MDPDF_MATCH`,
`MDPDF_WRAP`, and `MDPDF_BROWSER` for a browser installed somewhere the
command does not look. Any option of the underlying `md_to_pdf.py` may also
be given on the command line, where it overrides the default:

```bash
mdpdf notes.md --paper letter --margin 0.75
```

The stylesheet it applies is `sks/tns/markdown.css`; copy it, edit the copy,
and pass `--css yourcopy.css` to render to your own taste.


## Directory layout after build

```
klammertext/
├── bin/          ktext, kdesc, kdiag executables (after build)
├── lib/          libklammertext.so shared library (after build)
├── mac/          Klammermachine C++ source
├── sks/          Standard Klammer Set (.k files and .so modules)
│   ├── document/ document.so
│   ├── kutil/    kutil.o
│   └── target/   html_util.o, latex_util.o
├── com/          command source (ktext, kdesc, kdiag) and Makefile
├── doc/          installation guides (doc/install) and editor support (doc/edit)
└── tst/          test suites
```


## Verifying the installation

Run the following commands to verify that everything works:

```bash
# Basic evaluation (Python)
ktext -s '@eval 1 + 1 @' -d

# Shell evaluation
ktext -s '@eval :shell date @' -d

# Show machine state (SKS is loaded by default)
ktext -s '' -m

# HTML and PDF output (uses the hello.kt from the Build section)
ktext hello.kt -t html
ktext hello.kt -t pdf     # requires TeX Live

# Haskell evaluation (requires ghcup)
ktext -s '@eval :haskell main = putStr "42" @' -d

# Run unit tests
make -C $KLAMMERTEXT_HOME/tst test
```


## Updating

To update an existing source installation to the latest version:

```bash
cd $KLAMMERTEXT_HOME
git pull
make -C com -j             # rebuild library, SKS components, and commands (optimized)
```

The TeX Live tree only needs rebuilding if the SKS's package requirements
changed (rare); when they do, re-run the script from the TeX Live section
above.

## Troubleshooting

**"libklammertext.so: cannot open shared object file"**
Ensure `LD_LIBRARY_PATH` includes `$KLAMMERTEXT_HOME/lib`:

```bash
export LD_LIBRARY_PATH=$KLAMMERTEXT_HOME/lib:$LD_LIBRARY_PATH
```

**"KLAMMERTEXT_HOME is not set"**
Set the environment variable as described in the Environment variables
section above.

**"python3.XX/Python.h: No such file or directory"**
Install the Python development headers:

```bash
sudo apt-get install python3-dev
```

**"xelatex: command not found" (when using -t pdf)**
Install TeX Live and ensure its bin directory is in `PATH`.

**"@eval :haskell requires runghc"**
Install Haskell via ghcup as described in the Optional: Haskell section.
