move directory-handling logic to the processor

In preparation for supporting a separate --builddir (allowing minimal
disruption of real output directory during rebuild) factor all output
directory handling logic into the main processor object (BaseDoctype).
Simplify the generate() method.
Centralize all pre-build logic in hook_build_prepare().
Remove all hook logic from the OutputDirectory.
This commit is contained in:
Martin A. Brown 2016-03-07 11:34:09 -08:00
parent c390e71b4a
commit dfc20c5617
2 changed files with 76 additions and 65 deletions

View File

@ -7,6 +7,7 @@ import os
import sys import sys
import stat import stat
import errno import errno
import shutil
import logging import logging
import inspect import inspect
from tempfile import NamedTemporaryFile as ntf from tempfile import NamedTemporaryFile as ntf
@ -99,15 +100,52 @@ class BaseDoctype(object):
assert validator(thing) assert validator(thing)
return True return True
def build_chdir_output(self, config): def clear_output(self):
'''remove the entire output directory
This method must be --script aware. The method execute_shellscript()
generates scripts into the directory that would be removed. Thus, the
behaviour is different depending on --script mode or --build mode.
'''
logger.debug("%s removing dir %s.",
self.output.stem, self.output.dirname)
if self.config.script:
s = 'test -d "{output.dirname}" && rm -rf -- "{output.dirname}"'
return self.shellscript(s)
if os.path.exists(self.output.dirname):
shutil.rmtree(self.output.dirname)
return True
def mkdir_output(self):
'''create a new output directory
This method must be --script aware. The method execute_shellscript()
generates scripts into the directory that would be removed. Thus, the
behaviour is different depending on --script mode or --build mode.
'''
logger.debug("%s creating dir %s.",
self.output.stem, self.output.dirname)
if self.config.script:
s = 'mkdir -p -- "{output.logdir}"'
return self.shellscript(s)
for d in (self.output.dirname, self.output.logdir):
if not os.path.isdir(d):
os.mkdir(d)
return True
def chdir_output(self):
'''chdir to the output directory (or write the script that would)''' '''chdir to the output directory (or write the script that would)'''
if config.script: logger.debug("%s chdir to dir %s.",
self.output.stem, self.output.dirname)
if self.config.script:
s = 'cd -- "{output.dirname}"' s = 'cd -- "{output.dirname}"'
return self.shellscript(s) return self.shellscript(s)
os.chdir(self.output.dirname) os.chdir(self.output.dirname)
return True return True
def hook_build_prepare(self): def copy_static_resources(self):
logger.debug("%s copy resources %s.",
self.output.stem, self.output.dirname)
source = list() source = list()
for d in self.config.resources: for d in self.config.resources:
fullpath = os.path.join(self.source.dirname, d) fullpath = os.path.join(self.source.dirname, d)
@ -120,11 +158,36 @@ class BaseDoctype(object):
s = 'rsync --archive --verbose %s ./' % (' '.join(source)) s = 'rsync --archive --verbose %s ./' % (' '.join(source))
return self.shellscript(s) return self.shellscript(s)
def hook_build_prepare(self):
order = ['build_precheck',
'clear_output',
'mkdir_output',
'chdir_output',
'copy_static_resources',
]
# -- perform build preparation steps: clear
#
for methname in order:
method = getattr(self, methname, None)
assert method is not None
if not method():
logger.warning("%s %s failed (%s), skipping",
stem, methname, classname)
return False
return True
def hook_build_success(self): def hook_build_success(self):
self.cleanup() stem = self.output.stem
logdir = self.output.logdir
dirname = self.output.dirname
logger.info("%s build SUCCESS %s.", stem, dirname)
logger.debug("%s removing logs %s)", stem, logdir)
if os.path.isdir(logdir):
shutil.rmtree(logdir)
return True
def hook_build_failure(self): def hook_build_failure(self):
self.cleanup() pass
def shellscript(self, script, **kwargs): def shellscript(self, script, **kwargs):
if self.config.build: if self.config.build:
@ -208,33 +271,15 @@ class BaseDoctype(object):
stem = self.source.stem stem = self.source.stem
classname = self.__class__.__name__ classname = self.__class__.__name__
# -- ensure all the tools and data files are present before build # -- perform build preparation steps;
# # - check for all executables and data files
if not self.build_precheck(): # - clear output dir
logger.warning("%s %s failed (%s), skipping to next build", # - make output dir
stem, 'build_precheck', classname) # - chdir to output dir
return False # - copy source images/resources to output dir
# -- perform build preparation steps: mkdir
#
if not self.output.hook_build_prepare(self.config):
logger.warning("%s %s failed (output %s), skipping",
stem, 'hook_build_prepare', classname)
return False
# -- perform build preparation steps: chdir
# #
opwd = os.getcwd() opwd = os.getcwd()
if not self.build_chdir_output(self.config):
logger.warning("%s %s failed (%s), skipping to next build",
stem, 'build_chdir_output', classname)
return False
# -- perform build preparation steps: copy images/resources
#
if not self.hook_build_prepare(): if not self.hook_build_prepare():
logger.warning("%s %s failed (processor %s), skipping",
stem, 'hook_build_prepare', classname)
return False return False
# -- build # -- build
@ -244,11 +289,9 @@ class BaseDoctype(object):
# -- report on result and/or cleanup # -- report on result and/or cleanup
# #
if result: if result:
self.hook_build_success() # -- processor self.hook_build_success()
self.output.hook_build_success() # -- output document
else: else:
self.hook_build_failure() # -- processor self.hook_build_failure()
self.output.hook_build_failure() # -- output document
os.chdir(opwd) os.chdir(opwd)

View File

@ -125,38 +125,6 @@ class OutputDirectory(OutputNamingConvention):
self.source = source self.source = source
self.logdir = os.path.join(self.dirname, logdir) self.logdir = os.path.join(self.dirname, logdir)
def clean(self):
'''remove the output directory for this document
This is done as a matter of course when the output documents must be
regenerated. Better to start fresh.
'''
if os.path.isdir(self.dirname):
logger.debug("%s removing dir %s.", self.stem, self.dirname)
shutil.rmtree(self.dirname)
def hook_build_prepare(self, config):
if config.script:
return True
self.clean()
for d in (self.dirname, self.logdir):
if not os.path.isdir(d):
logger.debug("%s creating dir %s.", self.stem, d)
os.mkdir(d)
logger.info("%s ready to build %s.", self.stem, self.dirname)
return True
def hook_build_failure(self):
logger.error("%s FAILURE, see logs in %s", self.stem, self.logdir)
return True
def hook_build_success(self):
logger.info("%s build SUCCESS %s.", self.stem, self.dirname)
logger.debug("%s removing logs %s)", self.stem, self.logdir)
if os.path.isdir(self.logdir):
shutil.rmtree(logdir)
return True
def detail(self, widths, verbose, file=sys.stdout): def detail(self, widths, verbose, file=sys.stdout):
template = '{s.status:{w.status}} {s.stem:{w.stem}}' template = '{s.status:{w.status}} {s.stem:{w.stem}}'
outstr = template.format(s=self, w=widths) outstr = template.format(s=self, w=widths)