diff --git a/.github/workflows/amiga.yml b/.github/workflows/amiga.yml new file mode 100644 index 0000000..b84be1f --- /dev/null +++ b/.github/workflows/amiga.yml @@ -0,0 +1,73 @@ +name: amiga + +# Verifies that AmigaOS 3/4 and MorphOS still cross-compile. The uploaded +# json-c.lha is a CI convenience artifact only — official json-c releases +# remain source-only. Aminet uploads are a separate, manual process and +# are not published from this job. + +on: [push, pull_request, workflow_dispatch] + +concurrency: + group: ${{github.workflow}}-${{github.event.pull_request.number || github.ref}} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + package: + runs-on: ubuntu-latest + timeout-minutes: 120 + steps: + - uses: actions/checkout@v7 + + # Ubuntu's `lha` package is lhasa: extract-only, cannot create archives. + # Build LHa for UNIX (https://github.com/jca02266/lha) instead. + - name: Cache LHa for UNIX + id: cache-lha + uses: actions/cache@v6 + with: + path: ~/.local/bin/lha + key: lha-16619b066b189ef289bb8b07b37d1c38d550da99-${{ runner.os }} + + - name: Install LHa for UNIX + if: steps.cache-lha.outputs.cache-hit != 'true' + working-directory: ${{runner.temp}} + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake + curl -fsSL -o lha.tar.gz \ + https://github.com/jca02266/lha/archive/16619b066b189ef289bb8b07b37d1c38d550da99.tar.gz + tar -xzf lha.tar.gz + cd lha-16619b066b189ef289bb8b07b37d1c38d550da99 + autoreconf -is + ./configure --prefix="$HOME/.local" + make -j"$(nproc)" + make install + + - name: Add LHa to PATH + run: echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Check lha can create archives + run: | + command -v lha + d=$(mktemp -d) + echo probe > "$d/f" + ( cd "$d" && lha -aq2 t.lha f ) + test -s "$d/t.lha" + + - name: Pull compiler images + run: | + docker pull sacredbanana/amiga-compiler:m68k-amigaos & + docker pull sacredbanana/amiga-compiler:ppc-amigaos & + docker pull sacredbanana/amiga-compiler:ppc-morphos & + wait + + - name: Build and package + run: ./amiga/package-amiga.sh + + - uses: actions/upload-artifact@v7 + with: + name: json-c.lha + path: dist/json-c.lha + if-no-files-found: error diff --git a/.gitignore b/.gitignore index e50a283..1f36a17 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,7 @@ # Benchmarking input and output /bench/data /bench/work + +# AmigaOS packaging output +dist/ +bld-*/ diff --git a/CMakeLists.txt b/CMakeLists.txt index d46a3dc..361e39a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,16 +99,77 @@ if (AMIGA) else() set(NEWLIB ON) endif() - message(STATUS "Building for Motorola 68k AmigaOS using CRT: ${M68K_CRT}") + if (NOT DEFINED M68K_BASEREL) + set(M68K_BASEREL "baserel") + endif() + set(M68K_BASEREL "${M68K_BASEREL}" CACHE STRING "m68k base-relative addressing: baserel, baserel32, or off") + set_property(CACHE M68K_BASEREL PROPERTY STRINGS baserel baserel32 off) + if (NOT M68K_BASEREL STREQUAL "baserel" AND + NOT M68K_BASEREL STREQUAL "baserel32" AND + NOT M68K_BASEREL STREQUAL "off") + message(FATAL_ERROR "M68K_BASEREL must be baserel, baserel32, or off (got: ${M68K_BASEREL})") + endif() + set(M68K_CPU "${M68K_CPU}" CACHE STRING "m68k CPU target (e.g. 68020, 68040, 68060); empty uses the compiler default") + if (M68K_CPU STREQUAL "" OR M68K_CPU STREQUAL "off") + set(M68K_CPU_FLAG "") + elseif (M68K_CPU MATCHES "^-") + set(M68K_CPU_FLAG "${M68K_CPU}") + elseif (M68K_CPU MATCHES "^m") + set(M68K_CPU_FLAG "-${M68K_CPU}") + else() + set(M68K_CPU_FLAG "-m${M68K_CPU}") + endif() + if (M68K_CPU_FLAG STREQUAL "") + message(STATUS "Building for Motorola 68k AmigaOS using CRT: ${M68K_CRT}, baserel: ${M68K_BASEREL}") + else() + message(STATUS "Building for Motorola 68k AmigaOS using CRT: ${M68K_CRT}, baserel: ${M68K_BASEREL}, cpu: ${M68K_CPU_FLAG}") + endif() elseif ($ENV{CROSS_PFX} STREQUAL "ppc-amigaos") set(AMIGA_PPC ON) - message(STATUS "Building for PowerPC AmigaOS") + if (NOT DEFINED PPC_CRT) + set(PPC_CRT "newlib") + endif() + set(PPC_CRT "${PPC_CRT}" CACHE STRING "ppc-amigaos C runtime: newlib, clib2 or clib4") + set_property(CACHE PPC_CRT PROPERTY STRINGS newlib clib2 clib4) + if (NOT PPC_CRT STREQUAL "newlib" AND + NOT PPC_CRT STREQUAL "clib2" AND + NOT PPC_CRT STREQUAL "clib4") + message(FATAL_ERROR "PPC_CRT must be newlib, clib2 or clib4 (got: ${PPC_CRT})") + endif() + if (NOT PPC_CRT STREQUAL "newlib") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcrt=${PPC_CRT}") + endif() + message(STATUS "Building for PowerPC AmigaOS using CRT: ${PPC_CRT}") elseif($ENV{CROSS_PFX} STREQUAL "ppc-morphos") set(MORPHOS ON) + if (NOT DEFINED MORPHOS_CLIB) + set(MORPHOS_CLIB "default") + endif() + set(MORPHOS_CLIB "${MORPHOS_CLIB}" CACHE STRING "MorphOS C library: default or libnix") + set_property(CACHE MORPHOS_CLIB PROPERTY STRINGS default libnix) + if (NOT MORPHOS_CLIB STREQUAL "default" AND NOT MORPHOS_CLIB STREQUAL "libnix") + message(FATAL_ERROR "MORPHOS_CLIB must be default or libnix (got: ${MORPHOS_CLIB})") + endif() + option(MORPHOS_BASEREL32 "MorphOS: build with -mbaserel32" OFF) + if (MORPHOS_CLIB STREQUAL "libnix") + # Same situation as the -noixemul path below: libnix's headers + # disagree with the ixemul ones about va_list, so the varargs + # helpers in debug.c trip -Wincompatible-pointer-types. + set(DISABLE_WERROR ON) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types") + endif() + if (MORPHOS_CLIB STREQUAL "libnix") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mclib=libnix") + endif() + if (MORPHOS_BASEREL32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mbaserel32") + endif() + message(STATUS "Building for PowerPC MorphOS using clib: ${MORPHOS_CLIB}, baserel32: ${MORPHOS_BASEREL32}") if (NOIXEMUL) message(STATUS "Building for PowerPC MorphOS without ixemul") set(DISABLE_WERROR ON) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -noixemul") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types") else() message(STATUS "Building for PowerPC MorphOS with ixemul") endif() @@ -362,7 +423,14 @@ if ("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") endif() if (AMIGA_M68K) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fbaserel") + if (M68K_BASEREL STREQUAL "baserel") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fbaserel") + elseif (M68K_BASEREL STREQUAL "baserel32") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fbaserel32") + endif() + if (NOT M68K_CPU_FLAG STREQUAL "") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${M68K_CPU_FLAG}") + endif() endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual") diff --git a/README.md b/README.md index d15fa41..36bea72 100644 --- a/README.md +++ b/README.md @@ -275,7 +275,9 @@ make install Building for Commodore Amiga or MorphOS ---------------------- -Building for Commodore Amiga is supported for both Motorola 68k (AmigaOS 3) and PowerPC (AmigaOS 4) architectures. MorphOS on compatible PowerPC hardware is also supported. You can set up a cross compiler locally, however it is much easier to use the already preconfigured Amiga development environment wtthin a Docker container. +Building for Commodore Amiga is supported for both Motorola 68k (AmigaOS 3) and PowerPC (AmigaOS 4) architectures. MorphOS on compatible PowerPC hardware is also supported. You can set up a cross compiler locally, however it is much easier to use the already preconfigured Amiga development environment within a Docker container. + +Included below are instructions for building for specific individual systems, as well as how to build for *all* Amiga variants. Install Docker on your machine if you don't already have it. You can download Docker Desktop for Windows/macOS/Linux [here](https://www.docker.com/products/docker-desktop/). @@ -296,6 +298,41 @@ libjson-c.a will get created in the build directory. You can change newlib to nix20, nix13, ixemul or clib2 if you would like to build the library suited for libnix or clib2 instead. Newlib is default. +By default the m68k build uses `-fbaserel`. You can select a different base-relative mode with `-DM68K_BASEREL=`: + +* `baserel` (default) — `-fbaserel` +* `baserel32` — `-fbaserel32` +* `off` — no base-relative flag + +``` +cmake -DM68K_CRT=newlib -DM68K_BASEREL=baserel32 .. +cmake -DM68K_CRT=newlib -DM68K_BASEREL=off .. +``` + +You can also set the target CPU with `-DM68K_CPU=`. This is passed to the compiler as `-m` (for example `68020` becomes `-m68020`). If omitted, the compiler default is used. + +``` +cmake -DM68K_CRT=newlib -DM68K_CPU=68020 .. +cmake -DM68K_CRT=newlib -DM68K_CPU=68040 -DM68K_BASEREL=baserel32 .. +``` + +A note on base-relative addressing (`-fbaserel`, i.e. `M68K_BASEREL=baserel`) +on m68k: a base-relative library keeps its globals at an offset from `a4`, and +`a4` is established once by the program's startup code. Such a library is only +safe inside a program that is itself base-relative **and** that declares every +function the OS can call back into (BOOPSI/MUI dispatchers, `struct Hook` +entries, interrupt servers) with `__saveds`, so `a4` is re-established on entry. +Without that, a hook entered from the OS runs with a foreign `a4` and every +`a4`-relative access made by this library -- or by libnix's `malloc` -- reads +from a wrong address. + +Conversely, a library built *without* `-fbaserel` and compiled at `-O1` or above +may allocate `a4` as a scratch register. That is harmless in a program that is +also non-base-relative, but it corrupts the base pointer of a base-relative one. + +In short: match `M68K_BASEREL` to the consuming program, and if that program is +base-relative make sure its OS callbacks are `__saveds`. + ### To build for PowerPC Amiga: ``` @@ -311,6 +348,13 @@ make libjson-c.a will get created in the build directory. +Select the C runtime with `-DPPC_CRT=`: `newlib` (default), `clib2` or `clib4`. +Anything other than `newlib` is passed to the compiler as `-mcrt=`. + +``` +cmake -DPPC_CRT=clib4 .. +``` + ### To build for PowerPC MorphOS: ``` @@ -328,6 +372,72 @@ If you are making an application that absolutely requires ixemul, then remove th libjson-c.a will get created in the build directory. +Two further options apply to MorphOS: + +* `-DMORPHOS_CLIB=` selects `default` (ixemul) or `libnix` (passed as + `-mclib=libnix`). That is the same multilib `-noixemul` selects, and is the + self-contained ABI -- an ixemul build needs `ixemul.library` on the target. +* `-DMORPHOS_BASEREL32=ON` adds `-mbaserel32`. + +``` +cmake -DMORPHOS_CLIB=libnix .. +cmake -DMORPHOS_BASEREL32=ON .. +``` + +### Building and packaging every variant + +Two scripts in the `amiga/` directory build the whole matrix across all three +systems, so you do not have to drive the container by hand. + +* `amiga/build-amiga-sdk.sh [OS ...]` — builds every variant and installs + `libjson-c.a` plus the `json-c/` headers into an SDK tree. The location comes + from `$AMIGA_SDK` (default `/opt/amiga`); either a `/` or an + AmigaSDK-gcc-style `amigaos3/sdk//` layout is accepted, and an OS + whose subdirectory is absent is skipped rather than failing. +* `amiga/package-amiga.sh [OS ...]` — builds the same matrix into a staging tree and + produces `dist/json-c.lha` for distribution. + +Both default to `AmigaOS3 AmigaOS4 MorphOS`, and both take the variant list +from the same table in `amiga/build-amiga-sdk.sh`, so they cannot disagree. 22 +libraries are built in total: 15 for AmigaOS 3 (newlib, libnix and clib2, each +across the five multilib slots), 3 for AmigaOS 4 (newlib, clib2, clib4) and 4 +for MorphOS (ixemul and native, each with and without `-mbaserel32`). + +Which of them actually *link* is a separate question from whether they build, +and was checked by compiling a program against each one: + +| target | links | +|--------|-------| +| AmigaOS 3, libnix | all 5 slots | +| AmigaOS 3, clib2 | `lib/libm020/` and `lib/libb32/libm020/` only | +| AmigaOS 3, newlib | none -- the m68k newlib tree has no `gettimeofday()` | +| AmigaOS 4 | all 3 (newlib, clib2, clib4) | +| MorphOS | all 4 slots | + +The clib2 failures are inside clib2 itself: its 68000 multilib provides no +`strtoll`/`strtoull`, and 16-bit `-fbaserel` overflows with *truncated to fit: +DREL16* once json-c's data is added -- `-fbaserel32` has no 64 KB limit. Link +with `-ljson-c -lm` after your own objects, and add `-lunix` for clib2. + +The archive is a single `json-c/` directory at the root, with the readme and +one directory per system, each following the compiler's own layout so it can +be copied straight over an SDK: + +``` +json-c/json-c.readme +json-c/AmigaOS3/{include,lib,...}/ plus libnix/ and clib2/ subtrees +json-c/AmigaOS4/{include,lib,lib/clib2,lib/clib4}/ +json-c/MorphOS/{include,lib,lib/libb32,lib/libnix}/ +``` + +Note that m68k and PPC lay their C runtimes out differently, and the variant +table follows each. On m68k a runtime is a separate tree (`libnix/`, `clib2/`), +each with its own `include/` and `lib/`. On PPC a runtime is a multilib *slot* +beneath a single shared `lib/`: `-mcrt=clib4` searches `/lib/clib4/` +and takes headers from `/include/`. This was read off +`-print-search-dirs` rather than assumed, because installing to the wrong one +produces a library the compiler silently never finds. + Linking to `libjson-c` ---------------------- diff --git a/amiga/build-amiga-sdk.sh b/amiga/build-amiga-sdk.sh new file mode 100755 index 0000000..9669ae3 --- /dev/null +++ b/amiga/build-amiga-sdk.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# Cross-build json-c for Amiga systems across every multilib slot and install +# the archives plus headers into an SDK tree. +# +# ./amiga/build-amiga-sdk.sh [OS ...] # default: AmigaOS3 AmigaOS4 MorphOS +# +# SDK location comes from $AMIGA_SDK (default /opt/amiga). +set -uo pipefail + +SDK=${AMIGA_SDK:-/opt/amiga} +AMIGA_DIR=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$AMIGA_DIR/.." && pwd) +cd "$ROOT" +OSES=("$@"); [[ ${#OSES[@]} -eq 0 ]] && OSES=(AmigaOS3 AmigaOS4 MorphOS) + +# os | image tag | sdk subdir +os_image() { case "$1" in + AmigaOS3) echo "sacredbanana/amiga-compiler:m68k-amigaos m68k-amigaos" ;; + AmigaOS4) echo "sacredbanana/amiga-compiler:ppc-amigaos ppc-amigaos" ;; + MorphOS) echo "sacredbanana/amiga-compiler:ppc-morphos ppc-morphos" ;; + *) return 1 ;; +esac; } + +# Where an AmigaSDK-gcc-style checkout keeps each system's target tree. +os_sdkdir() { case "$1" in + AmigaOS3) echo "amigaos3/sdk" ;; + AmigaOS4) echo "amigaos4/sdk" ;; + MorphOS) echo "morphos/sdk" ;; + *) return 1 ;; +esac; } + +# Per-OS variant table: "tree|slot|cmake args" +variants_AmigaOS3=( + "libnix|.|-DM68K_CRT=nix20 -DM68K_CPU=68000 -DM68K_BASEREL=off" + "libnix|libm020|-DM68K_CRT=nix20 -DM68K_CPU=68020 -DM68K_BASEREL=off" + "libnix|libb|-DM68K_CRT=nix20 -DM68K_CPU=68000 -DM68K_BASEREL=baserel" + "libnix|libb/libm020|-DM68K_CRT=nix20 -DM68K_CPU=68020 -DM68K_BASEREL=baserel" + "libnix|libb32/libm020|-DM68K_CRT=nix20 -DM68K_CPU=68020 -DM68K_BASEREL=baserel32" + ".|.|-DM68K_CRT=newlib -DM68K_CPU=68000 -DM68K_BASEREL=off" + ".|libm020|-DM68K_CRT=newlib -DM68K_CPU=68020 -DM68K_BASEREL=off" + ".|libb|-DM68K_CRT=newlib -DM68K_CPU=68000 -DM68K_BASEREL=baserel" + ".|libb/libm020|-DM68K_CRT=newlib -DM68K_CPU=68020 -DM68K_BASEREL=baserel" + ".|libb32/libm020|-DM68K_CRT=newlib -DM68K_CPU=68020 -DM68K_BASEREL=baserel32" + "clib2|.|-DM68K_CRT=clib2 -DM68K_CPU=68000 -DM68K_BASEREL=off" + "clib2|libm020|-DM68K_CRT=clib2 -DM68K_CPU=68020 -DM68K_BASEREL=off" + "clib2|libb|-DM68K_CRT=clib2 -DM68K_CPU=68000 -DM68K_BASEREL=baserel" + "clib2|libb/libm020|-DM68K_CRT=clib2 -DM68K_CPU=68020 -DM68K_BASEREL=baserel" + "clib2|libb32/libm020|-DM68K_CRT=clib2 -DM68K_CPU=68020 -DM68K_BASEREL=baserel32" +) +# Unlike m68k, where each C runtime is its own tree (libnix/, clib2/), the PPC +# toolchains make the runtime a multilib SLOT under lib/, with headers coming +# from the shared /include/. Verified with -print-search-dirs: +# ppc-amigaos -mcrt=clib2 -> /lib/clib2/ +# ppc-amigaos -mcrt=clib4 -> /lib/clib4/ +# ppc-morphos -mclib=libnix-> /lib/libnix/ +# ppc-morphos -mbaserel32 -> /lib/libb32/ +variants_AmigaOS4=( + ".|.|-DPPC_CRT=newlib" + ".|clib2|-DPPC_CRT=clib2" + ".|clib4|-DPPC_CRT=clib4" +) +# MORPHOS_CLIB=libnix (-mclib=libnix) and NOIXEMUL (-noixemul) select the same +# multilib; it is the self-contained ABI, needing no ixemul.library on target. +variants_MorphOS=( + ".|.|" + ".|libb32|-DMORPHOS_BASEREL32=ON" + ".|libnix|-DMORPHOS_CLIB=libnix" + ".|libb32/libnix|-DMORPHOS_CLIB=libnix -DMORPHOS_BASEREL32=ON" +) + +# $1 out-dir-for-libs $2 out-dir-for-headers $3.. OS list +ok=0; fail=0; failed=() +for os in "${OSES[@]}"; do + read -r image sdksub <<<"$(os_image "$os")" || { echo "skip: unknown OS $os"; continue; } + # /opt/amiga keeps the target tree directly under /, while an + # AmigaSDK-gcc checkout nests it under /sdk/. Accept either. + osdir=$(os_sdkdir "$os") + if [[ -d $SDK/$sdksub ]]; then SDKROOT=$SDK/$sdksub + elif [[ -d $SDK/$osdir/$sdksub ]]; then SDKROOT=$SDK/$osdir/$sdksub + else echo "skip $os: neither $SDK/$sdksub nor $SDK/$osdir/$sdksub"; continue; fi + + eval "vars=(\"\${variants_$os[@]}\")" + for v in "${vars[@]}"; do + IFS='|' read -r tree slot cargs <<<"$v" + bdir="bld-$os-$(echo "$tree$slot$cargs" | tr -c 'A-Za-z0-9' '-' | cut -c1-40)" + printf ' %-9s %-7s %-16s ' "$os" "$tree" "$slot" + if ! docker run --rm -v "$ROOT":/work -e USER="$(id -u)" -e GROUP="$(id -g)" "$image" sh -c " + set -e + rm -rf /work/$bdir && mkdir -p /work/$bdir && cd /work/$bdir + cmake $cargs -DCMAKE_BUILD_TYPE=Release -DBUILD_APPS=NO \ + -DCMAKE_INSTALL_PREFIX=/work/$bdir/inst .. >cmake.log 2>&1 + make -j\$(nproc) json-c >make.log 2>&1 + make install >install.log 2>&1 + " 2>/dev/null; then + echo "BUILD FAILED (logs in $bdir)"; fail=$((fail+1)); failed+=("$os/$tree/$slot"); continue + fi + lib=$bdir/libjson-c.a + [[ -f $lib ]] || lib=$bdir/inst/lib/libjson-c.a + [[ -f $lib ]] || { echo "no libjson-c.a"; fail=$((fail+1)); failed+=("$os/$tree/$slot"); continue; } + + base=$SDKROOT; [[ $tree != "." ]] && base=$SDKROOT/$tree + dest=$base/lib; [[ $slot != "." ]] && dest=$base/lib/$slot + mkdir -p "$dest" "$base/include" + cp "$lib" "$dest/libjson-c.a" + cp -a "$bdir/inst/include/json-c" "$base/include/" + echo "ok"; ok=$((ok+1)); rm -rf "$bdir" + done +done +echo +echo "built+installed: $ok failed: $fail" +((fail)) && { printf 'failures: %s\n' "${failed[*]}"; exit 1; } +exit 0 diff --git a/amiga/json-c.readme b/amiga/json-c.readme new file mode 100644 index 0000000..10a18b3 --- /dev/null +++ b/amiga/json-c.readme @@ -0,0 +1,110 @@ +Short: JSON parser and generator library (static libs + headers) +Author: Michael Clark, Eric Hawicz et al. +Uploader: Cameron Armstrong (Nightfox) +Type: dev/lib +Version: 0.19 +Architecture: m68k-amigaos >= 2.0; ppc-amigaos; ppc-morphos +Requires: an Amiga gcc compiler +Replaces: dev/lib/json-c.lha + +json-c implements a reference-counted object model for JSON, a parser that +builds it from text, and a serialiser that writes it back out. This archive +contains static libraries and public headers for Amiga development. + + https://github.com/json-c/json-c + + +Contents + + json-c/json-c.readme this file + json-c/AmigaOS3/... m68k AmigaOS libraries and headers + json-c/AmigaOS4/... ppc-amigaos libraries and headers + json-c/MorphOS/... ppc-morphos libraries and headers + + Under each OS directory the trees follow the compiler's own layout, so + they can be copied straight over an existing SDK, e.g. + + cp -a json-c/AmigaOS3/* /opt/amiga/m68k-amigaos/ + + AmigaOS3 ships one set per C runtime -- newlib at the top level, plus + libnix/ and clib2/ -- and within each, one library per multilib: + + lib/ 68000, absolute addressing + lib/libm020/ 68020, absolute addressing + lib/libb/ 68000, -fbaserel + lib/libb/libm020/ 68020, -fbaserel + lib/libb32/libm020/ 68020, -fbaserel32 + + On PPC a C runtime is not a separate tree but a multilib slot beneath one + shared lib/, with headers in the single shared include/: + + AmigaOS4 lib/ newlib + lib/clib2/ clib2 + lib/clib4/ clib4 + + MorphOS lib/ ixemul + lib/libb32/ ixemul, -mbaserel32 + lib/libnix/ native C library (-noixemul) + lib/libb32/libnix/ native C library, -mbaserel32 + + -mclib=libnix and -noixemul select the same MorphOS multilib. That ABI is + the self-contained one -- an ixemul build needs ixemul.library on the + target -- so prefer lib/libnix/ for new code. + + +Usage + + #include + + struct json_object *o = json_tokener_parse("{\"a\":1}"); + printf("%s\n", json_object_to_json_string(o)); + json_object_put(o); + + Link with -ljson-c -lm, after your own objects on the command line. For + clib2 add -lunix as well, since clib2 keeps its POSIX layer there. + + +Which variants link + + Verified by compiling the snippet above against each shipped library. + + AmigaOS3 libnix all 5 slots link. + + clib2 lib/libm020/ and lib/libb32/libm020/ link. The other + three fail inside clib2 itself: its 68000 multilib has + no strtoll()/strtoull(), and 16-bit -fbaserel overflows + with "truncated to fit: DREL16" once json-c's data is + added. -fbaserel32 has no such limit. + + newlib builds, but does not link: the m68k newlib tree has no + gettimeofday(). The libraries are shipped anyway, since + that is a gap in the SDK rather than in json-c and a + newlib with gettimeofday() implemented would link. + + AmigaOS4 all three (newlib, clib2, clib4) link. + + MorphOS all four slots link. + + +A note on base-relative addressing (AmigaOS3) + + A base-relative library addresses its globals through a4, which the + program's startup code sets up once. Only link the libb/ or libb32/ + variants into a program that is itself base-relative AND that marks every + function the OS calls back into (BOOPSI/MUI dispatchers, struct Hook + entries, interrupt servers) with __saveds. The m68k SDI_hook.h macros set + up register arguments but do not add __saveds, so this is easy to get + wrong, and the failure mode is intermittent memory corruption rather than + a clean error -- libnix keeps malloc's heap state in a4-relative storage. + + Conversely a non-base-relative library built at -O1 or above may use a4 as + a scratch register, which is harmless in a non-base-relative program but + corrupts the base pointer of one that is. + + Match the variant to the program. If in doubt, use the plain lib/ or + lib/libm020/ builds. + + +Licence + + MIT; see COPYING in the source distribution. diff --git a/amiga/package-amiga.sh b/amiga/package-amiga.sh new file mode 100755 index 0000000..62c2407 --- /dev/null +++ b/amiga/package-amiga.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Build every json-c variant for the requested Amiga systems and package them +# into dist/json-c.lha. +# +# ./amiga/package-amiga.sh [OS ...] # default: all three +# +# Archive layout (single directory at the root): +# json-c/json-c.readme +# json-c/AmigaOS3/... per-OS libraries and headers +# json-c/AmigaOS4/... +# json-c/MorphOS/... +set -uo pipefail + +AMIGA_DIR=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$AMIGA_DIR/.." && pwd) +cd "$ROOT" +DIST=$ROOT/dist +STAGE=$DIST/stage +OSES=("$@"); [[ ${#OSES[@]} -eq 0 ]] && OSES=(AmigaOS3 AmigaOS4 MorphOS) + +command -v lha >/dev/null || { echo "error: lha not found on PATH" >&2; exit 1; } +# Ubuntu's `lha` package is lhasa, which can only extract. +_lha_probe=$(mktemp -d) +echo x > "$_lha_probe/f" +if ! ( cd "$_lha_probe" && lha -aq2 t.lha f >/dev/null 2>&1 && [[ -s t.lha ]] ); then + echo "error: lha cannot create archives (is it lhasa?). Install LHa for UNIX: https://github.com/jca02266/lha" >&2 + rm -rf "$_lha_probe" + exit 1 +fi +rm -rf "$_lha_probe" + +# Build straight into the staging tree by pointing AMIGA_SDK at it: the build +# script's SDK layout ($SDK/) is the same shape we want per OS, so each +# OS gets staged under its own directory. +rm -rf "$DIST"; mkdir -p "$STAGE/json-c" +cp "$AMIGA_DIR/json-c.readme" "$STAGE/json-c/json-c.readme" + +built=() +for os in "${OSES[@]}"; do + case "$os" in + AmigaOS3) sub=m68k-amigaos ;; + AmigaOS4) sub=ppc-amigaos ;; + MorphOS) sub=ppc-morphos ;; + *) echo "skip: unknown OS $os"; continue ;; + esac + tmp=$DIST/sdk-$os + mkdir -p "$tmp/$sub" + echo "=== $os ===" + # Stage whatever landed even if some variants failed -- a single unsupported + # multilib should not drop the whole OS from the archive. + AMIGA_SDK="$tmp" "$AMIGA_DIR/build-amiga-sdk.sh" "$os" || echo " ($os: some variants failed, see above)" + if [[ -n $(find "$tmp/$sub" -name 'libjson-c.a' -print -quit 2>/dev/null) ]]; then + mkdir -p "$STAGE/json-c/$os" + cp -a "$tmp/$sub/." "$STAGE/json-c/$os/" + built+=("$os") + else + echo " $os: nothing built, omitted from the archive" + fi + rm -rf "$tmp" +done + +[[ ${#built[@]} -eq 0 ]] && { echo "nothing built"; exit 1; } + +# NOTE: build the archive from a clean staging tree with `lha -aq2`. Do NOT try +# to refresh a single entry (e.g. the readme) in an existing archive with +# `lha u` -- it APPENDS a second entry with the same name rather than replacing +# it, and extraction then yields the stale copy. +( cd "$STAGE" && lha -aq2 "$DIST/json-c.lha" * >/dev/null ) +rm -rf "$STAGE" +echo +echo "packaged: ${built[*]}" +ls -la "$DIST"