{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Simple.Program.Ld
-- Copyright   :  Duncan Coutts 2009
--
-- Maintainer  :  [email protected]
-- Portability :  portable
--
-- This module provides an library interface to the @ld@ linker program.

module Distribution.Simple.Program.Ld (
    combineObjectFiles,
  ) where

import Prelude ()
import Distribution.Compat.Prelude

import Distribution.Simple.Program.Types
         ( ConfiguredProgram(..) )
import Distribution.Simple.Program.Run
         ( programInvocation, multiStageProgramInvocation
         , runProgramInvocation )
import Distribution.Verbosity
         ( Verbosity )

import System.Directory
         ( renameFile )
import System.FilePath
         ( (<.>) )

-- | Call @ld -r@ to link a bunch of object files together.
--
combineObjectFiles :: Verbosity -> ConfiguredProgram
                   -> FilePath -> [FilePath] -> IO ()
combineObjectFiles verbosity ld target files =

  -- Unlike "ar", the "ld" tool is not designed to be used with xargs. That is,
  -- if we have more object files than fit on a single command line then we
  -- have a slight problem. What we have to do is link files in batches into
  -- a temp object file and then include that one in the next batch.

  let simpleArgs  = ["-r", "-o", target]

      initialArgs = ["-r", "-o", target]
      middleArgs  = ["-r", "-o", target, tmpfile]
      finalArgs   = middleArgs

      simple      = programInvocation ld simpleArgs
      initial     = programInvocation ld initialArgs
      middle      = programInvocation ld middleArgs
      final       = programInvocation ld finalArgs

      invocations = multiStageProgramInvocation
                      simple (initial, middle, final) files

   in run invocations

  where
    tmpfile        = target <.> "tmp" -- perhaps should use a proper temp file

    run []         = return ()
    run [inv]      = runProgramInvocation verbosity inv
    run (inv:invs) = do runProgramInvocation verbosity inv
                        renameFile target tmpfile
                        run invs