Mercurial > piecrust2
view piecrust/processing/less.py @ 1155:fac4483867a5
less: Fix issues moving the map file on Windows. Again.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Tue, 11 Jun 2019 15:11:39 -0700 |
parents | a3dec0fbd9ce |
children |
line wrap: on
line source
import os import os.path import sys import json import shutil import hashlib import logging import platform import subprocess from piecrust.processing.base import ( SimpleFileProcessor, ExternalProcessException, FORCE_BUILD) logger = logging.getLogger(__name__) class LessProcessor(SimpleFileProcessor): PROCESSOR_NAME = 'less' def __init__(self): super(LessProcessor, self).__init__({'less': 'css'}) self._conf = None self._map_dir = None def onPipelineStart(self, ctx): self._map_dir = os.path.join(ctx.tmp_dir, 'less') if (ctx.is_main_process and not os.path.isdir(self._map_dir)): os.makedirs(self._map_dir) def getDependencies(self, path): map_path = self._getMapPath(path) try: with open(map_path, 'r') as f: dep_map = json.load(f) except OSError: # Map file not found... rebuild. logger.debug("No map file found for LESS file '%s' at '%s'. " "Rebuilding" % (path, map_path)) return FORCE_BUILD # Check the version, since the `sources` list has changed # meanings over time. if dep_map.get('version') != 3: logger.warning("Unknown LESS map version. Force rebuilding.") return FORCE_BUILD # Get the sources, but make all paths absolute. sources = dep_map.get('sources') path_dir = os.path.dirname(path) def _makeAbs(p): return os.path.join(path_dir, p) deps = list(map(_makeAbs, sources)) return deps def _doProcess(self, in_path, out_path): self._ensureInitialized() map_path = self._getMapPath(in_path) map_url = '/' + os.path.relpath( map_path, self.app.root_dir).replace('\\', '/') # On Windows, it looks like LESSC is confused with paths when the # map file is not to be created in the same directory as the input # file (it ends up writing invalid dependencies in the map file, with # a mix of relative and absolute paths stuck together). # So create it there and move it afterwards... :( temp_map_path = os.path.join( os.path.dirname(in_path), os.path.basename(map_path)) args = [self._conf['bin'], '--source-map=%s' % temp_map_path, '--source-map-url=%s' % map_url] args += self._conf['options'] args.append(in_path) args.append(out_path) logger.debug("Processing LESS file: %s" % args) try: proc = subprocess.Popen(args, stderr=subprocess.PIPE) stdout_data, stderr_data = proc.communicate() except FileNotFoundError as ex: logger.error("Tried running LESS processor with command: %s" % args) raise Exception("Error running LESS processor. " "Did you install it?") from ex if proc.returncode != 0: raise ExternalProcessException( stderr_data.decode(sys.stderr.encoding)) logger.debug("Moving map file: %s -> %s" % (temp_map_path, map_path)) if os.path.exists(map_path): os.remove(map_path) shutil.move(temp_map_path, map_path) return True def _ensureInitialized(self): if self._conf is not None: return bin_name = 'lessc' if platform.system() == 'Windows': bin_name += '.cmd' self._conf = self.app.config.get('less') or {} self._conf.setdefault('bin', bin_name) self._conf.setdefault('options', ['--compress']) if not isinstance(self._conf['options'], list): raise Exception("The `less/options` configuration setting " "must be an array of arguments.") def _getMapPath(self, path): map_name = "%s_%s.map" % ( os.path.basename(path), hashlib.md5(path.encode('utf8')).hexdigest()) map_path = os.path.join(self._map_dir, map_name) return map_path