NixOS / cabal2nix

Generate Nix build instructions from a Cabal file
https://haskell4nix.readthedocs.io
Other
354 stars 153 forks source link

cabal2nix does not generate framework dependencies on macOS #470

Open harendra-kumar opened 3 years ago

harendra-kumar commented 3 years ago

The streamly cabal file has the following stanza:

library

    if os(darwin)
      frameworks:    Cocoa
      include-dirs:  src/Streamly/Internal
      c-sources:     src/Streamly/Internal/Data/Time/Darwin.c
                   , src/Streamly/Internal/FileSystem/Event/Darwin.m
      exposed-modules:
                     Streamly.Internal.FileSystem.Event.Darwin

When cabal2nix is used on this file it does not generate a Cocoa framework dependency, therefore the nix-shell fails. After adding the dependency manually it works.

How to reproduce:

$ git clone https://github.com/composewell/streamly.git
$ cd streamly
$ mv benchmark/streamly-benchmarks.cabal benchmark/streamly-benchmarks.cabal.bak
$ rm default.nix
$ cabal2nix --shell . > shell.nix
$ cabal build streamly

The build will fail because of Cocoa framework dependency.

We have added the dependency manually in the shell derivation:

        buildInputs =
            if builtins.currentSystem == "x86_64-darwin"
            then [nixpkgs.darwin.apple_sdk.frameworks.Cocoa]
            else [];
sternenseemann commented 3 years ago

I'm pretty sure this is due to a limitation in which we parse cabal files, i. e. we can't resolve flags at all. I'd have to check what the state is here precisely atm, but this may be hard to fix unfortunately.

The trouble is also that even if we would resolve flags, we'd have to translate the logic involving of these flags to nix code which definitely is non-trivial (and probably why this wasn't implemented in the first place).

harendra-kumar commented 3 years ago

I think such parsing already exists, its probably a small bug. I am saying that based on the fact that the fsnotify package has this in the cabal file:

    if os(windows)
      CPP-Options:      -DOS_Win32
      Other-Modules:    System.FSNotify.Win32
                      , System.Win32.FileNotify
                      , System.Win32.Notify
      Build-Depends:    Win32
      Hs-Source-Dirs:   win-src
    else
      if os(darwin)
        CPP-Options:    -DOS_Mac
        Other-Modules:  System.FSNotify.OSX
        Build-Depends:  hfsevents >= 0.1.3

And cabal2nix generates this:

$ cabal2nix .  
{ mkDerivation, async, base, bytestring, containers, directory
, filepath, hfsevents, lib, random, tasty, tasty-hunit, temporary
, text, time, unix-compat
}:
mkDerivation {
  pname = "fsnotify";
  version = "0.3.0.1";
  src = ./.;
  revision = "1";
  editedCabalFile = "1pa9pa0kflkqlb4dysagy0aihn452hmf6wwlsr7fp4ygg86m7fsq";
  libraryHaskellDepends = [
    async base bytestring containers directory filepath hfsevents text
    time unix-compat
  ];

hfsevents is under the os(darwin) conditional just like in the case I reported in this issue. But it is still correctly parsed and we get a dependency on hfsevents.

For the streamly-0.8.0 package cabal2nix does not generate the framework dependency which is under a similar conditional:

streamly-0.8.0 $ cabal2nix .
{ mkDerivation, atomic-primops, base, containers, deepseq
, directory, exceptions, fusion-plugin-types, ghc-prim, heaps, lib
, lockfree-queue, monad-control, mtl, network, primitive
, transformers, transformers-base
}:
mkDerivation {
  pname = "streamly";
  version = "0.8.0";
  src = ./.;
  libraryHaskellDepends = [
    atomic-primops base containers deepseq directory exceptions
    fusion-plugin-types ghc-prim heaps lockfree-queue monad-control mtl
    network primitive transformers transformers-base
  ];
  homepage = "https://streamly.composewell.com";
  description = "Dataflow programming and declarative concurrency";
  license = lib.licenses.bsd3;
}

However, it does recognize a build-depends under the same conditional. If I add a build-depends entry under that conditional it recognizes it, it only does not recognize the frameworks stanza.

sternenseemann commented 3 years ago

I don't see any build-depends being included despite under a conditional, can you elaborate?

harendra-kumar commented 3 years ago

I don't see any build-depends being included despite under a conditional, can you elaborate?

hfsevents in the example above is under if os(darwin) but the output of cabal2nix includes it.

sternenseemann commented 3 years ago

Yeah, you're right. I've checked for real this time and cabal2nix completely ignores frameworks. This makes sense since cabal2nix doesn't factor the host platform into account at all, so adding framework bindings would cause problems on all non darwin platforms (meaning evaluation would fail).

There are probably multiple approaches to fix this, but it is definitely a non-trivial problem which is connected to a larger (design) issue of cabal2nix.

harendra-kumar commented 3 years ago

It does recognize the frameworks stanza outside a conditional. For example it emits the following for hfsevents.cabal:

mkDerivation {
  pname = "hfsevents";
  version = "0.1.6";
  src = ./.;
  libraryHaskellDepends = [ base bytestring cereal mtl text unix ];
  librarySystemDepends = [ Cocoa ];
  libraryToolDepends = [ CoreServices ];

For the conditional case, can't it emit something like this?:

if builtins.currentSystem == "x86_64-darwin"
then [Cocoa]
else [];
sternenseemann commented 3 years ago

hfsevents has a hard coded post processing filter which adds the framework:

https://github.com/NixOS/cabal2nix/blob/3d9f69c17bbb82546d747906a83dbf0c2ddfa0d5/src/Distribution/Nixpkgs/Haskell/FromCabal/PostProcess.hs#L351-L357

Unfortunately emitting conditionals is not as simple as you are outlining — we need to consider cross compilation as well. Also we'd need to parse and understand the Cabal conditionals in the first place…

harendra-kumar commented 3 years ago

Ah, I did not know that it has been special cased for hfsevents.

I did another experiment. In the streamly package, I added a Haskell dependency under the conditional:

    if os(darwin)
      build-depends: streaming
      frameworks:    Cocoa

And cabal2nix seems to parse the conditional and generate a proper dependency on "streaming":

mkDerivation {
  pname = "streamly";
  version = "0.8.0";
  src = ./.;
  libraryHaskellDepends = [
    atomic-primops base containers deepseq directory exceptions
    fusion-plugin-types ghc-prim heaps lockfree-queue monad-control mtl
    network primitive streaming transformers transformers-base
  ];

So it seems parsing of cabal conditionals is present.

Its only about generating nix expressions that are portable.