What are cabal components and how do I use them? - haskell

cabal build --help and others mention components like in sentences like
Build one or more targets from within the project. The available targets are
the packages in the project as well as individual components within those
packages, including libraries, executables, test-suites or benchmarks. Targets
can be specified by name or location. If no target is specified then the
default is to build the package in the current directory.
From the cabal user guide mentions them too in 9. Setup.hs Commands, and
gives two prefixes exe: and lib: to select those. Are there more of those prefixes?

A component is anything behind a stanza with its own set of dependencies, etc. So it can be multiple sublibraries, multiple executables, test-suites, etc.
You're right that this is underdocumented! In the source code we can see the following (https://github.com/haskell/cabal/blob/00a2351789a460700a2567eb5ecc42cca0af913f/Cabal/src/Distribution/Simple/BuildTarget.hs#L569)
matchComponentKind :: String -> Match ComponentKind
matchComponentKind s
| s `elem` ["lib", "library"] = return' LibKind
| s `elem` ["flib", "foreign-lib", "foreign-library"] = return' FLibKind
| s `elem` ["exe", "executable"] = return' ExeKind
| s `elem` ["tst", "test", "test-suite"] = return' TestKind
| s `elem` ["bench", "benchmark"] = return' BenchKind
| otherwise = matchErrorExpected "component kind" s
where
return' ck = increaseConfidence >> return ck
So that's the full list!

Related

Haskell-Stack failing to build project with DateTime dependency despite entry in stack.yaml

So I'm trying to add this package: datetime-0.3.1 and I added what I think is the correct reference in the stack.yaml file. I tried using stack solver but that doesn't seem exist anymore. I also looked for some equivalent of pip so I could just do stack install datetime-0.3.1 or something similar but that doesn't appear to be something stack does.
The code:
module FhirDataTypes (
FhirId (..),
toFhirId
) where
import Data.Maybe (Maybe(..))
import Data.List (length)
import Coding as Coding
import Data.Decimal
import FhirUri (FhirUri(..))
import FhirString (FhirString(..))
import SimpleQuantity (SimpleQuantity(..))
import Data.DateTime
newtype FhirId = FhirId FhirString deriving (Show)
toFhirId :: FhirString -> Maybe FhirId
toFhirId fs#(FhirString s)
| length s > 64 = Nothing
| otherwise = Just $ FhirId fs
data Money = Money { value :: Decimal
, currency :: Code
}
data Range = Range { low :: SimpleQuantity
, high :: SimpleQuantity
}
data Ratio = Ratio { numerator :: Quantity
, denominator :: Quantity
}
data Period = Period { start :: DateTime
, end :: DateTime
}
The error I'm getting:
PS C:\util\haskell\fhir-practice> stack build
Error: While constructing the build plan, the following exceptions were encountered:
In the dependencies for fhir-practice-0.1.0.0:
DateTime needed, but the stack configuration has no specified version (no package with that name found, perhaps there is a typo in
a package's build-depends or an omission from the stack.yaml packages list?) needed since fhir-practice is a build target.
Some different approaches to resolving this:
Plan construction failed.
My stack.yaml file:
flags: {}
packages:
- .
extra-deps:
- network- uri-2.6.1.0#sha256:62cc45c66023e37ef921d5fb546aca56a9c786615e05925fb193a70bf0913690
- Decimal-0.4.2
- datetime-0.3.1
resolver: lts-13.24
stack install is mostly used for installing binaries globally, not for project-specific packages.
You probably want to use the time package, not datetime. as the former is actively maintained. Moreover, in your case, time is present in LTS-13.24, so you shouldn't need to add it to extra-deps. The extra-deps field is only for dependencies (including transitive ones) which are not present in your resolver.

Cabal package difference between readPackageDescription and parsePackageDescription

Haskell package Cabal-1.24.2 has module Distribution.PackageDescription.Parse.
Module has 2 functions: readPackageDescription and parsePackageDescription.
When I run in ghci:
let d = readPackageDescription normal "C:\\somefile.cabal"
I got parsed GenericPackageDescription
But when I run in ghci:
content <- readFile "C:\\somefile.cabal"
let d = parsePackageDescription content
I got Parse error:
ParseFailed (FromString "Plain fields are not allowed in between stanzas: F 2 \"version\" \"0.1.0.0\"" (Just 2))
File example is a file that generated using cabal init
parsePackageDescription expects the file contents themselves to be passed it, not the file path they are stored at. You'll want to readFile first... though beware of file encoding issues. http://www.snoyman.com/blog/2016/12/beware-of-readfile

Ivory: how to use the ivory-hw package

I'm experimenting with Ivory (http://ivorylang.org, https://github.com/GaloisInc/ivory) and using the ivory-hw module to manipulate some registers in a microcontroller.
cmain :: Def ('[] :-> ())
cmain = voidProc "main" $ body $ do
setReg regFoo $ do
clearBit foo_bitbar
setBit foo_bitbaz
forever $ return ()
main_module :: Module
main_module = package "main" $ do
incl cmain
main :: IO ()
main = runCompiler [ main_module ] [] (initialOpts {constFold = True,
outDir = Just "out"})
Building and running gives:
$ exe
*** Procedure main
ERROR: [ No location available ]:
Unbound value: 'ivory_hw_io_write_u32'
exe: Sanity-check failed!
Adding the option scErrors = False to runCompiler turns sanity checks off and the code runs to completion generating sources.
However, main.c contains a call to ivory_hw_io_write_u32 but this function is not defined anywhere (perhaps explaining the error). Poking about github, I can find examples that have a file ivory_hw_prim.h.
After some experimentation, I can include this by adding a module for the hw stuff and then adding that as a dependency to my main_module:
hw_module :: Module
hw_module = package "ivory_hw_prim" hw_moduledef
main_module :: Module
main_module = package "main" $ do
depend hw_module
incl cmain
and calling the runCompiler with hw_artifacts added to generate the header:
main = runCompiler [ main_module ] hw_artifacts (initialOpts {scErrors = False,
constFold = True,
outDir = Just "out"})
This adds ivory_hw_prim.h to the collection of files generated and includes the necessary include in main.h.
However, this only works by retaining the scErrors = False option to runCompiler which suggests that I am still not doing this right.
My question is therefore: What is the correct way to use Ivory's HW package?
The solution is to include hw_moduledef in the package:
main_module :: Module
main_module = package "main" $
incl cmain >> hw_moduledef
(The depend function just includes the header.) Including hw_moduledef in the package "main" makes its definitions visible to the sanity-checker.
By the way, the Ivory module system may be improved in the future, so that Ivory computes, at compile time, the dependencies, relieving the programmer from having to make explicit includes.

"Could not find module ‘Test.HUnit’" Error when executing Haskell's unittest (HUnit) in CodeRunner

I have simple unit test code for Haskell's HUnit. I use Mac OS X 10.10, and I installed HUnit with cabal install hunit.
module TestSafePrelude where
import SafePrelude( safeHead )
import Test.HUnit
testSafeHeadForEmptyList :: Test
testSafeHeadForEmptyList =
TestCase $ assertEqual "Should return Nothing for empty list"
Nothing (safeHead ([]::[Int]))
testSafeHeadForNonEmptyList :: Test
testSafeHeadForNonEmptyList =
TestCase $ assertEqual "Should return (Just head) for non empty list" (Just 1)
(safeHead ([1]::[Int]))
main :: IO Counts
main = runTestTT $ TestList [testSafeHeadForEmptyList, testSafeHeadForNonEmptyList]
I can execute it with runhaskell TestSafePrelude.hs to get the results:
Cases: 2 Tried: 2 Errors: 0 Failures: 0
Counts {cases = 2, tried = 2, errors = 0, failures = 0}
However, when I run it in Code Runner, I have error message that can't find the HUnit module.
CodeRunner launches the test on a different shell environment, and this seems to be the issue. If so, what environment variables need to be added? If not, what might be causing the problem?
I also find that ghc-pkg list from the CodeRunner does not search for the directories in ~/.ghc which contains the HUnit.
/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/package.conf.d:
Cabal-1.18.1.4
array-0.5.0.0
...
xhtml-3000.2.1
This is the results when executed in shell:
/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/package.conf.d
Cabal-1.18.1.4
array-0.5.0.0
...
/Users/smcho/.ghc/x86_64-darwin-7.8.3/package.conf.d
...
HUnit-1.2.5.2
...
zlib-0.5.4.2
I added both ~/.cabal and ~/.ghc in the path, but it doesn't work.
The problem was the $HOME setup change. I used different $HOME for CodeRunner, but Haskell searches for $HOME/.cabal and $HOME/.ghc for installed package.
After my resetting $HOME to correct location, everything works fine.

How do I make a generated file depend on a java class in Scons

I have a piece of source code in our build that is generated by running a java class.
I can't for the life of me work out how to instruct scons on the dependency, and it keeps trying to build the source code before it builds the .class file.
When I do o = env.Java(target = 'target_dir', source = Dir('source_dir')), o is set to an empty list.
You can use the Depends() function for that, something like this:
o = env.Java(target = 'target_dir', source = Dir('source_dir'))
env.Depends(o, 'the_generated_file_and_path')
Here is a better option (since you dont have to worry about the gen'd file path when calling Depends()), assuming you are executing said java class with the SCons Command() function:
# $SOURCE and $TARGET will be filled in by SCons Command, $SOURCE is optional
cmdLine = 'theCommand $SOURCE $TARGET'
genTarget = env.Command(target = [list output files here],
source = 'java_class_executed',
action = cmdLine)
...
o = env.Java(...)
env.Depends(o, genTarget)
Regarding o being an empty list, is it always an empty list? If so, be careful how you specify the source, it should be the root of the package dir structure. For example, if your dir tree is something like this:
.
|-- SConstruct
`-- src
`-- com
`-- tanner
`-- application
`-- main.java
You should specify the source dir as follows: '#/src/com'

Resources