1 {-# LANGUAGE LambdaCase #-}
3 -----------------------------------------------------------------------------
5 -----------------------------------------------------------------------------
8 -- Module : Distribution.Client.Init.NonInteractive.Heuristics
9 -- Copyright : (c) Benedikt Huber 2009
12 -- Maintainer : cabal-devel@haskell.org
13 -- Stability : provisional
14 -- Portability : portable
16 -- Heuristics for creating initial cabal files.
17 module Distribution
.Client
.Init
.NonInteractive
.Heuristics
24 , guessCabalSpecVersion
27 , guessSourceDirectories
28 , guessApplicationDirectories
31 import Distribution
.Client
.Compat
.Prelude
hiding (many
, readFile, (<|
>))
33 import Distribution
.Simple
.Setup
(fromFlagOrDefault
)
35 import qualified Data
.List
as L
36 import qualified Data
.Set
as Set
37 import Distribution
.CabalSpecVersion
38 import Distribution
.Client
.Init
.Defaults
39 import Distribution
.Client
.Init
.FlagExtractors
(getCabalVersionNoPrompt
)
40 import Distribution
.Client
.Init
.Types
41 import Distribution
.Client
.Init
.Utils
42 import Distribution
.FieldGrammar
.Newtypes
43 import Distribution
.Simple
.Compiler
44 import Distribution
.Types
.PackageName
(PackageName
)
45 import Distribution
.Version
46 import Language
.Haskell
.Extension
47 import System
.FilePath
49 -- | Guess the main file, returns a default value if none is found.
50 guessMainFile
:: Interactive m
=> FilePath -> m HsFilePath
51 guessMainFile pkgDir
= do
52 exists
<- doesDirectoryExist pkgDir
55 files
<- filter isMain
<$> listFilesRecursive pkgDir
59 (f
: _
) -> toHsFilePath f
60 else return defaultMainIs
62 -- | Juggling characters around to guess the desired cabal version based on
63 -- the system's cabal version.
64 guessCabalSpecVersion
:: Interactive m
=> m CabalSpecVersion
65 guessCabalSpecVersion
= do
66 (_
, verString
, _
) <- readProcessWithExitCode
"cabal" ["--version"] ""
67 case simpleParsec
$ takeWhile (not . isSpace) $ dropWhile (not . isDigit) verString
of
68 Just v
-> pure
$ fromMaybe defaultCabalVersion
$ case versionNumbers v
of
69 [x
, y
, _
, _
] -> cabalSpecFromVersionDigits
[x
, y
]
70 [x
, y
, _
] -> cabalSpecFromVersionDigits
[x
, y
]
71 _
-> Just defaultCabalVersion
72 Nothing
-> pure defaultCabalVersion
74 -- | Guess the language specification based on the GHC version
75 guessLanguage
:: Interactive m
=> Compiler
-> m Language
76 guessLanguage Compiler
{compilerId
= CompilerId GHC ver
} =
78 if ver
< mkVersion
[7, 0, 1]
81 guessLanguage _
= return defaultLanguage
83 -- | Guess the package name based on the given root directory.
84 guessPackageName
:: Interactive m
=> FilePath -> m PackageName
85 guessPackageName
= filePathToPkgName
87 -- | Try to guess the license from an already existing @LICENSE@ file in
88 -- the package directory, comparing the file contents with the ones
89 -- listed in @Licenses.hs@, for now it only returns a default value.
90 guessLicense
:: Interactive m
=> InitFlags
-> m SpecLicense
91 guessLicense flags
= return . defaultLicense
$ getCabalVersionNoPrompt flags
93 guessExtraDocFiles
:: Interactive m
=> InitFlags
-> m
(Maybe (Set
FilePath))
94 guessExtraDocFiles flags
= do
95 pkgDir
<- fromFlagOrDefault
getCurrentDirectory $ return <$> packageDir flags
96 files
<- getDirectoryContents pkgDir
98 let extraDocCandidates
= ["CHANGES", "CHANGELOG", "README"]
99 extraDocs
= [y | x
<- extraDocCandidates
, y
<- files
, x
== map toUpper (takeBaseName y
)]
104 then Set
.singleton defaultChangelog
105 else Set
.fromList extraDocs
107 -- | Try to guess the package type from the files in the package directory,
108 -- looking for unique characteristics from each type, defaults to Executable.
109 guessPackageType
:: Interactive m
=> InitFlags
-> m PackageType
110 guessPackageType flags
= do
111 if fromFlagOrDefault
False (initializeTestSuite flags
)
112 then return TestSuite
114 let lastDir dirs
= L
.last . splitDirectories
$ dirs
115 srcCandidates
= [defaultSourceDir
, "src", "source"]
116 testCandidates
= [defaultTestDir
, "test", "tests"]
118 pkgDir
<- fromFlagOrDefault
getCurrentDirectory $ return <$> packageDir flags
119 files
<- listFilesInside
(\x
-> return $ lastDir x `
notElem` testCandidates
) pkgDir
121 filter (not . null . map (`
elem` testCandidates
) . splitDirectories
)
122 <$> listFilesRecursive pkgDir
124 let hasExe
= not $ null [f | f
<- files
, isMain
$ takeFileName f
]
125 hasLib
= not $ null [f | f
<- files
, lastDir f `
elem` srcCandidates
]
126 hasTest
= not $ null [f | f
<- files
', isMain
$ takeFileName f
]
128 return $ case (hasLib
, hasExe
, hasTest
) of
129 (True, True, _
) -> LibraryAndExecutable
130 (True, False, _
) -> Library
131 (False, False, True) -> TestSuite
134 -- | Try to guess the application directories from the package directory,
135 -- using a default value as fallback.
136 guessApplicationDirectories
:: Interactive m
=> InitFlags
-> m
[FilePath]
137 guessApplicationDirectories flags
= do
141 (return <$> packageDir flags
)
142 pkgDirsContents
<- listDirectory pkgDirs
144 let candidates
= [defaultApplicationDir
, "app", "src-exe"]
145 in return $ case [y | x
<- candidates
, y
<- pkgDirsContents
, x
== y
] of
146 [] -> [defaultApplicationDir
]
147 x
-> map (</> pkgDirs
) . nub $ x
149 -- | Try to guess the source directories, using a default value as fallback.
150 guessSourceDirectories
:: Interactive m
=> InitFlags
-> m
[FilePath]
151 guessSourceDirectories flags
= do
152 pkgDir
<- fromFlagOrDefault
getCurrentDirectory $ return <$> packageDir flags
154 doesDirectoryExist (pkgDir
</> "src")
156 False -> [defaultSourceDir
]
159 -- | Guess author and email using git configuration options.
160 guessAuthorName
:: Interactive m
=> m
(Maybe String)
161 guessAuthorName
= guessGitInfo
"user.name"
163 guessAuthorEmail
:: Interactive m
=> m
(Maybe String)
164 guessAuthorEmail
= guessGitInfo
"user.email"
166 guessGitInfo
:: Interactive m
=> String -> m
(Maybe String)
167 guessGitInfo target
= do
168 localInfo
<- maybeReadProcessWithExitCode
"git" ["config", "--local", target
] ""
170 Nothing
-> return Nothing
171 Just
(_
, localStdout
, _
) ->
174 globalInfo
<- maybeReadProcessWithExitCode
"git" ["config", "--global", target
] ""
176 Just
(ExitSuccess
, globalStdout
, _
) -> return $ Just
(trim globalStdout
)
178 else return $ Just
(trim localStdout
)