From f9419291dbbfe38d9dfc3d28b9c1b5b7bb480d6c Mon Sep 17 00:00:00 2001 From: Janus Troelsen Date: Thu, 12 Nov 2026 12:42:34 -0600 Subject: [PATCH] Install binutils-gold for ghc < 9.6 on Linux GHC < 9.6 bindists ship an hsc2hs wrapper with a hardcoded "--lflag=-fuse-ld=gold". Since binutils 2.44 deprecated gold, Ubuntu 25.10 and later no longer include ld.gold in binutils, so hsc2hs fails to link there. Install the separate binutils-gold package via apt-get when ld.gold is missing. This runs before the isInstalled check, so preinstalled or cached GHCs are covered too. Failure to install only emits a warning. --- dist/index.js | 34 ++++++++++++++++++++++++++++++++++ lib/installer.js | 34 ++++++++++++++++++++++++++++++++++ src/installer.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/dist/index.js b/dist/index.js index 9690917..61f652d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -35124,6 +35124,19 @@ async function isInstalled(tool, version, os, arch) { return false; } async function installTool(tool, version, os, arch) { + // GHC < 9.6 bindists ship an hsc2hs wrapper with a hardcoded + // "--lflag=-fuse-ld=gold", so ld.gold has to be present whenever hsc2hs + // runs, even if GHC itself is already installed (preinstalled or cached). + // Hence, do this before the isInstalled check. + // “version” may not be a semantic version (e.g. “latest-nightly”), + // so guard “compareVersions” with “validate”. + if (os === 'linux' && + tool === 'ghc' && + (0, compare_versions_1.validate)(version) && + (0, compare_versions_1.compareVersions)(version, '9.6') < 0 // meaning version < 9.6 + ) { + await aptBinutilsGold(); + } if (await isInstalled(tool, version, os, arch)) return; warn(tool, version); @@ -35241,6 +35254,27 @@ async function aptBuildEssential() { const returnCode = await exec(`sudo -- sh -c "apt-get update && apt-get -y install build-essential"`); return returnCode === 0; } +async function aptBinutilsGold() { + // Up to ubuntu-24.04, ld.gold is part of binutils. Since binutils 2.44 + // deprecated gold, ubuntu-25.10 and later ship it in a separate + // binutils-gold package instead. + if (await (0, io_1.which)('ld.gold', false)) { + core.debug('ld.gold is already installed'); + return true; + } + if (!(await (0, io_1.which)('apt-get', false))) { + core.warning('ld.gold not found and apt-get is unavailable; hsc2hs of ghc < 9.6 may fail to link'); + return false; + } + core.info(`Installing binutils-gold using apt-get (for ghc < 9.6)`); + // exec throws (instead of returning non-zero) if e.g. sudo is not + // available; this must not fail the action, as hsc2hs may not be needed. + const returnCode = await exec(`sudo -- sh -c "apt-get update && apt-get -y install binutils-gold"`).catch(() => -1); + if (returnCode !== 0) { + core.warning('Failed to install binutils-gold; hsc2hs of ghc < 9.6 may fail to link'); + } + return returnCode === 0; +} async function aptLibNCurses5() { core.info(`Installing libcurses5 and libtinfo5 using apt-get (for ghc < 8.3)`); // ubuntu-24.04 requires the specific source to install libncurses5 and libtinfo5, diff --git a/lib/installer.js b/lib/installer.js index e21a346..116208e 100644 --- a/lib/installer.js +++ b/lib/installer.js @@ -148,6 +148,19 @@ async function isInstalled(tool, version, os, arch) { return false; } async function installTool(tool, version, os, arch) { + // GHC < 9.6 bindists ship an hsc2hs wrapper with a hardcoded + // "--lflag=-fuse-ld=gold", so ld.gold has to be present whenever hsc2hs + // runs, even if GHC itself is already installed (preinstalled or cached). + // Hence, do this before the isInstalled check. + // “version” may not be a semantic version (e.g. “latest-nightly”), + // so guard “compareVersions” with “validate”. + if (os === 'linux' && + tool === 'ghc' && + (0, compare_versions_1.validate)(version) && + (0, compare_versions_1.compareVersions)(version, '9.6') < 0 // meaning version < 9.6 + ) { + await aptBinutilsGold(); + } if (await isInstalled(tool, version, os, arch)) return; warn(tool, version); @@ -265,6 +278,27 @@ async function aptBuildEssential() { const returnCode = await exec(`sudo -- sh -c "apt-get update && apt-get -y install build-essential"`); return returnCode === 0; } +async function aptBinutilsGold() { + // Up to ubuntu-24.04, ld.gold is part of binutils. Since binutils 2.44 + // deprecated gold, ubuntu-25.10 and later ship it in a separate + // binutils-gold package instead. + if (await (0, io_1.which)('ld.gold', false)) { + core.debug('ld.gold is already installed'); + return true; + } + if (!(await (0, io_1.which)('apt-get', false))) { + core.warning('ld.gold not found and apt-get is unavailable; hsc2hs of ghc < 9.6 may fail to link'); + return false; + } + core.info(`Installing binutils-gold using apt-get (for ghc < 9.6)`); + // exec throws (instead of returning non-zero) if e.g. sudo is not + // available; this must not fail the action, as hsc2hs may not be needed. + const returnCode = await exec(`sudo -- sh -c "apt-get update && apt-get -y install binutils-gold"`).catch(() => -1); + if (returnCode !== 0) { + core.warning('Failed to install binutils-gold; hsc2hs of ghc < 9.6 may fail to link'); + } + return returnCode === 0; +} async function aptLibNCurses5() { core.info(`Installing libcurses5 and libtinfo5 using apt-get (for ghc < 8.3)`); // ubuntu-24.04 requires the specific source to install libncurses5 and libtinfo5, diff --git a/src/installer.ts b/src/installer.ts index 1295598..a3a0cf5 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -146,6 +146,21 @@ export async function installTool( os: OS, arch: Arch ): Promise { + // GHC < 9.6 bindists ship an hsc2hs wrapper with a hardcoded + // "--lflag=-fuse-ld=gold", so ld.gold has to be present whenever hsc2hs + // runs, even if GHC itself is already installed (preinstalled or cached). + // Hence, do this before the isInstalled check. + // “version” may not be a semantic version (e.g. “latest-nightly”), + // so guard “compareVersions” with “validate”. + if ( + os === 'linux' && + tool === 'ghc' && + validate(version) && + compareVersions(version, '9.6') < 0 // meaning version < 9.6 + ) { + await aptBinutilsGold(); + } + if (await isInstalled(tool, version, os, arch)) return; warn(tool, version); @@ -281,6 +296,35 @@ async function aptBuildEssential(): Promise { return returnCode === 0; } +async function aptBinutilsGold(): Promise { + // Up to ubuntu-24.04, ld.gold is part of binutils. Since binutils 2.44 + // deprecated gold, ubuntu-25.10 and later ship it in a separate + // binutils-gold package instead. + if (await which('ld.gold', false)) { + core.debug('ld.gold is already installed'); + return true; + } + if (!(await which('apt-get', false))) { + core.warning( + 'ld.gold not found and apt-get is unavailable; hsc2hs of ghc < 9.6 may fail to link' + ); + return false; + } + + core.info(`Installing binutils-gold using apt-get (for ghc < 9.6)`); + // exec throws (instead of returning non-zero) if e.g. sudo is not + // available; this must not fail the action, as hsc2hs may not be needed. + const returnCode = await exec( + `sudo -- sh -c "apt-get update && apt-get -y install binutils-gold"` + ).catch(() => -1); + if (returnCode !== 0) { + core.warning( + 'Failed to install binutils-gold; hsc2hs of ghc < 9.6 may fail to link' + ); + } + return returnCode === 0; +} + async function aptLibNCurses5(): Promise { core.info( `Installing libcurses5 and libtinfo5 using apt-get (for ghc < 8.3)`