Minimal haskell (ghc) program installation (deployment without ghc/cabal) - haskell

(My problem is about distribute binaries without haskell-platform, ghc, cabal, ...)
I need deploy a well cabal formed haskell application (a Yesod scaffolded) but I have disk space restrictions.
GHC size is about 1Gbytes, store all cabal source code, packages, etc... require more disk space, etc...
Obviously, haskell-platform, ghc, ... is about development (not deployment).
In my specific case I can generate
cabal clean && cabal configure && cabal build
and run succesfully (some like)
./dist/build/MyEntryPoint/MyEntryPoint arg arg arg
But, what about dependencies?, how move it to production environment? (together my "dist" compilation)
Can I put binary dependencies without cabal? How?
Thank you very much!

By default, ghc uses static linking of the Haskell libraries. So the resulting binary is independent of the Haskell ecosystem. If your program does not need any data files, just copy the binary out from ./dist/build/MyEntryPoint/MyEntryPoint to the host
If you also have data files (e.g templates, images, static html pages) that are referenced by the binary using the data path finding logic of Cabal, you can use Setup copy as follows (using happy as an example):
/tmp/happy-1.18.10 $ ./Setup configure
Warning: defaultUserHooks in Setup script is deprecated.
Configuring happy-1.18.10...
/tmp/happy-1.18.10 $ ./Setup build
Building happy-1.18.10...
Preprocessing executable 'happy' for happy-1.18.10...
[ 1 of 18] Compiling NameSet ( src/NameSet.hs, dist/build/happy/happy-tmp/NameSet.o )
[..]
[18 of 18] Compiling Main ( src/Main.lhs, dist/build/happy/happy-tmp/Main.o )
Linking dist/build/happy/happy ...
/tmp/happy-1.18.10 $ ./Setup copy --destdir=/tmp/to_be_deployed/
Installing executable(s) in /tmp/to_be_deployed/usr/local/bin
/tmp/happy-1.18.10 $ find /tmp/to_be_deployed
/tmp/to_be_deployed
/tmp/to_be_deployed/usr
/tmp/to_be_deployed/usr/local
/tmp/to_be_deployed/usr/local/bin
/tmp/to_be_deployed/usr/local/bin/happy
/tmp/to_be_deployed/usr/local/share
/tmp/to_be_deployed/usr/local/share/doc
/tmp/to_be_deployed/usr/local/share/doc/happy-1.18.10
/tmp/to_be_deployed/usr/local/share/doc/happy-1.18.10/LICENSE
/tmp/to_be_deployed/usr/local/share/happy-1.18.10
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/GLR_Lib-ghc-debug
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/GLR_Lib-ghc
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/GLR_Lib
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/GLR_Base
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-arrays-coerce-debug
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-arrays-ghc-debug
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-arrays-debug
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-arrays-coerce
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-arrays-ghc
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-arrays
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-coerce
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate-ghc
/tmp/to_be_deployed/usr/local/share/happy-1.18.10/HappyTemplate
/tmp/happy-1.18.10 $ rsync -rva /tmp/to_be_deployed/ production.host:/
[..]
If you do not want to install into /usr/local then pass the desired prefix to Setup configure.
This works well if the target host is otherwise similar (same versions of C libraries such as gmp and ffi installed). If you also need to statically link some C library, see the question that hammar has linked in his comment.

Related

Cabal cannot find locally sourced (yet correctly installed) packages

I recently upgraded to Cabal 3.2 (and GHC 8.10) and I am running into some major issues that make some of my project non-buildable anymore...
Thorough description of the problem
Here is a minimal (not) working configuration that fails every time:
I start off with a clean Cabal configuration (by deleting ~/.cabal); the reason for that will appear later in the post. I run cabal update to recreate the .cabal directory and to ensure Cabal is working.
I create a project (let's call it test1) using cabal init. This is a library project with one exposed module (conveniently named Test1) that exports some dummy function foo. I run cabal build, then cabal install --lib; everything is running smooth, so far so good.
Just to be sure, I leave the project directory and fire up GHCi. I type in :m Test1 to load the module I created earlier, and it works! I can type in foo ... and see my function executed. Also, I list the content of ~/.cabal/store/ghc-8.10.xxx and see that the test1-xxx directory is there.
I then create a new project, test2, still using cabal init. This time, I configure it to be an executable, and I add test1 as a dependency (using the build-depends field). But this time when I run cabal build, I run into some issue:
~/projects/haskell/test2> cabal build
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] trying: test2-0.1.0.0 (user goal)
[__1] unknown package: test1 (dependency of test2)
[__1] fail (backjumping, conflict set: test1, test2)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test2, test1
It seems to me like package test1 cannot be found, however I can access it from GHCi (and GHC for that matters) and it is present in ~/.cabal/store...
But unfortunately there is more.
I create a third project, test3. This is a library, and it depends on nothing else than base (so in particular it does not depend on test1). The lib exposes one module, Test3, with one function exported, bar. I run cabal build, no problem here. But when I want to install test3 with cabal install --lib I run into some errors:
~/projects/haskell/test3> cabal install --lib
Wrote tarball sdist to
/home/<user>/projects/haskell/test3/dist-newstyle/sdist/test3-0.1.0.0.tar.gz
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] unknown package: test1 (user goal)
[__0] fail (backjumping, conflict set: test1)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test1
It seems that it cannot find test1, although it has been installed correctly; may be this is a remnant of the failed build of test2 though...
Just to be sure, I fire up GHCi and type in :m Test3, but GHCi tells me that it cannot find module Test3 (and even suggests this is a typo and I was meaning Test1), showing that test3 indeed did not get installed, although it got successfully built...
Okay there is one more quirk to this whole situation: I create once again a new project with cabal init, called test4, which is an executable that (again) depends on nothing else than base. I keep the default Main.hs (that just prints "Hello, Haskell!"). I run cabal build: no problem. Then I run cabal install and... No problem either? I run test4 in a random location and it fires up the executable, printing "Hello, Haskell!" in the terminal...
And there is one last thing: I go to some random location and I run cabal install xxx --lib where xxx is a library package available on Hackage (for example xml) and:
~> cabal install xml --lib
Resolving dependencies...
cabal: Could not resolve dependencies:
[__0] unknown package: test1 (user goal)
[__0] fail (backjumping, conflict set: test1)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: test1
This is the reason why I need to nuke .cabal regularly... Right now I seem to be in some kind of stale state where I cannot install any library anymore.
Technical configuration and notes
I am running Cabal 3.2.0.0 and GHC 8.10.0.20200123. I installed them from the hvr/ghc PPA, and I made sure there are no other versions of those tools anywhere on my computer.
Just as a note, I am running Ubuntu 18.04.4 LTS (with XFCE so XUbuntu to be exact). Everything else (seem to be like it) is up to date.
Last thing, regarding the *.cabal files I use for building, they are pretty much the ones generated by cabal init, except I switch executable xxx for library in the case of libraries, and I simply add a exposed-modules field for exposing modules for the libraries (so Test1 for test1 and Test3 for test3 respectively). I also use build-depends in test2 to make the project depend on test1. Apart from that, they are pretty much left untouched.
Notes and thoughts
I must confess that I am new to Cabal 3; until last week I was using Cabal 1 (because I never bothered to update it; yes I know this is bad). With Cabal 1 I did not have any problem whatsoever, and I was perfectly able to install a package from local sources and depend on it in other projects...
I feel like I am doing something wrong; maybe am I not using the correct Cabal commands? I saw somewhere something about cabal new-build and cabal new-install but it does not seem to do anything more than cabal build and cabal install, at least in my case. I also wanted to investigate sandboxes but it seems that has disappeared since version 2 of Cabal.
There is also a slight possibility this is a Cabal bug, but I don't find any relevant issue on the bug tracker that may be related to my problem...
What do you think about this? What am I doing wrong? Do you see any alternative or possible fix?
Thanks a lot!
GHC environment files
A GHC installation comes with a certain number of packages out-of-the box. base is one of them but there are others, for example text. If you install GHC alone (no cabal or stack) and open ghci, it should let you import Data.Text without problems.
What if you want GHC or ghci to be aware of other compiled packages present in your filesystem? You can point GHC to additional package databases using command-line flags, but there's also the concept of package environment files.
Environments are plain text files that contain a list of package-related GHC flags. There might be a global environment at ~/.ghc/$ARCH-$OS-$GHCVER/environments/default, and there might also exist local environments which only affect GHC and ghci commands invoked inside the same folder. The exact rules for search are described in the GHC User Guide.
What does cabal install --lib actually do?
By default, it modifies the global environment file, so that GHC and ghci can now find that library. That's why point 3) worked. The actual compiled binaries of the library still reside in the cabal store though.
We can also create local environment files. For example cabal install sop-core --lib --package-env . will create the environment file .ghc.environment.xxx in the current folder, and the library will be available to ghc and ghci when they are invoked there.
Why isn't test1 available for test2?
Modern cabal makes a distinction between local packages and external packages.
local packages is the set of packages you are developing together in a project, being edited, recompiled and changed repeatedly. They are built "inplace" and not seen outside the project. They can depend on each other.
external packages are dependencies from build-depends: whose source code is downloaded from a package repository and which, when compiled, are put in the cabal store so that other Cabal projects might make use of them without re-compiling.
The list of local packages and other project-level configuration details are specified in a cabal.project file. But you don't need one if you work on a single isolated package; the default list of packages is simply ./*.cabal.
cabal wants to completely control the build environment of local packages, and will ignore the global environment file. In your case, you'll have to make test1 and test2 local packages in the same project (likely the best option) or publish test1 and treat it as an external package.
Note that "cabal project" is a concept relevant only during development. Packages are published independently, there are no "projects" in Hackage or other repositories, just packages.
What if I want to treat test1 as external without publishing it to Hackage?
You will have to set up a local package repository, basically a non-public Hackage.
You can tell Cabal about additional package repositories in the Cabal configuration file, that is, the file that configures cabal itself. Its location is given in the last line of cabal --help.
But how to set up the repository? The hackage-repo-tool can help with that.
Why did test3 fail? Why did further library installs fail?
That's weird, I have no idea why that happens. Did you by perchance delete the ~/.cabal folder between steps 3) and 5) ? What happens if you delete the global GHC environment file and try again?

Duplicate packages in Haskell environment?

I put this in a file (see bottom) and got the following error when loading it in ghci (i.e. start ghci then :l file):
7:13: error:
• Couldn't match expected type ‘network-uri-2.6.1.0#network-uri-2.6.1.0-7BN1tbB3iHQ2XgvmqLAYph:Network.URI.URI’
with actual type ‘Network.URI.URI’
NB: ‘Network.URI.URI’
is defined in ‘Network.URI’
in package ‘network-uri-2.6.1.0#network-uri-2.6.1.0-80FpvaNUTSDFCPv0sSze40’
‘network-uri-2.6.1.0#network-uri-2.6.1.0-7BN1tbB3iHQ2XgvmqLAYph:Network.URI.URI’
is defined in ‘Network.URI’
in package ‘network-uri-2.6.1.0#network-uri-2.6.1.0-7BN1tbB3iHQ2XgvmqLAYph’
Have I somehow installed two slightly different versions of network-uri, or what is this? How do I resolve it?
I've hardly used cabal but tried cabal install --reinstall network-uri, went fine but problem persists.
File contents:
import Network.HTTP
import Network.URI (parseURI)
import Data.Maybe (fromJust)
myRequestURL = "http://www.virginia.edu/cgi-local/ldapweb"
myRequest :: String -> Request_String
myRequest query = Request {
rqURI = fromJust $ parseURI myRequestURL
, rqMethod = POST
, rqHeaders = [ mkHeader HdrContentType "text/html"
, mkHeader HdrContentLength $ show $ length body ]
, rqBody = body
}
where body = "whitepages=" ++ query
main :: IO ()
main = do
response <- simpleHTTP $ myRequest "poon"
putStrLn ""
EDIT Re-installed haskell-platform. After this, ghc-pkg list reports all packages to be under (?) /usr/lib/ghc/package.conf.d, and the program I tried to copy loads just fine.
EDIT 2 After running cabal install some-pkg, ghc-pkg list reports that the newly installed stuff is in another directory. Indeed, ~/.cabal/config specifies install dirs for user, as well as install dirs for global. How is all this supposed to be managed properly?
GHC understands the concept of package databases. Cabal manages multiple package databases. There's a system package database, and a user package database. When you compile something with Cabal V1, it uses the union of these package databases. This is a bit of a time bomb, because, as you see, GHC does not like it when you have multiple of the same package. When you issued cabal install network-uri, cabal decided to install one copy of that package into your user database, though you already had one in the system database. When you tried to use ghc, it picked up on the two copies and got confused. Because cabal does not support removing packages, your Haskell installation was pretty much borked, and you reinstalled. I believe you could have done a more conservative rm ~/.ghc, or, had you been really adventurous, used the low-level ghc-pkg command to edit the package database.
Now that you've reinstalled the Platform, I would heavily recommend never using cabal install again. It's old, it's broken, cabal itself tells you not to use it. If you're using the Platform, I think it has stack in it, which is a different build manager, based on Cabal, which is not prone to this kind of breakage. Or, you can just stick to the V2 series of cabal commands, which is what I prefer.
Cabal V2 does not directly support your usecase, where you install a package globally and compile a program against it. This is because doing so is fundamentally broken: global package databases really just don't work for GHC. What Cabal V2 does is install packages into your home directory, ~/.cabal/store. You then have to explicitly tell it which packages you want, and it will construct a package database that contains each required package, without any wonkiness. You can get a REPL with a certain set of packages by issuing (this will take you to a temporary directory; you may need to :cd yourself back.)
cabal v2-repl -b network-uri -b package2 -b etc
If you want to actually compile files with ghc with a set packages, you have to actually define a package with a .cabal file. Here's a stub .cabal file:
name: temporary-pkg
version: 0.0.0.0
build-type: Simple
cabal-version: >=2.0
executable main -- if there is no Main, say "library" instead of "executable <name>"
default-language: Haskell2010
main-is: Main.hs -- only valid in executable stanzas
hs-source-dirs: src -- source files go here
build-depends: base, network-uri, package2, etc -- packages go here
Create and enter a directory, place that in temporary-pkg.cabal, place your sources in src/, and you can issue stuff like cabal v2-build and cabal v2-repl and cabal v2-exec main, etc. cabal will go off and install dependencies from Hackage into ~/.cabal/store as needed. cabal v2-install is therefore not that useful here. In general, v2-installing libraries is unnecessary, but v2-installing packages with executables is useful. Again, for hygiene reasons, you have to list all the packages you are importing from, but you don't need to list their dependencies or anything insane like that.
Cabal V2 still doesn't support uninstalling packages, though the point is that should not be necessary. However, if you find ~/.cabal/store getting too large for your liking, you can just nuke it, and should be built back up as packages are requested.

What is the preferred way to write quick Haskell test programs that depend on Stack libraries in local directories?

I have a Haskell library that I am developing using Stack. As I am developing the library, I like to write small test/experimentation programs that use the library. I keep a collection of these test programs for myself in a directory locally. These test modules are very quick and informal, and not appropriate to include as unit tests in the committed library code. Typically, most of them aren't even maintained and won't compile against the latest version of the library, but I keep them around in case I want to update them later. When I'm working on a test program, I want it to build against my working copy of the library, with any changes that I've made to the library locally.
How should I set up my Stack build environment for this situation? Here are some options I've tried, and the problems with each options.
Two Cabal packages, one Stack configuration. The stack.yaml file lists both packages and defines the build environment for both at once.
Problem: The stack.yaml file needs to be included as part of the committed library source code, so that other developers can build the library from source reproducibly. I don't want the public stack.yaml file for my library to include build information for my local test projects.
Problem: As far as I know, to make this work I need to have a .cabal file that lists all the executables and modules for my test programs. This is annoying to update whenever I want to throw together a quick experimental script, and will fail to build any of the test programs if I have even a single module that doesn't compile. I can't have a .cabal file with no sections, because Cabal gives "No executables, libraries,tests, or benchmarks found. Nothing to do.", and because this offers nowhere to list build-depends.
Create a Cabal sandbox for the test programs. Use cabal sandbox add-source to add the local library as a package. See also this answer.
Problem: Using Cabal sandboxes instead of Stack reintroduces a lot of the dependency problems that Stack is supposed to fix, such as using the system-global GHC instead of the GHC defined by the resolver.
Have a separate stack.yaml for the test programs. Add the library under packages as location: 'C:\Path\To\Local\Library' and set extra-dep: true for that dependency. (See here for more info on this feature.) Don't put any other Cabal packages under packages in the stack.yaml for the test programs. Use stack runghc to invoke test programs within the scope of their stack.yaml.
Problem: I just can't get this one to work. Running stack build inside the test program directory gives "Error parsing targets: The project contains no local packages (packages not marked with 'extra-dep')". Running stack runghc acts as if no dependencies are present at all. I don't want to add a Cabal package for the test programs because this has the same problem as option 1 with needing to construct an explicit .cabal file describing the modules to build.
Problem: Stack build configuration info that I want to be identical between the library and the test programs has to be copied manually. For example, if I change the resolver in my library's stack.yaml, I also need to change it in the stack.yaml for my test programs separately.
Have a directory inside my working copy of the library that contains all of my test programs. Use stack runghc to invoke test programs in the context of the library.
Problem: I'd like the directory with my test programs to be outside of the directory containing my library source code and build configuration, so that I don't have to tell the version control for my library to ignore my test code, and can have my own local version control just for the test programs.
Problem: Only works with a single local library dependency. If my test programs need to depend on local working copies of two different libraries with their own stack.yaml files, I'm out of luck.
Add a symbolic link inside my working copy of the library to a separate directory that contains all of my test programs. Navigate through the symlink and use stack runghc to invoke test programs in the context of the library.
Problem: Super awkward to use, especially since I'm on Windows and Windows has terrible symlink support.
Problem: Still need to tell my version control system to ignore the symlink.
Problem: Still only works with a single library dependency.
If only one local library is involved, I use option 4. You can put your tests outside the directory of your library, and either invoke stack from the directory of your library, or using --stack-yaml path/to/library/stack.yaml.
Otherwise, I use option 3, creating a separate stack project without setting extra-dep.
...
packages:
- 'path/to/package1'
- 'path/to/package2'
...
I can't think of a good workaround for the issue of configuration duplication. There would otherwise be conflicts if multiple packages specified different resolvers/package versions.
Edit: Actually a stub library works better, so edited to add.
I think the way to get #3 to work is -- under your scratch program directory -- (1) add . under packages in stack.yaml alongside the location/extra-dep: true package:
packages:
- '.'
- location: ../mylib
extra-dep: true
(2) create an executable clause in scratch.cabal that points to a stub main program (i.e., a "Hello World" program that compiles but need not do anything) which depends on your library:
executable main
hs-source-dirs: src
main-is: Stub.hs
build-depends: base
, mylib
default-language: Haskell2010
or a library clause with no exposed modules, again that depends on your mylib library:
library
hs-source-dirs: src
build-depends: base >= 4.7 && < 5
, mylib
default-language: Haskell2010
and (3) run stack build in the scratch directory. This should build and register mylib, and now stack runghc Prog1.hs should work fine for running programs that depend on mylib modules.
If you use the executable approach, the stub program is compiled as a side effect but otherwise ignored. If you use the library approach, it looks like the stub library isn't even built; and you then have the option of actually building a scratch library by adding some exposed modules of shared code for your test programs to use, if it's convenient, so the stub library might be best.
None of this solves the problem of keeping stack.yaml info like the resolver version in sync, but it seems to address all the problems you list in 1, 2, 4, and 5. In particular, it should work fine for test programs that depend on multiple local libraries you're developing.

Minimal cabal file for use in sandbox

I’m trying to write a project with the Hakyll library. In order to avoid messing up with my system, I’d install it in a cabal sandbox in the same folder where my Hakyll project lives.
Being more or less a beginner, I’m still struggling with getting the pest practices right. A simple approach would be to just do
$ cabal sandbox init
$ cabal install hakyll
$ cabal exec ghc -- --make site.hs
where the last line compiles my Hakyll generator using the libraries in the sandbox. The obvious disadvantage is that this is not reproducible. The main version of Hakyll could have change when I try to run it again from a clean checkout.
Another approach would be to write a proper project.cabal file (For example like this: chromaticleaves.cabal) and then do cabal install or cabal run.
However, I feel that this may be a bit too much information. As I do not intend to publish this project any more than needed, I’m not really convinced I need to put a project name and version number in there. (For example, in a Ruby Gemfile, I would also only specify the libraries and nothing else unless I wanted to publish a gem myself.)
So, eventually I figured that with a file like
$ cat project.cabal
cabal-version: >= 1.2
library
build-depends: base >=4.6
, containers
, process
, hakyll >=4.5
, pandoc
, pandoc-types
I can type
$ cabal sandbox init
$ cabal install --only-dependencies
$ cabal exec ghc -- --make site.hs
and it seems to download all dependencies and is able to compile the file.
Is this a reasonable approach or is the best practice really to give a full specification with name, version and executable sections in the cabal file?
Edit: Apparently, my approach does not let me do cabal repl. So either there exists a completely different way of doing it or it seems I have to go with a fuller specification.
I use your first approach myself for my Hakyll-based webpage. You don't need to create a .cabal file to pin the Hakyll version, you only need to add the following line to cabal.config:
constraints: hakyll == 4.5
I think that cabal repl will work with this approach, but you will need to load site.hs manually (:l site.hs). Or you can use cabal exec ghci -- site.hs.

Cabal with multiple Library sections

Is it possbile to write a Cabal configuration file, which contains multiple Library sections?
I found in the documentation the description of Library section and Executables sections, so it seems, that it is impossible to put more Library section in one Cabal configuration file.
But what should I do if I'm developing several Haskell libraries and several executables
simultaneously and want to compile and test them all?
AFAIK, you can't put more than one library in a cabal file. The name specified in the Name field (at the top level of the cabal file) is used as the name of the library, so there doesn't seem to be a mechanism for specifying names of additional libraries.
In practice, I haven't found this to be a problem. I develop each library in a separate directory, with its own cabal file. Once you run cabal install on a library you've developed, then it can be referenced in the cabal file for your executable (in the Build-Depends section), just the same as a package on Hackage.
So, for example, if you have two libraries with cabal files that look like this:
Name: my-library-1
. . .
and
Name: my-library-2
. . .
Then the cabal file for your executable can reference them like this:
Name: my-program
. . .
Executable run-program
Main-Is: Main.hs
Build-Depends: my-library1,
my-library2,
. . .
You can even require specific versions of your libraries. For example:
Build-Depends: my-library1==1.2.*,
my-library2>=1.3
This is possible in Cabal 2 with internal libraries, so called "convenience" libraries: https://github.com/haskell/cabal/pull/3022. This will not let you install these libraries though, they are just allowed to be composed into the final executables and public library exposed by a .cabal file. If you want to build multiple things in progress, you should use a cabal.project file - http://blog.ezyang.com/2016/05/announcing-cabal-new-build-nix-style-local-builds/ has some information on this.
I found out, that my problem can be easly solved with the newest cabal-dev.
If you've got 2 projects: A and B and you want to develop them in parallel, its nice to use cabal-dev install A B - it will build and install them both to the local cabal-dev repository. If you re-run this command, they will be rebuilt and reinstalled if necessary.
According to the documentation - If you want to register new or override existing package on local cabal-dev hackage, you should use cabal-dev add-source, which basically copy the source and allows you to install it like it was available on hackage.

Resources