cabal-install-0.14.0: Distribution/Client/SrcDist.hs
-- Implements the \"@.\/cabal sdist@\" command, which creates a source
-- distribution for this package. That is, packs up the source code
-- into a tarball, making use of the corresponding Cabal module.
module Distribution.Client.SrcDist (
sdist
) where
import Distribution.Simple.SrcDist
( printPackageProblems, prepareTree, snapshotPackage )
import Distribution.Client.Tar (createTarGzFile)
import Distribution.Package
( Package(..), packageVersion )
import Distribution.PackageDescription
( PackageDescription )
import Distribution.PackageDescription.Parse
( readPackageDescription )
import Distribution.Simple.Utils
( defaultPackageDesc, die, warn, notice, setupMessage
, createDirectoryIfMissingVerbose, withTempDirectory
, withUTF8FileContents, writeUTF8File )
import Distribution.Client.Setup
( SDistFlags(..), SDistExFlags(..), ArchiveFormat(..) )
import Distribution.Simple.Setup
( fromFlag, flagToMaybe )
import Distribution.Verbosity (Verbosity)
import Distribution.Simple.PreProcess (knownSuffixHandlers)
import Distribution.Simple.BuildPaths ( srcPref)
import Distribution.Simple.Configure(maybeGetPersistBuildConfig)
import Distribution.PackageDescription.Configuration ( flattenPackageDescription )
import Distribution.Simple.Program (requireProgram, simpleProgram, programPath)
import Distribution.Simple.Program.Db (emptyProgramDb)
import Distribution.Text
( display )
import Distribution.Version
( Version )
import System.Time (getClockTime, toCalendarTime)
import System.FilePath ((</>), (<.>))
import Control.Monad (when, unless)
import Data.Maybe (isNothing)
import Data.Char (toLower)
import Data.List (isPrefixOf)
import System.Directory (doesFileExist, removeFile, canonicalizePath)
import System.Process (runProcess, waitForProcess)
import System.Exit (ExitCode(..))
-- |Create a source distribution.
sdist :: SDistFlags -> SDistExFlags -> IO ()
sdist flags exflags = do
pkg <- return . flattenPackageDescription
=<< readPackageDescription verbosity
=<< defaultPackageDesc verbosity
mb_lbi <- maybeGetPersistBuildConfig distPref
-- do some QA
printPackageProblems verbosity pkg
when (isNothing mb_lbi) $
warn verbosity "Cannot run preprocessors. Run 'configure' command first."
date <- toCalendarTime =<< getClockTime
let pkg' | snapshot = snapshotPackage date pkg
| otherwise = pkg
case flagToMaybe (sDistDirectory flags) of
Just targetDir -> do
generateSourceDir targetDir pkg' mb_lbi
notice verbosity $ "Source directory created: " ++ targetDir
Nothing -> do
createDirectoryIfMissingVerbose verbosity True tmpTargetDir
withTempDirectory verbosity tmpTargetDir "sdist." $ \tmpDir -> do
let targetDir = tmpDir </> tarBallName pkg'
generateSourceDir targetDir pkg' mb_lbi
targzFile <- createArchive verbosity format pkg' tmpDir targetPref
notice verbosity $ "Source tarball created: " ++ targzFile
where
generateSourceDir targetDir pkg' mb_lbi = do
setupMessage verbosity "Building source dist for" (packageId pkg')
prepareTree verbosity pkg' mb_lbi distPref targetDir pps
when snapshot $
overwriteSnapshotPackageDesc verbosity pkg' targetDir
verbosity = fromFlag (sDistVerbosity flags)
snapshot = fromFlag (sDistSnapshot flags)
format = fromFlag (sDistFormat exflags)
pps = knownSuffixHandlers
distPref = fromFlag $ sDistDistPref flags
targetPref = distPref
tmpTargetDir = srcPref distPref
tarBallName :: PackageDescription -> String
tarBallName = display . packageId
overwriteSnapshotPackageDesc :: Verbosity -- ^verbosity
-> PackageDescription -- ^info from the cabal file
-> FilePath -- ^source tree
-> IO ()
overwriteSnapshotPackageDesc verbosity pkg targetDir = do
-- We could just writePackageDescription targetDescFile pkg_descr,
-- but that would lose comments and formatting.
descFile <- defaultPackageDesc verbosity
withUTF8FileContents descFile $
writeUTF8File (targetDir </> descFile)
. unlines . map (replaceVersion (packageVersion pkg)) . lines
where
replaceVersion :: Version -> String -> String
replaceVersion version line
| "version:" `isPrefixOf` map toLower line
= "version: " ++ display version
| otherwise = line
-- | Create an archive from a tree of source files.
--
createArchive :: Verbosity
-> ArchiveFormat
-> PackageDescription
-> FilePath
-> FilePath
-> IO FilePath
createArchive _verbosity TargzFormat pkg tmpDir targetPref = do
createTarGzFile tarBallFilePath tmpDir (tarBallName pkg)
return tarBallFilePath
where
tarBallFilePath = targetPref </> tarBallName pkg <.> "tar.gz"
createArchive verbosity ZipFormat pkg tmpDir targetPref = do
createZipFile verbosity zipFilePath tmpDir (tarBallName pkg)
return zipFilePath
where
zipFilePath = targetPref </> tarBallName pkg <.> "zip"
createZipFile :: Verbosity -> FilePath -> FilePath -> FilePath -> IO ()
createZipFile verbosity zipfile base dir = do
(zipProg, _) <- requireProgram verbosity zipProgram emptyProgramDb
-- zip has an annoying habbit of updating the target rather than creating
-- it from scratch. While that might sound like an optimisation, it doesn't
-- remove files already in the archive that are no longer present in the
-- uncompressed tree.
alreadyExists <- doesFileExist zipfile
when alreadyExists $ removeFile zipfile
-- we call zip with a different CWD, so have to make the path absolute
zipfileAbs <- canonicalizePath zipfile
--TODO: use runProgramInvocation, but has to be able to set CWD
hnd <- runProcess (programPath zipProg) ["-q", "-r", zipfileAbs, dir] (Just base)
Nothing Nothing Nothing Nothing
exitCode <- waitForProcess hnd
unless (exitCode == ExitSuccess) $
die $ "Generating the zip file failed "
++ "(zip returned exit code " ++ show exitCode ++ ")"
where
zipProgram = simpleProgram "zip"