Please find attached an example of make.py script.
This is extracted universal building script based on make.bat, Makefile and 
our internal experiences.
It may looks a bit complicated at the first time, but this solution is 
flexible enough to cover most of the needs if you want to customize 
building process.

Target is a function, so if you want to disable a target just delete it or 
comment out it.
If you want to add new target, just add new function.
Help is generated dynamicaly, so no additional actions are required, just 
add your target function and document it.

Some use cases:

   - html - simple example
   - latex - target witt command line options
   - latexpdf - target that calls another target
   - clean
   
I've tested it on Win7(64bit), Py2.7.3(32bit), Sphinx1.1.3
I will test it on MacOSX but more tests are needed.
If someone can help, please get the script and test it on your Sphinx 
projects.
Some configuration will be needed.
It depends on the particular project, but BUILDDIR and SOURCEDIR (lines 
6-7) should be enough.
If you want to test qthelp target, change .qhcp and .qhc file names to 
propper (lines 58,60).


I think that it could be merged with Sphinx (quickstart.py) right now,
but I'm looking forward for your feedback before i will make pull request.

--
Regards
Tawez



On Thursday, August 23, 2012 11:32:02 PM UTC+2, Tawez wrote:
>
> I am working with the team on quite a big project that utilizes Sphinx.
> We have a heterogeneous environment (Win, Linux, Mac OS X).
>
> We had to change a bit the make script to fit it to our requirements.
> Doing it twice (make.bat and Makefile) is not so user friendly and it is 
> hard to maintain.
> Moreover, it is not possible to do everything in Windows shell.
>
> So, why not to use Python to write make script?
> IMHO it sounds more natural.
>
> This is what we have done actually.
> We've got make.py that runs on every team machine.
> It is specific to our project, but I think it will be easy to extract 
> universal core similar to the current make scripts.
>
> Sounds interesting?
> I'm waiting for Your opinions.
>
> --
> Tawez
>

-- 
You received this message because you are subscribed to the Google Groups 
"sphinx-users" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
Visit this group at http://groups.google.com/group/sphinx-users?hl=en.


import sys, os, shutil, subprocess, logging
from getopt import getopt, GetoptError


OPTIONS = {
	"BUILDDIR"      : "build",
	"SOURCEDIR"     : "source",
	"PAPER"         : "",
	"SPHINXOPTS"    : "",
	"SPHINXBUILD"   : "sphinx-build",
	"DOCTREES"      : "%(BUILDDIR)s/doctrees",
	"TARGETDIR"     : "%(BUILDDIR)s/%(TARGET)s",
	"ALLSPHINXOPTS" : "%(DOCTREES)s %(PAPER)s %(SPHINXOPTS)s %(SOURCEDIR)s",
	"CMD"           : "%(SPHINXBUILD)s -b %(TARGET)s %(ALLSPHINXOPTS)s %(TARGETDIR)s",
}


def getTarget(targetName=None):
	# Targets supported by this make.
	# Remove or comment out function to remove it from supported build targets.
	# Add custom function to extend set of supported build targets.

	### {{{ Targets
	def html(argv):
		"""to make standalone HTML files"""
		callBuilder()
		show.info("Build finished. The HTML pages are in %(TARGETDIR)s.", OPTIONS)

	def dirhtml(argv):
		"""to make HTML files named index.html in directories"""
		callBuilder()
		show.info("Build finished. The HTML pages are in %(TARGETDIR)s.", OPTIONS)

	def singlehtml(argv):
		"""to make a single large HTML file"""
		callBuilder()
		show.info("Build finished. The HTML page are in %(TARGETDIR)s.", OPTIONS)

	def pickle(argv):
		"""to make pickle files"""
		callBuilder()
		show.info("Build finished; now you can process the pickle files.")

	def json(argv):
		"""to make JSON files"""
		callBuilder()
		show.info("Build finished; now you can process the JSON files.")

	def htmlhelp(argv):
		"""to make HTML files and a HTML help project"""
		callBuilder()
		show.info("Build finished; now you can run HTML Help Workshop with the .hhp project file in %(TARGETDIR)s.", OPTIONS)

	def qthelp(argv):
		"""to make HTML files and a qthelp project"""
		callBuilder()
		show.info("Build finished; now you can run 'qcollectiongenerator' with the .qhcp project file in %(TARGETDIR)s, like this:", OPTIONS)
		show.info("# qcollectiongenerator %s", os.path.join(OPTIONS["TARGETDIR"], "qweasd.qhcp"))
		show.info("To view the help file:")
		show.info("# assistant -collectionFile %s", os.path.join(OPTIONS["TARGETDIR"], "qweasd.qhc"))

	def devhelp(argv):
		"""to make HTML files and a Devhelp project"""
		callBuilder()
		show.info("Build finished.")

	def epub(argv):
		"""to make an epub"""
		callBuilder()
		show.info("Build finished. The epub file is in %(TARGETDIR)s.", OPTIONS)

	def latex(argv):
		"""\
to make LaTeX files

options:
  -p <paper>    Sets PAPER to <paper> value (defaults to "").
                Only the first occurrence is taken into account.
                Example: make.py latex -p a4"""
		try:
			opts, args = getopt(argv, "p:")
		except GetoptError as e:
			log.error(e)
			args = []
		else:
			if len(opts):
				OPTIONS["PAPER"] = opts[0][1]
		callBuilder()
		show.info("Build finished; the LaTeX files are in %(TARGETDIR)s.", OPTIONS)
		show.info("Run 'make' in that directory to run these through (pdf)latex (use 'make.py latexpdf' here to do that automatically).")

	def latexpdf(argv):
		"""\
to make LaTeX files and run them through pdflatex

options:
  See help for target 'latex'"""
		OPTIONS["TARGET"] = "latex"
		latex(argv)
		show.info("Running LaTeX files through pdflatex...")
		subprocess.check_call(("make -C %(TARGETDIR)s all-pdf" % OPTIONS).split())
		show.info("pdflatex finished; the PDF files are in %(TARGETDIR)s.", OPTIONS)

	def text(argv):
		"""to make text files"""
		callBuilder()
		show.info("Build finished. The text files are in %(TARGETDIR)s.", OPTIONS)

	def man(argv):
		"""to make manual pages"""
		callBuilder()
		show.info("Build finished. The manual pages are in %(TARGETDIR)s.", OPTIONS)

	def texinfo(argv):
		"""to make Texinfo files"""
		callBuilder()
		show.info("Build finished. The Texinfo files are in %(TARGETDIR)s.", OPTIONS)
		show.info("Run 'make' in that directory to run these through makeinfo (use 'make.py info' here to do that automatically).")

	def info(argv):
		"""to make Texinfo files and run them through makeinfo"""
		OPTIONS["TARGET"] = "texinfo"
		texinfo(argv)
		show.info("Running Texinfo files through makeinfo...")
		subprocess.check_call(("make -C %(TARGETDIR)s info" % OPTIONS).split())
		show.info("makeinfo finished; the Info files are in %(TARGETDIR)s.", OPTIONS)

	def gettext(argv):
		"""to make PO message catalogs"""
		OPTIONS["DOCTREESDIR"] = ""
		callBuilder()
		show.info("Build finished. The message catalogs are in %(TARGETDIR)s.", OPTIONS)

	def changes(argv):
		"""to make an overview of all changed/added/deprecated items"""
		callBuilder()
		show.info("The overview file is in %(TARGETDIR)s.", OPTIONS)

	def linkcheck(argv):
		"""to check all external links for integrity"""
		callBuilder()
		show.info("Link check complete; look for any errors in the above output or in %s.",
			os.path.join(OPTIONS["TARGETDIR"], output.txt))

	def doctest(argv):
		"""to run all doctests embedded in the documentation (if enabled)"""
		callBuilder()
		show.info("Testing of doctests in the sources finished, look at the results in %s.",
			os.path.join(OPTIONS["TARGETDIR"], output.txt))

	def clean(argv):
		"""to remove BUIDLDIR and its contents"""
		parseOptions()
		buildDir = OPTIONS["BUILDDIR"]
		if os.path.exists(buildDir):
			try:
				shutil.rmtree(buildDir)
				show.info("Build folder '%s' cleaned", buildDir)
			except Exception as e:
				log.error("Cannot remove folder '%s'!\n\t%s", buildDir, e)
		else:
			log.warning("Folder '%s' not found, nothing to clean", buildDir)
	### Targets }}}

	TARGETS = dict([(name, item) for name, item in locals().iteritems() if hasattr(item, "__call__")])
	if targetName:
		return TARGETS.get(targetName, None)
	else:
		return TARGETS


def parseOptions():
	o = OPTIONS
	o["BUILDDIR"] = os.path.normpath(o["BUILDDIR"])
	o["SOURCEDIR"] = os.path.normpath(o["SOURCEDIR"])
	o["TARGETDIR"] = os.path.normpath(o["TARGETDIR"] % o)
	if o["DOCTREES"]:
		o["DOCTREES"] = "-d %s" % os.path.normpath(o["DOCTREES"] % o)
	if o["PAPER"]:
		o["PAPER"] = "-D latex_paper_size=%(PAPER)s" % o
	o["ALLSPHINXOPTS"] = o["ALLSPHINXOPTS"] % o
	o["CMD"] = o["CMD"] % o
	return o["CMD"]


def callBuilder():
	cmd = parseOptions()
	show.info("Running Sphinx builder with the following command:\n\n  %s\n", cmd)
	subprocess.check_call(cmd.split())
	show.info("")


def showFnHelp(targetName, target):
	HELP = """\
Use 'make.py %(name)s [options]' %(begin)s
%(more)s%(rest)s"""

	doc = (target.__doc__ or "*** help is not available ***").split("\n")
	o = {
		"name": targetName,
		"begin": doc[0],
		"rest": "\n".join(doc[1:])
	}
	o["more"] = o["rest"] and " " or "\nThis target has no options defined"
	show.info(HELP, o)


def showHelp(targets):
	HELP = """\
Use 'make.py <target> [options]' where <target> is one of
%s

Use 'make.py -h <target>' to see help for <target> and it's options
(not every <target> has options defined)

Use 'make.py [-h]' to show this help and exit"""

	doc = []
	if targets:
		maxNameLen = max([len(i) for i in targets.keys()])
		tpl = "  %%-%ds%%s" % (maxNameLen + 1)
		for name, fn in sorted(targets.items()):
			doc.append(tpl % (name, (fn.__doc__ or "*** help is not available ***").split("\n")[0]))
	else:
		doc.append("*** No targets defined ***")
	show.info(HELP, "\n".join(doc))


show = logging.getLogger("make")
log = logging.getLogger("make.log")

def configureLoggers():
	show.setLevel(logging.INFO)
	showHandler = logging.StreamHandler()
	showHandler.setFormatter(logging.Formatter("%(message)s"))
	show.addHandler(showHandler)
	log.setLevel(logging.WARNING)
	logHandler = logging.StreamHandler()
	logHandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
	log.addHandler(logHandler)
	log.propagate = 0


def main(argv):
	try:
		opts, args = getopt(argv, "h")
	except GetoptError as e:
		log.error(e)
		show.info("Use 'make.py [-h]' for help")
	else:
		targetName = args and args[0] or None
		target = getTarget(targetName)
		helpMode = bool(opts)
		if not targetName:
			showHelp(target)
		elif not target:
			log.error("Target '%s' not found", targetName)
		elif helpMode:
			showFnHelp(targetName, target)
		else:
			OPTIONS["TARGET"] = targetName
			target(args[1:])


if __name__ == "__main__":
	configureLoggers()
	main(sys.argv[1:])
	show.info("")

Reply via email to