packages feed

haste-compiler-0.3: src/ArgSpecs.hs

-- | All of hastec's command line arguments.
module ArgSpecs (argSpecs) where
import Args
import Haste.Config
import Haste.Environment
import Data.JSTarget.PP (debugPPOpts)
import Data.List (isSuffixOf)
import System.FilePath ((</>))

argSpecs :: [ArgSpec Config]
argSpecs = [
    ArgSpec { optName = "debug",
              updateCfg = \cfg _ -> cfg {ppOpts = debugPPOpts},
              info = "Output indented, fairly readable code, with all " ++
                     "external names included in comments."},
    ArgSpec { optName = "full-unicode",
              updateCfg = \cfg _ -> fullUnicode cfg,
              info = "Enable full Unicode support. Will bloat your output by "
                     ++ " 10-150 KB depending on usage and other options."},
    ArgSpec { optName = "dont-link",
              updateCfg = \cfg _ -> cfg {performLink = False},
              info = "Don't perform linking."},
    ArgSpec { optName = "libinstall",
              updateCfg = \cfg _ -> cfg {targetLibPath = jsmodDir,
                                         performLink   = False},
              info = "Install all compiled modules into the user's jsmod "
                     ++ "library rather than linking them together into a JS"
                     ++ "blob."},
    ArgSpec { optName = "opt-all",
              updateCfg = optAllSafe,
              info = "Enable all safe optimizations. "
                     ++ "Equivalent to -O2 --opt-google-closure --opt-whole-program."},
    ArgSpec { optName = "opt-all-unsafe",
              updateCfg = optAllUnsafe,
              info = "Enable all safe and unsafe optimizations. "
                     ++ "Equivalent to --opt-all --opt-unsafe-ints."},
    ArgSpec { optName = "opt-google-closure",
              updateCfg = updateClosureCfg,
              info = "Run the Google Closure compiler on the output. "
                   ++ "Use --opt-google-closure=foo.jar to hint that foo.jar "
                   ++ "is the Closure compiler."},
    ArgSpec { optName = "opt-google-closure-flag=",
              updateCfg = updateClosureFlags,
              info = "Add an extra flag for the Google Closure compiler to take. "
                   ++ "Use --opt-google-closure-flag='--language_in=ECMASCRIPT5_STRICT', "
                   ++ "to optimize programs to strict mode"},
    ArgSpec { optName = "opt-sloppy-tce",
              updateCfg = useSloppyTCE,
              info = "Allow the possibility that some tail recursion may not "
                     ++ "be optimized, to get slightly smaller code."},
    ArgSpec { optName = "opt-unsafe-ints",
              updateCfg = unsafeMath,
              info = "Enable all unsafe Int math optimizations. Equivalent to "
                     ++ "--opt-unsafe-mult --opt-vague-ints"},
    ArgSpec { optName = "opt-unsafe-mult",
              updateCfg = unsafeMul,
              info = "Use Javascript's built-in multiplication operator for "
                     ++ "fixed precision integer multiplication. This speeds "
                     ++ "up Int multiplication by a factor of at least four, "
                     ++ "but may give incorrect results when the product "
                     ++ "falls outside the interval [-2^52, 2^52]."},
    ArgSpec { optName = "opt-vague-ints",
              updateCfg = vagueInts,
              info = "Int math has 53 bits of precision, but gives incorrect "
                     ++ "results rather than properly wrapping around when "
                     ++ "those 53 bits are exceeded. Bitwise operations still "
                     ++ "only work on the lowest 32 bits. This option should "
                     ++ "give a substantial performance boost for Int math "
                     ++ "heavy code."},
    ArgSpec { optName = "opt-whole-program",
              updateCfg = enableWholeProgramOpts,
              info = "Perform optimizations over the whole program at link "
                     ++ "time. May significantly increase compilation time."},
    ArgSpec { optName = "out=",
              updateCfg = \cfg outfile -> cfg {outFile = \_ _ -> head outfile},
              info = "Write compiler output to <arg>."},
    ArgSpec { optName = "output-html",
              updateCfg = \cfg _ -> cfg {outputHTML = True},
              info = "Produce a skeleton HTML file containing the program."},
    ArgSpec { optName = "separate-namespace",
              updateCfg = \cfg _ -> cfg {wrapProg = True},
              info = "Wrap the program in its own namespace? "
                     ++ "Off by default since it may hurt performance."},
    ArgSpec { optName = "start=",
              updateCfg = \cfg str -> cfg {appStart = startCustom (head str)},
              info = "Specify how the Haste application will launch. Can be "
                     ++ "either asap, onload or a custom string "
                     ++ "containing the character sequence '%%', which will "
                     ++ "be replaced with the program's entry point function. "
                     ++ "The default is onload."
                     ++ " Note that '%%' will be replaced by the main function"
                     ++ " itself, not a call to it. Thus, in order to actually"
                     ++ " call the function, one would use '%%()'."},
    ArgSpec { optName = "trace-primops",
              updateCfg = \cfg _ -> cfg {tracePrimops = True,
                                         rtsLibs = debugLib : rtsLibs cfg},
              info = "Turn on run-time tracing of primops."},
    ArgSpec { optName = "verbose",
              updateCfg = \cfg _ -> cfg {verbose = True},
              info = "Display even the most obnoxious warnings."},
    ArgSpec { optName = "with-js=",
              updateCfg = \cfg args -> cfg {jsExternals = args},
              info = "Comma-separated list of .js files to include in the "
                   ++ "final JS bundle."}
  ]

-- | Compose two config update functions. Be careful about using any args with
--   them though!
(|||) :: (Config -> [String] -> Config)
     -> (Config -> [String] -> Config)
     -> (Config -> [String] -> Config)
a ||| b = \cfg args -> b (a cfg args) args

-- | Don't wrap Ints.
vagueInts :: Config -> [String] -> Config
vagueInts cfg _ = cfg {wrapIntMath = id}

-- | Use fast but unsafe multiplication.
unsafeMul :: Config -> [String] -> Config
unsafeMul cfg _ = cfg {multiplyIntOp = fastMultiply}

-- | Enable all unsafe math ops. Remember to update the info text when changing
--   this!
unsafeMath :: Config -> [String] -> Config
unsafeMath = vagueInts ||| unsafeMul

-- | Enable all optimizations, both safe and unsafe.
optAllUnsafe :: Config -> [String] -> Config
optAllUnsafe = optAllSafe ||| unsafeMath ||| enableWholeProgramOpts

-- | Enable all safe optimizations.
optAllSafe :: Config -> [String] -> Config
optAllSafe = updateClosureCfg

-- | Set the path to the Closure compiler.jar to use.
updateClosureCfg :: Config -> [String] -> Config
updateClosureCfg cfg ['=':arg] =
  cfg {useGoogleClosure = Just arg}
updateClosureCfg cfg _ =
  cfg {useGoogleClosure = Just closureCompiler}

-- | Add flags for Google Closure to use
updateClosureFlags :: Config -> [String] -> Config
updateClosureFlags cfg args = cfg {
  useGoogleClosureFlags = useGoogleClosureFlags cfg ++ args}

-- | Enable optimizations over the entire program.
enableWholeProgramOpts :: Config -> [String] -> Config
enableWholeProgramOpts cfg _ = cfg {wholeProgramOpts = True}

-- | Enable sloppy TCE; see Config for more info.
useSloppyTCE :: Config -> [String] -> Config
useSloppyTCE cfg _ = cfg {sloppyTCE = True}

-- | Save some space and performance by using degenerate implementations of
--   the Unicode functions.
fullUnicode :: Config -> Config
fullUnicode cfg =
    cfg {rtsLibs = unicode : filter (not . (cheap `isSuffixOf`)) libs}
  where
    libs = rtsLibs cfg
    unicode = jsDir </> "unicode.js"
    cheap = jsDir </> "cheap-unicode.js"