summaryrefslogtreecommitdiff
path: root/Biz/Bild.hs
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2020-11-17 16:24:29 -0500
committerBen Sima <ben@bsima.me>2020-11-17 16:24:29 -0500
commitac3d455a9c0dc0b2f4afb88b56db3d16c0508428 (patch)
tree0c2c558fb9c281bc79eb5ea560ead68f6b0f6918 /Biz/Bild.hs
parent3887308a3ad006b487f180d29b495c65294a7d26 (diff)
Refactor bild logic a bit
This should make it easier to add nix builds, which is the next task. I need to move some files and nix code around so that I have e.g. Que/Prod.nix as the actual full 'build.os' expression.
Diffstat (limited to 'Biz/Bild.hs')
-rw-r--r--Biz/Bild.hs311
1 files changed, 155 insertions, 156 deletions
diff --git a/Biz/Bild.hs b/Biz/Bild.hs
index ee0d78a..92054f9 100644
--- a/Biz/Bild.hs
+++ b/Biz/Bild.hs
@@ -3,140 +3,141 @@
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}
-{- | A general purpose build tool.
-
-Not all of the below design is implemented. Currently:
-
-- with a nix build, results are linked in _/bild/nix/<target>
-- for a dev build, results are stored in _/bild/dev/<target>
-
------------------------------------------------------------------------------
-
-== Design constraints
-
- * only input is a namespace, no subcommands, no packages
-
- * no need to write specific build rules
-
- * one rule for hs, one for rs, one for scm, and so on
-
- * no need to distinguish between exe and lib, just have a single output
-
- * never concerned with deployment/packaging - leave that to another tool
- (scp? tar?)
-
-== Features
-
- * namespace maps to filesystem
-
- * no need for `bild -l` for listing available targets. Use `ls` or `tree`
-
- * you build namespaces, not files/modules/packages/etc
-
- * namespace maps to language modules
-
- * build settings can be set in the file comments
-
- * pwd is always considered the the source directory, no `src` vs `doc` etc.
-
- * build methods automaticatly detected with file extensions
-
- * flags modify the way to interact with the build, some ideas:
-
- * -s = jump into a shell and/or repl
-
- * -p = turn on profiling
-
- * -t = limit build by type (file extension)
-
- * -e = exclude some regex in the ns tree
-
- * -o = optimize level
-
-== Example Commands
-
-> bild [-spt] <target..>
-
-The general scheme is to build the things described by the targets. A target
-is a namespace. You can list as many as you want, but you must list at least
-one. It could just be `.` for the current directory. Build outputs will go
-into the _/bild directory in the root of the project.
-
-> bild a.b
-
-Or `bild a/b`. This shows building a file at ./a/b.hs, this will translate to
-something like `ghc --make A.B`.
-
-> bild -s <target>
-
-Starts a repl/shell for target.
- - if target.hs, load ghci
- - if target.scm, load scheme repl
- - if target.clj, load a clojure repl
- - if target.nix, load nix-shell
- - and so on.
-
-> bild -p <target>
-
-build target with profiling (if available)
-
-> bild -t nix target
-
-only build target.nix, not target.hs and so on (in the case of multiple
-targets with the same name but different extension).
-
-== Build Metadata
-
-Metadata is set in the comments with a special syntax. For third-party deps,
-we list the deps in comments in the target file, like:
-
-> -- : dep aeson
-
-The output executable is named with:
-
-> -- : exe my-program
-
-or
-
-> -- : exe my-ap.js
-
-When multiple compilers are possible (e.g. ghc vs ghcjs) we chose ghcjs when
-the target exe ends in .js.
-
-This method of setting metadata in the module comments works pretty well,
-and really only needs to be done in the entrypoint module anyway.
-
-Local module deps are included by just giving the repo root to the compiler
-that bild calls out to.
-
-== Questions
-
- * how to handle multiple output formats?
-
- * e.g. that ghcjs and ghc take the same input files...
-
- * say you have a .md file, you want to bild it to pdf, html, and more. What
- do?
-
--}
+-- | A general purpose build tool.
+--
+-- Not all of the below design is implemented. Currently:
+--
+-- - with a nix build, results are linked in _/bild/nix/<target>
+-- - for a dev build, results are stored in _/bild/dev/<target>
+--
+-- -----------------------------------------------------------------------------
+--
+-- == Design constraints
+--
+-- * only input is a namespace, no subcommands, no packages
+--
+-- * no need to write specific build rules
+--
+-- * one rule for hs, one for rs, one for scm, and so on
+--
+-- * no need to distinguish between exe and lib, just have a single output
+--
+-- * never concerned with deployment/packaging - leave that to another tool
+-- (scp? tar?)
+--
+-- == Features
+--
+-- * namespace maps to filesystem
+--
+-- * no need for `bild -l` for listing available targets. Use `ls` or `tree`
+--
+-- * you build namespaces, not files/modules/packages/etc
+--
+-- * namespace maps to language modules
+--
+-- * build settings can be set in the file comments
+--
+-- * pwd is always considered the the source directory, no `src` vs `doc` etc.
+--
+-- * build methods automaticatly detected with file extensions
+--
+-- * flags modify the way to interact with the build, some ideas:
+--
+-- * -s = jump into a shell and/or repl
+--
+-- * -p = turn on profiling
+--
+-- * -t = limit build by type (file extension)
+--
+-- * -e = exclude some regex in the ns tree
+--
+-- * -o = optimize level
+--
+-- == Example Commands
+--
+-- > bild [-spt] <target..>
+--
+-- The general scheme is to build the things described by the targets. A target
+-- is a namespace. You can list as many as you want, but you must list at least
+-- one. It could just be `.` for the current directory. Build outputs will go
+-- into the _/bild directory in the root of the project.
+--
+-- > bild A/B.hs
+--
+-- This will build the file at ./A/B.hs, this will translate to something like
+-- `ghc --make A.B`.
+--
+-- > bild -s <target>
+--
+-- Starts a repl/shell for target.
+-- - if target.hs, load ghci
+-- - if target.scm, load scheme repl
+-- - if target.clj, load a clojure repl
+-- - if target.nix, load nix-shell
+-- - and so on.
+--
+-- > bild -p <target>
+--
+-- build target with profiling (if available)
+--
+-- > bild -t nix target
+--
+-- only build target.nix, not target.hs and so on (in the case of multiple
+-- targets with the same name but different extension).
+--
+-- == Build Metadata
+--
+-- Metadata is set in the comments with a special syntax. For third-party deps,
+-- we list the deps in comments in the target file, like:
+--
+-- > -- : dep aeson
+--
+-- The output executable is named with:
+--
+-- > -- : exe my-program
+--
+-- or
+--
+-- > -- : exe my-ap.js
+--
+-- When multiple compilers are possible (e.g. ghc vs ghcjs) we chose ghcjs when
+-- the target exe ends in .js.
+--
+-- This method of setting metadata in the module comments works pretty well,
+-- and really only needs to be done in the entrypoint module anyway.
+--
+-- Local module deps are included by just giving the repo root to the compiler
+-- that bild calls out to.
+--
+-- == Questions
+--
+-- * how to handle multiple output formats?
+--
+-- * e.g. that ghcjs and ghc take the same input files...
+--
+-- * say you have a .md file, you want to bild it to pdf, html, and more. What
+-- do?
module Biz.Bild where
-import Alpha hiding ((<.>), sym)
+import Alpha hiding (sym, (<.>))
import qualified Data.Char as Char
import qualified Data.List as List
+import qualified Data.String as String
import qualified Data.Text as Text
import qualified System.Directory as Dir
import qualified System.Environment as Env
import qualified System.Exit as Exit
import System.FilePath ((</>))
+import qualified System.FilePath as File
import qualified System.Process as Process
import qualified Text.Regex.Applicative as Regex
import qualified Prelude
main :: IO ()
-main = Env.getArgs /> head >>= \case
- Nothing -> Exit.die "usage: bild <target>"
- Just target -> analyze target >>= build
+main =
+ Env.getArgs /> head >>= \case
+ Nothing -> Exit.die "usage: bild <target>"
+ Just target -> analyze target >>= build
type Namespace = String
@@ -147,50 +148,48 @@ type Exe = String
data Compiler = Ghc | Ghcjs | Nix
deriving (Show)
-data Target
- = Target
- { -- | Output executable name
- exe :: Exe,
- -- | Fully qualified namespace partitioned by '.'
- namespace :: Namespace,
- -- | Absolute path to file
- path :: FilePath,
- -- | Parsed/detected dependencies
- deps :: [Dep],
- -- | Which compiler should we use?
- compiler :: Compiler
- }
+data Target = Target
+ { -- | Output executable name
+ exe :: Exe,
+ -- | Fully qualified namespace partitioned by '.'
+ namespace :: Namespace,
+ -- | Absolute path to file
+ path :: FilePath,
+ -- | Parsed/detected dependencies
+ deps :: [Dep],
+ -- | Which compiler should we use?
+ compiler :: Compiler
+ }
deriving (Show)
analyze :: String -> IO Target
analyze s = do
root <- Env.getEnv "BIZ_ROOT"
cwd <- Dir.getCurrentDirectory
- -- this is a hack to support multiple file types. Ideally we would just detect
- -- which file extensions exist, then return [Target], which can then be built
- -- in parallel
- let path = cwd </> reps "." "/" s |> reps "/hs" ".hs" |> reps "/nix" ".nix"
- content <- lines </ Prelude.readFile path
- let exe = content /> Regex.match metaExe |> catMaybes |> head |> require "exe"
- return
- Target
- { namespace =
- require "namespace"
- <| path
- |> reps root ""
- |> reps ".hs" ""
- |> reps ".nix" ""
- |> reps "/" "."
- |> List.stripPrefix "."
- >>= Regex.match metaNamespace,
+ let path = cwd </> s
+ case File.takeExtension path of
+ ".hs" -> do
+ content <- String.lines </ Prelude.readFile path
+ let exe = content /> Regex.match metaExe |> catMaybes |> head |> require "exe"
+ let compiler = if ".js" `List.isSuffixOf` exe then Ghcjs else Ghc
+ return Target {
+ namespace = require "namespace"
+ <| path
+ |> reps root ""
+ |> File.dropExtension
+ |> reps "/" "."
+ |> List.stripPrefix "."
+ >>= Regex.match metaNamespace,
deps = content /> Regex.match metaDep |> catMaybes,
- compiler =
- if ".hs" `List.isSuffixOf` path
- then if ".js" `List.isSuffixOf` exe then Ghcjs else Ghc
- else Nix,
..
}
+ ".nix" -> return Target {
+ namespace = s, path = path, deps = [], compiler = Nix, exe = ""
+ }
+
+ e -> panic <| "bild does not know this extension: " <> Text.pack e
+
build :: Target -> IO ()
build Target {..} = do
root <- Env.getEnv "BIZ_ROOT"
@@ -265,8 +264,8 @@ require s Nothing = panic <| s <> " not found"
reps :: String -> String -> String -> String
reps a b s@(x : xs) =
if isPrefixOf a s
- then-- then, write 'b' and replace jumping 'a' substring
+ then -- then, write 'b' and replace jumping 'a' substring
b ++ reps a b (drop (length a) s)
- else-- then, write 'x' char and try to replace tail string
+ else -- then, write 'x' char and try to replace tail string
x : reps a b xs
reps _ _ [] = []