Allow `stack build` to target sub-libraries
philderbeast opened this issue · 6 comments
Could we add lib as a comptype in docs section for target syntax?
- component. Instead of referring to an entire package and letting Stack decide which components to build, you select individual components from inside a package. This can be done for more fine-grained control over which test suites to run, or to have a faster compilation cycle. There are multiple ways to refer to a specific component (provided for convenience):
packagename:comptype:compnameis the most explicit. The available comptypes areexe,test, andbench.
SOURCE: Target syntax
There's no mention of lib as comptype but I can use it.
$ stack repl stack:lib
stack> initial-build-steps (lib)
Configuring GHCi with the following packages: stack
GHCi, version 9.2.5: https://www.haskell.org/ghc/ :? for help
[ 1 of 117] Compiling Network.HTTP.StackClient
...
[117 of 117] Compiling Stack.Ls
Ok, 117 modules loaded.
ghci>
I see it listed as a packagename:comptype target:
$ stack ide targets
stack:lib
stack:exe:stack
stack:exe:stack-integration-test
stack:test:stack-test
The library name is the same as the package name but we can't use that as a packagename:comptype:compname target.
$ stack repl stack:lib:stack
Error: [S-6948]
TargetParseException ["Directory not found: stack:lib:stack"]
Note that to specify options to be passed to GHCi, use the '--ghci-options' flag.
When we support multiple library packages we'll need to be able to specify the compname of libraries. Before that feature lands we could allow compname for libraries.
@philderbeast, it looks to me that that documentation is indeed incomplete, because it does not discuss packagename:lib.
Some further notes for my own use:
-
My simple test is
stack new sublibwith thepackage.yamlfile edited to replace the public library with an internal library namedmy-lib:internal-libraries: my-lib: source-dirs: src
Building that with the version of Stack in the
masterbranch yields:Warning: SubLibrary dependency is not supported, this will almost certainly fail. Building all executables for `sublib' once. After a successful build of all of them, only specified executables will be rebuilt. sublib> configure (internal-lib + exe) Configuring sublib-0.1.0.0... sublib> build (internal-lib + exe) Preprocessing library 'my-lib' for sublib-0.1.0.0.. Building library 'my-lib' for sublib-0.1.0.0.. [1 of 2] Compiling Lib [2 of 2] Compiling Paths_sublib Preprocessing executable 'sublib-exe' for sublib-0.1.0.0.. Building executable 'sublib-exe' for sublib-0.1.0.0.. [1 of 2] Compiling Main [2 of 2] Compiling Paths_sublib Linking .stack-work\dist\8a54c84f\build\sublib-exe\sublib-exe.exe ... sublib> copy/register Installing internal library my-lib in C:\Users\mikep\Documents\Code\Haskell\sublib\.stack- work\install\b0212ea3\lib\x86_64-windows-ghc-9.2.5\sublib-0.1.0.0-5SwOnzRGzitA9U58Nrua6j-my-lib Installing executable sublib-exe in C:\Users\mikep\Documents\Code\Haskell\sublib\.stack-work\install\b0212ea3\binIn this case, the initial warning seems misplaced, as the build does not fail.
The initial warning is generated by
Stack.Build.checkSubLibraryDependencies; perhaps it is no longer applicable? -
Type
Stack.Build.Target.RawTargethas a data constructorRTComponent. -
There is also
Stack.Build.Target.resolveRawTarget, which accepts (among other things)RawTargetand yields aResolveResult(including, among other things,Maybe NamedComponent). -
Type
Stack.Types.NamedComponent.NamedComponentdoes have a data constructorCLib(for a public library) and alsoCInternalLib !Text(for a named internal library). -
There is also
Stack.Build.Target.parseRawTargetwhich has (extract below) where the internal library is currently filtered out:parsePackageComponent = case T.splitOn ":" t of [pname, "lib"] | Just pname' <- parsePackageName (T.unpack pname) -> Just $ RTPackageComponent pname' $ ResolvedComponent CLib [pname, cname] | Just pname' <- parsePackageName (T.unpack pname) -> Just $ RTPackageComponent pname' $ UnresolvedComponent cname [pname, typ, cname] | Just pname' <- parsePackageName (T.unpack pname) , Just wrapper <- parseCompType typ -> Just $ RTPackageComponent pname' $ ResolvedComponent $ wrapper cname _ -> Nothing parseCompType t' = case t' of "exe" -> Just CExe "test" -> Just CTest "bench" -> Just CBench _ -> Nothing
-
Adding
"lib"toparseCompTypeallows the component to be specified, but then Stack does not recognise it as being in the package:While parsing, Stack encountered the error:
Component internal-lib:my-lib does not exist in package foo. -
Extending
Stack.Types.Config.ppComponentscan include internal libraries (extract):-- | All components available in the given 'ProjectPackage' ppComponents :: MonadIO m => ProjectPackage -> m (Set NamedComponent) ppComponents pp = do gpd <- ppGPD pp pure $ Set.fromList $ concat [ maybe [] (const [CLib]) (C.condLibrary gpd) , go CInternalLib (fst <$> C.condSubLibraries gpd) , go CExe (fst <$> C.condExecutables gpd) , go CTest (fst <$> C.condTestSuites gpd) , go CBench (fst <$> C.condBenchmarks gpd) ] where go :: (T.Text -> NamedComponent) -> [C.UnqualComponentName] -> [NamedComponent] go wrapper = map (wrapper . T.pack . C.unUnqualComponentName)
Then Stack complains:
❯ stack build foo:lib:my-lib
Error: [S-7086]
The following components have 'buildable: False' set in the Cabal configuration, and so cannot be targets:
foo:exe:my-lib
To resolve this, either provide flags such that these components are buildable, or only specify buildable targets.
@philderbeast, I've improved the existing online documentation.
I don't know if this should be a separate issue, but I'm having trouble getting sub-libs into HLS. I suspect it's related to this issue.
@theobat, currently, Stack's target syntax my-package:lib targets all of the library components of package my-package. Given the changes you have already made, do you think it would be difficult to change things so that my-package:lib targets only the main library and (eg) my-package:lib:my-sub-library targets the named library my-sub-library?
yes, this is a significant change, mainly because of how the cache system was built in stack, it's package based so building anything less than "all the libraries" means we have to deal with partial values in the cache. I don't remember all the details, but this is somewhat orthogonal to the component based builds : using component based builds, using package based builds we can also build "partially" a package.
Note that for component based builds I made a change in the cache system that is a step in the right direction for this, but it's only a step, dealing with partial cache values is going to be the real thing here.