@@ -6,10 +6,16 @@ import ArgParse.Basic (ArgParser)
66import ArgParse.Basic as Arg
77import Data.Array as Array
88import Data.Array.NonEmpty as NEA
9+ import Data.Codec.Argonaut as CA
10+ import Data.Codec.Argonaut.Record as CA.Record
11+ import Data.Codec.Argonaut.Variant as CA.Variant
912import Data.Formatter.DateTime as Formatter.DateTime
1013import Data.Map as Map
14+ import Data.Maybe as Maybe
15+ import Data.Profunctor as Profunctor
16+ import Data.Semigroup.Foldable as Semigroup.Foldable
1117import Data.String as String
12- import Data.Tuple ( uncurry )
18+ import Data.Variant as Variant
1319import Effect.Class.Console as Console
1420import Node.FS.Aff as FS.Aff
1521import Node.Path as Path
@@ -30,33 +36,42 @@ import Registry.App.Effect.Storage as Storage
3036import Registry.Foreign.FSExtra as FS.Extra
3137import Registry.Foreign.Octokit as Octokit
3238import Registry.Foreign.Tmp as Tmp
39+ import Registry.Internal.Codec as Internal.Codec
3340import Registry.Internal.Format as Internal.Format
3441import Registry.Manifest (Manifest (..))
42+ import Registry.Manifest as Manifest
3543import Registry.ManifestIndex as ManifestIndex
3644import Registry.PackageName as PackageName
45+ import Registry.Solver (DependencyIndex )
46+ import Registry.Solver as Solver
3747import Registry.Version as Version
3848import Run (AFF , EFFECT , Run )
3949import Run as Run
4050import Run.Except (EXCEPT )
4151import Run.Except as Except
4252
43- data InputMode
44- = File FilePath
45- | Package PackageName Version
46- | AllPackages
47-
48- parser :: ArgParser InputMode
49- parser = Arg .choose " input (--file or --package or --all)"
50- [ Arg .argument [ " --file" ]
51- """ Compute supported compiler versions for packages from a JSON file like: [ "prelude", "console" ]"""
52- # Arg .unformat " FILE_PATH" pure
53- # map File
54- , Arg .argument [ " --package" ]
55- " Compute supported compiler versions for the indicated package"
56- # Arg .unformat " NAME@VERSION" parsePackage
57- # map (uncurry Package )
58- , Arg .flag [ " --all" ] " Compute supported compiler versions for all packages" $> AllPackages
59- ]
53+ type Arguments =
54+ { package :: Maybe (Tuple PackageName Version )
55+ , compiler :: Maybe Version
56+ }
57+
58+ parser :: ArgParser Arguments
59+ parser = Arg .fromRecord
60+ { package: Arg .choose " input (--all-packages or --package)"
61+ [ Arg .flag [ " --all-packages" ] " Check compiler versions for all packages" $> Nothing
62+ , Arg .argument [ " --package" ]
63+ " Check compiler versions for specific package"
64+ # Arg .unformat " NAME@VERSION" parsePackage
65+ # map Just
66+ ]
67+ , compiler: Arg .choose " input (--all-compilers or --compiler)"
68+ [ Arg .flag [ " --all-compilers" ] " Check all compiler versions" $> Nothing
69+ , Arg .argument [ " --compiler" ]
70+ " Check compiler versions for specific package"
71+ # Arg .unformat " VERSION" Version .parse
72+ # map Just
73+ ]
74+ }
6075 where
6176 parsePackage :: String -> Either String (Tuple PackageName Version )
6277 parsePackage input = do
@@ -123,13 +138,21 @@ main = launchAff_ do
123138 >>> Log .interpret (\log -> Log .handleTerminal Normal log *> Log .handleFs Verbose logPath log)
124139 >>> Run .runBaseAff'
125140
126- case arguments of
127- File _ -> Console .log " Unsupported at this time." *> liftEffect (Process .exit 1 )
128- Package package version -> interpret $ determineCompilerVersionsForPackage package version
129- AllPackages -> Console .log " Unsupported at this time." *> liftEffect (Process .exit 1 )
141+ case arguments.package of
142+ Just (Tuple package version) -> interpret $ determineCompilerVersionsForPackage package version arguments.compiler
143+ Nothing -> do
144+ { failures, results } <- interpret $ determineAllCompilerVersions arguments.compiler
145+ let resultsDir = Path .concat [ scratchDir, " results" ]
146+ FS.Extra .ensureDirectory resultsDir
147+ let
148+ resultsFile = " compiler-versions-results-" <> String .take 19 (Formatter.DateTime .format Internal.Format .iso8601DateTime now) <> " .json"
149+ failuresFile = " compiler-versions-failures-" <> String .take 19 (Formatter.DateTime .format Internal.Format .iso8601DateTime now) <> " .json"
150+
151+ writeJsonFile (Internal.Codec .packageMap (Internal.Codec .versionMap (CA .array Version .codec))) (Path .concat [ resultsDir, resultsFile ]) results
152+ writeJsonFile (Internal.Codec .versionMap (CA .array failureCodec)) (Path .concat [ resultsDir, failuresFile ]) failures
130153
131- determineCompilerVersionsForPackage :: forall r . PackageName -> Version -> Run (AFF + EFFECT + REGISTRY + EXCEPT String + LOG + STORAGE + r ) Unit
132- determineCompilerVersionsForPackage package version = do
154+ determineCompilerVersionsForPackage :: forall r . PackageName -> Version -> Maybe Version -> Run (AFF + EFFECT + REGISTRY + EXCEPT String + LOG + STORAGE + r ) Unit
155+ determineCompilerVersionsForPackage package version mbCompiler = do
133156 allManifests <- map ManifestIndex .toMap Registry .readAllManifests
134157 compilerVersions <- PursVersions .pursVersions
135158 Log .debug $ " Checking Manifest Index for " <> formatPackageVersion package version
@@ -180,10 +203,133 @@ determineCompilerVersionsForPackage package version = do
180203 else
181204 goCompilerVersions supported tail
182205
183- supported <- goCompilerVersions [] (Array .sort (NEA .toArray compilerVersions))
206+ supported <- goCompilerVersions [] (Maybe .maybe ( Array .sort (NEA .toArray compilerVersions)) Array .singleton mbCompiler )
184207
185208 if Array .null supported then do
186209 Log .error $ " Could not find supported compiler versions for " <> formatPackageVersion package version
187210 Run .liftEffect $ Process .exit 1
188211 else
189212 Log .info $ " Found supported compiler versions for " <> formatPackageVersion package version <> " : " <> Array .intercalate " , " (map Version .print supported)
213+
214+ data FailureReason
215+ = CannotSolve
216+ | CannotCompile
217+ | UnknownReason
218+
219+ failureReasonCodec :: JsonCodec FailureReason
220+ failureReasonCodec = Profunctor .dimap toVariant fromVariant $ CA.Variant .variantMatch
221+ { cannotSolve: Left unit
222+ , cannotCompile: Left unit
223+ , unknownReason: Left unit
224+ }
225+ where
226+ toVariant = case _ of
227+ CannotSolve -> Variant .inj (Proxy :: _ " cannotSolve" ) unit
228+ CannotCompile -> Variant .inj (Proxy :: _ " cannotCompile" ) unit
229+ UnknownReason -> Variant .inj (Proxy :: _ " unknownReason" ) unit
230+
231+ fromVariant = Variant .match
232+ { cannotSolve: \_ -> CannotSolve
233+ , cannotCompile: \_ -> CannotCompile
234+ , unknownReason: \_ -> UnknownReason
235+ }
236+
237+ type Failure =
238+ { name :: PackageName
239+ , version :: Version
240+ , reason :: FailureReason
241+ }
242+
243+ failureCodec :: JsonCodec Failure
244+ failureCodec = CA.Record .object " Failure"
245+ { name: PackageName .codec
246+ , version: Version .codec
247+ , reason: failureReasonCodec
248+ }
249+
250+ type CompilerVersionResults =
251+ { results :: Map PackageName (Map Version (Array Version ))
252+ , failures :: Map Version (Array Failure )
253+ }
254+
255+ determineAllCompilerVersions :: forall r . Maybe Version -> Run (AFF + EFFECT + REGISTRY + EXCEPT String + LOG + STORAGE + r ) CompilerVersionResults
256+ determineAllCompilerVersions mbCompiler = do
257+ allManifests <- Array .mapWithIndex Tuple <<< ManifestIndex .toSortedArray ManifestIndex.ConsiderRanges <$> Registry .readAllManifests
258+ compilerVersions <- PursVersions .pursVersions
259+ let
260+ compilersToCheck = Maybe .maybe compilerVersions NEA .singleton mbCompiler
261+ total = Array .length allManifests
262+ supportedForVersion <- map Map .fromFoldable $ for compilersToCheck \compiler -> do
263+ Log .info $ " Starting checks for " <> Version .print compiler
264+ Tuple compiler <$> Array .foldM (checkCompilation compiler total) { failures: [] , results: Map .empty } allManifests
265+
266+ let
267+ results = Map .fromFoldableWith (Map .unionWith append) do
268+ Tuple compiler supported <- Map .toUnfoldable (map _.results supportedForVersion)
269+ Tuple package versions <- Map .toUnfoldable supported
270+ Tuple version _ <- Map .toUnfoldable versions
271+ [ Tuple package (Map .singleton version [ compiler ]) ]
272+
273+ failures = map _.failures supportedForVersion
274+
275+ pure { results, failures }
276+ where
277+ -- Adds packages which compile with `version` to the `DependencyIndex`
278+ checkCompilation :: Version -> Int -> { failures :: Array Failure , results :: DependencyIndex } -> Tuple Int Manifest -> Run _ { failures :: Array Failure , results :: DependencyIndex }
279+ checkCompilation compiler total { failures: prevFailures, results: prevResults } (Tuple index manifest@(Manifest { name, version, dependencies })) = do
280+ let progress = fold [ " [" , Version .print compiler, " " , show (1 + index), " /" , show total, " ]" ]
281+ Log .info $ progress <> " Checking " <> formatPackageVersion name version
282+ Log .debug $ " Solving " <> PackageName .print name <> " @" <> Version .print version
283+ case Solver .solve prevResults dependencies of
284+ Left unsolvable -> do
285+ Log .debug $ " Could not solve " <> formatPackageVersion name version <> " with manifest " <> printJson Manifest .codec manifest
286+ Log .debug $ Semigroup.Foldable .foldMap1 (append " \n " <<< Solver .printSolverError) unsolvable
287+ pure { failures: prevFailures <> [ { name, version, reason: CannotSolve } ], results: prevResults }
288+ Right resolutions -> do
289+ supported <- installAndBuildWithVersion compiler (Map .insert name version resolutions)
290+ case supported of
291+ Nothing -> do
292+ Log .debug $ " Including package version " <> formatPackageVersion name version
293+ pure $ { failures: prevFailures, results: Map .insertWith Map .union name (Map .singleton version dependencies) prevResults }
294+ Just reason -> do
295+ Log .debug $ " Skipping package version " <> formatPackageVersion name version
296+ pure $ { failures: prevFailures <> [ { name, version, reason } ], results: prevResults }
297+
298+ installAndBuildWithVersion :: Version -> Map PackageName Version -> Run _ (Maybe FailureReason )
299+ installAndBuildWithVersion compiler resolutions = do
300+ tmp <- Tmp .mkTmpDir
301+ let dependenciesDir = Path .concat [ tmp, " .registry" ]
302+ FS.Extra .ensureDirectory dependenciesDir
303+ Log .debug $ " Created tmp dir for dependencies: " <> dependenciesDir
304+ let globs = [ Path .concat [ dependenciesDir, " */src/**/*.purs" ] ]
305+
306+ Log .debug " Downloading dependencies..."
307+ forWithIndex_ resolutions \name version -> do
308+ let
309+ filename = PackageName .print name <> " -" <> Version .print version <> " .tar.gz"
310+ filepath = Path .concat [ dependenciesDir, filename ]
311+ Storage .download name version filepath
312+ Tar .extract { cwd: dependenciesDir, archive: filename }
313+ Run .liftAff $ FS.Aff .unlink filepath
314+
315+ Log .debug $ " Compiling with purs@" <> Version .print compiler <> " and globs " <> String .joinWith " " globs
316+ compilerOutput <- Run .liftAff $ Purs .callCompiler
317+ { command: Purs.Compile { globs }
318+ , version: Just compiler
319+ , cwd: Just tmp
320+ }
321+
322+ FS.Extra .remove tmp
323+
324+ case compilerOutput of
325+ Left (Purs.UnknownError error) -> do
326+ Log .error $ " Failed to compile because of an unknown compiler error: " <> error
327+ pure $ Just UnknownReason
328+ Left (Purs.MissingCompiler ) ->
329+ Except .throw " Failed to compile because the compiler was not found."
330+ Left (Purs.CompilationError errors) -> do
331+ Log .debug $ " Failed to compile with purs@" <> Version .print compiler <> " : " <> Purs .printCompilerErrors errors
332+ pure $ Just CannotCompile
333+ Right _ -> do
334+ Log .debug $ " Successfully compiled with purs@" <> Version .print compiler
335+ pure Nothing
0 commit comments