content: module Rules.Libffi (libffiRules, libffiDependencies) where
  
  import Hadrian.Utilities
  
  import Packages
  import Settings.Builders.Common
  import Target
  import Utilities
  
  libffiDependencies :: [FilePath]
  libffiDependencies = ["ffi.h", "ffitarget.h"]
  
  libffiLibrary :: FilePath
  libffiLibrary = "inst/lib/libffi.a"
  
  rtsLibffiLibrary :: Way -> Action FilePath
  rtsLibffiLibrary way = do
      name    <- libffiLibraryName
      suf     <- libsuf way
      rtsPath <- rtsBuildPath
      return $ rtsPath -/- "lib" ++ name ++ suf
  
  fixLibffiMakefile :: FilePath -> String -> String
  fixLibffiMakefile top =
        replace "-MD" "-MMD"
      . replace "@toolexeclibdir@" "$(libdir)"
      . replace "@INSTALL@" ("$(subst ../install-sh," ++ top ++ "/install-sh,@INSTALL@)")
  
  -- TODO: check code duplication w.r.t. ConfCcArgs
  configureEnvironment :: Action [CmdOption]
  configureEnvironment = do
      cFlags  <- interpretInContext libffiContext $ mconcat
                 [ cArgs
                 , getStagedSettingList ConfCcArgs ]
      ldFlags <- interpretInContext libffiContext ldArgs
      sequence [ builderEnvironment "CC" $ Cc CompileC Stage1
               , builderEnvironment "CXX" $ Cc CompileC Stage1
               , builderEnvironment "LD" (Ld Stage1)
               , builderEnvironment "AR" (Ar Unpack Stage1)
               , builderEnvironment "NM" Nm
               , builderEnvironment "RANLIB" Ranlib
               , return . AddEnv  "CFLAGS" $ unwords  cFlags ++ " -w"
               , return . AddEnv "LDFLAGS" $ unwords ldFlags ++ " -w" ]
  
  libffiRules :: Rules ()
  libffiRules = do
      root <- buildRootRules
      fmap ((root <//> "rts/build") -/-) libffiDependencies &%> \_ -> do
          libffiPath <- libffiBuildPath
          need [libffiPath -/- libffiLibrary]
  
      -- we set a higher priority because this overlaps
      -- with the static lib rule from Rules.Library.libraryRules.
      priority 2.0 $ root <//> libffiLibrary %> \_ -> do
          useSystemFfi <- flag UseSystemFfi
          rtsPath      <- rtsBuildPath
          if useSystemFfi
          then do
              ffiIncludeDir <- setting FfiIncludeDir
              putBuild "| System supplied FFI library will be used"
              forM_ ["ffi.h", "ffitarget.h"] $ \file ->
                  copyFile (ffiIncludeDir -/- file) (rtsPath -/- file)
              putSuccess "| Successfully copied system FFI library header files"
          else do
              libffiPath <- libffiBuildPath
              build $ target libffiContext (Make libffiPath) [] []
  
              hs <- getDirectoryFiles "" [libffiPath -/- "inst/include/*"]
              forM_ hs $ \header ->
                  copyFile header (rtsPath -/- takeFileName header)
  
              ways <- interpretInContext libffiContext (getLibraryWays <> getRtsWays)
              forM_ (nubOrd ways) $ \way -> do
                  rtsLib <- rtsLibffiLibrary way
                  copyFileUntracked (libffiPath -/- libffiLibrary) rtsLib
  
              putSuccess "| Successfully built custom library 'libffi'"
  
      root <//> "libffi/build/Makefile.in" %> \mkIn -> do
          libffiPath <- libffiBuildPath
          removeDirectory libffiPath
          tarball <- unifyPath . fromSingleton "Exactly one LibFFI tarball is expected"
                 <$> getDirectoryFiles "" ["libffi-tarballs/libffi*.tar.gz"]
  
          need [tarball]
          -- Go from 'libffi-3.99999+git20171002+77e130c.tar.gz' to 'libffi-3.99999'
          let libname = takeWhile (/= '+') $ takeFileName tarball
  
          root <- buildRoot
          removeDirectory (root -/- libname)
          -- TODO: Simplify.
          actionFinally (do
              build $ target libffiContext (Tar Extract) [tarball] [root]
              moveDirectory (root -/- libname) libffiPath) $
                  removeFiles root [libname <//> "*"]
  
          top <- topDirectory
          fixFile mkIn (fixLibffiMakefile top)
  
      -- TODO: Get rid of hard-coded @libffi@.
      root <//> "libffi/build/Makefile" %> \mk -> do
          need [mk <.> "in"]
          libffiPath <- libffiBuildPath
          forM_ ["config.guess", "config.sub"] $ \file ->
              copyFile file (libffiPath -/- file)
  
          env <- configureEnvironment
          buildWithCmdOptions env $
              target libffiContext (Configure libffiPath) [mk <.> "in"] [mk]
  