diff --git a/nix/lib.nix b/nix/lib.nix index 0fe10056b..642dd510f 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -20,10 +20,13 @@ in rec { phases = [ "buildPhase" ]; }; + # Generate a nix file from an opam file opam2nix = { src, opamFile ? findOpamFile src, ... }@args: pkgs.runCommandNoCC (args.pname or opamFile + ".nix") args "cat ${src}/${opamFile} | ${pkgs.glibc.bin}/bin/iconv -c -f utf-8 -t ascii | ${opam-parser} > $out"; + # Traverse OPAM repository, producing an extension to + # ocamlPackages than includes all the packages in the repo. traverseOPAMRepo = repo: self: super: let pkgNames = builtins.readDir "${repo}/packages"; @@ -53,6 +56,7 @@ in rec { }) pkgNames; in opamPkgs; + # Same as traverseOPAMRepo, but packages in super take precedence over packages from the new repo. traverseOPAMRepo' = repo: self: super: let traversed = traverseOPAMRepo repo self super; in traversed // builtins.mapAttrs (name: v: @@ -60,6 +64,7 @@ in rec { versions = (traversed.${name} or { versions = { }; }).versions; }) super; + # Extension that adds callOPAMPackage to the package set callOPAMPackage = self: super: { callOPAMPackage = src: newAttrs: overrides: (self.callPackage (opam2nix (newAttrs // { inherit src; })) diff --git a/nix/opam-parser.hs b/nix/opam-parser.hs index 1dafb875f..b4a4d8cb3 100644 --- a/nix/opam-parser.hs +++ b/nix/opam-parser.hs @@ -19,6 +19,8 @@ data OPAM , checkPhase :: Maybe [[String]] , source :: Maybe String } deriving Show + +-- Turn a description into a nix file opam2nix :: OPAM -> String opam2nix OPAM {..} = let @@ -62,6 +64,7 @@ opam2nix OPAM {..} = update :: Maybe a -> a -> Maybe a update old new = if isNothing old then Just new else old +-- Evaluate a Field and update OPAM description accordingly evaluateField :: OPAM -> Field -> OPAM evaluateField o@OPAM {..} = \case Name s -> o { name = update name s } @@ -86,29 +89,18 @@ evaluateField o@OPAM {..} = \case evaluateFields :: OPAM -> [Field] -> OPAM evaluateFields = foldl evaluateField + +-- Descriptions for various Fields of an opam file + data Package = Package { identifier :: String , additionalPackageInfo :: [String] } deriving Show +-- An expression as found in a Command data Exp = Str String | Var String deriving Show -data Command - = Command - { command :: [Exp] - , additionalCommandInfo :: [String] - } deriving Show - -data Field - = Name String - | Version String - | Depends [Package] - | Build [Command] - | URL String - | Other String - deriving Show - evaluateExp :: Exp -> String evaluateExp = let @@ -126,9 +118,27 @@ evaluateExp = Var "jobs" -> "1" Var s -> "${"<>s<>"}" +data Command + = Command + { command :: [Exp] + , additionalCommandInfo :: [String] + } deriving Show + +data Field + = Name String + | Version String + | Depends [Package] + | Build [Command] + | URL String + | Other String + deriving Show + + +-- An opam file is a collection of fields, opamFile :: ParsecT String u Identity [Field] opamFile = many field <* eof +-- Each has a name and a type; field :: ParsecT String u Identity Field field = Name <$> fieldParser "name" stringParser <|> Version <$> fieldParser "version" stringParser @@ -137,40 +147,43 @@ field = Name <$> fieldParser "name" stringParser <|> sectionParser "url" (URL <$> (fieldParser "src" stringParser <* many (noneOf "}"))) <|> Other <$> (many (noneOf "\n") <* char '\n') +-- Field's structure is "name: value" fieldParser :: String -> ParsecT String u Identity t -> ParsecT String u Identity t fieldParser name valueParser = try $ between (string (name<>":") >> many (oneOf " \n")) (many $ oneOf " \n") valueParser <* commentParser +-- Sections's structure is "name { fields }" sectionParser :: String -> ParsecT String u Identity t -> ParsecT String u Identity t sectionParser name valueParser = try $ between (string name >> many (oneOf " ") >> string "{" >> many (oneOf " \n")) (many (oneOf " \n") >> char '}' >> char '\n') valueParser +-- String is enclosed in quotes stringParser :: ParsecT String u Identity String stringParser = between (char '"') (char '"') (many $ noneOf "\"") +-- Expression is either a string or a variable expParser :: ParsecT String u Identity Exp expParser = try (Str <$> stringParser) <|> Var <$> many1 (noneOf " \n\"{}[]") +-- "Additional Info" is additional information about a package or command, "{like-this}" additionalInfoParser :: ParsecT String u Identity [String] additionalInfoParser = option [] $ try $ between (many (char ' ') >> char '{') (char '}') ((many $ noneOf " &}") `sepBy` (oneOf " &")) +-- Command is a [expressions] with additionional information commandParser :: ParsecT String u Identity Command -commandParser = do - command <- listParser $ try expParser - additionalInfo <- additionalInfoParser - return $ Command command additionalInfo +commandParser = Command <$> (listParser $ try expParser) <*> additionalInfoParser +-- Comment starts with # and goes to the end of line commentParser :: ParsecT String u Identity () commentParser = optional $ do void $ string "#" many $ noneOf "\n" +-- Package is a "string" with additional information packageParser :: ParsecT String u Identity Package -packageParser = do - name <- stringParser - additionalInfo <- additionalInfoParser - return $ Package name additionalInfo +packageParser = Package <$> stringParser <*> additionalInfoParser + listParser :: ParsecT String u Identity t -> ParsecT String u Identity [t] listParser valueParser =