1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE StrictData #-}
4 {-# OPTIONS_GHC -Wno-missing-signatures #-}
5 module Textphile.CLI.Compile where
7 import Control.Applicative (Applicative(..), Alternative(..))
8 import Control.Monad (Monad(..), forM_, unless, when)
9 import Control.Monad.Trans.Except (runExcept)
10 import Control.Monad.Trans.Maybe (MaybeT(..))
11 import Control.Monad.Trans.State.Strict (runState)
13 import Data.Either (Either(..))
14 import Data.Eq (Eq(..))
15 import Data.Foldable as Foldable (Foldable(..))
16 import Data.Function (($), (.), id, flip)
17 import Data.Functor ((<$>))
18 import Data.Maybe (Maybe(..), fromMaybe)
19 import Data.Ord (Ord(..))
20 import Data.Proxy (Proxy(..))
21 import Data.Semigroup (Semigroup(..))
22 import Data.Text (Text)
23 import Prelude (error)
24 import Symantic.CLI as CLI
25 import System.FilePath ((-<.>), (</>))
26 import System.IO (IO, FilePath)
27 import Text.Show (Show(..))
28 import qualified Data.ByteString as BS
29 import qualified Data.List as List
30 import qualified Data.Text as Text
31 import qualified Data.Text.Lazy as TL
32 import qualified Data.Text.Lazy.IO as TL
33 import qualified Data.TreeSeq.Strict as Tree
34 import qualified Paths_textphile as Textphile
35 import qualified Symantic.Document as Doc
36 import qualified System.Directory as IO
37 import qualified System.Environment as Env
38 import qualified System.FilePath as FP
39 import qualified System.FilePath as FilePath
40 import qualified System.IO as IO
41 import qualified Text.Blaze.DTC as Blaze.DTC
42 import qualified Text.Blaze.HTML5 as Blaze.HTML5
43 import qualified Text.Blaze.Renderer.Utf8 as Blaze
44 import qualified Text.Blaze.Utils as Blaze
45 import qualified Text.Megaparsec as P
46 import qualified Textphile.DTC.Read.TCT as DTC
47 import qualified Textphile.DTC.Sym as DTC
48 import qualified Textphile.DTC.Write.HTML5 as DTC
49 import qualified Textphile.DTC.Write.XML as DTC
50 import qualified Textphile.TCT as TCT
51 import qualified Textphile.TCT.Write.HTML5 as TCT
52 import qualified Textphile.TCT.Write.Plain as TCT
53 import qualified Textphile.TCT.Write.XML as TCT
54 import qualified Textphile.Utils as FS
56 import Textphile.CLI.Lang
57 import Textphile.CLI.Utils
59 data Cfg_Compile = Cfg_Compile
60 { cfg_compile_dump_tct :: Bool
61 , cfg_compile_dump_xml :: Bool
62 , cfg_compile_dump_deps :: Bool
65 data Cfg_Compile_HTML5
67 { cfg_compile_html5_output_css :: Maybe FP.FilePath
68 , cfg_compile_html5_output_js :: Maybe FP.FilePath
69 , cfg_compile_html5_dump_dtc :: Bool
73 helps l10n_help_command_compile $
93 helps l10n_help_opt_output_css $
94 optionalTag "output-css" $
97 helps l10n_help_opt_output_js $
98 optionalTag "output-js" $
116 helps l10n_help_opt_input $
117 var @FP.FilePath "INPUT"
120 cfg_global@Cfg_Global{..}
121 cfg_compile@Cfg_Compile{..} =
122 run_compile_html5 :!:
125 run_compile_dtc cfg_compile_input cfg_compile_output = do
126 outputInfo cfg_global $ "compiling... " <> Doc.from cfg_compile_input
127 TCT.readTCT cfg_compile_input >>= \case
128 Left err -> error $ show err
130 when cfg_compile_dump_tct $ do
131 FS.writeFile (cfg_compile_output-<.>"tct.dump") $
132 TL.pack $ Tree.prettyTrees tct
133 let xml = TCT.writeXML tct
134 when cfg_compile_dump_xml $ do
135 FS.writeFile (cfg_compile_output-<.>"xml.dump") $
136 TL.pack $ Tree.prettyTrees xml
137 case DTC.readDTC xml of
139 FS.removeFile $ cfg_compile_output-<.>"deps"
140 error $ List.unlines $ Foldable.toList $ P.parseErrorPretty <$> P.bundleErrors err
142 when cfg_compile_dump_deps $ do
143 writeDependencies cfg_compile cfg_compile_input tct
144 when cfg_compile_dump_xml $ do
145 FS.writeFile (cfg_compile_output-<.>"dtc.dump") $
148 run_compile_html5 Cfg_Compile_HTML5{..} cfg_compile_input = do
149 let cfg_compile_output = cfg_compile_input-<.>"html5"
150 dtc <- run_compile_dtc cfg_compile_input cfg_compile_output
151 config_css <- installFile cfg_compile_html5_output_css $ "src"</>"style"</>"dtc-html5.css"
152 config_js <- installFile cfg_compile_html5_output_js $ "src"</>"style"</>"dtc-html5.js"
153 let conf = DTC.Config
156 , DTC.config_locale = cfg_global_lang
157 , DTC.config_generator = TL.fromStrict version
159 FS.withFile cfg_compile_output IO.WriteMode $ \h -> do
160 html <- DTC.writeHTML5 conf dtc
162 Blaze.HTML5.isInlinedElement
166 installFile out name = do
167 dataDir <- Textphile.getDataDir
168 let src = dataDir</>name
170 Nothing -> Right <$> FS.readFile src
171 Just "" -> return $ Left ""
173 IO.createDirectoryIfMissing True $ FilePath.takeDirectory dst
176 run_compile_xml cfg_compile_input = do
177 let cfg_compile_output = cfg_compile_input-<.>"xml"
178 dtc <- run_compile_dtc cfg_compile_input cfg_compile_output
179 FS.withFile cfg_compile_output IO.WriteMode $ \h ->
180 Blaze.prettyMarkupIO Blaze.DTC.isInlinedElement (BS.hPutStr h) $
181 DTC.writeXML cfg_global_lang dtc
183 writeDependencies :: Cfg_Compile -> FilePath -> TCT.Roots -> IO ()
184 writeDependencies Cfg_Compile{..} cfg_compile_input tct =
185 let dir = FilePath.takeDirectory cfg_compile_input in
186 FS.writeFile (cfg_compile_input-<.>"deps") $
187 foldMap (TL.pack . (("\n" <>) . FilePath.normalise))
188 ((dir </>) <$> TCT.dependencies tct) <>