hledger/Shake.hs

343 lines
13 KiB
Haskell
Raw Normal View History

#!/usr/bin/env stack
2018-03-31 04:43:28 +03:00
{- stack exec
--verbosity=info
--package base-prelude
--package directory
--package extra
--package safe
--package shake
--package time
ghc
-}
{-
2016-10-28 21:15:10 +03:00
One of two project scripts files (Makefile, Shake.hs).
This one provides a stronger programming language and more
platform independence than Make. It will build needed packages (above)
on first run and whenever the resolver in stack.yaml changes.
To minimise such startup delays, and reduce sensitivity to git checkout,
2016-12-29 22:21:32 +03:00
compiling is recommended; run the script in interpreted mode to do that.
2016-10-28 21:15:10 +03:00
It requires stack (https://haskell-lang.org/get-started) and
auto-installs the packages above. Also, some rules require:
2016-10-29 19:45:59 +03:00
- runhaskell
2016-10-28 21:15:10 +03:00
- groff
- m4
- makeinfo
2016-10-29 19:45:59 +03:00
- git
- patch
2016-10-28 21:15:10 +03:00
2016-12-31 01:32:43 +03:00
Usage: see below. Also:
$ find hledger-lib hledger | entr ./Shake website # rebuild web files on changes in these dirs
2016-10-28 21:15:10 +03:00
Shake wishes:
just one shake import
wildcards in phony rules
multiple individually accessible wildcards
not having to write :: Action ExitCode after a non-final cmd
-}
{-# LANGUAGE PackageImports, ScopedTypeVariables #-}
import Prelude ()
import "base-prelude" BasePrelude
2018-03-31 04:43:28 +03:00
import "directory" System.Directory as S (getDirectoryContents)
import "extra" Data.List.Extra
import "safe" Safe
import "shake" Development.Shake
import "shake" Development.Shake.FilePath
import "time" Data.Time
2016-10-28 21:15:10 +03:00
usage = unlines
["Usage:"
2016-12-29 22:21:32 +03:00
,"./Shake.hs # compile this script"
2016-10-28 21:15:10 +03:00
,"./Shake # show commands"
2017-08-01 01:53:34 +03:00
,"./Shake manuals # generate the txt/man/info manuals"
,"./Shake website # generate the html manuals and website"
-- ,"./Shake manpages # generate nroff files for man"
-- ,"./Shake txtmanpages # generate text man pages for embedding"
-- ,"./Shake infomanpages # generate info files for info"
-- ,"./Shake webmanpages # generate individual web man pages for hakyll"
-- ,"./Shake webmanall # generate all-in-one web manual for hakyll"
,"./Shake site/doc/VER/.snapshot # generate and save a versioned web site snapshot"
2016-10-28 21:15:10 +03:00
,"./Shake all # generate everything"
,"./Shake clean # clean generated files"
,"./Shake Clean # clean harder"
,"./Shake --help # show options, eg --color"
]
pandoc = "stack exec -- pandoc" -- pandoc from project's stackage snapshot
pandocSiteFilter = "tools/pandoc-site"
hakyllstd = "site/hakyll-std/hakyll-std"
2016-04-19 03:54:55 +03:00
makeinfo = "makeinfo"
-- nroff = "nroff"
groff = "groff"
dropDirectory2 = dropDirectory1 . dropDirectory1
main = do
shakeArgs
shakeOptions{
2016-04-13 06:32:01 +03:00
shakeVerbosity=Loud
-- ,shakeReport=[".shake.html"]
} $ do
want ["help"]
phony "help" $ liftIO $ putStrLn usage
2016-12-29 22:21:32 +03:00
-- phony "compile" $ need ["Shake"]
--
-- "Shake" %> \out -> do
-- need [out <.> "hs"]
-- unit $ cmd "./Shake.hs" -- running as stack script installs deps and compiles
2016-12-29 22:21:32 +03:00
-- putLoud "You can now run ./Shake instead of ./Shake.hs"
2017-08-01 01:53:34 +03:00
phony "all" $ need ["manuals", "website"]
2017-08-01 01:53:34 +03:00
-- manuals
let
manpageNames = [ -- in suggested reading order
"hledger.1"
,"hledger-ui.1"
,"hledger-web.1"
,"hledger-api.1"
,"hledger_journal.5"
,"hledger_csv.5"
2016-04-13 07:10:02 +03:00
,"hledger_timeclock.5"
,"hledger_timedot.5"
]
2017-01-26 17:40:50 +03:00
2017-12-07 05:16:35 +03:00
manualNames = map manpageNameToManualName manpageNames
-- hledger.1 -> hledger, hledger_journal.5 -> hledger_journal
manpageNameToManualName = dropNumericSuffix
where
dropNumericSuffix s = reverse $
case reverse s of
c : '.' : cs | isDigit c -> cs
cs -> cs
-- hledger -> hledger.1, hledger_journal -> hledger_journal.5
manualNameToManpageName s
| '_' `elem` s = s <.> "5"
| otherwise = s <.> "1"
-- manuals m4 source; may include other source files (hledger/hledger.m4.md)
m4manpages = [manualDir m </> m <.> "m4.md" | m <- manualNames]
-- manuals rendered to nroff, ready for man (hledger/hledger.1)
nroffmanpages = [manpageDir m </> m | m <- manpageNames]
2017-01-26 17:40:50 +03:00
2017-12-07 05:16:35 +03:00
-- manuals rendered to text, ready for embedding (hledger/hledger.txt)
txtmanpages = [manualDir m </> m <.> "txt" | m <- manualNames]
2017-01-26 17:40:50 +03:00
2017-12-07 05:16:35 +03:00
-- manuals rendered to info, ready for info (hledger/hledger.info)
infomanpages = [manualDir m </> m <.> "info" | m <- manualNames]
2017-01-26 17:40:50 +03:00
-- manuals rendered to markdown, ready for web output by pandoc (site/hledger.md)
webmanpages = ["site" </> manpageNameToUri m <.> "md" | m <- manpageNames]
-- versions of documentation (excluding 0.27)
docversions = [ "1.0" , "1.1" , "1.2" , "1.3" , "1.4" , "1.5" , "1.9" ]
-- manuals rendered to html by pandoc
webhtmlpages
= map (normalise . ("site/_site" </>))
$ ( [ prefix </> manpageNameToUri mPage <.> "html"
| prefix <- "" : [ "doc" </> v | v <- docversions ]
, mPage <- manpageNames
]
++ [ mPage <.> "html"
| mPage <- [ "contributors" , "download" , "ledgertips" , "index" , "intro" , "release-notes" ]
]
++ [ prefix </> "manual" <.> "html"
| prefix <- "" : "doc/0.27" : [ "doc" </> v | v <- docversions ]
]
)
2017-01-26 17:40:50 +03:00
-- manuals rendered to markdown and combined, ready for web output by hakyll
webmanall = "site/manual.md"
-- file extensions which should just be copied directly over to the website
webcopyfileexts = ["png", "gif", "cur", "js", "css", "eot", "ttf", "woff", "svg"]
-- hledger.1 -> hledger/doc, hledger_journal.5 -> hledger-lib/doc
manpageDir m
2017-12-07 05:16:35 +03:00
| '_' `elem` m = "hledger-lib"
| otherwise = dropExtension m
-- hledger -> hledger, hledger_journal -> hledger-lib
manualDir m
| '_' `elem` m = "hledger-lib"
| otherwise = m
-- hledger.1 -> hledger, hledger_journal.5 -> journal
manpageNameToUri m | "hledger_" `isPrefixOf` m = dropExtension $ drop 8 m
| otherwise = dropExtension m
-- hledger -> hledger.1, journal -> hledger_journal.5
manpageUriToName u | "hledger" `isPrefixOf` u = u <.> "1"
| otherwise = "hledger_" ++ u <.> "5"
2017-08-01 01:53:34 +03:00
phony "manuals" $ do
need $
nroffmanpages
2016-04-19 03:54:55 +03:00
++ infomanpages
++ txtmanpages
-- man pages
-- use m4 and pandoc to process macros, filter content, and convert to nroff suitable for man output
phony "manpages" $ need nroffmanpages
2017-12-07 05:16:35 +03:00
nroffmanpages |%> \out -> do -- hledger/hledger.1
let src = manpageNameToManualName out <.> "m4.md"
lib = "doc/lib.m4"
dir = takeDirectory out
tmpl = "doc/manpage.nroff"
-- assume all other m4 files in dir are included by this one XXX not true in hledger-lib
deps <- liftIO $ filter (/= src) . filter (".m4.md" `isSuffixOf`) . map (dir </>) <$> S.getDirectoryContents dir
need $ src : lib : tmpl : deps
cmd Shell
"m4 -P -DMAN -I" dir lib src "|"
pandoc "-f markdown -s --template" tmpl
"--lua-filter tools/pandoc-drop-html-blocks.lua"
"--lua-filter tools/pandoc-drop-html-inlines.lua"
"--lua-filter tools/pandoc-drop-links.lua"
"-o" out
-- render man page nroffs to fixed-width text for embedding in executables, with nroff
phony "txtmanpages" $ need txtmanpages
2017-12-07 05:16:35 +03:00
txtmanpages |%> \out -> do -- hledger/hledger.txt
2017-12-07 07:53:36 +03:00
let src = manualNameToManpageName $ dropExtension out
need [src]
cmd Shell groff "-t -e -mandoc -Tascii" src "| col -bx >" out -- http://www.tldp.org/HOWTO/Man-Page/q10.html
2016-04-19 03:54:55 +03:00
-- use m4 and pandoc to process macros, filter content, and convert to info, suitable for info viewing
phony "infomanpages" $ need infomanpages
2017-12-07 05:16:35 +03:00
infomanpages |%> \out -> do -- hledger/hledger.info
2016-04-19 03:54:55 +03:00
let src = out -<.> "m4.md"
lib = "doc/lib.m4"
dir = takeDirectory out
-- assume all other m4 files in dir are included by this one XXX not true in hledger-lib
deps <- liftIO $ filter (/= src) . filter (".m4.md" `isSuffixOf`) . map (dir </>) <$> S.getDirectoryContents dir
need $ src : lib : deps
2016-04-19 03:54:55 +03:00
cmd Shell
"m4 -P -I" dir lib src "|"
pandoc "-f markdown"
"--lua-filter tools/pandoc-drop-html-blocks.lua"
"--lua-filter tools/pandoc-drop-html-inlines.lua"
"--lua-filter tools/pandoc-drop-links.lua"
2016-04-19 03:54:55 +03:00
"-t texinfo |"
makeinfo "--force --no-split -o" out
-- web site
phony "website" $ do
need $ [ "website-copy" , "website-render" ]
-- website also links to old manuals, which are generated manually
-- with ./Shake websnapshot and committed
-- TODO: when pandoc filters are missing, ./Shake website complains about them before building them
2017-03-27 15:09:42 +03:00
-- ./Shake.hs && ./Shake Clean && (cd site/hakyll-std; ./hakyll-std.hs) && ./Shake website
-- use m4 and pandoc to process macros and filter content, leaving markdown suitable for web output
phony "webmanpages" $ need webmanpages
webmanpages |%> \out -> do -- site/hledger.md
2017-12-07 05:16:35 +03:00
let manpage = manpageUriToName $ dropExtension $ takeFileName out -- hledger
manual = manpageNameToManualName manpage
dir = manpageDir manpage
src = dir </> manual <.> "m4.md"
lib = "doc/lib.m4"
2017-12-07 05:16:35 +03:00
heading = let h = manual
2016-04-09 23:36:48 +03:00
in if "hledger_" `isPrefixOf` h
then drop 8 h ++ " format"
else h
-- assume all other m4 files in dir are included by this one XXX not true in hledger-lib
deps <- liftIO $ filter (/= src) . filter (".m4.md" `isSuffixOf`) . map (dir </>) <$> S.getDirectoryContents dir
need $ src : lib : deps
2016-04-09 23:36:48 +03:00
liftIO $ writeFile out $ "# " ++ heading ++ "\n\n"
cmd Shell
"m4 -P -DMAN -DWEB -I" dir lib src "|"
pandoc "-f markdown -t markdown-fenced_divs --atx-headers"
"--lua-filter tools/pandoc-demote-headers.lua"
2016-04-09 23:36:48 +03:00
">>" out
-- adjust and combine man page mds for single-page web output, using pandoc
phony "webmanall" $ need [ webmanall ]
webmanall %> \out -> do
need webmanpages
2017-01-27 02:25:06 +03:00
liftIO $ writeFile webmanall "* toc\n\n" -- # Big Manual\n\n -- TOC style is better without main heading,
forM_ webmanpages $ \f -> do -- site/hledger.md, site/journal.md
cmd Shell ("printf '\\n\\n' >>") webmanall :: Action ExitCode
cmd Shell "pandoc" f "-t markdown-fenced_divs --atx-headers"
"--lua-filter tools/pandoc-drop-toc.lua"
"--lua-filter tools/pandoc-demote-headers.lua"
">>" webmanall :: Action ExitCode
2017-01-26 17:40:50 +03:00
-- adjust and combine recipe mds for single-page web output, using pandoc
-- build the currently checked out web docs and save as a named snapshot
"site/doc/*/.snapshot" %> \out -> do
need [ webmanall ]
let snapshot = takeDirectory out
cmd Shell "mkdir -p" snapshot :: Action ExitCode
forM_ webmanpages $ \f -> do -- site/hledger.md, site/journal.md
cmd Shell "cp" f (snapshot </> takeFileName f) :: Action ExitCode
cmd Shell "cp" "site/manual.md" snapshot :: Action ExitCode
cmd Shell "cp -r site/images" snapshot :: Action ExitCode
cmd Shell "touch" out -- :: Action ExitCode
phony "website-copy" $ do
orig_files <- getDirectoryFiles "site" (map ("//*" <.>) webcopyfileexts)
need [ "site/_site" </> file
| file <- "files/README" : orig_files
, not ("_site//*" ?== file)
]
phony "website-render" $ do
need webhtmlpages
pandocSiteFilter %> \out -> do
let source = out <.> "hs"
need [source]
cmd "stack --stack-yaml=stack-ghc8.2.yaml ghc --package pandoc -- -o" out source
2018-04-28 06:23:14 +03:00
"site/_site/files/README" : [ "site/_site//*" <.> ext | ext <- webcopyfileexts ] |%> \out -> do
let input = "site" </> dropDirectory2 out
copyFile' input out
"site/_site//*.html" %> \out -> do
let source = "site" </> dropDirectory2 out -<.> "md"
pageTitle = takeBaseName out
template = "site/site.tmpl"
siteRoot = if "site/_site/doc//*" ?== out then "../.." else "."
need [source, template, pandocSiteFilter]
cmd Shell pandoc "--from markdown --to html" source
"--template" template
("--metadata=siteRoot:" ++ siteRoot)
("--metadata=title:" ++ pageTitle)
"--filter" pandocSiteFilter
"--output" out
2016-04-06 01:40:59 +03:00
-- cleanup
phony "clean" $ do
putNormal "Cleaning generated files"
removeFilesAfter "." webmanpages
removeFilesAfter "." [webmanall]
phony "Clean" $ do
need ["clean"]
putNormal "Cleaning all site generated files"
removeFilesAfter "site" ["_*"]
2016-10-29 19:45:59 +03:00
putNormal "Cleaning object files" -- also forces rebuild of executables
removeFilesAfter "tools" ["*.o","*.p_o","*.hi"]
removeFilesAfter "site" ["*.o","*.p_o","*.hi"]
putNormal "Cleaning shake build files"
2016-04-13 06:32:01 +03:00
removeFilesAfter ".shake" ["//*"]