# HG changeset patch # User Gregory Szorc # Date 1551637883 28800 # Node ID d80d48928eb115337dea1b19cb10bf9864749f22 # Parent d22198b4b3dd2f5c12d51bdfdef2eddfbedbd964 setup: define build_doc command Currently, various processes for packaging Mercurial state to manually invoke `make -C doc` in order to generate the documentation. This Makefile merely invokes `gendoc.py` and `runrst` to produce man pages and HTML pages. Not all environments may have the ability to easily run Makefiles. Windows is notably in this set. This commit ports the man page and HTML generation logic from doc/Makefile to setup.py. We introduce a new build_doc command which generates documentation by calling gendoc.py and runrst. The documentation can now be built via pure Python by running `python setup.py build_doc`. We don't implement dependency tracking because IMO it is more effort than it is worth. We could potentially remove the duplicated functionality in doc/Makefile. But I'm not sure what all is depending on it. So I plan to keep it around. # no-check-commit because forced foo_bar function names Differential Revision: https://phab.mercurial-scm.org/D6063 diff -r d22198b4b3dd -r d80d48928eb1 doc/Makefile --- a/doc/Makefile Sun Mar 03 09:16:37 2019 -0800 +++ b/doc/Makefile Sun Mar 03 10:31:23 2019 -0800 @@ -17,6 +17,7 @@ html: $(HTML) +# This logic is duplicated in setup.py:hgbuilddoc() common.txt $(SOURCES) $(SOURCES:%.txt=%.gendoc.txt): $(GENDOC) ${PYTHON} gendoc.py "$(basename $@)" > $@.tmp mv $@.tmp $@ diff -r d22198b4b3dd -r d80d48928eb1 setup.py --- a/setup.py Sun Mar 03 09:16:37 2019 -0800 +++ b/setup.py Sun Mar 03 10:31:23 2019 -0800 @@ -240,9 +240,9 @@ except ImportError: py2exeloaded = False -def runcmd(cmd, env): +def runcmd(cmd, env, cwd=None): p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env=env) + stderr=subprocess.PIPE, env=env, cwd=cwd) out, err = p.communicate() return p.returncode, out, err @@ -702,6 +702,117 @@ dir = os.path.dirname(self.get_ext_fullpath('dummy')) return os.path.join(self.build_temp, dir, 'hg.exe') +class hgbuilddoc(Command): + description = 'build documentation' + user_options = [ + ('man', None, 'generate man pages'), + ('html', None, 'generate html pages'), + ] + + def initialize_options(self): + self.man = None + self.html = None + + def finalize_options(self): + # If --man or --html are set, only generate what we're told to. + # Otherwise generate everything. + have_subset = self.man is not None or self.html is not None + + if have_subset: + self.man = True if self.man else False + self.html = True if self.html else False + else: + self.man = True + self.html = True + + def run(self): + def normalizecrlf(p): + with open(p, 'rb') as fh: + orig = fh.read() + + if b'\r\n' not in orig: + return + + log.info('normalizing %s to LF line endings' % p) + with open(p, 'wb') as fh: + fh.write(orig.replace(b'\r\n', b'\n')) + + def gentxt(root): + txt = 'doc/%s.txt' % root + log.info('generating %s' % txt) + res, out, err = runcmd( + [sys.executable, 'gendoc.py', root], + os.environ, + cwd='doc') + if res: + raise SystemExit('error running gendoc.py: %s' % + '\n'.join([out, err])) + + with open(txt, 'wb') as fh: + fh.write(out) + + def gengendoc(root): + gendoc = 'doc/%s.gendoc.txt' % root + + log.info('generating %s' % gendoc) + res, out, err = runcmd( + [sys.executable, 'gendoc.py', '%s.gendoc' % root], + os.environ, + cwd='doc') + if res: + raise SystemExit('error running gendoc: %s' % + '\n'.join([out, err])) + + with open(gendoc, 'wb') as fh: + fh.write(out) + + def genman(root): + log.info('generating doc/%s' % root) + res, out, err = runcmd( + [sys.executable, 'runrst', 'hgmanpage', '--halt', 'warning', + '--strip-elements-with-class', 'htmlonly', + '%s.txt' % root, root], + os.environ, + cwd='doc') + if res: + raise SystemExit('error running runrst: %s' % + '\n'.join([out, err])) + + normalizecrlf('doc/%s' % root) + + def genhtml(root): + log.info('generating doc/%s.html' % root) + res, out, err = runcmd( + [sys.executable, 'runrst', 'html', '--halt', 'warning', + '--link-stylesheet', '--stylesheet-path', 'style.css', + '%s.txt' % root, '%s.html' % root], + os.environ, + cwd='doc') + if res: + raise SystemExit('error running runrst: %s' % + '\n'.join([out, err])) + + normalizecrlf('doc/%s.html' % root) + + # This logic is duplicated in doc/Makefile. + sources = {f for f in os.listdir('mercurial/help') + if re.search('[0-9]\.txt$', f)} + + # common.txt is a one-off. + gentxt('common') + + for source in sorted(sources): + assert source[-4:] == '.txt' + root = source[:-4] + + gentxt(root) + gengendoc(root) + + if self.man: + genman(root) + if self.html: + genhtml(root) + class hginstall(install): user_options = install.user_options + [ @@ -827,6 +938,7 @@ fp.write(data) cmdclass = {'build': hgbuild, + 'build_doc': hgbuilddoc, 'build_mo': hgbuildmo, 'build_ext': hgbuildext, 'build_py': hgbuildpy,