sphinx-scons / SConstruct

This is a generic SCons script for running Sphinx (

Type 'scons -h' for help.  This prints the available build targets on your
system, and the configuration options you can set.

If you set the 'cache' option, the option settings are cached into a file
called '.sconsrc-sphinx' in the current directory.  When running
subsequently, this file is reread.  A file with this name is also read from
your home directory, if it exists, so you can put global settings there.

The script looks into your '' file for information about the
project.  This is used in various places (e.g., to print the introductory
message, and create package files).

Here's some examples.  To build HTML docs:

   scons html

To create a package containing HTML and PDF docs, remembering the 'install'

   scons install=html,pdf cache=True package

To clean up everything:

   scons -c all

# Script info.
__author__  = "Glenn Hutchings"
__email__   = ""
__url__     = ""
__license__ = "BSD"
__version__ = "0.4"

import sys, os

# Build targets.
targets = (
    ("html",      "make standalone HTML files"),
    ("dirhtml",   "make HTML files named index.html in directories"),
    ("pickle",    "make pickle files"),
    ("json",      "make JSON files"),
    ("htmlhelp",  "make HTML files and a HTML help project"),
    ("qthelp",    "make HTML files and a qthelp project"),
    ("devhelp",   "make HTML files and a GNOME DevHelp project"),
    ("epub",      "make HTML files and an EPUB file for bookreaders"),
    ("latex",     "make LaTeX sources"),
    ("text",      "make text file for each RST file"),
    ("pdf",       "make PDF file from LaTeX sources"),
    ("ps",        "make PostScript file from LaTeX sources"),
    ("dvi",       "make DVI file from LaTeX sources"),
    ("changes",   "make an overview over all changed/added/deprecated items"),
    ("linkcheck", "check all external links for integrity"),
    ("doctest",   "run all doctests embedded in the documentation if enabled"),

# LaTeX builders.
latex_builders = {"pdf": "PDF", "ps": "PostScript", "dvi": "DVI"}

# List of target names.
targetnames = [name for name, desc in targets]

# Configuration cache filename.
cachefile = ".sconsrc-sphinx"

# User cache file.
homedir = os.environ["HOME"]
usercache = os.path.join(homedir, cachefile)

# Configuration options.
config = Variables([usercache, cachefile], ARGUMENTS)

    EnumVariable("default", "default build target", "html", targetnames),
    PathVariable("config", "sphinx configuration file", ""),
    PathVariable("srcdir", "source directory", ".",
    PathVariable("builddir", "build directory", "build",
    PathVariable("doctrees", "place to put doctrees", None,
    EnumVariable("paper", "LaTeX paper size", None,
                 ["a4", "letter"], ignorecase = False),
    ("tags", "comma-separated list of 'only' tags", None),
    ("builder", "program to run to build things", "sphinx-build"),
    ("opts", "extra builder options to use", None),
    ListVariable("install", "targets to install", ["html"], targetnames),
    PathVariable("instdir", "installation directory", "/usr/local/doc",
    EnumVariable("pkgtype", "package type to build with 'scons package'",
                 "zip", ["zip", "targz", "tarbz2"], ignorecase = False),
    BoolVariable("cache", "whether to cache settings in %s" % cachefile, False),
    BoolVariable("debug", "debugging flag", False),

# Create a new environment, inheriting PATH to find builder program.  Also
# force LaTeX instead of TeX, since the .tex file won't exist at the right
# time to check which one to use.
env = Environment(ENV = {"PATH" : os.environ["PATH"]},
                  TEX = "latex", PDFTEX = "pdflatex",
                  tools = ['default', 'packaging'],
                  variables = config)

# Get configuration values from environment.
sphinxconf = env["config"]
builder = env["builder"]
default = env["default"]

srcdir = env["srcdir"]
builddir = env["builddir"]
doctrees = env.get("doctrees", os.path.join(builddir, "doctrees"))

cache = env["cache"]
debug = env["debug"]

options = env.get("opts", None)
paper = env.get("paper", None)
tags = env.get("tags", None)

instdir = env["instdir"]
install = env["install"]
pkgtype = env["pkgtype"]

# Dump internals if debugging.
if debug:
    print "Environment:"
    print env.Dump()

# Get parameters from Sphinx config file.
sphinxparams = {}
execfile(sphinxconf, sphinxparams)

project = sphinxparams["project"]
release = sphinxparams["release"]
copyright = sphinxparams["copyright"]

    texfilename = sphinxparams["latex_documents"][0][1]
except KeyError:
    texfilename = None

name2tag = lambda name: name.replace(" ", "-").strip("()")
project_tag = name2tag(project)
release_tag = name2tag(release)
package_tag = project_tag.lower() + "-" + release_tag.lower()

# Build project description string.
description = "%(project)s, release %(release)s, " \
               "copyright %(copyright)s" % locals()

Help(description + "\n\n")
help_format = "   %-10s  %s\n"

# Print banner if required.
if not any(map(GetOption, ("silent", "clean", "help"))):
    print "This is", description

# Build sphinx command-line options.
opts = []

if tags:
    opts.extend(["-t %s" % tag for tag in tags.split(",")])

if paper:
    opts.append("-D latex_paper_size=%s" % paper)

if options:

options = " ".join(opts)

# Build Sphinx command template.
sphinxcmd = """
%(builder)s -b %(name)s -d %(doctrees)s %(options)s %(srcdir)s %(targetdir)s

# Set up LaTeX input builder if required.
if texfilename:
    latexdir = Dir("latex", builddir)
    texinput = File(texfilename, latexdir)
    env.SideEffect(texinput, "latex")

# Add build targets.
Help("Build targets:\n\n")

for name, desc in targets:
    target = Dir(name, builddir)
    targetdir = str(target)

    if name not in latex_builders:
        # Standard Sphinx target.
        env.Command(name, sphinxconf, sphinxcmd % locals())
        env.Alias(target, name)
    elif texinput:
        # Target built from LaTeX sources.
            buildfunc = getattr(env, latex_builders[name])
        except AttributeError:

        filename = project_tag + "." + name
        outfile = File(filename, latexdir)

        buildfunc(outfile, texinput)

        # Copy built file to separate directory.
        target = File(filename, target)
        env.Command(target, outfile, Move(target, outfile))

        env.Alias(name, target)

    env.Clean(name, [target])
    env.Clean('all', target)

    if name == default: desc += " (default)"
    Help(help_format % (name, desc))

Clean('all', doctrees)

# Add installation targets and collect package sources.
Help("\nOther targets:\n\n")

Help(help_format % ("install", "install documentation"))
projectdir = os.path.join(instdir, project_tag)
sources = []

for name in install:
    source = Dir(name, builddir)

    inst = env.Install(projectdir, source)
    env.Alias('install', inst)

    for node in env.Glob(os.path.join(str(source), '*')):
        filename = str(node).replace(builddir + os.path.sep, "")
        dirname = os.path.dirname(filename)
        dest = os.path.join(projectdir, dirname)
        inst = env.Install(dest, node)
        env.Alias('install', inst)

# Add uninstall target.
env.Command('uninstall', None, Delete(projectdir))
Help(help_format % ("uninstall", "uninstall documentation"))

# Add package builder.
packageroot = "-".join([project_tag, release_tag])
archive, package = env.Package(NAME = project_tag, VERSION = release,
                               PACKAGEROOT = packageroot,
                               PACKAGETYPE = pkgtype,
                               source = sources)

env.AddPostAction(archive, Delete(packageroot))
Help(help_format % ("package", "build documentation package"))

env.Clean('all', archive)

# Add config settings to help.
Help("\nConfiguration variables:")
for line in config.GenerateHelpText(env).split("\n"):
    Help("\n   " + line)

# Save local configuration if required.
if cache:
    config.Save(cachefile, env)