1 {-# LANGUAGE MultiWayIf #-}
2 {-# LANGUAGE NamedFieldPuns #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE RecordWildCards #-}
5 {-# LANGUAGE TupleSections #-}
6 {-# LANGUAGE ViewPatterns #-}
8 module Distribution
.Client
.CmdSdist
15 import Distribution
.Client
.Compat
.Prelude
18 import Distribution
.Client
.CmdErrorMessages
22 import Distribution
.Client
.DistDirLayout
26 import Distribution
.Client
.NixStyleOptions
28 , defaultNixStyleFlags
30 import Distribution
.Client
.ProjectConfig
32 , commandLineFlagsToProjectConfig
33 , projectConfigConfigFile
35 , withProjectOrGlobalConfig
37 import Distribution
.Client
.ProjectFlags
42 import Distribution
.Client
.ProjectOrchestration
44 , ProjectBaseContext
(..)
45 , establishProjectBaseContext
46 , establishProjectBaseContextWithRoot
48 import Distribution
.Client
.Setup
51 import Distribution
.Client
.TargetSelector
55 , reportTargetSelectorProblems
57 import Distribution
.Client
.Types
58 ( PackageLocation
(..)
59 , PackageSpecifier
(..)
60 , UnresolvedSourcePackage
62 import Distribution
.Solver
.Types
.SourcePackage
66 import Distribution
.Client
.Errors
67 import Distribution
.Client
.SrcDist
70 import Distribution
.Compat
.Lens
74 import Distribution
.Package
77 import Distribution
.PackageDescription
.Configuration
78 ( flattenPackageDescription
80 import Distribution
.ReadE
83 import Distribution
.Simple
.Command
91 import Distribution
.Simple
.PreProcess
94 import Distribution
.Simple
.Setup
106 import Distribution
.Simple
.SrcDist
107 ( listPackageSourcesWithDie
109 import Distribution
.Simple
.Utils
115 import Distribution
.Types
.ComponentName
119 import Distribution
.Types
.GenericPackageDescription
(GenericPackageDescription
)
120 import Distribution
.Types
.PackageName
124 import Distribution
.Verbosity
128 import qualified Data
.ByteString
.Lazy
.Char8
as BSL
129 import System
.Directory
130 ( createDirectoryIfMissing
131 , getCurrentDirectory
134 import System
.FilePath
141 -------------------------------------------------------------------------------
143 -------------------------------------------------------------------------------
145 sdistCommand
:: CommandUI
(ProjectFlags
, SdistFlags
)
148 { commandName
= "v2-sdist"
149 , commandSynopsis
= "Generate a source distribution file (.tar.gz)."
150 , commandUsage
= \pname
->
151 "Usage: " ++ pname
++ " v2-sdist [FLAGS] [PACKAGES]\n"
152 , commandDescription
= Just
$ \_
->
154 "Generates tarballs of project packages suitable for upload to Hackage."
155 , commandNotes
= Nothing
156 , commandDefaultFlags
= (defaultProjectFlags
, defaultSdistFlags
)
157 , commandOptions
= \showOrParseArgs
->
158 map (liftOptionL _1
) (projectFlagsOptions showOrParseArgs
)
159 ++ map (liftOptionL _2
) (sdistOptions showOrParseArgs
)
162 -------------------------------------------------------------------------------
164 -------------------------------------------------------------------------------
166 data SdistFlags
= SdistFlags
167 { sdistVerbosity
:: Flag Verbosity
168 , sdistDistDir
:: Flag
FilePath
169 , sdistListSources
:: Flag
Bool
170 , sdistNulSeparated
:: Flag
Bool
171 , sdistOutputPath
:: Flag
FilePath
174 defaultSdistFlags
:: SdistFlags
177 { sdistVerbosity
= toFlag normal
178 , sdistDistDir
= mempty
179 , sdistListSources
= toFlag
False
180 , sdistNulSeparated
= toFlag
False
181 , sdistOutputPath
= mempty
184 sdistOptions
:: ShowOrParseArgs
-> [OptionField SdistFlags
]
185 sdistOptions showOrParseArgs
=
188 (\v flags
-> flags
{sdistVerbosity
= v
})
191 (\dd flags
-> flags
{sdistDistDir
= dd
})
196 "Just list the sources, do not make a tarball"
198 (\v flags
-> flags
{sdistListSources
= v
})
203 "Separate the source files with NUL bytes rather than newlines."
205 (\v flags
-> flags
{sdistNulSeparated
= v
})
209 ["output-directory", "outputdir"]
210 "Choose the output directory of this command. '-' sends all output to stdout"
212 (\o flags
-> flags
{sdistOutputPath
= o
})
213 (reqArg
"PATH" (succeedReadE Flag
) flagToList
)
216 -------------------------------------------------------------------------------
218 -------------------------------------------------------------------------------
220 sdistAction
:: (ProjectFlags
, SdistFlags
) -> [String] -> GlobalFlags
-> IO ()
221 sdistAction
(pf
@ProjectFlags
{..}, SdistFlags
{..}) targetStrings globalFlags
= do
222 (baseCtx
, distDirLayout
) <- withProjectOrGlobalConfig verbosity flagIgnoreProject globalConfigFlag withProject withoutProject
224 let localPkgs
= localPackages baseCtx
227 either (reportTargetSelectorProblems verbosity
) return
228 =<< readTargetSelectors localPkgs Nothing targetStrings
230 -- elaborate path, create target directory
231 mOutputPath
' <- case mOutputPath
of
232 Just
"-" -> return (Just
"-")
234 abspath
<- makeAbsolute path
235 createDirectoryIfMissing
True abspath
236 return (Just abspath
)
238 createDirectoryIfMissing
True (distSdistDirectory distDirLayout
)
241 let format
:: OutputFormat
244 | listSources
, nulSeparated
-> SourceList
'\0'
245 | listSources
-> SourceList
'\n'
246 |
otherwise -> TarGzArchive
249 SourceList _
-> "list"
250 TarGzArchive
-> "tar.gz"
252 outputPath pkg
= case mOutputPath
' of
255 |
otherwise -> path
</> prettyShow
(packageId pkg
) <.> ext
258 |
otherwise -> distSdistFile distDirLayout
(packageId pkg
)
260 case reifyTargetSelectors localPkgs targetSelectors
of
261 Left errs
-> dieWithException verbosity
$ SdistActionException
. fmap renderTargetProblem
$ errs
265 , Just
"-" <- mOutputPath
' ->
266 dieWithException verbosity Can
'tWriteMultipleTarballs
268 traverse_
(\pkg
-> packageToSdist verbosity
(distProjectRootDirectory distDirLayout
) format
(outputPath pkg
) pkg
) pkgs
270 verbosity
= fromFlagOrDefault normal sdistVerbosity
271 listSources
= fromFlagOrDefault
False sdistListSources
272 nulSeparated
= fromFlagOrDefault
False sdistNulSeparated
273 mOutputPath
= flagToMaybe sdistOutputPath
275 prjConfig
:: ProjectConfig
277 commandLineFlagsToProjectConfig
279 (defaultNixStyleFlags
())
281 (configFlags
$ defaultNixStyleFlags
())
282 { configVerbosity
= sdistVerbosity
283 , configDistPref
= sdistDistDir
289 globalConfigFlag
= projectConfigConfigFile
(projectConfigShared prjConfig
)
291 withProject
:: IO (ProjectBaseContext
, DistDirLayout
)
293 baseCtx
<- establishProjectBaseContext verbosity prjConfig OtherCommand
294 return (baseCtx
, distDirLayout baseCtx
)
296 withoutProject
:: ProjectConfig
-> IO (ProjectBaseContext
, DistDirLayout
)
297 withoutProject config
= do
298 cwd
<- getCurrentDirectory
299 baseCtx
<- establishProjectBaseContextWithRoot verbosity
(config
<> prjConfig
) (ProjectRootImplicit cwd
) OtherCommand
300 return (baseCtx
, distDirLayout baseCtx
)
307 packageToSdist
:: Verbosity
-> FilePath -> OutputFormat
-> FilePath -> UnresolvedSourcePackage
-> IO ()
308 packageToSdist verbosity projectRootDir format outputFile pkg
= do
309 let death
= dieWithException verbosity
$ ImpossibleHappened
(show pkg
)
310 dir0
<- case srcpkgSource pkg
of
311 LocalUnpackedPackage path
-> pure
(Right path
)
312 RemoteSourceRepoPackage _
(Just tgz
) -> pure
(Left tgz
)
313 RemoteSourceRepoPackage
{} -> death
314 LocalTarballPackage tgz
-> pure
(Left tgz
)
315 RemoteTarballPackage _
(Just tgz
) -> pure
(Left tgz
)
316 RemoteTarballPackage
{} -> death
317 RepoTarballPackage
{} -> death
320 -- Write String to stdout or file, using the default TextEncoding.
322 | outputFile
== "-" = putStr (withOutputMarker verbosity str
)
324 writeFile outputFile str
325 notice verbosity
$ "Wrote source list to " ++ outputFile
++ "\n"
326 -- Write raw ByteString to stdout or file as it is, without encoding.
328 | outputFile
== "-" = BSL
.putStr lbs
330 BSL
.writeFile outputFile lbs
331 notice verbosity
$ "Wrote tarball sdist to " ++ outputFile
++ "\n"
337 writeLBS
=<< BSL
.readFile tgz
338 _
-> dieWithException verbosity
$ CannotConvertTarballPackage
(show format
)
339 Right dir
-> case format
of
340 SourceList nulSep
-> do
341 let gpd
:: GenericPackageDescription
342 gpd
= srcpkgDescription pkg
344 files
' <- listPackageSourcesWithDie verbosity dieWithException dir
(flattenPackageDescription gpd
) knownSuffixHandlers
345 let files
= nub $ sort $ map normalise files
'
346 let prefix
= makeRelative projectRootDir dir
347 write
$ concat [prefix
</> i
++ [nulSep
] | i
<- files
]
349 packageDirToSdist verbosity
(srcpkgDescription pkg
) dir
>>= writeLBS
353 reifyTargetSelectors
:: [PackageSpecifier UnresolvedSourcePackage
] -> [TargetSelector
] -> Either [TargetProblem
] [UnresolvedSourcePackage
]
354 reifyTargetSelectors pkgs sels
=
355 case partitionEithers
(foldMap go sels
) of
356 ([], sels
') -> Right sels
'
357 (errs
, _
) -> Left errs
359 -- there can be pkgs which are in extra-packages:
360 -- these are not SpecificSourcePackage
362 -- Why these packages are in localPkgs, it's confusing.
363 -- Anyhow, better to be lenient here.
365 flatten
(SpecificSourcePackage pkg
@SourcePackage
{}) = Just pkg
368 pkgs
' = mapMaybe flatten pkgs
370 getPkg pid
= case find ((== pid
) . packageId
) pkgs
' of
371 Just pkg
-> Right pkg
372 Nothing
-> error "The impossible happened: we have a reference to a local package that isn't in localPackages."
374 go
:: TargetSelector
-> [Either TargetProblem UnresolvedSourcePackage
]
375 go
(TargetPackage _ pids Nothing
) = fmap getPkg pids
376 go
(TargetAllPackages Nothing
) = Right
<$> pkgs
'
377 go
(TargetPackage _ _
(Just kind
)) = [Left
(AllComponentsOnly kind
)]
378 go
(TargetAllPackages
(Just kind
)) = [Left
(AllComponentsOnly kind
)]
379 go
(TargetPackageNamed pname _
) = [Left
(NonlocalPackageNotAllowed pname
)]
380 go
(TargetComponentUnknown pname _ _
) = [Left
(NonlocalPackageNotAllowed pname
)]
381 go
(TargetComponent _ cname _
) = [Left
(ComponentsNotAllowed cname
)]
384 = AllComponentsOnly ComponentKind
385 | NonlocalPackageNotAllowed PackageName
386 | ComponentsNotAllowed ComponentName
388 renderTargetProblem
:: TargetProblem
-> String
389 renderTargetProblem
(AllComponentsOnly kind
) =
390 "It is not possible to package only the "
391 ++ renderComponentKind Plural kind
392 ++ " from a package "
393 ++ "for distribution. Only entire packages may be packaged for distribution."
394 renderTargetProblem
(ComponentsNotAllowed cname
) =
396 ++ showComponentName cname
397 ++ " cannot be packaged for distribution on its own. "
398 ++ "Only entire packages may be packaged for distribution."
399 renderTargetProblem
(NonlocalPackageNotAllowed pname
) =
401 ++ unPackageName pname
402 ++ " cannot be packaged for distribution, because it is not "
403 ++ "local to this project."