diff --git a/cabal-install/Distribution/Client/CmdInstall.hs b/cabal-install/Distribution/Client/CmdInstall.hs index 2fe38fa963b..0a330707faf 100644 --- a/cabal-install/Distribution/Client/CmdInstall.hs +++ b/cabal-install/Distribution/Client/CmdInstall.hs @@ -15,6 +15,8 @@ module Distribution.Client.CmdInstall ( TargetProblem(..), selectPackageTargets, selectComponentTarget, + -- * Internals exposed for CmdRepl + CmdRun + establishDummyDistDirLayout, establishDummyProjectBaseContext ) where @@ -28,6 +30,7 @@ import Distribution.Client.CmdErrorMessages import Distribution.Client.CmdSdist import Distribution.Client.CmdInstall.ClientInstallFlags +import Distribution.Client.CmdInstall.ClientInstallTargetSelector import Distribution.Client.Setup ( GlobalFlags(..), ConfigFlags(..), ConfigExFlags, InstallFlags(..) @@ -43,6 +46,10 @@ import Distribution.Package ( Package(..), PackageName, mkPackageName, unPackageName ) import Distribution.Types.PackageId ( PackageIdentifier(..) ) +import Distribution.Client.ProjectConfig + ( ProjectPackageLocation(..) + , fetchAndReadSourcePackages + ) import Distribution.Client.ProjectConfig.Types ( ProjectConfig(..), ProjectConfigShared(..) , ProjectConfigBuildOnly(..), PackageConfig(..) @@ -135,6 +142,7 @@ import Data.Ord import qualified Data.Map as Map import Distribution.Utils.NubList ( fromNubList ) +import Network.URI (URI) import System.Directory ( getHomeDirectory, doesFileExist, createDirectoryIfMissing , getTemporaryDirectory, makeAbsolute, doesDirectoryExist @@ -261,7 +269,7 @@ installAction ( configFlags, configExFlags, installFlags targetFilter = if installLibs then Just LibKind else Just ExeKind targetStrings' = if null targetStrings then ["."] else targetStrings - withProject :: IO ([PackageSpecifier UnresolvedSourcePackage], [TargetSelector], ProjectConfig) + withProject :: IO ([PackageSpecifier UnresolvedSourcePackage], [URI], [TargetSelector], ProjectConfig) withProject = do let verbosity' = lessVerbose verbosity @@ -291,7 +299,7 @@ installAction ( configFlags, configExFlags, installFlags flip TargetPackageNamed targetFilter . pkgName <$> packageIds if null targetStrings' - then return (packageSpecifiers, packageTargets, projectConfig localBaseCtx) + then return (packageSpecifiers, [], packageTargets, projectConfig localBaseCtx) else do targetSelectors <- either (reportTargetSelectorProblems verbosity) return @@ -396,16 +404,13 @@ installAction ( configFlags, configExFlags, installFlags else return (local ++ hackagePkgs, targets' ++ hackageTargets) return ( specs ++ packageSpecifiers + , [] , selectors ++ packageTargets , projectConfig localBaseCtx ) - withoutProject :: ProjectConfig -> IO ([PackageSpecifier pkg], [TargetSelector], ProjectConfig) + withoutProject :: ProjectConfig -> IO ([PackageSpecifier pkg], [URI], [TargetSelector], ProjectConfig) withoutProject globalConfig = do - let - parsePkg pkgName - | Just (pkg :: PackageId) <- simpleParse pkgName = return pkg - | otherwise = die' verbosity ("Invalid package ID: " ++ pkgName) - packageIds <- mapM parsePkg targetStrings' + tss <- mapM (parseWithoutProjectTargetSelector verbosity) targetStrings' cabalDir <- getCabalDir let @@ -431,31 +436,28 @@ installAction ( configFlags, configExFlags, installFlags verbosity buildSettings (getSourcePackages verbosity) - for_ targetStrings' $ \case - name - | null (lookupPackageName packageIndex (mkPackageName name)) - , xs@(_:_) <- searchByName packageIndex name -> - die' verbosity . concat $ - [ "Unknown package \"", name, "\". " - , "Did you mean any of the following?\n" - , unlines (("- " ++) . unPackageName . fst <$> xs) - ] - _ -> return () + for_ (concatMap woPackageNames tss) $ \name -> do + when (null (lookupPackageName packageIndex name)) $ do + let xs = searchByName packageIndex (unPackageName name) + let emptyIf True _ = [] + emptyIf False zs = zs + die' verbosity $ concat $ + [ "Unknown package \"", unPackageName name, "\". " + ] ++ emptyIf (null xs) + [ "Did you mean any of the following?\n" + , unlines (("- " ++) . unPackageName . fst <$> xs) + ] let - packageSpecifiers = flip fmap packageIds $ \case - PackageIdentifier{..} - | pkgVersion == nullVersion -> NamedPackage pkgName [] - | otherwise -> NamedPackage pkgName - [PackagePropertyVersion - (thisVersion pkgVersion)] - packageTargets = flip TargetPackageNamed Nothing . pkgName <$> packageIds - return (packageSpecifiers, packageTargets, projectConfig) + (uris, packageSpecifiers) = partitionEithers $ map woPackageSpecifiers tss + packageTargets = map woPackageTargets tss + + return (packageSpecifiers, uris, packageTargets, projectConfig) let ignoreProject = fromFlagOrDefault False (cinstIgnoreProject clientInstallFlags) - (specs, selectors, config) <- + (specs, uris, selectors, config) <- withProjectOrGlobalConfigIgn ignoreProject verbosity globalConfigFlag withProject withoutProject home <- getHomeDirectory @@ -558,16 +560,21 @@ installAction ( configFlags, configExFlags, installFlags envSpecs' | installLibs = envSpecs | otherwise = [] - withTempDirectory - verbosity - globalTmp - "cabal-install." - $ \tmpDir -> do + withTempDirectory verbosity globalTmp "cabal-install." $ \tmpDir -> do + distDirLayout <- establishDummyDistDirLayout verbosity config tmpDir + + uriSpecs <- runRebuild tmpDir $ fetchAndReadSourcePackages + verbosity + distDirLayout + (projectConfigShared config) + (projectConfigBuildOnly config) + [ ProjectPackageRemoteTarball uri | uri <- uris ] + baseCtx <- establishDummyProjectBaseContext verbosity config - tmpDir - (envSpecs' ++ specs) + distDirLayout + (envSpecs' ++ specs ++ uriSpecs) InstallCommand buildCtx <- @@ -874,21 +881,15 @@ entriesForLibraryComponents = Map.foldrWithKey' (\k v -> mappend (go k v)) [] establishDummyProjectBaseContext :: Verbosity -> ProjectConfig - -> FilePath + -> DistDirLayout -- ^ Where to put the dist directory -> [PackageSpecifier UnresolvedSourcePackage] -- ^ The packages to be included in the project -> CurrentCommand -> IO ProjectBaseContext -establishDummyProjectBaseContext verbosity cliConfig tmpDir - localPackages currentCommand = do +establishDummyProjectBaseContext verbosity cliConfig distDirLayout localPackages currentCommand = do cabalDir <- getCabalDir - -- Create the dist directories - createDirectoryIfMissingVerbose verbosity True $ distDirectory distDirLayout - createDirectoryIfMissingVerbose verbosity True $ - distProjectCacheDirectory distDirLayout - globalConfig <- runRebuild "" $ readGlobalConfig verbosity $ projectConfigConfigFile @@ -919,13 +920,21 @@ establishDummyProjectBaseContext verbosity cliConfig tmpDir buildSettings, currentCommand } + +establishDummyDistDirLayout :: Verbosity -> ProjectConfig -> FilePath -> IO DistDirLayout +establishDummyDistDirLayout verbosity cliConfig tmpDir = do + let distDirLayout = defaultDistDirLayout projectRoot mdistDirectory + + -- Create the dist directories + createDirectoryIfMissingVerbose verbosity True $ distDirectory distDirLayout + createDirectoryIfMissingVerbose verbosity True $ distProjectCacheDirectory distDirLayout + + return distDirLayout where mdistDirectory = flagToMaybe $ projectConfigDistDir $ projectConfigShared cliConfig projectRoot = ProjectRootImplicit tmpDir - distDirLayout = defaultDistDirLayout projectRoot - mdistDirectory -- | This defines what a 'TargetSelector' means for the @bench@ command. -- It selects the 'AvailableTarget's that the 'TargetSelector' refers to, diff --git a/cabal-install/Distribution/Client/CmdInstall/ClientInstallTargetSelector.hs b/cabal-install/Distribution/Client/CmdInstall/ClientInstallTargetSelector.hs new file mode 100644 index 00000000000..137ad2ee7fd --- /dev/null +++ b/cabal-install/Distribution/Client/CmdInstall/ClientInstallTargetSelector.hs @@ -0,0 +1,70 @@ +module Distribution.Client.CmdInstall.ClientInstallTargetSelector ( + WithoutProjectTargetSelector (..), + parseWithoutProjectTargetSelector, + woPackageNames, + woPackageTargets, + woPackageSpecifiers, + ) where + +import Distribution.Client.Compat.Prelude +import Prelude () + +import Network.URI (URI, parseURI) + +import Distribution.Client.TargetSelector +import Distribution.Client.Types +import Distribution.Compat.CharParsing (char, optional) +import Distribution.Package +import Distribution.Parsec +import Distribution.Simple.LocalBuildInfo (ComponentName (CExeName)) +import Distribution.Simple.Utils (die') +import Distribution.Solver.Types.PackageConstraint (PackageProperty (..)) +import Distribution.Verbosity (Verbosity) +import Distribution.Version + +data WithoutProjectTargetSelector + = WoPackageId PackageId + | WoPackageComponent PackageId ComponentName + | WoURI URI + deriving (Show) + +parseWithoutProjectTargetSelector :: Verbosity -> String -> IO WithoutProjectTargetSelector +parseWithoutProjectTargetSelector verbosity input = + case explicitEitherParsec parser input of + Right ts -> return ts + Left err -> case parseURI input of + Just uri -> return (WoURI uri) + Nothing -> die' verbosity $ "Invalid package ID: " ++ input ++ "\n" ++ err + where + parser :: ParsecParser WithoutProjectTargetSelector + parser = do + pid <- parsec + cn <- optional (char ':' *> parsec) + return $ case cn of + Nothing -> WoPackageId pid + Just cn' -> WoPackageComponent pid (CExeName cn') + +woPackageNames :: WithoutProjectTargetSelector -> [PackageName] +woPackageNames (WoPackageId pid) = [pkgName pid] +woPackageNames (WoPackageComponent pid _) = [pkgName pid] +woPackageNames (WoURI _) = [] + +woPackageTargets :: WithoutProjectTargetSelector -> TargetSelector +woPackageTargets (WoPackageId pid) = + TargetPackageNamed (pkgName pid) Nothing +woPackageTargets (WoPackageComponent pid cn) = + TargetComponentUnknown (pkgName pid) (Right cn) WholeComponent +woPackageTargets (WoURI _) = + TargetAllPackages (Just ExeKind) + +woPackageSpecifiers :: WithoutProjectTargetSelector -> Either URI (PackageSpecifier pkg) +woPackageSpecifiers (WoPackageId pid) = Right (pidPackageSpecifiers pid) +woPackageSpecifiers (WoPackageComponent pid _) = Right (pidPackageSpecifiers pid) +woPackageSpecifiers (WoURI uri) = Left uri + +pidPackageSpecifiers :: PackageId -> PackageSpecifier pkg +pidPackageSpecifiers pid + | pkgVersion pid == nullVersion = NamedPackage (pkgName pid) [] + | otherwise = NamedPackage (pkgName pid) + [ PackagePropertyVersion (thisVersion (pkgVersion pid)) + ] diff --git a/cabal-install/Distribution/Client/CmdRepl.hs b/cabal-install/Distribution/Client/CmdRepl.hs index 4a737b4ab9e..b5fd07aff7f 100644 --- a/cabal-install/Distribution/Client/CmdRepl.hs +++ b/cabal-install/Distribution/Client/CmdRepl.hs @@ -25,7 +25,9 @@ import qualified Distribution.Types.Lens as L import Distribution.Client.CmdErrorMessages import Distribution.Client.CmdInstall - ( establishDummyProjectBaseContext ) + ( establishDummyDistDirLayout + , establishDummyProjectBaseContext + ) import qualified Distribution.Client.InstallPlan as InstallPlan import Distribution.Client.ProjectBuilding ( rebuildTargetsDryRun, improveInstallPlanWithUpToDatePackages ) @@ -419,11 +421,12 @@ withoutProject config verbosity extraArgs = do cwd <- getCurrentDirectory writeFile ghciScriptPath (":cd " ++ cwd) + distDirLayout <- establishDummyDistDirLayout verbosity config tempDir baseCtx <- establishDummyProjectBaseContext verbosity config - tempDir + distDirLayout [SpecificSourcePackage sourcePackage] OtherCommand diff --git a/cabal-install/Distribution/Client/CmdRun.hs b/cabal-install/Distribution/Client/CmdRun.hs index 2eaf1238452..33eec4176dd 100644 --- a/cabal-install/Distribution/Client/CmdRun.hs +++ b/cabal-install/Distribution/Client/CmdRun.hs @@ -47,7 +47,8 @@ import Distribution.Simple.Utils ( wrapText, warn, die', ordNub, info , createTempDirectory, handleDoesNotExist ) import Distribution.Client.CmdInstall - ( establishDummyProjectBaseContext ) + ( establishDummyDistDirLayout + , establishDummyProjectBaseContext ) import Distribution.Client.ProjectConfig ( ProjectConfig(..), ProjectConfigShared(..) , withProjectOrGlobalConfigIgn ) @@ -200,13 +201,14 @@ runAction ( configFlags, configExFlags, installFlags , clientRunFlags ) targetStrings globalFlags = do globalTmp <- getTemporaryDirectory - tempDir <- createTempDirectory globalTmp "cabal-repl." + tmpDir <- createTempDirectory globalTmp "cabal-repl." let with = establishProjectBaseContext verbosity cliConfig OtherCommand - without config = - establishDummyProjectBaseContext verbosity (config <> cliConfig) tempDir [] OtherCommand + without config = do + distDirLayout <- establishDummyDistDirLayout verbosity (config <> cliConfig) tmpDir + establishDummyProjectBaseContext verbosity (config <> cliConfig) distDirLayout [] OtherCommand let ignoreProject = fromFlagOrDefault False (crunIgnoreProject clientRunFlags) @@ -219,7 +221,7 @@ runAction ( configFlags, configExFlags, installFlags let pol | takeExtension script == ".lhs" = LiterateHaskell | otherwise = PlainHaskell if exists - then BS.readFile script >>= handleScriptCase verbosity pol baseCtx tempDir + then BS.readFile script >>= handleScriptCase verbosity pol baseCtx tmpDir else reportTargetSelectorProblems verbosity err (baseCtx', targetSelectors) <- @@ -337,7 +339,7 @@ runAction ( configFlags, configExFlags, installFlags elaboratedPlan } - handleDoesNotExist () (removeDirectoryRecursive tempDir) + handleDoesNotExist () (removeDirectoryRecursive tmpDir) where verbosity = fromFlagOrDefault normal (configVerbosity configFlags) cliConfig = commandLineFlagsToProjectConfig @@ -441,7 +443,7 @@ handleScriptCase -> FilePath -> BS.ByteString -> IO (ProjectBaseContext, [TargetSelector]) -handleScriptCase verbosity pol baseCtx tempDir scriptContents = do +handleScriptCase verbosity pol baseCtx tmpDir scriptContents = do (executable, contents') <- readScriptBlockFromScript verbosity pol scriptContents -- We need to create a dummy package that lives in our dummy project. @@ -453,7 +455,7 @@ handleScriptCase verbosity pol baseCtx tempDir scriptContents = do sourcePackage = SourcePackage { packageInfoId = pkgId , SP.packageDescription = genericPackageDescription - , packageSource = LocalUnpackedPackage tempDir + , packageSource = LocalUnpackedPackage tmpDir , packageDescrOverride = Nothing } genericPackageDescription = emptyGenericPackageDescription @@ -477,8 +479,8 @@ handleScriptCase verbosity pol baseCtx tempDir scriptContents = do } pkgId = fakePackageId - writeGenericPackageDescription (tempDir "fake-package.cabal") genericPackageDescription - BS.writeFile (tempDir mainName) contents' + writeGenericPackageDescription (tmpDir "fake-package.cabal") genericPackageDescription + BS.writeFile (tmpDir mainName) contents' let baseCtx' = baseCtx diff --git a/cabal-install/Distribution/Client/HttpUtils.hs b/cabal-install/Distribution/Client/HttpUtils.hs index 80a4b137672..6dee1c0552b 100644 --- a/cabal-install/Distribution/Client/HttpUtils.hs +++ b/cabal-install/Distribution/Client/HttpUtils.hs @@ -34,7 +34,6 @@ import Control.DeepSeq ( force ) import Control.Monad ( guard ) -import qualified Data.ByteString.Lazy.Char8 as BS import qualified Paths_cabal_install (version) import Distribution.Verbosity (Verbosity) import Distribution.Pretty (prettyShow) @@ -57,6 +56,7 @@ import System.IO ( withFile, IOMode(ReadMode), hGetContents, hClose ) import System.IO.Error ( isDoesNotExistError ) +import Distribution.Parsec (explicitEitherParsec) import Distribution.Simple.Program ( Program, simpleProgram, ConfiguredProgram, programPath , ProgramInvocation(..), programInvocation @@ -74,6 +74,13 @@ import System.Random (randomRIO) import System.Exit (ExitCode(..)) import Data.Version (showVersion) +import qualified Crypto.Hash.SHA256 as SHA256 +import qualified Data.ByteString.Base16 as Base16 +import qualified Distribution.Compat.CharParsing as P +import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as BS8 +import qualified Data.ByteString.Lazy as LBS +import qualified Data.ByteString.Lazy.Char8 as LBS8 ------------------------------------------------------------------------------ -- Downloading a URI, given an HttpTransport @@ -83,6 +90,12 @@ data DownloadResult = FileAlreadyInCache | FileDownloaded FilePath deriving (Eq) +data DownloadCheck + = Downloaded -- ^ already downloaded and sha256 matches + | CheckETag String -- ^ already downloaded and we have etag + | NeedsDownload (Maybe BS.ByteString) -- ^ needs download with optional hash check + deriving Eq + downloadURI :: HttpTransport -> Verbosity -> URI -- ^ What to download @@ -96,13 +109,34 @@ downloadURI _transport verbosity uri path | uriScheme uri == "file:" = do downloadURI transport verbosity uri path = do - let etagPath = path <.> "etag" - targetExists <- doesFileExist path - etagPathExists <- doesFileExist etagPath - -- In rare cases the target file doesn't exist, but the etag does. - etag <- if targetExists && etagPathExists - then Just <$> readFile etagPath - else return Nothing + targetExists <- doesFileExist path + + downloadCheck <- + -- if we have uriFrag, then we expect there to be #sha256=... + if not (null uriFrag) + then case sha256parsed of + -- we know the hash, and target exists + Right expected | targetExists -> do + contents <- LBS.readFile path + let actual = SHA256.hashlazy contents + if expected == actual + then return Downloaded + else return (NeedsDownload (Just expected)) + + -- we known the hash, target doesn't exist + Right expected -> return (NeedsDownload (Just expected)) + + -- we failed to parse uriFragment + Left err -> die' verbosity $ + "Cannot parse URI fragment " ++ uriFrag ++ " " ++ err + + -- if there are no uri fragment, use ETag + else do + etagPathExists <- doesFileExist etagPath + -- In rare cases the target file doesn't exist, but the etag does. + if targetExists && etagPathExists + then return (CheckETag etagPath) + else return (NeedsDownload Nothing) -- Only use the external http transports if we actually have to -- (or have been told to do so) @@ -114,12 +148,29 @@ downloadURI transport verbosity uri path = do | otherwise = transport - withTempFileName (takeDirectory path) (takeFileName path) $ \tmpFile -> do + case downloadCheck of + Downloaded -> return FileAlreadyInCache + CheckETag etag -> makeDownload transport' Nothing (Just etag) + NeedsDownload hash -> makeDownload transport' hash Nothing + + where + makeDownload transport' sha256 etag = withTempFileName (takeDirectory path) (takeFileName path) $ \tmpFile -> do result <- getHttp transport' verbosity uri etag tmpFile [] -- Only write the etag if we get a 200 response code. -- A 304 still sends us an etag header. case result of + -- if we have hash, we don't care about etag. + (200, _) | Just expected <- sha256 -> do + contents <- LBS.readFile tmpFile + let actual = SHA256.hashlazy contents + unless (actual == expected) $ + die' verbosity $ unwords + [ "Failed to download", show uri + , ": SHA256 don't match; expected:", BS8.unpack (Base16.encode expected) + , "actual:", BS8.unpack (Base16.encode actual) + ] + (200, Just newEtag) -> writeFile etagPath newEtag _ -> return () @@ -131,9 +182,20 @@ downloadURI transport verbosity uri path = do 304 -> do notice verbosity "Skipping download: local and remote files match." return FileAlreadyInCache - errCode -> die' verbosity $ "Failed to download " ++ show uri + errCode -> die' verbosity $ "failed to download " ++ show uri ++ " : HTTP code " ++ show errCode + etagPath = path <.> "etag" + uriFrag = uriFragment uri + + sha256parsed :: Either String BS.ByteString + sha256parsed = explicitEitherParsec fragmentParser uriFrag + + fragmentParser = do + _ <- P.string "#sha256=" + str <- some P.hexDigit + return (fst (Base16.decode (BS8.pack str))) + ------------------------------------------------------------------------------ -- Utilities for repo url management -- @@ -463,7 +525,7 @@ wgetTransport prog = \responseFile responseHandle -> do hClose responseHandle (body, boundary) <- generateMultipartBody path - BS.hPut tmpHandle body + LBS.hPut tmpHandle body hClose tmpHandle let args = [ "--post-file=" ++ tmpFile , "--user-agent=" ++ userAgent @@ -586,7 +648,7 @@ powershellTransport prog = withTempFile (takeDirectory path) (takeFileName path) $ \tmpFile tmpHandle -> do (body, boundary) <- generateMultipartBody path - BS.hPut tmpHandle body + LBS.hPut tmpHandle body hClose tmpHandle fullPath <- canonicalizePath tmpFile @@ -736,7 +798,7 @@ plainHttpTransport = rqHeaders = [ Header HdrIfNoneMatch t | t <- maybeToList etag ] ++ reqHeaders, - rqBody = BS.empty + rqBody = LBS.empty } (_, resp) <- cabalBrowse verbosity Nothing (request req) let code = convertRspCode (rspCode resp) @@ -752,7 +814,7 @@ plainHttpTransport = (body, boundary) <- generateMultipartBody path let headers = [ Header HdrContentType ("multipart/form-data; boundary="++boundary) - , Header HdrContentLength (show (BS.length body)) + , Header HdrContentLength (show (LBS8.length body)) , Header HdrAccept ("text/plain") ] req = Request { @@ -765,11 +827,11 @@ plainHttpTransport = return (convertRspCode (rspCode resp), rspErrorString resp) puthttpfile verbosity uri path auth headers = do - body <- BS.readFile path + body <- LBS8.readFile path let req = Request { rqURI = uri, rqMethod = PUT, - rqHeaders = Header HdrContentLength (show (BS.length body)) + rqHeaders = Header HdrContentLength (show (LBS8.length body)) : Header HdrAccept "text/plain" : headers, rqBody = body @@ -783,7 +845,7 @@ plainHttpTransport = case lookupHeader HdrContentType (rspHeaders resp) of Just contenttype | takeWhile (/= ';') contenttype == "text/plain" - -> BS.unpack (rspBody resp) + -> LBS8.unpack (rspBody resp) _ -> rspReason resp cabalBrowse verbosity auth act = do @@ -829,17 +891,17 @@ trim = f . f -- Multipart stuff partially taken from cgi package. -- -generateMultipartBody :: FilePath -> IO (BS.ByteString, String) +generateMultipartBody :: FilePath -> IO (LBS.ByteString, String) generateMultipartBody path = do - content <- BS.readFile path + content <- LBS.readFile path boundary <- genBoundary - let !body = formatBody content (BS.pack boundary) + let !body = formatBody content (LBS8.pack boundary) return (body, boundary) where formatBody content boundary = - BS.concat $ + LBS8.concat $ [ crlf, dd, boundary, crlf ] - ++ [ BS.pack (show header) | header <- headers ] + ++ [ LBS8.pack (show header) | header <- headers ] ++ [ crlf , content , crlf, dd, boundary, dd, crlf ] @@ -851,8 +913,8 @@ generateMultipartBody path = do , Header HdrContentType "application/x-gzip" ] - crlf = BS.pack "\r\n" - dd = BS.pack "--" + crlf = LBS8.pack "\r\n" + dd = LBS8.pack "--" genBoundary :: IO String genBoundary = do diff --git a/cabal-install/cabal-install.cabal b/cabal-install/cabal-install.cabal index 8a85e482a95..ea7bf9bc6f6 100644 --- a/cabal-install/cabal-install.cabal +++ b/cabal-install/cabal-install.cabal @@ -168,6 +168,7 @@ executable cabal Distribution.Client.CmdHaddock Distribution.Client.CmdInstall Distribution.Client.CmdInstall.ClientInstallFlags + Distribution.Client.CmdInstall.ClientInstallTargetSelector Distribution.Client.CmdRepl Distribution.Client.CmdRun Distribution.Client.CmdRun.ClientRunFlags diff --git a/cabal-install/cabal-install.cabal.pp b/cabal-install/cabal-install.cabal.pp index 15546093db9..23d26f7133f 100644 --- a/cabal-install/cabal-install.cabal.pp +++ b/cabal-install/cabal-install.cabal.pp @@ -107,6 +107,7 @@ Distribution.Client.CmdHaddock Distribution.Client.CmdInstall Distribution.Client.CmdInstall.ClientInstallFlags + Distribution.Client.CmdInstall.ClientInstallTargetSelector Distribution.Client.CmdRepl Distribution.Client.CmdRun Distribution.Client.CmdRun.ClientRunFlags