diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-13 14:23:18 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-13 14:23:18 +0000 |
| commit | 2fc1af63b20ab67683b74e6bb8f2a3edf5ef2a3d (patch) | |
| tree | fe261e313a653fad59f89a3031557eb96374ac35 | |
| parent | 1c64adaad7ad89386ae9cb44c8e3050672a59d6e (diff) | |
Install manuals in an independent directory and register manpath
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| -rw-r--r-- | AGENTS/ARCH.md | 20 | ||||
| -rw-r--r-- | doc/install.md | 58 | ||||
| -rw-r--r-- | install.ps1 | 31 | ||||
| -rwxr-xr-x | install.sh | 115 |
4 files changed, 193 insertions, 31 deletions
diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md index 30c4a3b..e551c8f 100644 --- a/AGENTS/ARCH.md +++ b/AGENTS/ARCH.md @@ -93,9 +93,17 @@ LLVM 18 executable has only libc, libm and the system loader as dynamic dependen and runs in a minimal chroot without LLVM/lli. Windows EMBEDDED is untested. On Linux, `make -C build install` builds the manual and calls the POSIX -`install.sh` to copy `cpsc` and `doc/cpsc.1` into `CMAKE_INSTALL_PREFIX/bin` and -`CMAKE_INSTALL_PREFIX/share/man/man1` (default prefix `/usr/local`). The script -also supports `--prefix`, `--source-dir` and `DESTDIR` staging. On macOS it removes +`install.sh` to copy `cpsc` into `CMAKE_INSTALL_PREFIX/bin` and all manuals into +`CMAKE_INSTALL_PREFIX/share/cpsc/man/man1` and `man7` (default prefix `/usr/local`). +Linux man-db registration appends a deduplicated MANDATORY_MANPATH entry to +`/etc/manpath.config` or `/etc/man_db.conf` as root, or `~/.manpath` as a user. +macOS root registration uses `/usr/local/etc/man.d/cpsc.conf`; user installation +adds a MANPATH block to zsh/bash login configuration and prints optional sudo +commands for global registration. Existing configuration is preserved. Explicit +MANPATH settings may need updating; Linux registration rejects whitespace in the +prefix, and Unix prefixes cannot contain colons/newlines. +The script also supports `--prefix`, `--source-dir` and `DESTDIR` staging; +staging never changes host manpath or shell configuration. On macOS it removes `com.apple.quarantine` from the installed executable if present; attribute inspection or removal failures abort installation with a diagnostic. Ordinary builds do not require Pandoc; `doc`, installation and release packaging do. The modular @@ -108,8 +116,8 @@ as `build/cpsc-VERSION-SYSTEM-ARCH.tar.xz`, retaining the configured execution m timestamp (`yyyy-mm-dd-hh-mm-ss`) followed by its short ID. Git is only required for packaging; recipients can install the extracted archive using just the script. -The Windows `install.ps1` installs `cpsc.exe` and `doc/cpsc.html` for the current -user under `%LOCALAPPDATA%\Programs\cpsc` (or `-Prefix`). It appends that directory +The Windows `install.ps1` installs `cpsc.exe`, `doc/cpsc.html` and +`doc/language/*.html` for the current user under `%LOCALAPPDATA%\Programs\cpsc` (or `-Prefix`). It appends that directory to the user PATH without copying the combined process PATH, and creates a per-user Start Menu help shortcut. `-SourceDir` selects the artifact directory; `DESTDIR` staging is unsupported on Windows. The script is compatible with @@ -127,7 +135,7 @@ Windows `mingw32-make -C build install` builds the HTML manual and invokes directory above; explicit prefixes are preserved. Unix still invokes `install.sh`. The release target explicitly passes the configured Windows flag to `cmake/Release.cmake`, since a standalone CMake script otherwise describes the -host platform. Windows packages `cpsc.exe`, `doc/cpsc.html`, `install.ps1` and +host platform. Windows packages `cpsc.exe`, `doc/cpsc.html`, `doc/language/*.html`, `install.ps1` and `LICENSE` as `build/cpsc-VERSION-Windows-ARCH.zip` using CMake's default Deflate compression. Unix retains `.tar.xz`, the POSIX installer and the man pages. Linux smoke tests can configure an isolated build with `-DWIN32=TRUE` to exercise diff --git a/doc/install.md b/doc/install.md index ef4c3f6..9fe660a 100644 --- a/doc/install.md +++ b/doc/install.md @@ -9,12 +9,15 @@ After configuring and building, install the compiler and manual with: ```bash sudo make -C build install cpsc test.cps -r -man cpsc +man 1 cpsc +man 7 cpsc-language-overview +man 7 cpsc-keyword-if +man 7 cpsc-function-num_to_str ``` -This calls `install.sh` and installs `cpsc` to `/usr/local/bin` and its manual to -`/usr/local/share/man/man1`. Installation generates the manual automatically and -requires Pandoc. Ordinary builds still do not require Pandoc. +This calls `install.sh` and installs `cpsc` to `/usr/local/bin` and its manuals to +`/usr/local/share/cpsc/man/man1` and `/usr/local/share/cpsc/man/man7`. +Installation generates all manuals automatically and requires Pandoc. Ordinary builds still do not require Pandoc. For installation without sudo, set a writable prefix in the existing build: @@ -28,6 +31,38 @@ You can also run `./install.sh --source-dir build --prefix "$HOME/.local"` directly after building `cpsc` and running `make -C build doc`. `DESTDIR` is supported for staging, for example `DESTDIR=/tmp/cpsc-stage make -C build install`. +### Manual search path registration + +On Linux with man-db, root installation appends a cpsc entry to +`/etc/manpath.config` (or `/etc/man_db.conf`); installation as an ordinary user +uses `~/.manpath`. Existing configuration is preserved, and repeated installation +does not add the same entry again. Linux registration requires a prefix without +whitespace. Unix prefixes cannot contain colons or newlines. + +On macOS, root installation registers the directory in +`/usr/local/etc/man.d/cpsc.conf`. Without sudo, the installer appends a cpsc +MANPATH block to `~/.zprofile` for zsh or `~/.bash_profile` for bash, preserving +existing and default search paths. Open a new login terminal afterward. Other +shells receive a manual setup hint. For a user installation, global registration +is optional; the installer recommends these manual commands (adjust the path): + +```bash +sudo mkdir -p /usr/local/etc/man.d +printf '%s\n' "MANPATH $HOME/.local/share/cpsc/man" | sudo tee -a /usr/local/etc/man.d/cpsc.conf +``` + +An explicitly set MANPATH overrides man-db configuration. If you already set it, +add the cpsc directory and retain a colon to include default system paths: + +```bash +export MANPATH="$HOME/.local/share/cpsc/man:${MANPATH:-}" +``` + +DESTDIR staging copies files only and never changes the host's system/user +manpath or shell configuration. Register the final directory when deploying. +The installer leaves old shared-directory manuals in place; when upgrading, +remove the previous `PREFIX/share/man/man1/cpsc.1` yourself if it is obsolete. + On macOS, `install.sh` also removes `com.apple.quarantine` from the installed executable when present. An executable without that attribute installs normally; failure to inspect or remove the attribute is reported as an installation error. @@ -50,9 +85,10 @@ Use `EMBEDDED` when recipients should not need LLVM or `lli` at run time. Extract the archive with `tar -xJf`, enter the extracted directory, then run `sudo ./install.sh`, or `./install.sh --prefix "$HOME/.local"` for a local installation. The package installer does not require CMake, Git or Pandoc. -For a custom prefix, add its `bin` directory to PATH; `man` normally discovers the -adjacent `share/man` directory from PATH. If your man configuration overrides this, -use `man -M "$HOME/.local/share/man" cpsc` for the local prefix. +For a custom prefix, add its `bin` directory to PATH. The installer registers +`PREFIX/share/cpsc/man` in manpath configuration; it does not rely on automatic +discovery of an adjacent shared man directory. For explicit lookup, use +`man -M "$HOME/.local/share/cpsc/man" 7 cpsc-keyword-if`. ## Windows installation @@ -69,15 +105,17 @@ prefix is preserved. To change it, run `cmake -S . -B build -DCMAKE_INSTALL_PREFIX="D:/My Programs/cpsc"` before installing. You can also call `install.ps1` directly. It installs for the current user without -administrator rights. Its source directory must contain `cpsc.exe` and `doc/cpsc.html`: +administrator rights. Its source directory must contain `cpsc.exe`, +`doc/cpsc.html` and `doc/language/*.html`: ```powershell powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -SourceDir .\build ``` The default destination is `%LOCALAPPDATA%\Programs\cpsc`: `cpsc.exe` goes in -that directory and the HTML manual goes in its `doc` subdirectory. The installer -adds the directory to the user PATH and creates Start Menu > cpsc > cpsc Help, +that directory, the command manual goes in `doc`, and syntax manuals go in +`doc/language`. The command manual links to the language overview and its pages. +The installer adds the directory to the user PATH and creates Start Menu > cpsc > cpsc Help, which opens the manual in the default browser. Open a new terminal afterward; existing parent terminals retain their old PATH. Reinstalling does not duplicate the PATH entry. Use `-Prefix 'D:\My Programs\cpsc'` for a custom destination, diff --git a/install.ps1 b/install.ps1 index b26923a..7d8ccce 100644 --- a/install.ps1 +++ b/install.ps1 @@ -3,7 +3,7 @@ .SYNOPSIS Installs cpsc and its HTML manual for the current user. .DESCRIPTION -Copies cpsc.exe and doc\cpsc.html, creates a Start Menu help shortcut, and +Copies cpsc.exe, doc\cpsc.html and doc\language\*.html, creates a help shortcut, and adds the installation directory to the user PATH. No administrator is needed for the default location: %LOCALAPPDATA%\Programs\cpsc. .EXAMPLE @@ -26,7 +26,7 @@ $ErrorActionPreference = 'Stop' if ($Help) { Write-Output 'Usage: install.ps1 [-Prefix DIR] [-SourceDir DIR] [-Help]' Write-Output 'Default destination: %LOCALAPPDATA%\Programs\cpsc' - Write-Output 'Source must contain cpsc.exe and doc\cpsc.html; default: script directory.' + Write-Output 'Source must contain cpsc.exe, doc\cpsc.html and doc\language\*.html; default: script directory.' return } @@ -71,16 +71,29 @@ try { } } + $sourceLanguageDir = Join-Path $SourceDir 'doc\language' + if (-not (Test-Path -LiteralPath $sourceLanguageDir -PathType Container)) { + throw "Missing language manuals: $sourceLanguageDir; build the doc target first." + } + $languageManuals = @(Get-ChildItem -LiteralPath $sourceLanguageDir -Filter 'cpsc-*.html' -File) + if ($languageManuals.Count -eq 0) { + throw "Missing language manuals: $sourceLanguageDir; build the doc target first." + } + $docDir = Join-Path $Prefix 'doc' + $languageDir = Join-Path $docDir 'language' $targetExe = Join-Path $Prefix 'cpsc.exe' $targetManual = Join-Path $docDir 'cpsc.html' - foreach ($directory in @($Prefix, $docDir)) { + foreach ($directory in @($Prefix, $docDir, $languageDir)) { if ((Test-Path -LiteralPath $directory) -and -not (Test-Path -LiteralPath $directory -PathType Container)) { throw "Installation directory is occupied by a file: $directory" } } - foreach ($file in @($targetExe, $targetManual)) { + $targetFiles = @($targetExe, $targetManual) + @($languageManuals | ForEach-Object { + Join-Path $languageDir $_.Name + }) + foreach ($file in $targetFiles) { if (Test-Path -LiteralPath $file -PathType Container) { throw "Installation file is occupied by a directory: $file" } @@ -94,13 +107,19 @@ try { $shell = New-Object -ComObject WScript.Shell # Directory.CreateDirectory treats paths literally, including spaces and brackets. - [IO.Directory]::CreateDirectory($docDir) | Out-Null + [IO.Directory]::CreateDirectory($languageDir) | Out-Null if ($sourceExe -ine $targetExe) { Copy-Item -LiteralPath $sourceExe -Destination $targetExe -Force } if ($sourceManual -ine $targetManual) { Copy-Item -LiteralPath $sourceManual -Destination $targetManual -Force } + foreach ($manual in $languageManuals) { + $destination = Join-Path $languageDir $manual.Name + if ($manual.FullName -ine $destination) { + Copy-Item -LiteralPath $manual.FullName -Destination $destination -Force + } + } [IO.Directory]::CreateDirectory($shortcutDir) | Out-Null $shortcut = $shell.CreateShortcut((Join-Path $shortcutDir 'cpsc Help.lnk')) $shortcut.TargetPath = $targetManual @@ -117,7 +136,7 @@ try { $env:PATH = "$env:PATH;$Prefix" } Write-Output "Installed $targetExe" - Write-Output "Installed $targetManual" + Write-Output "Installed $targetManual and $($languageManuals.Count) language manuals in $languageDir" Write-Output 'Open the manual from Start Menu > cpsc > cpsc Help.' Write-Output 'Open a new terminal to use cpsc by name (existing parent terminals keep their old PATH).' } @@ -5,11 +5,12 @@ usage() { cat <<'EOF' Usage: install.sh [--prefix DIR] [--source-dir DIR] -Install cpsc and its man page to DIR/bin and DIR/share/man/man1. +Install cpsc to DIR/bin and its man pages to DIR/share/cpsc/man/man1 and man7. The default prefix is /usr/local. The source directory defaults to the -directory containing this script and must contain cpsc and doc/cpsc.1. +directory containing this script and must contain cpsc, doc/cpsc.1 and +doc/language/*.7. Installation registers the independent manual search path. Use --source-dir build when running this script from the source checkout. -DESTDIR may be set to stage an installation without changing the prefix. +DESTDIR may be set to stage files without changing host manpath configuration. EOF } @@ -45,18 +46,108 @@ source_dir=$(CDPATH= cd -- "$source_dir" && pwd) || die "cannot open source dire || die "missing readable executable: $source_dir/cpsc; build cpsc first" [ -f "$source_dir/doc/cpsc.1" ] && [ -r "$source_dir/doc/cpsc.1" ] \ || die "missing readable manual: $source_dir/doc/cpsc.1; run make doc first" +set -- "$source_dir"/doc/language/cpsc-*.7 +[ -f "$1" ] || die "missing language manuals: $source_dir/doc/language; run make doc first" +for manual do + [ -f "$manual" ] && [ -r "$manual" ] || die "missing readable manual: $manual" +done command -v install >/dev/null 2>&1 || die "required command not found: install" +platform=$(uname -s) +man_root=${prefix%/}/share/cpsc/man +case "$man_root" in + *:*) die "--prefix cannot contain a colon (manpath separator)" ;; + *' +'*) die "--prefix cannot contain a newline" ;; +esac +if [ "$platform" = Linux ] && [ -z "$destdir" ]; then + case "$man_root" in + *[[:space:]]*) die "Linux man-db registration requires a --prefix without whitespace" ;; + esac +fi + +# Append only our entry; leave existing system/user configuration intact. +register_line() { + config=$1 + entry=$2 + if [ -e "$config" ]; then + [ -f "$config" ] && [ -r "$config" ] && [ -w "$config" ] \ + || die "cannot update manpath configuration: $config" + if grep -Fqx -- "$entry" "$config"; then return; fi + fi + printf '\n# cpsc manual: %s\n%s\n' "$man_root" "$entry" >> "$config" \ + || die "cannot register manuals in $config" + printf 'Registered manuals in %s\n' "$config" +} + +register_manpath() { + case "$platform" in + Linux) + if [ "$(id -u)" -eq 0 ]; then + if [ -f /etc/manpath.config ]; then + config=/etc/manpath.config + elif [ -f /etc/man_db.conf ]; then + config=/etc/man_db.conf + else + die "cannot locate man-db configuration; register $man_root manually" + fi + else + [ -n "${HOME:-}" ] || die "HOME is unset; cannot locate user manpath configuration" + config=$HOME/.manpath + fi + register_line "$config" "MANDATORY_MANPATH $man_root" + ;; + Darwin) + if [ "$(id -u)" -eq 0 ]; then + install -d /usr/local/etc/man.d || die "cannot create macOS man configuration directory" + register_line /usr/local/etc/man.d/cpsc.conf "MANPATH $man_root" + else + [ -n "${HOME:-}" ] || die "HOME is unset; cannot locate user shell configuration" + case "${SHELL:-}" in + */zsh) config=$HOME/.zprofile ;; + */bash) config=$HOME/.bash_profile ;; + *) + printf 'Set MANPATH manually for your shell: %s:\n' "$man_root" + config= + ;; + esac + # Quote a literal path inside double quotes in the shell snippet. + shell_man_root=$(printf '%s' "$man_root" | sed 's/[\\`"$]/\\&/g') + if [ -n "$config" ]; then + marker="# cpsc manual: $man_root" + if ! grep -Fqx -- "$marker" "$config" 2>/dev/null; then + printf '\n%s\n%s\n%s\n%s\n%s\n' "$marker" \ + 'case ":${MANPATH:-}:" in' \ + " *\":$shell_man_root:\"*) ;;" \ + " *) export MANPATH=\"$shell_man_root:\${MANPATH:-}\" ;;" \ + 'esac' >> "$config" || die "cannot update shell configuration: $config" + fi + printf 'Registered manuals in %s; open a new login terminal.\n' "$config" + fi + printf '%s\n' 'For optional global registration, run manually:' \ + ' sudo mkdir -p /usr/local/etc/man.d' \ + " printf '%s\\n' \"MANPATH $shell_man_root\" | sudo tee -a /usr/local/etc/man.d/cpsc.conf" + fi + ;; + *) printf 'Register %s in your system or user manpath configuration.\n' "$man_root" ;; + esac +} + bin_dir=$destdir${prefix%/}/bin -man_dir=$destdir${prefix%/}/share/man/man1 -install -d "$bin_dir" "$man_dir" \ +man_dir=$destdir$man_root +install -d "$bin_dir" "$man_dir/man1" "$man_dir/man7" \ || die "cannot create installation directories; check permissions or choose a writable --prefix" install -m 755 "$source_dir/cpsc" "$bin_dir/cpsc" \ || die "cannot install cpsc to $bin_dir; check permissions" -install -m 644 "$source_dir/doc/cpsc.1" "$man_dir/cpsc.1" \ +install -m 644 "$source_dir/doc/cpsc.1" "$man_dir/man1/cpsc.1" \ || die "cannot install the manual to $man_dir; check permissions" -if [ "$(uname -s)" = Darwin ]; then +for manual do + install -m 644 "$manual" "$man_dir/man7/$(basename -- "$manual")" \ + || die "cannot install language manual: $manual" +done + +if [ "$platform" = Darwin ]; then command -v xattr >/dev/null 2>&1 || die "required command not found: xattr" attributes=$(xattr "$bin_dir/cpsc") \ || die "cannot read extended attributes of $bin_dir/cpsc" @@ -66,11 +157,17 @@ if [ "$(uname -s)" = Darwin ]; then fi fi -printf 'Installed %s\nInstalled %s\n' "$bin_dir/cpsc" "$man_dir/cpsc.1" +printf 'Installed %s\nInstalled manuals in %s\n' "$bin_dir/cpsc" "$man_dir" if [ -z "$destdir" ]; then + register_manpath case ":${PATH:-}:" in *":${prefix%/}/bin:"*) ;; *) printf 'Add %s/bin to PATH to run cpsc by name.\n' "${prefix%/}" ;; esac - printf 'Read the manual with: man cpsc\n' + printf 'Read manuals with: man 1 cpsc; man 7 cpsc-language-overview\n' + if [ -n "${MANPATH:-}" ]; then + printf 'Your explicit MANPATH overrides configuration; also include %s (retain a colon for defaults).\n' "$man_root" + fi +else + printf 'Staged files only; register %s in manpath when deploying.\n' "$man_root" fi |
