packages feed

autonix-deps (empty) → 0.1.0.0

raw patch · 11 files changed

+443/−0 lines, 11 filesdep +basedep +bytestringdep +conduitsetup-changed

Dependencies added: base, bytestring, conduit, containers, errors, filepath, lens, libarchive-conduit, mtl, optparse-applicative, process, regex-tdfa, resourcet, transformers, xml

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Thomas Tuegel++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Thomas Tuegel nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ autonix-deps.cabal view
@@ -0,0 +1,49 @@+name:                autonix-deps+version:             0.1.0.0+synopsis:            Library for Nix expression dependency generation+license:             BSD3+license-file:        LICENSE+author:              Thomas Tuegel+maintainer:          ttuegel@gmail.com+copyright:           2014 Thomas Tuegel+category:            System+build-type:          Simple+cabal-version:       >=1.10+bug-reports: https://github.com/ttuegel/autonix-deps/issues+description:+  @autonix-deps@ is a library for building automatic dependency detectors for+  software collections to be built using the Nix package manager.++source-repository head+  type: git+  location: https://github.com/ttuegel/autonix-deps.git++library+  exposed-modules:+    Autonix.Analyze+    Autonix.Args+    Autonix.CMake+    Autonix.Deps+    Autonix.Generate+    Autonix.Manifest+    Autonix.PkgDeps+    Autonix.Regex+  build-depends:+      base >=4.7 && <5+    , bytestring >=0.10+    , conduit >=1.2+    , containers >=0.5+    , errors >=1.4+    , filepath >=1.3+    , lens >=4.0+    , libarchive-conduit ==0.1.*+    , mtl >=2.1+    , optparse-applicative >=0.11+    , process >=1.2+    , regex-tdfa >=1.2+    , resourcet >=1.1+    , transformers >=0.3+    , xml >=1.3+  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options: -Wall
+ src/Autonix/Analyze.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module Autonix.Analyze+       ( Analyzer+       , analyzeFiles+       , analyzePackages+       ) where++import Codec.Archive+import Control.Lens hiding (act)+import Control.Monad.State+import Control.Monad.Trans.Resource+import qualified Data.ByteString.Char8 as B+import Data.Conduit+import qualified Data.Map as M+import Data.Monoid+import qualified Data.Set as S+import Prelude hiding (mapM)++import Autonix.Deps+import Autonix.Manifest++type Analyzer m = ByteString -> Sink (FilePath, ByteString) m ()++analyzePackages :: (MonadIO m, MonadState Deps m)+                => (ByteString -> FilePath -> m a)+                -> FilePath -> Maybe FilePath -> m ()+analyzePackages perPackage manifestPath renamesPath = do+    manifest <- readManifest manifestPath+    renames <- readRenames renamesPath+    names %= flip M.union renames+    forM_ manifest $ \(pkg, _) -> at pkg .= Just mempty+    let (pkgs, _) = unzip manifest+    mapM_ (uncurry perPackage) manifest+    deps %= M.filterWithKey (\k _ -> S.member k $ S.fromList pkgs)++sequenceSinks_ :: (Traversable f, Monad m) => f (Sink i m ()) -> Sink i m ()+sequenceSinks_ = void . sequenceSinks++analyzeFiles :: (MonadBaseControl IO m, MonadIO m, MonadThrow m)+             => [Analyzer (ResourceT m)] -> ByteString -> FilePath -> m ()+analyzeFiles analyzers pkg src+    | null src = error $ B.unpack $ "No store path specified for " <> pkg+    | otherwise = do+        liftIO $ B.putStrLn $ "package " <> pkg+        runResourceT+            $ sourceArchive src $$ sequenceSinks_ (map ($ pkg) analyzers)
+ src/Autonix/Args.hs view
@@ -0,0 +1,16 @@+module Autonix.Args where++import Control.Applicative+import Control.Monad (join)+import Control.Monad.IO.Class+import Options.Applicative++withArgs :: MonadIO m => (FilePath -> Maybe FilePath -> m a) -> m a+withArgs child =+    join $ liftIO $ execParser $ info (helper <*> parser) fullDesc+  where+    parser = child+             <$> strArgument (metavar "MANIFEST")+             <*> option+                (Just <$> str)+                (long "renames" <> metavar "FILENAME" <> value Nothing)
+ src/Autonix/CMake.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}++module Autonix.CMake+       ( analyzeCMakePackages+       , analyzeCMakePrograms+       , cmakeAnalyzers+       , detectCMake+       ) where++import Control.Lens+import Control.Monad.IO.Class+import Control.Monad.State+import Data.Conduit+import qualified Data.Set as S+import System.FilePath (takeFileName)++import Autonix.Analyze+import Autonix.Deps+import Autonix.Regex++detectCMake :: MonadState Deps m => Analyzer m+detectCMake pkg = awaitForever $ \(path, _) -> do+    when (takeFileName path == "CMakeLists.txt")+        $ ix pkg . nativeBuildInputs %= S.insert "cmake"++analyzeCMakePackages :: (MonadIO m, MonadState Deps m) => Analyzer m+analyzeCMakePackages pkg = awaitForever $ \(path, contents) -> do+    when (takeFileName path == "CMakeLists.txt") $ do+        let new = concatMap (take 1 . drop 1) $ match regex contents+            regex = makeRegex+                    "find_package[[:space:]]*\\([[:space:]]*([^[:space:],$\\)]+)"+        ix pkg . buildInputs %= S.union (S.fromList new)++analyzeCMakePrograms :: (MonadIO m, MonadState Deps m) => Analyzer m+analyzeCMakePrograms pkg = awaitForever $ \(path, contents) -> do+    when (takeFileName path == "CMakeLists.txt") $ do+        let new = concatMap (take 1 . drop 1) $ match regex contents+            regex = makeRegex+                    "find_program[[:space:]]*\\([[:space:]]*([^[:space:],$\\)]+)"+        ix pkg . nativeBuildInputs %= S.union (S.fromList new)++cmakeAnalyzers :: (MonadIO m, MonadState Deps m) => [Analyzer m]+cmakeAnalyzers = [detectCMake, analyzeCMakePackages, analyzeCMakePrograms]
+ src/Autonix/Deps.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++module Autonix.Deps+       ( Deps, names, deps, rename+       , module Autonix.PkgDeps+       ) where++import Control.Lens+import Control.Monad.State+import qualified Data.Map as M+import Data.Monoid+import qualified Data.Set as S+import Prelude hiding (foldr)++import Autonix.PkgDeps++data Deps =+    Deps { _names :: Map ByteString ByteString+         , _deps :: Map ByteString PkgDeps+         }+  deriving (Read, Show)+makeLenses ''Deps++instance Monoid Deps where+    mempty = Deps { _names = M.empty, _deps = M.empty }++    mappend a b = flip execState a $ do+        let b' = execState (iforMOf_ (names.>itraversed) a rename) b+        iforMOf_ (names.>itraversed) b' rename+        names %= M.union (b'^.names)+        deps %= M.unionWith mappend (b'^.deps)++lookupNewName :: Deps -> ByteString -> ByteString+lookupNewName r idx = M.findWithDefault idx idx (r^.names)++renamePkgDeps :: ByteString -> ByteString -> PkgDeps -> PkgDeps+renamePkgDeps old new = execState $ do+    buildInputs %= S.map go+    nativeBuildInputs %= S.map go+    propagatedBuildInputs %= S.map go+    propagatedNativeBuildInputs %= S.map go+    propagatedUserEnvPkgs %= S.map go+  where+    go pkg | pkg == old = new+           | otherwise = pkg++rename :: (MonadState Deps m) => ByteString -> ByteString -> m ()+rename old new = do+    names %= M.insert old new+    names %= M.map (\tgt -> if tgt == old then new else tgt)+    deps %= M.map (renamePkgDeps old new)+    ds <- get+    deps %= M.mapKeysWith mappend (lookupNewName ds)++applyRenames :: Deps -> PkgDeps -> PkgDeps+applyRenames r = execState $ do+    buildInputs %= S.map (lookupNewName r)+    propagatedBuildInputs %= S.map (lookupNewName r)+    nativeBuildInputs %= S.map (lookupNewName r)+    propagatedNativeBuildInputs %= S.map (lookupNewName r)+    propagatedUserEnvPkgs %= S.map (lookupNewName r)++type instance Index Deps = ByteString+type instance IxValue Deps = PkgDeps++instance Ixed Deps where+    ix idx f r =+        (deps . ix (lookupNewName r idx)) (fmap (applyRenames r) . f) r++instance At Deps where+    at idx f r =+        (deps . at (lookupNewName r idx)) (fmap (fmap $ applyRenames r) . f) r
+ src/Autonix/Generate.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ViewPatterns #-}++module Autonix.Generate (generateDeps, writeDeps, writeRenames) where++import Control.Lens+import Control.Monad.IO.Class+import Control.Monad.Trans.Resource+import Control.Monad.State+import qualified Data.ByteString.Char8 as B+import qualified Data.Map as M+import Data.Monoid+import qualified Data.Set as S++import Autonix.Analyze+import Autonix.Args+import Autonix.Deps++generateDeps :: (MonadBaseControl IO m, MonadIO m, MonadThrow m)+             => [Analyzer (ResourceT (StateT Deps m))]+             -> StateT Deps m ()+generateDeps analyzers = do+    withArgs $ analyzePackages (analyzeFiles analyzers)+    get >>= writeDeps+    get >>= writeRenames++writeDeps :: MonadIO m => Deps -> m ()+writeDeps = liftIO . B.writeFile "dependencies.nix" . depsToNix++writeRenames :: MonadIO m => Deps -> m ()+writeRenames ds = do+    let renames = B.unlines $ do+            (old, new) <- M.toList (ds^.names)+            return $ old <> " " <> new+    liftIO $ B.writeFile "renames.txt" renames++packageDeps :: (ByteString, PkgDeps) -> ByteString+packageDeps (name, ds) =+    B.unlines+    [ "  " <> name <> " = {"+    , "    buildInputs = [ "+      <> listInputs buildInputs+      <> " ];"+    , "    nativeBuildInputs = [ "+      <> listInputs nativeBuildInputs+      <> " ];"+    , "    propagatedBuildInputs = [ "+      <> listInputs propagatedBuildInputs+      <> " ];"+    , "    propagatedNativeBuildInputs = [ "+      <> listInputs propagatedNativeBuildInputs+      <> " ];"+    , "    propagatedUserEnvPkgs = [ "+      <> listInputs propagatedUserEnvPkgs+      <> " ];"+    , "  };"+    ]+  where+    listInputs l = B.unwords $ map quoted $ S.toList $ ds^.l+    quoted x = "\"" <> x <> "\""++depsToNix :: Deps -> ByteString+depsToNix (view deps -> ds) =+    B.unlines+    ( [ "# DO NOT EDIT! This file is generated automatically."+      , "{ }:"+      , "{"+      ]+      ++ map packageDeps (M.toList ds) +++      [ "}" ]+    )
+ src/Autonix/Manifest.hs view
@@ -0,0 +1,47 @@+module Autonix.Manifest (readManifest, readRenames) where++import Control.Applicative+import Control.Error+import Control.Monad (liftM)+import Control.Monad.IO.Class+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as B+import Data.Map (Map)+import qualified Data.Map as M+import Text.XML.Light++type Manifest = [(ByteString, FilePath)]++readManifest :: MonadIO m => FilePath -> m Manifest+readManifest path =+    liftM (fromMaybe $ error "malformed manifest.xml")+    $ liftIO $ runMaybeT $ do+        xml <- MaybeT $ parseXMLDoc <$> B.readFile path+        attrs <-+            MaybeT $ return+            $ if qName (elName xml) == "expr"+              then case headMay (elChildren xml) of+                  Just el | qName (elName el) == "attrs" -> Just el+                  _ -> Nothing+              else Nothing+        return $ attrsToManifest attrs++attrsToManifest :: Element -> Manifest+attrsToManifest = mapMaybe go . filterChildrenName isAttr where+  isAttr name = qName name == "attr"+  go el = do+      name <- findAttr (blank_name { qName = "name" }) el+      child <- headMay $ elChildren el+      value <- findAttr (blank_name { qName = "value"}) child+      return (B.pack name, value)++readRenames :: MonadIO m => Maybe FilePath -> m (Map ByteString ByteString)+readRenames renamesPath = do+    case renamesPath of+        Nothing -> return M.empty+        Just path ->+            liftM (M.fromList . map (toPair . B.words) . B.lines)+            $ liftIO $ B.readFile path+  where+    toPair [old, new] = (old, new)+    toPair _ = error "readRenames: invalid line"
+ src/Autonix/PkgDeps.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE TemplateHaskell #-}++module Autonix.PkgDeps+       ( PkgDeps+       , buildInputs, nativeBuildInputs+       , propagatedBuildInputs, propagatedNativeBuildInputs+       , propagatedUserEnvPkgs+       , module Data.ByteString+       , module Data.Map+       , module Data.Set+       ) where++import Control.Lens+import Control.Monad.State (execState)+import Data.ByteString (ByteString)+import Data.Map (Map)+import Data.Monoid+import Data.Set (Set)+import qualified Data.Set as S++data PkgDeps =+    PkgDeps+    { _buildInputs :: Set ByteString+    , _nativeBuildInputs :: Set ByteString+    , _propagatedBuildInputs :: Set ByteString+    , _propagatedNativeBuildInputs :: Set ByteString+    , _propagatedUserEnvPkgs :: Set ByteString+    }+  deriving (Eq, Ord, Read, Show)+makeLenses ''PkgDeps++instance Monoid PkgDeps where+    mempty =+        PkgDeps+        { _buildInputs = S.empty+        , _nativeBuildInputs = S.empty+        , _propagatedBuildInputs = S.empty+        , _propagatedNativeBuildInputs = S.empty+        , _propagatedUserEnvPkgs = S.empty+        }++    mappend a = execState $ do+        buildInputs %= mappend (a^.buildInputs)+        nativeBuildInputs %= mappend (a^.nativeBuildInputs)+        propagatedBuildInputs %= mappend (a^.propagatedBuildInputs)+        propagatedNativeBuildInputs %= mappend (a^.propagatedNativeBuildInputs)+        propagatedUserEnvPkgs %= mappend (a^.propagatedUserEnvPkgs)
+ src/Autonix/Regex.hs view
@@ -0,0 +1,11 @@+module Autonix.Regex (match, makeRegex) where++import Data.ByteString (ByteString)+import Text.Regex.TDFA (Regex)+import qualified Text.Regex.TDFA as R++match :: Regex -> ByteString -> [[ByteString]]+match = R.match++makeRegex :: ByteString -> Regex+makeRegex = R.makeRegex