Skip to content

Commit

Permalink
Make targets extensible
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Mar 3, 2017
1 parent 31dd643 commit 68cae83
Show file tree
Hide file tree
Showing 17 changed files with 540 additions and 128 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

__pycache__
.cache
.stack-work
*.pyc

Expand Down
19 changes: 18 additions & 1 deletion docs/package.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Package manifest
----------------

Every Nirum package has its own package.toml file. It contains metadata
about the package (e.g. `version`, `authors`). The following TOML code
about the package (e.g. `version`, `authors`). The following [TOML][] code
is an example:

version = "1.0.0" # (required)
Expand Down Expand Up @@ -67,4 +67,21 @@ It consists of the following fields (*emphasized fields* are required):
`uri` (string)
: An optional URI to author's website.

`targets` (table)
: Settings of each target. See also the below target settings section
for details.

[semver]: http://semver.org/
[toml]: https://github.com/toml-lang/toml


Target settings
---------------

Beside common metadata to every target, there can be configurations specific
to each target. For example, Python target need a PyPI distribution name
(which is specified of `setup()` function's `name` parameter in setup.py file)
of the package to be generated by Nirum, hence `name`:

[targets.python]
name = "py-foobar" # will be submitted to: pypi.python.org/pypi/py-foobar
6 changes: 6 additions & 0 deletions examples/package.toml
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
version = "0.3.0"

[targets.python]
name = "nirum-examples"

[targets.dummy]
# for unit test
10 changes: 10 additions & 0 deletions nirum.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ library
, Nirum.Package.Metadata
, Nirum.Package.ModuleSet
, Nirum.Parser
, Nirum.Targets
, Nirum.Targets.List
, Nirum.Targets.Python
, Nirum.Version
build-depends: base >=4.7 && <5
, bytestring
, containers >=0.5.6.2 && <0.6
, cmdargs >=0.10.14 && <0.11
, directory >=1.2.5 && <1.3
Expand All @@ -58,6 +61,7 @@ library
, parsec
-- only for dealing with htoml's ParserError
, semver >=0.3.0 && <1.0
, template-haskell >=2.11 && <3
, text >=0.9.1.0 && <1.3
, unordered-containers
-- only for dealing with htoml's data structures
Expand All @@ -72,6 +76,7 @@ executable nirum
, nirum
main-is: Main.hs
default-language: Haskell2010
default-extensions: OverloadedStrings
ghc-options: -Wall -Werror -fwarn-incomplete-uni-patterns
-threaded -with-rtsopts=-N

Expand All @@ -80,6 +85,7 @@ executable nirum-static
, nirum
main-is: Main.hs
default-language: Haskell2010
default-extensions: OverloadedStrings
ghc-options: -Wall -Werror -fwarn-incomplete-uni-patterns
-threaded -with-rtsopts=-N
-static
Expand All @@ -100,22 +106,26 @@ test-suite spec
, Nirum.Constructs.ServiceSpec
, Nirum.Constructs.TypeDeclarationSpec
, Nirum.Constructs.TypeExpressionSpec
, Nirum.Package.MetadataSpec
, Nirum.Package.ModuleSetSpec
, Nirum.PackageSpec
, Nirum.ParserSpec
, Nirum.Targets.PythonSpec
, Nirum.TargetsSpec
, Nirum.VersionSpec
, Util
default-language: Haskell2010
default-extensions: OverloadedStrings
build-depends: base >=4.7 && <5
, bytestring
, containers >=0.5.6.2 && <0.6
, directory
, email-validate >=2.0.0 && <3.0.0
, filepath >=1.4 && <1.5
, hspec
, hspec-core
, hspec-meta
, htoml >=1.0.0.0 && <1.1.0.0
, interpolatedstring-perl6 >=1.0.0 && <1.1.0
, megaparsec >=5 && <5.2
, mtl >=2.2.1 && <3
Expand Down
50 changes: 27 additions & 23 deletions src/Nirum/Cli.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import Control.Monad (forM_)
import GHC.Exts (IsList (toList))
import System.IO.Error (catchIOError, ioeGetErrorString)

import qualified Data.ByteString as B
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.IO as TI
import System.Console.CmdArgs.Implicit ( Data
, Typeable
, argPos
Expand Down Expand Up @@ -41,14 +41,16 @@ import Nirum.Package ( PackageError ( ImportError
, ScanError
)
, scanModules
, scanPackage
)
import Nirum.Package.ModuleSet ( ImportError ( CircularImportError
, MissingImportError
, MissingModulePathError
)
)
import Nirum.Targets.Python (compilePackage)
import Nirum.Targets ( BuildError (CompileError, PackageError, TargetNameError)
, BuildResult
, buildPackage
)
import Nirum.Version (versionString)

data NirumCli = NirumCli { sourcePath :: FilePath
Expand Down Expand Up @@ -132,10 +134,12 @@ nirumCli = NirumCli { objectPath = def &= explicit

main' :: IO ()
main' = do
NirumCli src obj <- cmdArgs nirumCli
scanResult <- scanPackage src
case scanResult of
Left (ParseError modulePath error') -> do
NirumCli src outDir <- cmdArgs nirumCli
result <- buildPackage "python" src
case result of
Left (TargetNameError targetName) ->
putStrLn [qq|Couldn't find "$targetName" target.|]
Left (PackageError (ParseError modulePath error')) -> do
{- FIXME: find more efficient way to determine filename from
the given module path -}
filePaths <- scanModules src
Expand All @@ -144,26 +148,26 @@ main' = do
m <- parseErrortoPrettyMessage error' filePath'
putStrLn m
Nothing -> putStrLn [qq|$modulePath not found|]
Left (ImportError importErrors) ->
Left (PackageError (ImportError importErrors)) ->
putStrLn [qq|Import error:
{importErrorsToPrettyMessage importErrors}
|]
Left (ScanError _ error') -> putStrLn [qq|Scan error: $error'|]
Left (MetadataError error') -> putStrLn [qq|Metadata error: $error'|]
Right pkg -> writeFiles obj $ compilePackage pkg
Left (PackageError (ScanError _ error')) ->
putStrLn [qq|Scan error: $error'|]
Left (PackageError (MetadataError error')) ->
putStrLn [qq|Metadata error: $error'|]
Left (CompileError errors) ->
forM_ (M.toList errors) $ \ (filePath, compileError) ->
putStrLn [qq|error: $filePath: $compileError|]
Right buildResult -> writeFiles outDir buildResult

writeFiles :: FilePath -> M.Map FilePath (Either T.Text T.Text) -> IO ()
writeFiles obj m =
forM_ files $ \ (filePath, result) ->
case result of
Left compileError -> putStrLn [qq|error: $filePath: $compileError|]
Right code -> do
createDirectoryIfMissing True $ takeDirectory filePath
putStrLn filePath
TI.writeFile filePath code
where
files :: [(FilePath, Either T.Text T.Text)]
files = [(obj </> f, r) | (f, r) <- M.toList m]
writeFiles :: FilePath -> BuildResult -> IO ()
writeFiles outDir files =
forM_ (M.toAscList files) $ \ (filePath, code) -> do
let outPath = outDir </> filePath
createDirectoryIfMissing True $ takeDirectory outPath
putStrLn outPath
B.writeFile outPath code

main :: IO ()
main = catchIOError main' $ putStrLn . ioeGetErrorString
38 changes: 22 additions & 16 deletions src/Nirum/Package.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE RankNTypes, StandaloneDeriving #-}
module Nirum.Package ( BoundModule (boundPackage, modulePath)
, Package (Package, metadata, modules)
, PackageError ( ImportError
Expand All @@ -13,6 +14,7 @@ module Nirum.Package ( BoundModule (boundPackage, modulePath)
, resolveModule
, scanModules
, scanPackage
, target
, types
) where

Expand Down Expand Up @@ -40,23 +42,23 @@ import Nirum.Constructs.TypeDeclaration ( Type
, type'
)
)
import Nirum.Package.Metadata ( Metadata
, MetadataError
import Nirum.Package.Metadata ( MetadataError
, Package (Package, metadata, modules)
, Target
, metadataPath
, packageTarget
, readFromPackage
)
import qualified Nirum.Package.ModuleSet as MS
import Nirum.Parser (ParseError, parseFile)

-- | Represents a package which consists of modules.
data Package = Package { metadata :: Metadata
, modules :: MS.ModuleSet
} deriving (Eq, Ord, Show)
target :: Target t => Package t -> t
target = packageTarget

resolveModule :: ModulePath -> Package -> Maybe Mod.Module
resolveModule :: ModulePath -> Package t -> Maybe Mod.Module
resolveModule path Package { modules = ms } = MS.lookup path ms

resolveBoundModule :: ModulePath -> Package -> Maybe BoundModule
resolveBoundModule :: ModulePath -> Package t -> Maybe (BoundModule t)
resolveBoundModule path package =
case resolveModule path package of
Just _ -> Just $ BoundModule package path
Expand All @@ -69,7 +71,7 @@ data PackageError = ScanError FilePath IOError
deriving (Eq, Show)

-- | Scan the given package path, and then return the read package.
scanPackage :: FilePath -> IO (Either PackageError Package)
scanPackage :: Target t => FilePath -> IO (Either PackageError (Package t))
scanPackage packagePath = runExceptT $ do
metadataE <- catch (readFromPackage packagePath)
(ScanError $ metadataPath packagePath)
Expand Down Expand Up @@ -127,11 +129,15 @@ scanModules packagePath = do
[] -> packagePath
p -> packagePath </> p

data BoundModule = BoundModule { boundPackage :: Package
, modulePath :: ModulePath
} deriving (Eq, Ord, Show)
data BoundModule t = BoundModule { boundPackage :: Target t => Package t
, modulePath :: ModulePath
}

findInBoundModule :: (Mod.Module -> a) -> a -> BoundModule -> a
deriving instance (Eq t, Target t) => Eq (BoundModule t)
deriving instance (Ord t, Target t) => Ord (BoundModule t)
deriving instance (Show t, Target t) => Show (BoundModule t)

findInBoundModule :: Target t => (Mod.Module -> a) -> a -> BoundModule t -> a
findInBoundModule valueWhenExist valueWhenNotExist
BoundModule { boundPackage = Package { modules = ms }
, modulePath = path
Expand All @@ -140,18 +146,18 @@ findInBoundModule valueWhenExist valueWhenNotExist
Nothing -> valueWhenNotExist
Just mod' -> valueWhenExist mod'

types :: BoundModule -> DS.DeclarationSet TypeDeclaration
types :: Target t => BoundModule t -> DS.DeclarationSet TypeDeclaration
types = findInBoundModule Mod.types DS.empty

docs :: BoundModule -> Maybe Docs
docs :: Target t => BoundModule t -> Maybe Docs
docs = findInBoundModule Mod.docs Nothing

data TypeLookup = Missing
| Local Type
| Imported ModulePath Type
deriving (Eq, Ord, Show)

lookupType :: Identifier -> BoundModule -> TypeLookup
lookupType :: Target t => Identifier -> BoundModule t -> TypeLookup
lookupType identifier boundModule =
case DS.lookup identifier (types boundModule) of
Nothing -> toType Mod.coreModulePath
Expand Down
Loading

0 comments on commit 68cae83

Please sign in to comment.