5

I have a project that is split up into two parts:

  • Executable
  • Library

The executable part has client specific code, but the library has generic functionality that doesn't change across the different client projects.

Right now I have 5 projects that when compiled creates a 25 MB executable each, since GHC compiles the executable and all dependencies statically into one file.

What I would like to do instead is to compile my library with all it's dependencies statically into one file, that the executables then can load dynamically. So that all the .a files for the external dependencies gets compiled into one .so file.

A quote that explains what I want to do (Generating single .so from multiple C++ and C object files):

The easiest way is to combine them into a single .so file by either combining all object files, or building two static .a libraries and then linking them into a single shared library.

Is this even possible with GHC?

I have been looking at different solutions, dynamically linking all libraries, using split-objs, but I can't seem to find a solution for what I would like to do.

EDIT: Add information about my setup and what I have tried so far.

My setup

I'm currently using cabal-install version 1.18.1.2 and 1.18.1.1 of the library.

I have my project setup in a sandbox:

$ cabal sandbox init
$ cabal install --dependencies-only
$ cabal configure

This installs all the dependencies in .cabal-sandbox in my project directory. Inside each dependency folder there is a compiled .a file, see acid-state-0.12.1 as an example:

▾.cabal-sandbox/
    ▾lib/
        ▾x86_64-linux-ghc-7.6.3/
            ▸acid-state-0.12.1/
                ▸ Data/
                FileIO.hi
                libHSacid-state-0.12.1.a
                Paths_acid_state.hi
            ▸aeson-0.6.2.1/
            ▸base-unicode-symbols-0.2.2.4/
            ▸base64-bytestring-1.0.0.1/
            ▸blaze-builder-0.3.1.1/
            ▸blaze-html-0.6.1.1/
            ▸blaze-markup-0.5.1.5/
            ▸blaze-textual-0.2.0.8/
            ▸cereal-0.4.0.0/
            ▸clay-0.8/
            ▸clock-0.3/
            ▸dlist-0.5/
            ▸email-validate-1.0.0/
            ▸entropy-0.2.2.4/
            ▸extensible-exceptions-0.1.1.4/
            ▸gd-3000.7.3/
            ▸happstack-lite-7.3.1/
            ▸happstack-server-7.3.1/
            ▸hslogger-1.2.3/
            ▸MissingH-1.2.0.2/
            ▸monad-control-0.3.2.2/
            ▸postgresql-libpq-0.8.2.4/
            ▸postgresql-simple-0.3.8.0/
            ▸safecopy-0.8.2/
            ▸scrypt-0.3.6/
            ▸sendfile-0.7.9/
            ▸system-filepath-0.4.8/
            ▸threads-0.5.0.2/
            ▸time-compat-0.1.0.3/
            ▸transformers-base-0.4.1/
            ▸utf8-string-0.3.7/

Like I said in the inro, my project is divided into two parts as shown here in my cabal file (Note that some information is removed to keep the example s shorter):

name:                myserver
version:             0.1.0.0
build-type:          Simple
cabal-version:       >=1.8

library
    hs-source-dirs:  lib
    ghc-options:     -Wall -fno-warn-orphans -fno-warn-unused-do-bind -threaded

    exposed-modules:
         Crypto.SimpleScrypt,
         Util.Response,
         Html.Components,
         Storage.Memory,
         Storage.Disk

    build-depends:       
        base >=4.6 && <4.7,
        bytestring >=0.10 && <0.11,
        MissingH >=1.2 && <1.3,
        happstack-lite >=7.3 && <7.4,
        clay >=0.8 && <0.9,
        text >=0.11 && <0.12,
        blaze-markup >=0.5 && <0.6,
        blaze-html >=0.6 && <0.7,
        postgresql-simple >=0.3 && <0.4,
        mtl >=2.1 && <2.2,
        acid-state >=0.12 && <0.13,
        safecopy >=0.8 && <0.9,
        containers >=0.5 && <0.6,
        scrypt >=0.3 && <0.4,
        transformers >=0.3 && <0.4,
        happstack-server >=7.3 && <7.4,
        time >=1.4 && <1.5,
        filepath >=1.3 && <1.4,
        directory >=1.2 && <1.3,
        gd >=3000.7 && <3000.8,
        aeson >=0.6 && <0.7,
        email-validate >=1.0 && <1.1,
        clock >=0.3 && <0.4,
        random >=1.0 && <1.1

executable myserver
    hs-source-dirs:  app
    main-is:         App.hs

    ghc-options:     -Wall -fno-warn-orphans -fno-warn-unused-do-bind -threaded

    build-depends:
        base >=4.6 && <4.7,
        bytestring >=0.10 && <0.11,
        MissingH >=1.2 && <1.3,
        happstack-lite >=7.3 && <7.4,
        clay >=0.8 && <0.9,
        text >=0.11 && <0.12,
        blaze-markup >=0.5 && <0.6,
        blaze-html >=0.6 && <0.7,
        postgresql-simple >=0.3 && <0.4,
        mtl >=2.1 && <2.2,
        acid-state >=0.12 && <0.13,
        safecopy >=0.8 && <0.9,
        containers >=0.5 && <0.6,
        scrypt >=0.3 && <0.4,
        transformers >=0.3 && <0.4,
        happstack-server >=7.3 && <7.4,
        time >=1.4 && <1.5,
        filepath >=1.3 && <1.4,
        directory >=1.2 && <1.3,
        gd >=3000.7 && <3000.8,
        aeson >=0.6 && <0.7,
        email-validate >=1.0 && <1.1,
        clock >=0.3 && <0.4,
        random >=1.0 && <1.1,
        myserver

What I have tried so far

I have tried adding -shared -fPIC to the ghc-options for the library and -dynamic for the executable. But that produced this error:

Linking a.out ...
/usr/bin/ld: /home/rzetterberg/development/haskell/myserver/.cabal-sandbox/lib/x86_64-linux-ghc-7.6.3/scrypt-0.3.6/libHSscrypt-0.3.6.a(Scrypt.o): relocation R_X86_64_32S against `stg_CAF_BLACKHOLE_info' can not be used when making a shared object; recompile with -fPIC                                             
/home/rzetterberg/development/haskell/myserver/.cabal-sandbox/lib/x86_64-linux-ghc-7.6.3/scrypt-0.3.6/libHSscrypt-0.3.6.a: could not read symbols: Bad value
collect2: error: ld returned 1 exit status

The problem is that really I don't know where to start, that's why I'm asking for help.

Community
  • 1
  • 1
rzetterberg
  • 10,146
  • 4
  • 44
  • 54
  • http://www.haskell.org/ghc/docs/7.6.1/html/users_guide/using-shared-libs.html have you tried it? – n. m. could be an AI Nov 02 '13 at 17:47
  • Shared libraries most certainly are possible and support exists in cabal, but it sounds like what you really should do is split the package into a library and a separate package for the executable that depends on said library. – Thomas M. DuBuisson Nov 02 '13 at 17:51
  • See my edit about my setup and what I have tried so far! – rzetterberg Nov 02 '13 at 18:39
  • When I said "split the package" I was suggesting you create two packages, not one package with two parts as you currently have. Also, feel free to enable the shared libraries ("shared" option) in your cabal config if that's what you'd like to use. – Thomas M. DuBuisson Nov 02 '13 at 20:46

0 Answers0