Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,26 @@

Release notes:

* After an upgrade from an earlier version of Stack, on first use only,
Stack UNRELEASED may warn that it had trouble loading the CompilerPaths cache
and is ignoring the cache (code S-9841). That is because a field has been
added to the cache.

**Changes since v3.11.1:**

Major changes:

Behavior changes:

* On non-Windows operating systems, the Flag `--[no-]semaphore` to Stack's
`build` command has no effect if `ghc --info` does not report a semaphore
version. This is a limitation of the `semaphore-compat-2.0.0` package.

Other enhancements:

* Flag `--[no-]semaphore` to Stack's `build` command is no longer classified as
experimental in documentation.

Bug fixes:

## v3.11.1 - 2026-06-13
Expand Down
18 changes: 8 additions & 10 deletions doc/commands/build_command.md
Original file line number Diff line number Diff line change
Expand Up @@ -948,8 +948,6 @@ the [`stack path --local-install-root`](path_command.md) command.

### `--[no]-semaphore` flag

:octicons-beaker-24: Experimental

[:octicons-tag-24: 3.11.1](https://github.com/commercialhaskell/stack/releases/tag/v3.11.1)

Default: Disabled
Expand All @@ -959,18 +957,18 @@ parallel when possible.

!!! info

GHC 9.8.1 and later can act as a jobserver client, which enables two or more
Some GHC versions can act as a jobserver client, which enables two or more
GHC processes running at once to share system resources with each other,
communicating via a system semaphore. This GHC feature is supported by
Cabal 3.12.0.0 (a boot package of GHC 9.10.1) and later. The flag is ignored
with a warning when the feature is unsupported.
Cabal 3.12.0.0 (a boot package of GHC 9.10.1) and later. On Windows, this is
supported by GHC 9.8.1 and later. On other operating systems, this is
supported by GHC if `ghc --info` reports a semaphore version. The flag is
ignored with a warning when the feature is unsupported.

!!! warning
!!! note

On Linux, musl and non-musl system semaphores are incompatible. That means
that a Stack executable built on Alpine Linux (such as the official Stack
for Linux) creates system semaphores that cannot be used by a GHC executable
built on non-musl Linux distributions.
On non-Windows operating systems, GHC 9.8.1 to 9.8.4, GHC 9.10.1 to 9.10.3
and GHC 9.12.1 to 9.12.4 do not report a supported semaphore version.

### `--[no-]split-objs` flag

Expand Down
4 changes: 3 additions & 1 deletion doc/maintainers/stack_errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
In connection with considering Stack's support of the
[Haskell Error Index](https://errors.haskell.org/) initiative, this page seeks
to take stock of the errors that Stack itself can raise, by reference to the
`master` branch of the Stack repository. Last updated: 2026-04-24.
`master` branch of the Stack repository. Last updated: 2026-06-25.

* `Stack.main`: catches exceptions from action `commandLineHandler`.

Expand Down Expand Up @@ -297,6 +297,7 @@ to take stock of the errors that Stack itself can raise, by reference to the
[S-2965] | GHCInfoMissingGlobalPackageDB
[S-5219] | GHCInfoMissingTargetPlatform
[S-8299] | GHCInfoTargetPlatformInvalid String
[S-6307] | GHCInfoSemaphoreVersionUnknown String
[S-2574] | CabalNotFound (Path Abs File)
[S-8488] | GhcBootScriptNotFound
[S-1128] | HadrianScriptNotFound
Expand Down Expand Up @@ -331,6 +332,7 @@ to take stock of the errors that Stack itself can raise, by reference to the
[S-5378] | GlobalPackageCacheFileMetadataMismatch
[S-2673] | GlobalDumpParseFailure
[S-8441] | CompilerCacheArchitectureInvalid Text
[S-9841] | GhcSemaphoreProtocolVersionUnknown
~~~

- `Stack.Templates.TemplatesPrettyException`
Expand Down
51 changes: 40 additions & 11 deletions src/Stack/Build/ExecuteEnv.hs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import Stack.Constants
, relDirSetupExeCache, relDirSetupExeSrc, relFileBuildLock
, relFileSetupHs, relFileSetupLhs, relFileSetupLower
, relFileSetupMacrosH, setupGhciShimCode, stackProgName
, osIsWindows
)
import Stack.Constants.Config ( distDirFromDir, distRelativeDir )
import Stack.Package ( buildLogPath )
Expand Down Expand Up @@ -122,7 +123,9 @@ import System.Environment ( lookupEnv )
import System.FileLock
( SharedExclusive (..), withFileLock, withTryFileLock )
import System.Semaphore
( Semaphore, destroySemaphore, freshSemaphore )
( ServerSemaphore, destroyServerSemaphore, freshSemaphore
, semaphoreVersion, versionsAreCompatible
)

-- | Type representing environments in which the @Setup.hs@ commands of Cabal
-- (the library) can be executed.
Expand Down Expand Up @@ -155,8 +158,9 @@ data ExecuteEnv = ExecuteEnv
-- ^ For nicer interleaved output: track the largest package name size
, pathEnvVar :: !Text
-- ^ Value of the PATH environment variable
, semaphore :: !(Maybe Semaphore)
-- ^ The semaphore that is used for job control, if --semaphore is given
, serverSemaphore :: !(Maybe ServerSemaphore)
-- ^ The server semaphore that is used for job control, if --semaphore is
-- given
}

-- | Type representing setup executable circumstances.
Expand Down Expand Up @@ -345,26 +349,51 @@ withExecuteEnv
logFiles <- liftIO $ atomically newTChan
let totalWanted = length $ filter (.wanted) locals
pathEnvVar <- liftIO $ maybe mempty T.pack <$> lookupEnv "PATH"
ghcSemaphoreVersion <- view $ compilerPathsL . to (.semaphoreVersion)
jobs <- view $ configL . to (.jobs)
let semaphoreSupported =
(cabalPkgVer >= mkVersion [3, 12, 0, 0])
&& (ghcVersion >= mkVersion [9, 8, 1])
&& maybe
False
(versionsAreCompatible semaphoreVersion)
ghcSemaphoreVersion
semaphoreUnsupportedWarning =
prettyWarnL
[ "The"
, style Shell "--semaphore"
, flow "flag was specified, which is supported by GHC 9.8.1 or \
\later with Cabal 3.12.0.0 (a boot package of GHC 9.10.1) \
\or later. GHC version"
, flow "flag was specified, which is supported by"
, ghcSemaphoreSupportMessage
, flow "with Cabal 3.12.0.0 (a boot package of GHC 9.10.1) or \
\later. GHC version"
, fromString (versionString ghcVersion)
, flow "and Cabal version"
, fromString (versionString cabalPkgVer)
, flow "was found. The flag will be ignored."
]
semaphore <- if not buildOpts.semaphore
ghcSemaphoreSupportMessage = if osIsWindows
then
flow "GHC 9.8.1 or later (on Windows)"
else
-- Protocol version 1 was problematic on non-musl Linux
-- distributions only, because non-musl and musl semaphores are
-- incompatible and statically-linked binaries are musl. The GHC
-- project has decided that the semaphore-compat-2.0.0 package
-- will not be backwards compatible on all operating systems other
-- than Windows.
fillSep
[ flow "GHC if"
, style Shell "ghc --info"
, flow "reports a semaphore version (on operating systems \
\other than Windows)"
]
serverSemaphore <- if not buildOpts.semaphore
then pure Nothing
else if semaphoreSupported
then Just <$> liftIO (freshSemaphore semaphorePrefix jobs)
then do
result <- liftIO $ freshSemaphore semaphorePrefix jobs
case result of
Left err -> throwM err
Right serverSemaphore -> pure $ Just serverSemaphore
else semaphoreUnsupportedWarning >> pure Nothing
inner ExecuteEnv
{ buildOpts
Expand All @@ -391,9 +420,9 @@ withExecuteEnv
, customBuilt
, largestPackageName
, pathEnvVar
, semaphore
, serverSemaphore
} `finally` do
liftIO (whenJust semaphore destroySemaphore)
liftIO (whenJust serverSemaphore destroyServerSemaphore)
dumpLogs logFiles totalWanted
where
toDumpPackagesByGhcPkgId = Map.fromList . map (\dp -> (dp.ghcPkgId, dp))
Expand Down
15 changes: 9 additions & 6 deletions src/Stack/Build/ExecutePackage.hs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ import System.IO.Error ( isDoesNotExistError )
import System.PosixCompat.Files
( createLink, getFileStatus, modificationTime )
import System.Random ( randomIO )
import System.Semaphore ( Semaphore (..), SemaphoreName (..) )
import System.Semaphore
( ServerSemaphore (..), clientSemaphoreName
, semaphoreIdentifier
)

-- | Generate the t'ConfigCache' value.
getConfigCache ::
Expand Down Expand Up @@ -576,7 +579,7 @@ realConfigAndBuild
<> display actualCompiler
)
config <- view configL
extraOpts <- extraBuildOptions wc ee.buildOpts ee.semaphore
extraOpts <- extraBuildOptions wc ee.buildOpts ee.serverSemaphore
let stripTHLoading
| config.hideTHLoading = ExcludeTHLoading
| otherwise = KeepTHLoading
Expand Down Expand Up @@ -1326,15 +1329,15 @@ extraBuildOptions ::
(HasEnvConfig env, HasRunner env)
=> WhichCompiler
-> BuildOpts
-> Maybe Semaphore
-> Maybe ServerSemaphore
-> RIO env [String]
extraBuildOptions wc bopts semaphore = do
extraBuildOptions wc bopts mServerSemaphore = do
colorOpt <- appropriateGhcColorFlag
let optsFlag = compilerOptionsCabalFlag wc
semaphoreFlag = maybe
[]
(("--semaphore":) . L.singleton . getSemaphoreName . semaphoreName)
semaphore
(("--semaphore":) . L.singleton . semaphoreIdentifier . clientSemaphoreName . serverClientSemaphore)
mServerSemaphore
baseOpts = maybe "" (" " ++) colorOpt
if bopts.testOpts.coverage
then do
Expand Down
25 changes: 24 additions & 1 deletion src/Stack/Setup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ import System.IO.Error ( isPermissionError )
import System.FilePath ( searchPathSeparator )
import qualified System.FilePath as FP
import System.Permissions ( setFileExecutable )
import System.Semaphore ( SemaphoreProtocolVersion (..) )
import System.Uname ( getRelease )

-- | Type representing exceptions thrown by functions exported by the
Expand Down Expand Up @@ -233,6 +234,7 @@ data SetupPrettyException
| GHCInfoMissingGlobalPackageDB
| GHCInfoMissingTargetPlatform
| GHCInfoTargetPlatformInvalid !String
| GHCInfoSemaphoreVersionUnknown !String
| CabalNotFound !(Path Abs File)
| GhcBootScriptNotFound
| HadrianScriptNotFound
Expand Down Expand Up @@ -468,6 +470,13 @@ instance Pretty SetupPrettyException where
[ flow "Invalid target platform in GHC info:"
, fromString targetPlatform <> "."
]
pretty (GHCInfoSemaphoreVersionUnknown s) =
"[S-6307]"
<> line
<> fillSep
[ flow "Unknown semaphore version in GHC info:"
, fromString s <> "."
]
pretty (CabalNotFound compiler) =
"[S-2574]"
<> line
Expand Down Expand Up @@ -1527,7 +1536,20 @@ pathsFromCompiler wc build sandboxed compiler =
case Map.lookup cabalPackageName globalDump of
Nothing -> prettyThrowIO $ CabalNotFound compiler
Just dp -> pure $ pkgVersion dp.packageIdent

let semaphoreSupported =
getGhcVersion compilerVersion >= mkVersion [9, 8, 1]
semaphoreVersion = case Map.lookup "Semaphore version" infoMap of
Nothing -> if osIsWindows && semaphoreSupported
then
Just $ SemaphoreProtocolVersion 1
else
-- If GHC (via ghc --info) does not know what protocol version
-- for the semaphore it supports, we can be confident that it does
-- not support a protocol version >= 2:
Nothing
Just "1" -> Just $ SemaphoreProtocolVersion 1
Just "2" -> Just $ SemaphoreProtocolVersion 2
Just s -> prettyThrowM $ GHCInfoSemaphoreVersionUnknown s
pure CompilerPaths
{ build
, arch
Expand All @@ -1540,6 +1562,7 @@ pathsFromCompiler wc build sandboxed compiler =
, cabalVersion
, globalDB
, ghcInfo
, semaphoreVersion
, globalDump
}
where
Expand Down
20 changes: 19 additions & 1 deletion src/Stack/Storage/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ import qualified RIO.FilePath as FP
import Stack.Prelude
import Stack.Storage.Util
( handleMigrationException, setUpdateDiff, updateCollection )
import Stack.Types.Cache ( Action (..), PrecompiledCache (..) )
import Stack.Types.Cache
( Action (..), GhcSemaphoreProtocolVersion (..)
, PrecompiledCache (..)
)
import Stack.Types.Compiler ( ActualCompiler, compilerVersionText )
import Stack.Types.CompilerBuild ( CompilerBuild )
import Stack.Types.CompilerPaths
Expand All @@ -70,6 +73,7 @@ data StorageUserException
| GlobalPackageCacheFileMetadataMismatch
| GlobalDumpParseFailure
| CompilerCacheArchitectureInvalid Text
| GhcSemaphoreProtocolVersionUnknown
deriving Show

instance Exception StorageUserException where
Expand All @@ -88,6 +92,9 @@ instance Exception StorageUserException where
, "Invalid arch: "
, show compilerCacheArch
]
displayException GhcSemaphoreProtocolVersionUnknown =
"Error: [S-9841]\n"
++ "GHC semaphore protocol version unknown, ignoring cache."

share [ mkPersist sqlSettings
, mkMigrate "migrateAll"
Expand Down Expand Up @@ -141,6 +148,7 @@ CompilerCache
globalDb FilePath
globalDbCacheSize Int64
globalDbCacheModified Int64
semaphoreVersion GhcSemaphoreProtocolVersion Maybe
info ByteString

-- This is the ugliest part of this table, simply storing a Show/Read version of the
Expand Down Expand Up @@ -366,6 +374,13 @@ loadCompilerPaths compiler build sandboxed = do
Nothing -> throwIO $
CompilerCacheArchitectureInvalid compilerCache.compilerCacheArch
Just arch -> pure arch
semaphoreVersion <- maybe
(throwIO GhcSemaphoreProtocolVersionUnknown)
( \case
Unsupported -> pure Nothing
Supported spv -> pure $ Just spv
)
compilerCache.compilerCacheSemaphoreVersion
pure CompilerPaths
{ compiler
, compilerVersion = compilerCache.compilerCacheActualVersion
Expand All @@ -378,6 +393,7 @@ loadCompilerPaths compiler build sandboxed = do
, cabalVersion
, globalDB
, ghcInfo = compilerCache.compilerCacheInfo
, semaphoreVersion
, globalDump
}

Expand Down Expand Up @@ -405,6 +421,8 @@ saveCompilerPaths cp = withUserStorage $ do
, compilerCacheGlobalDbCacheSize = sizeToInt64 $ fileSize globalDbStatus
, compilerCacheGlobalDbCacheModified =
timeToInt64 $ modificationTime globalDbStatus
, compilerCacheSemaphoreVersion = Just $
maybe Unsupported Supported cp.semaphoreVersion
, compilerCacheInfo = cp.ghcInfo
, compilerCacheGlobalDump = tshow cp.globalDump
, compilerCacheArch = T.pack $ Distribution.Text.display cp.arch
Expand Down
Loading
Loading