Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
domenkozar committed Dec 14, 2017
0 parents commit fc675c2
Show file tree
Hide file tree
Showing 11 changed files with 364 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
.stack-work/
elm2nix.cabal
*~
5 changes: 5 additions & 0 deletions ChangeLog.md
@@ -0,0 +1,5 @@
# Changelog for elm2nix

## 0.0.1.0 (2017-12-14)

- Initial release (Domen Kožar)
30 changes: 30 additions & 0 deletions LICENSE
@@ -0,0 +1,30 @@
Copyright Domen Kožar (c) 2017

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 Domen Kožar 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.
22 changes: 22 additions & 0 deletions README.md
@@ -0,0 +1,22 @@
# elm2nix (alpha/unstable)

Convert Elm project into Nix expressions.

## Installation

$ stack install --nix

## Usage

$ cd my-project
$ ~/.local/bin/elm2nix init > default.nix
$ ~/.local/bin/elm2nix convert > elm-srcs.nix
$ nix-build

## FAQ

Q: Why are there no Nix expressions yet to install elm2nix?
A: Waiting on https://github.com/input-output-hk/stack2nix 0.2 release

Q: Why is mkDerivation inlined into `default.nix`?
A: As it's considered unstable, it's generated for now. Might change in the future.
2 changes: 2 additions & 0 deletions Setup.hs
@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain
48 changes: 48 additions & 0 deletions data/default.nix
@@ -0,0 +1,48 @@
{ nixpkgs ? <nixpkgs>
, config ? {} }:

with (import nixpkgs config);

let
mkDerivation =
{ srcs ? ./elm-srcs.nix
, exactDependencies ? ./elm-stuff/exact-dependencies.json
, src
, name
}:
let
sources = import srcs { inherit fetchzip; };
in stdenv.mkDerivation {
inherit name src;

buildInputs = [ elmPackages.elm ];

preConfigurePhases = ["setupElmStuffPhase"];

setupElmStuffPhase = ''
runHook preSetupElmStuffPhase
rm -rf elm-stuff
mkdir elm-stuff
ln -s \${exactDependencies} elm-stuff/exact-dependencies.json
\${lib.concatStrings (lib.mapAttrsToList (name: src: ''
mkdir -p elm-stuff/packages/\${name}
ln -s \${src} elm-stuff/packages/\${name}/\${src.meta.version}
'') sources)}
runHook postSetupElmStuffPhase
'';

buildPhase = ''
mkdir -p $out/share/doc
elm make --warn --output $out/$name.js --docs $out/share/doc/$name.json
'';

installPhase = ":";
};
in mkDerivation {
name = "${name}";
srcs = ./elm-srcs.nix;
src = ./.;
}
60 changes: 60 additions & 0 deletions elm2nix/Main.hs
@@ -0,0 +1,60 @@
{-# LANGUAGE QuasiQuotes #-}
module Main where

import Data.Semigroup ((<>))
import qualified Control.Monad (join)
import Data.Version (showVersion)
import Data.String.Here
import Options.Applicative
import qualified Lib as Lib
import qualified Paths_elm2nix as This
import qualified Text.PrettyPrint.ANSI.Leijen as PP

main :: IO ()
main = do
cmd <- getOpts
case cmd of
Convert -> Lib.convert
Init -> Lib.init'

getOpts :: IO Command
getOpts = customExecParser p (infoH opts rest)
where
rest = fullDesc
<> progDesc "Convert Elm project to Nix expressions"
<> header ("elm2nix " ++ (showVersion This.version))
<> footerDoc (Just $ PP.string exampleText)
p = prefs showHelpOnEmpty

exampleText :: String
exampleText = [hereLit|

Usage:

$ elm2nix init > default.nix
$ elm2nix convert > elm-srcs.nix
$ nix-build

Note: You have to run elm2nix from top-level directory of an Elm project.
|]

convertExample :: String
convertExample = [hereLit|

Steps:

1) resolves `elm-package.json` into pinned `elm-stuff/exact-dependencies.json`
2) converts `elm-stuff/exact-dependencies.json` into Nix expressions by using `nix-prefetch-url`

|]

opts :: Parser Command
opts = subparser
( command "init" (infoH (pure Init) (progDesc "Print default.nix to stdout"))
<> command "convert" (infoH (pure Convert) (progDesc "Generate Nix expressions for Elm sources" <> footerDoc (Just $ PP.string convertExample))) )

infoH a = info (helper <*> a)

data Command
= Init
| Convert
106 changes: 106 additions & 0 deletions src/Lib.hs
@@ -0,0 +1,106 @@
{-# LANGUAGE QuasiQuotes #-}

module Lib
( convert
, init'
) where

import Control.Monad (mapM)
import Control.Monad.Except (liftIO, throwError)
import Data.List (intercalate)
import System.Directory (createDirectoryIfMissing, doesFileExist)
import System.Exit (exitFailure)
import System.IO ( hPutStrLn, stdout, stderr)
import qualified Data.Map as Map
import Data.String.Here

import qualified Install.Solver as Solver
import qualified Reporting.Error as Error
import qualified Install.Plan as Plan
import qualified Reporting.Error as Error
import qualified Manager
import qualified Install
import qualified Elm.Package as Package
import qualified Elm.Package.Description as Desc
import qualified Elm.Package.Paths as Path
import qualified Elm.Package.Solution as Solution

import Prefetch

-- CMDs

convert :: IO ()
convert = runCLI solveDependencies


init' :: IO ()
init' = runCLI generateDefault

-- Utils

runCLI f = do
result <- Manager.run f
case result of
Right () ->
return ()

Left err -> do
Error.toStderr err
exitFailure

generateDefault :: Manager.Manager ()
generateDefault = do
desc <- readDescription
let defaultNix = [template|data/default.nix|]
name = Package.toUrl (Desc.name desc) ++ "-" ++ show (Desc.version desc)
liftIO $ hPutStrLn stdout defaultNix

solveDependencies :: Manager.Manager ()
solveDependencies = do
liftIO $ hPutStrLn stderr $ "Resolving elm-package.json dependencies into elm-stuff/exact-dependencies.json ..."

desc <- readDescription
newSolution <- Solver.solve (Desc.elmVersion desc) (Desc.dependencies desc)
liftIO (createDirectoryIfMissing True Path.stuffDirectory)
liftIO (Solution.write Path.solvedDependencies newSolution)

liftIO $ hPutStrLn stderr $ "Prefetching tarballs and computing sha256 hashes ..."

let solL = Map.toList newSolution
sources <- liftIO $ mapM Prefetch.prefetchURL solL

liftIO $ hPutStrLn stdout $ generateNixSources sources

readDescription :: Manager.Manager Desc.Description
readDescription = do
exists <- liftIO (doesFileExist Path.description)

desc <-
if exists
then
Desc.read Error.CorruptDescription Path.description
else
Install.initialDescription
return desc


generateNixSource :: DerivationSource -> String
generateNixSource ds =
-- TODO: pass name to fetchzip
[iTrim|
"${Package.toUrl (drvName ds)}" = fetchzip {
url = "${drvUrl ds}";
sha256 = "${drvHash ds}";
meta = {
version = "${drvVersion ds}";
};
};
|]

generateNixSources :: [DerivationSource] -> String
generateNixSources dss =
[iTrim|
{ fetchzip }: {
${intercalate "\n" (map generateNixSource dss)}
}
|]
61 changes: 61 additions & 0 deletions src/Prefetch.hs
@@ -0,0 +1,61 @@
-- Partially taken from cabal2nix/src/Distribution/Nixpkgs/Fetch.hs

module Prefetch where

import System.Environment
import System.Exit
import System.Process
import qualified Data.ByteString.Lazy.Char8 as BS

import qualified Elm.Package as Package


data DerivationSource = DerivationSource
{ drvHash :: String -- ^ Computed sha256 hash
, drvPath :: String -- ^ Nix store path of the derivation
, drvUrl :: String
, drvName :: Package.Name
, drvVersion :: Package.Version
} deriving (Show, Eq)

instance Show Package.Version where
show ver = Package.versionToString ver

-- | Use nix-prefetch-url to obtain resulting path and it's hash
prefetchURL :: (Package.Name, Package.Version) -> IO DerivationSource
prefetchURL (name, version) =
let
url = toZipballUrl name version
args :: [String]
args = ["--unpack", "--print-path", url]
in do
envs <- getEnvironment
(Nothing, Just stdoutH, _, processH) <- createProcess (proc "nix-prefetch-url" args)
{ env = Nothing
, std_in = Inherit
, std_err = Inherit
, std_out = CreatePipe
}

exitCode <- waitForProcess processH
case exitCode of
ExitFailure _ -> error "nix-prefetch-url exited with non-zero"
ExitSuccess -> do
buf <- BS.hGetContents stdoutH
let ls = BS.lines buf
case length ls of
0 -> error "unknown nix-prefetch-url output"
2 -> return $
DerivationSource
(BS.unpack $ head ls)
(BS.unpack $ head $ tail ls)
url
name
version
_ -> error "unknown nix-prefetch-url output"


toZipballUrl :: Package.Name -> Package.Version -> String
toZipballUrl name version =
"https://github.com/" ++ Package.toUrl name
++ "/archive/" ++ Package.versionToString version ++ ".zip"
25 changes: 25 additions & 0 deletions stack.yaml
@@ -0,0 +1,25 @@
resolver: lts-7.24
packages:
- .
- location:
git: https://github.com/domenkozar/elm-package.git
commit: 2a5d2de0b55d4c9a30bec71f1cc6ff80130d7dfe
extra-dep: true
- location:
git: https://github.com/elm-lang/elm-compiler.git
commit: 7ee7742a16188df7ff498ec4ef9f8b49e58a35fe
extra-dep: true

extra-deps:
- http-types-0.8.6
- aeson-pretty-0.7.2
- containers-0.5.10.2
- binary-0.7.6.1
- optparse-applicative-0.13.2.0

flags: {}

nix:
enable: true
pure: false
packages: [zlib,haskellPackages.happy]
2 changes: 2 additions & 0 deletions test/Spec.hs
@@ -0,0 +1,2 @@
main :: IO ()
main = putStrLn "Test suite not yet implemented"

0 comments on commit fc675c2

Please sign in to comment.