Exemple #1
0
    def write(self, filename, debug=False):

        if Image is None:
            raise UserError("Missing Python PIL which is required to create sprite sheets!")

        img = Image.new('RGBA', (self.width, self.height))
        draw = ImageDraw.Draw(img)

        #draw.rectangle((0, 0, self.width, self.height), fill=(255, 255, 0, 255))

        # Load images and pack them in
        for block in self.blocks:
            res = Image.open(block.image.src)

            x, y = block.fit.x, block.fit.y
            if block.rotated:
                Console.debug('%s is rotated' % block.image.src)
                res = res.rotate(90)

            img.paste(res, (x, y))
            del res

            if debug:
                x, y, w, h = block.fit.x, block.fit.y, block.w, block.h
                draw.rectangle((x, y , x + w , y + h), outline=(0, 0, 255, 255) if block.rotated else (255, 0, 0, 255))

        if debug:
            for i, block in enumerate(self.packer.getUnused()):
                x, y, w, h = block.x, block.y, block.w, block.h
                draw.rectangle((x, y , x + w , y + h), fill=(255, 255, 0, 255))

        img.save(filename)
Exemple #2
0
def cleanup(node):
    """"""

    if not hasattr(node, "variables"):
        ScopeScanner.scan(node)

    # Re cleanup until nothing to remove is found
    iteration = 0
    cleaned = False

    Console.debug("Removing unused variables...")
    Console.indent()

    while True:
        iteration += 1

        modified = __cleanup(node)
        if modified > 0:
            Console.debug("Removed %s unused variables", modified)
            ScopeScanner.scan(node)
            cleaned = True
        else:
            break

    Console.outdent()

    return cleaned
Exemple #3
0
def patch(node, permutation):
    """ Replaces all occourences with incoming values """

    modified = False
    
    if node.type == "identifier" or node.type == "dot":

        if node.type == "dot":
            name = assembleDot(node)
        else:
            name = node.value

        replacement = __translateToValue(permutation.get(name))
        if replacement:
            Console.debug("Found permutation query (%s => %s) in line %s", name, replacement, node.line)
            replacementNode = Parser.parseExpression(replacement)
            node.parent.replace(node, replacementNode)
            modified = True
         
    # Process children
    for child in reversed(node):
        if child != None:
            if patch(child, permutation):
                modified = True

    return modified
Exemple #4
0
    def __addDir(self, directory, regex, type, package):
        check = re.compile(regex)

        path = os.path.join(self.__path, directory)
        if not os.path.exists(path):
            return

        Console.debug("Scanning directory: %s" % directory)
        Console.indent()

        for dirPath, dirNames, fileNames in os.walk(path):
            for dirName in dirNames:
                # Filter dotted directories like .git, .bzr, .hg, .svn, etc.
                if dirName.startswith("."):
                    dirNames.remove(dirName)

            relDirPath = os.path.relpath(dirPath, path)

            for fileName in fileNames:

                if fileName[0] == ".":
                    continue

                relPath = os.path.normpath(os.path.join(relDirPath,
                                                        fileName)).replace(
                                                            os.sep, "/")

                if not check.match(
                        os.path.join(directory, relPath).replace(os.sep, "/")):
                    continue

                fullPath = os.path.join(dirPath, fileName)
                self.addFile(relPath, fullPath, type, package)

        Console.outdent()
Exemple #5
0
def cleanup(node):
    """"""

    if not hasattr(node, "variables"):
        ScopeScanner.scan(node)

    # Re cleanup until nothing to remove is found
    x = 0
    cleaned = False

    Console.debug("Removing unused variables...")
    while True:
        x = x + 1
        #debug("Removing unused variables [Iteration: %s]...", x)
        Console.indent()

        if __cleanup(node):
            ScopeScanner.scan(node)
            cleaned = True
            Console.outdent()
        else:
            Console.outdent()
            break

    return cleaned
Exemple #6
0
 def __structurize(self, data):
     """
     This method structurizes the incoming data into a cascaded structure representing the
     file system location (aka file IDs) as a tree. It further extracts the extensions and
     merges files with the same name (but different extensions) into the same entry. This is
     especially useful for alternative formats like audio files, videos and fonts. It only
     respects the data of the first entry! So it is not a good idea to have different files
     with different content stored with the same name e.g. content.css and content.png.
     """
     
     root = {}
     
     # Easier to debug and understand when sorted
     for fileId in sorted(data):
         current = root
         splits = fileId.split("/")
         
         # Extract the last item aka the filename itself
         basename = splits.pop()
         
         # Find the current node to store info on
         for split in splits:
             if not split in current:
                 current[split] = {}
             elif type(current[split]) != dict:
                 raise UserError("Invalid asset structure. Folder names must not be identical to any filename without extension: \"%s\" in %s" % (split, fileId))
                 
             current = current[split]
         
         # Create entry
         Console.debug("Adding %s..." % fileId)
         current[basename] = data[fileId]
     
     return root
Exemple #7
0
def __combineSiblings(node):
    """Backwards processing and insertion into previous sibling if both are declarations"""
    length = len(node)
    pos = length - 1
    while pos > 0:
        child = node[pos]
        prevChild = node[pos - 1]

        # Special FOR loop optimization, emulate faked VAR
        if child.type == "for" and prevChild.type == "var":
            setup = getattr(child, "setup", None)
            if setup and setup.type == "var":
                Console.debug("Removing for-loop setup section at line %s" %
                              setup.line)
                child.remove(setup)
                child = setup

        # Combine declarations of VAR statements
        if child.type == "var" and prevChild.type == "var":
            # debug("Combining var statement at line %s" % child.line)

            # Fix loop through casting node to list()
            for variable in list(child):
                prevChild.append(variable)

            if child in node:
                node.remove(child)

        pos -= 1
Exemple #8
0
    def __addDir(self, directory, regex, type, package):
        check = re.compile(regex)

        path = os.path.join(self.__path, directory)
        if not os.path.exists(path):
            return

        Console.debug("Scanning directory: %s" % directory)
        Console.indent()

        for dirPath, dirNames, fileNames in os.walk(path):
            for dirName in dirNames:
                # Filter dotted directories like .git, .bzr, .hg, .svn, etc.
                if dirName.startswith("."):
                    dirNames.remove(dirName)

            relDirPath = os.path.relpath(dirPath, path)

            for fileName in fileNames:

                if fileName[0] == ".":
                    continue

                relPath = os.path.normpath(os.path.join(relDirPath, fileName)).replace(os.sep, "/")

                if not check.match(os.path.join(directory, relPath).replace(os.sep, "/")):
                    continue

                fullPath = os.path.join(dirPath, fileName)
                self.addFile(relPath, fullPath, type, package)

        Console.outdent()
Exemple #9
0
def cleanup(node):
    """"""

    if not hasattr(node, "variables"):
        ScopeScanner.scan(node)

    # Re cleanup until nothing to remove is found
    x = 0
    cleaned = False

    Console.debug("Removing unused variables...")
    while True:
        x = x + 1
        #debug("Removing unused variables [Iteration: %s]...", x)
        Console.indent()

        if __cleanup(node):
            ScopeScanner.scan(node)
            cleaned = True
            Console.outdent()
        else:
            Console.outdent()
            break

    return cleaned
Exemple #10
0
    def __addContent(self, content):
        Console.info("Adding manual content")

        Console.indent()
        for fileId in content:
            entry = content[fileId]
            if not isinstance(entry, dict):
                raise UserError("Invalid manual content section for file %s. Requires a dict with type and source definition!" % fileId)

            itemType = entry["type"]
            fileContent = entry["source"]

            if len(fileContent) == 0:
                raise UserError("Empty content!")

            # Support for joining text content
            if len(fileContent) == 1:
                filePath = os.path.join(self.__path, fileContent[0])
            else:
                filePath = [os.path.join(self.__path, filePart) for filePart in fileContent]

            name, construct = self.__resolveConstructor(itemType)
            item = construct(self, fileId).attach(filePath)
            Console.debug("Registering %s %s" % (item.kind, fileId))

            if not itemType in self.items:
                self.items[itemType] = {}

            # Check for duplication
            if fileId in self.items[itemType]:
                raise UserError("Item ID was registered before: %s" % fileId)

            self.items[itemType][fileId] = item

        Console.outdent()
Exemple #11
0
    def __getOptimizedTree(self, permutation=None, context=None):
        """Returns an optimized tree with permutations applied"""

        field = "opt-tree[%s]-%s" % (self.id, permutation)
        tree = self.project.getCache().read(field, self.mtime)
        if not tree:
            tree = copy.deepcopy(self.__getTree("%s:plain" % context))

            # Logging
            msg = "Processing class %s" % Console.colorize(self.id, "bold")
            if permutation:
                msg += Console.colorize(" (%s)" % permutation, "grey")
            if context:
                msg += Console.colorize(" [%s]" % context, "cyan")
                
            Console.info("%s..." % msg)
            Console.indent()

            # Apply permutation
            if permutation:
                Console.debug("Patching tree with permutation: %s", permutation)
                Console.indent()
                jasy.js.clean.Permutate.patch(tree, permutation)
                Console.outdent()

            # Cleanups
            jasy.js.clean.DeadCode.cleanup(tree)
            ScopeScanner.scan(tree)
            jasy.js.clean.Unused.cleanup(tree)
        
            self.project.getCache().store(field, tree, self.mtime, True)
            Console.outdent()

        return tree
Exemple #12
0
    def __clean(node):
        """
        Removes all empty rules.

        Starting from inside out for a deep cleanup. This is a required step for the next one where we combine media
        queries and selectors and need to have an easy reference point to the previous node.

        """

        # Process children first
        for child in reversed(node):
            if child is not None:
                __clean(child)

        if hasattr(node, "rules") and len(node.rules) == 0:
            Console.debug("Cleaning up empty selector/mixin/@media/@supports at line %s" % node.line)
            node.parent.remove(node)

        elif node.type == "content":
            Console.debug("Cleaning up left over @content at line %s" % node.line)
            node.parent.remove(node)

        elif node.type == "meta":
            Console.debug("Cleaning up left over @meta at line %s" % node.line)
            node.parent.remove(node)

        elif node.type == "block" and node.parent.type in ("sheet", "block"):
            Console.debug("Inlining content of unnecessary block node at line %s" % node.line)
            node.parent.insertAllReplace(node, node)

        elif node.type == "root" and len(node) == 0:
            Console.debug("Cleaning up left over @root at line %s" % node.line)
            node.parent.remove(node)
Exemple #13
0
    def __getOptimizedTree(self, permutation=None, context=None):
        """Returns an optimized tree with permutations applied"""

        field = "opt-tree[%s]-%s" % (self.id, permutation)
        tree = self.project.getCache().read(field, self.mtime)
        if not tree:
            tree = copy.deepcopy(self.__getTree("%s:plain" % context))

            # Logging
            msg = "Processing class %s" % Console.colorize(self.id, "bold")
            if permutation:
                msg += Console.colorize(" (%s)" % permutation, "grey")
            if context:
                msg += Console.colorize(" [%s]" % context, "cyan")

            Console.info("%s..." % msg)
            Console.indent()

            # Apply permutation
            if permutation:
                Console.debug("Patching tree with permutation: %s",
                              permutation)
                Console.indent()
                jasy.js.clean.Permutate.patch(tree, permutation)
                Console.outdent()

            # Cleanups
            jasy.js.clean.DeadCode.cleanup(tree)
            ScopeScanner.scan(tree)
            jasy.js.clean.Unused.cleanup(tree)

            self.project.getCache().store(field, tree, self.mtime, True)
            Console.outdent()

        return tree
Exemple #14
0
    def __generateTranslationBundle(self):
        """ 
        Returns a translation object for the given language containing 
        all relevant translation files for the current project set. 
        """

        language = self.getCurrentPermutation().get("locale")
        if language is None:
            return None

        if language in self.__translationBundles:
            return self.__translationBundles[language]

        Console.info("Creating translation bundle: %s", language)
        Console.indent()

        # Initialize new Translation object with no project assigned
        # This object is used to merge all seperate translation instances later on.
        combined = jasy.item.Translation.TranslationItem(None, id=language)
        relevantLanguages = self.__expandLanguage(language)

        # Loop structure is build to prefer finer language matching over project priority
        for currentLanguage in reversed(relevantLanguages):
            for project in self.__projects:
                for translation in project.getTranslations().values():
                    if translation.getLanguage() == currentLanguage:
                        Console.debug("Adding %s entries from %s @ %s...", len(translation.getTable()), currentLanguage, project.getName())
                        combined += translation

        Console.debug("Combined number of translations: %s", len(combined.getTable()))
        Console.outdent()

        self.__translationBundles[language] = combined
        return combined
Exemple #15
0
def cleanup(node):
    """"""

    if not hasattr(node, "variables"):
        ScopeScanner.scan(node)

    # Re cleanup until nothing to remove is found
    iteration = 0
    cleaned = False

    Console.debug("Removing unused variables...")
    Console.indent()

    while True:
        iteration += 1

        modified = __cleanup(node)
        if modified > 0:
            Console.debug("Removed %s unused variables", modified)
            ScopeScanner.scan(node)
            cleaned = True
        else:
            break

    Console.outdent()

    return cleaned
Exemple #16
0
    def getTranslationBundle(self, language=None):
        """Returns a translation object for the given language containing all relevant translation files for the current
        project set."""

        if language is None:
            return None

        if language in self.__translationBundles:
            return self.__translationBundles[language]

        Console.info("Creating translation bundle: %s", language)
        Console.indent()

        # Initialize new Translation object with no project assigned
        # This object is used to merge all seperate translation instances later on.
        combined = jasy.item.Translation.TranslationItem(None, id=language)
        relevantLanguages = self.__expandLanguage(language)

        # Loop structure is build to prefer finer language matching over project priority
        for currentLanguage in reversed(relevantLanguages):
            for project in self.__projects:
                for translation in project.getTranslations().values():
                    if translation.getLanguage() == currentLanguage:
                        Console.debug("Adding %s entries from %s @ %s...", len(translation.getTable()), currentLanguage, project.getName())
                        combined += translation

        Console.info("Combined number of translations: %s", len(combined.getTable()))
        Console.outdent()

        self.__translationBundles[language] = combined
        return combined
Exemple #17
0
    def __addDir(self, directory, distname):

        Console.debug("Scanning directory: %s" % directory)
        Console.indent()

        path = os.path.join(self.__path, directory)
        if not os.path.exists(path):
            return

        for dirPath, dirNames, fileNames in os.walk(path):
            for dirName in dirNames:
                # Filter dotted directories like .git, .bzr, .hg, .svn, etc.
                if dirName.startswith("."):
                    dirNames.remove(dirName)

                # Filter sub projects
                if os.path.exists(
                        os.path.join(dirPath, dirName, "jasyproject.json")):
                    dirNames.remove(dirName)

            relDirPath = os.path.relpath(dirPath, path)

            for fileName in fileNames:

                if fileName[0] == ".":
                    continue

                relPath = os.path.normpath(os.path.join(relDirPath,
                                                        fileName)).replace(
                                                            os.sep, "/")
                fullPath = os.path.join(dirPath, fileName)

                self.addFile(relPath, fullPath, distname)

        Console.outdent()
Exemple #18
0
def __combineSiblings(node):
    """Backwards processing and insertion into previous sibling if both are declarations"""
    length = len(node)
    pos = length - 1
    while pos > 0:
        child = node[pos]
        prevChild = node[pos - 1]

        # Special FOR loop optimization, emulate faked VAR
        if child.type == "for" and prevChild.type == "var":
            setup = getattr(child, "setup", None)
            if setup and setup.type == "var":
                Console.debug("Removing for-loop setup section at line %s" % setup.line)
                child.remove(setup)
                child = setup

        # Combine declarations of VAR statements
        if child.type == "var" and prevChild.type == "var":
            # debug("Combining var statement at line %s" % child.line)

            # Fix loop through casting node to list()
            for variable in list(child):
                prevChild.append(variable)

            if child in node:
                node.remove(child)

        pos -= 1
Exemple #19
0
    def loadLibrary(self, objectName, fileName, encoding="utf-8", doc=None):
        """
        Creates a new object inside the user API (jasyscript.py) with the given name 
        containing all @share'd functions and fields loaded from the given file.
        """

        if objectName in self.__scriptEnvironment:
            raise UserError("Could not import library %s as the object name %s is already used." % (fileName, objectName))

        # Create internal class object for storing shared methods
        class Shared(object): pass
        exportedModule = Shared()
        exportedModule.__doc__ = doc or "Imported from %s" % os.path.relpath(fileName, os.getcwd())
        counter = 0

        # Method for being used as a decorator to share methods to the outside
        def share(func):
            nonlocal counter
            setattr(exportedModule, func.__name__, func)
            counter += 1

            return func

        # Execute given file. Using clean new global environment
        # but add additional decorator for allowing to define shared methods
        # and the session object (self).
        code = open(fileName, "r", encoding=encoding).read()
        exec(compile(code, os.path.abspath(fileName), "exec"), {"share" : share, "session" : self})

        # Export destination name as global    
        Console.debug("Importing %s shared methods under %s...", counter, objectName)
        self.__scriptEnvironment[objectName] = exportedModule

        return counter
Exemple #20
0
def __extendContent(node, call, targetBlock, stopCombineAt):
    """
    Builds up a list of selector/mediaqueries to insert after the extend to produce
    the @content sections on the intended selectors.
    """

    for child in reversed(node):
        if child:
            __extendContent(child, call, targetBlock, stopCombineAt)

    if node.type == "content" and hasattr(call, "rules"):
        # Extends support @content as well. In this case we produce a new selector
        # which matches the position of the content section and append it after
        # the original extended mixin on return

        Console.debug("Inserting content section into new virtual selector")

        selector, media = Util.combineSelector(node, stop=stopCombineAt)

        selectorNode = Node.Node(type="selector")
        selectorNode.name = selector

        selectorNode.append(copy.deepcopy(call.rules), "rules")

        # Support media queries, too
        if media:
            mediaNode = Node.Node(type="media")
            mediaNode.name = media
            mediaNode.append(selectorNode)
            targetBlock.append(mediaNode)

        else:
            targetBlock.append(selectorNode)
Exemple #21
0
    def __addDir(self, directory, distname):
        
        Console.debug("Scanning directory: %s" % directory)
        Console.indent()
        
        path = os.path.join(self.__path, directory)
        if not os.path.exists(path):
            return
            
        for dirPath, dirNames, fileNames in os.walk(path):
            for dirName in dirNames:
                # Filter dotted directories like .git, .bzr, .hg, .svn, etc.
                if dirName.startswith("."):
                    dirNames.remove(dirName)

                # Filter sub projects
                if os.path.exists(os.path.join(dirPath, dirName, "jasyproject.json")):
                    dirNames.remove(dirName)
                    
            relDirPath = os.path.relpath(dirPath, path)

            for fileName in fileNames:
                
                if fileName[0] == ".":
                    continue
                    
                relPath = os.path.normpath(os.path.join(relDirPath, fileName)).replace(os.sep, "/")
                fullPath = os.path.join(dirPath, fileName)
                
                self.addFile(relPath, fullPath, distname)
        
        Console.outdent()
Exemple #22
0
def executeCommand(args, failMessage=None, path=None, wrapOutput=True):
    """
    Executes the given process and outputs failMessage when errors happen.

    :param args:
    :type args: str or list
    :param failMessage: Message for exception when command fails
    :type failMessage: str
    :param path: Directory path where the command should be executed
    :type path: str
    :raise Exception: Raises an exception whenever the shell command fails in execution
    :type wrapOutput: bool
    :param wrapOutput: Whether shell output should be wrapped and returned (and passed through to Console.debug())

    """

    if isinstance(args, str):
        args = shlex.split(args)

    prevpath = os.getcwd()

    # Execute in custom directory
    if path:
        path = os.path.abspath(os.path.expanduser(path))
        os.chdir(path)

    Console.debug("Executing command: %s", " ".join(args))
    Console.indent()

    # Using shell on Windows to resolve binaries like "git"
    if not wrapOutput:
        returnValue = subprocess.call(args, shell=sys.platform == "win32")
        result = returnValue

    else:
        output = tempfile.TemporaryFile(mode="w+t")
        returnValue = subprocess.call(args,
                                      stdout=output,
                                      stderr=output,
                                      shell=sys.platform == "win32")

        output.seek(0)
        result = output.read().strip("\n\r")
        output.close()

    # Change back to previous path
    os.chdir(prevpath)

    if returnValue != 0 and failMessage:
        raise Exception("Error during executing shell command: %s (%s)" %
                        (failMessage, result))

    if wrapOutput:
        for line in result.splitlines():
            Console.debug(line)

    Console.outdent()

    return result
Exemple #23
0
    def exportToJson(self, items=None):
        """
        Exports asset data for usage at the client side.

        Utilizes JavaScript class jasy.Asset to inject data into the client at runtime.

        """

        # Processing assets
        assets = self.__assets

        # Destination folder for assets
        assetPath = os.path.join(self.__profile.getDestinationPath(),
                                 self.__profile.getAssetOutputFolder())

        result = {}
        filterExpr = self.__compileFilterExpr(items) if items else None
        for fileId in assets:
            if filterExpr and not filterExpr.match(fileId):
                continue

            entry = {}
            # t = file type
            # u = full file url
            # h = file hash (based on content)
            # d = asset data (image size, etc.)

            assetItem = assets[fileId]
            self.__copylist.add(assetItem)

            if self.__profile.getUseSource():
                # Compute relative folder from asset location to even external
                # locations (e.g. auto cloned remote projects)
                entry["u"] = os.path.relpath(assetItem.getPath(), assetPath)
            elif self.__profile.getHashAssets():
                # Export checksum (SHA1 encoded as URL-safe Base62)
                entry["h"] = assetItem.getChecksum()

            # Store file type as analyzed by asset item
            entry["t"] = assetItem.getType(short=True)

            # Store additional data figured out by asset item e.g.
            # image dimensions, video format, play duration, ...
            assetData = assetItem.exportData()
            if assetData:
                entry["d"] = assetData

            result[fileId] = entry

        # Ignore empty result
        if not result:
            return None

        Console.debug("Exported %s assets", len(result))

        return json.dumps({"assets": self.__structurize(result)},
                          indent=2,
                          sort_keys=True)
Exemple #24
0
def __process(node, scanMixins=False, active=None):
    """
    Recursively processes the given node.

    - scanMixins: Whether mixins or selectors should be processed (phase1 vs. phase2)
    - active: Whether replacements should happen

    """

    modified = 0

    if active is None:
        active = not scanMixins

    for child in reversed(list(node)):
        if child is not None:
            if child.type == "mixin":
                if scanMixins:
                    modified += __process(child,
                                          scanMixins=scanMixins,
                                          active=True)

            else:
                # Only process non mixin childs
                modified += __process(child,
                                      scanMixins=scanMixins,
                                      active=active)

    if active and isMixinCall(node) and not isExtendCall(node):
        name = node.name

        mixin = __findMixin(node.parent, name)
        if not mixin:
            raise Exception(
                "Unknown mixin \"%s\" to include! Do you miss an include for another style sheet?"
                % (name))

        replacements = __resolveMixin(mixin, getattr(node, "params", None))

        Console.debug("Replacing call %s at line %s with mixin from line %s" %
                      (name, node.line, replacements.line))

        __injectContent(replacements, node)

        # Reverse inject all children of that block
        # at the same position as the original call
        parent = node.parent
        pos = parent.index(node)
        for child in reversed(replacements):
            parent.insert(pos, child)

        # Finally remove original node
        parent.remove(node)

        modified += 1

    return modified
Exemple #25
0
    def exportToJson(self, items=None):
        """
        Exports asset data for usage at the client side.

        Utilizes JavaScript class jasy.Asset to inject data into the client at runtime.

        """

        # Processing assets
        assets = self.__assets

        # Destination folder for assets
        assetPath = os.path.join(self.__profile.getDestinationPath(), self.__profile.getAssetOutputFolder())

        result = {}
        filterExpr = self.__compileFilterExpr(items) if items else None
        for fileId in assets:
            if filterExpr and not filterExpr.match(fileId):
                continue

            entry = {}
            # t = file type
            # u = full file url
            # h = file hash (based on content)
            # d = asset data (image size, etc.)

            assetItem = assets[fileId]
            self.__copylist.add(assetItem)

            if self.__profile.getUseSource():
                # Compute relative folder from asset location to even external
                # locations (e.g. auto cloned remote projects)
                entry["u"] = os.path.relpath(assetItem.getPath(), assetPath)
            elif self.__profile.getHashAssets():
                # Export checksum (SHA1 encoded as URL-safe Base62)
                entry["h"] = assetItem.getChecksum()

            # Store file type as analyzed by asset item
            entry["t"] = assetItem.getType(short=True)

            # Store additional data figured out by asset item e.g.
            # image dimensions, video format, play duration, ...
            assetData = assetItem.exportData()
            if assetData:
                entry["d"] = assetData

            result[fileId] = entry

        # Ignore empty result
        if not result:
            return None

        Console.debug("Exported %s assets", len(result))

        return json.dumps({
            "assets" : self.__structurize(result)
        }, indent=2, sort_keys=True)
Exemple #26
0
def addTask(task):
    """Registers the given task with its name."""

    if task.name in __taskRegistry:
        Console.debug("Overriding task: %s" % task.name)
    else:
        Console.debug("Registering task: %s" % task.name)

    __taskRegistry[task.name] = task
Exemple #27
0
    def setCurrentPrefix(self, path):
        """Interface for Task class to configure the current prefix to use"""

        if path is None:
            self.__currentPrefix = None
            Console.debug("Resetting prefix to working directory")
        else:
            self.__currentPrefix = os.path.normpath(os.path.abspath(os.path.expanduser(path)))
            Console.debug("Setting prefix to: %s" % self.__currentPrefix)
Exemple #28
0
def processSelectors(tree):
    """Processes all mixin includes inside selectors."""

    Console.info("Merging mixins into selectors")
    Console.indent()
    modified = __process(tree, scanMixins=False)
    Console.debug("Merged %s mixins", modified)
    Console.outdent()

    return modified
Exemple #29
0
def processMixins(tree):
    """Processes all mixin includes inside mixins."""

    Console.info("Merging mixins with each other...")
    Console.indent()
    modified = __process(tree, scanMixins=True)
    Console.debug("Merged %s mixins", modified)
    Console.outdent()

    return modified
Exemple #30
0
def processExtends(tree):
    """Processes all requests for mixin extends."""

    Console.info("Processing extend requests...")
    Console.indent()
    modified = __extend(tree)
    Console.debug("Processed %s selectors", modified)
    Console.outdent()

    return modified
Exemple #31
0
def cleanup(node):
    """Reprocesses JavaScript to remove dead paths."""

    Console.debug("Removing dead code branches...")

    Console.indent()
    result = __cleanup(node)
    Console.outdent()

    return result
Exemple #32
0
def processSelectors(tree):
    """Processes all mixin includes inside selectors."""

    Console.info("Merging mixins into selectors")
    Console.indent()
    modified = __process(tree, scanMixins=False)
    Console.debug("Merged %s mixins", modified)
    Console.outdent()

    return modified
Exemple #33
0
def processMixins(tree):
    """Processes all mixin includes inside mixins."""

    Console.info("Merging mixins with each other...")
    Console.indent()
    modified = __process(tree, scanMixins=True)
    Console.debug("Merged %s mixins", modified)
    Console.outdent()

    return modified
Exemple #34
0
def processExtends(tree):
    """Processes all requests for mixin extends."""

    Console.info("Processing extend requests...")
    Console.indent()
    modified = __extend(tree)
    Console.debug("Processed %s selectors", modified)
    Console.outdent()

    return modified
Exemple #35
0
    def setCurrentPrefix(self, path):
        """Interface for Task class to configure the current prefix to use"""

        if path is None:
            self.__currentPrefix = None
            Console.debug("Resetting prefix to working directory")
        else:
            self.__currentPrefix = os.path.normpath(
                os.path.abspath(os.path.expanduser(path)))
            Console.debug("Setting prefix to: %s" % self.__currentPrefix)
Exemple #36
0
def cleanup(node):
    """Reprocesses JavaScript to remove dead paths."""

    Console.debug("Removing dead code branches...")

    Console.indent()
    result = __cleanup(node)
    Console.outdent()

    return result
Exemple #37
0
def executeCommand(args, failMessage=None, path=None, wrapOutput=True):
    """
    Executes the given process and outputs failMessage when errors happen.

    :param args:
    :type args: str or list
    :param failMessage: Message for exception when command fails
    :type failMessage: str
    :param path: Directory path where the command should be executed
    :type path: str
    :raise Exception: Raises an exception whenever the shell command fails in execution
    :type wrapOutput: bool
    :param wrapOutput: Whether shell output should be wrapped and returned (and passed through to Console.debug())

    """

    if isinstance(args, str):
        args = shlex.split(args)

    prevpath = os.getcwd()

    # Execute in custom directory
    if path:
        path = os.path.abspath(os.path.expanduser(path))
        os.chdir(path)

    Console.debug("Executing command: %s", " ".join(args))
    Console.indent()

    # Using shell on Windows to resolve binaries like "git"
    if not wrapOutput:
        returnValue = subprocess.call(args, shell=sys.platform == "win32")
        result = returnValue

    else:
        output = tempfile.TemporaryFile(mode="w+t")
        returnValue = subprocess.call(args, stdout=output, stderr=output, shell=sys.platform == "win32")

        output.seek(0)
        result = output.read().strip("\n\r")
        output.close()

    # Change back to previous path
    os.chdir(prevpath)

    if returnValue != 0 and failMessage:
        raise Exception("Error during executing shell command: %s (%s)" % (failMessage, result))

    if wrapOutput:
        for line in result.splitlines():
            Console.debug(line)

    Console.outdent()

    return result
Exemple #38
0
    def getFields(self):
        """Returns the fields which are used by this stylesheet."""

        field = "style:fields[%s]" % self.id
        fields = self.project.getCache().read(field, self.mtime)
        if fields is None:
            Console.debug("Collecting fields %s...", Console.colorize(self.id, "bold"))
            fields = collectFields(self.__getTree())
            self.project.getCache().store(field, fields, self.mtime)

        return fields
Exemple #39
0
    def clear(self):
        """Clears the cache file(s)"""

        if self.__shelve is not None:
            Console.debug("Closing cache file %s..." % self.__file)

            self.__shelve.close()
            self.__shelve = None

        for fileName in glob.glob("%s*" % self.__file):
            Console.debug("Clearing cache file %s..." % fileName)
            os.remove(fileName)
Exemple #40
0
 def addClassName(self, className):
     """ Adds a class to the initial dependencies """
     
     if not className in self.__classes:
         raise Exception("Unknown Class: %s" % className)
         
     Console.debug("Adding class: %s", className)
     self.__required.append(self.__classes[className])
     
     del self.__included[:]
     
     return self
Exemple #41
0
    def close(self):
        """Closes the session and stores cache to the harddrive."""

        Console.debug("Closing session...")
        Console.indent()

        for project in self.__projects:
            project.close()

        self.__projects = None

        Console.outdent()
Exemple #42
0
    def clear(self):
        """Clears the cache file(s)"""

        if self.__shelve is not None:
            Console.debug("Closing cache file %s..." % self.__file)

            self.__shelve.close()
            self.__shelve = None

        for fileName in glob.glob("%s*" % self.__file):
            Console.debug("Clearing cache file %s..." % fileName)
            os.remove(fileName)
Exemple #43
0
def __extendContent(node, call, targetBlock, stopCombineAt):
    """
    Builds up a list of selector/@media/@support to insert after
    the extend to produce the @content sections on the intended selectors.
    """

    for child in reversed(list(node)):
        if child:
            __extendContent(child, call, targetBlock, stopCombineAt)

    if node.type == "content" and hasattr(call, "rules"):
        # Extends support @content as well. In this case we produce a new selector
        # which matches the position of the content section and append it after
        # the original extended mixin on return

        Console.debug("Inserting content section into new virtual selector")

        selector, media, supports = Util.combineSelector(node,
                                                         stop=stopCombineAt)

        selectorNode = Node.Node(type="selector")
        selectorNode.name = selector

        selectorNode.append(copy.deepcopy(call.rules), "rules")

        # Support @supports
        if supports:
            supportsNode = Node.Node(type="supports")
            supportsNode.name = supports

            supportsBlock = Node.Node(type="block")
            supportsBlock.append(selectorNode)
            supportsNode.append(supportsBlock, "rules")

            # Update reference
            selectorNode = supportsNode

        # Support @media
        if media:
            mediaNode = Node.Node(type="media")
            mediaNode.name = media

            mediaBlock = Node.Node(type="block")
            mediaBlock.append(selectorNode)
            mediaNode.append(mediaBlock, "rules")

            # Update reference
            selectorNode = mediaNode

        # Insert selectorNode (or media node or supports node when updated)
        # If all kinds are used we should have the following structure:
        # @media->@supports->selector
        targetBlock.append(selectorNode)
Exemple #44
0
def __extendContent(node, call, targetBlock, stopCombineAt):
    """
    Builds up a list of selector/@media/@support to insert after
    the extend to produce the @content sections on the intended selectors.
    """

    for child in reversed(list(node)):
        if child:
            __extendContent(child, call, targetBlock, stopCombineAt)

    if node.type == "content" and hasattr(call, "rules"):
        # Extends support @content as well. In this case we produce a new selector
        # which matches the position of the content section and append it after
        # the original extended mixin on return

        Console.debug("Inserting content section into new virtual selector")

        selector, media, supports = Util.combineSelector(node, stop=stopCombineAt)

        selectorNode = Node.Node(type="selector")
        selectorNode.name = selector

        selectorNode.append(copy.deepcopy(call.rules), "rules")

        # Support @supports
        if supports:
            supportsNode = Node.Node(type="supports")
            supportsNode.name = supports

            supportsBlock = Node.Node(type="block")
            supportsBlock.append(selectorNode)
            supportsNode.append(supportsBlock, "rules")

            # Update reference
            selectorNode = supportsNode

        # Support @media
        if media:
            mediaNode = Node.Node(type="media")
            mediaNode.name = media

            mediaBlock = Node.Node(type="block")
            mediaBlock.append(selectorNode)
            mediaNode.append(mediaBlock, "rules")

            # Update reference
            selectorNode = mediaNode

        # Insert selectorNode (or media node or supports node when updated)
        # If all kinds are used we should have the following structure:
        # @media->@supports->selector
        targetBlock.append(selectorNode)
Exemple #45
0
def mergeMixin(className, mixinName, classApi, mixinApi):
    Console.debug("Merging %s into %s", mixinName, className)

    sectionLink = ["member", "property", "event"]

    for pos, section in enumerate(("members", "properties", "events")):
        mixinItems = getattr(mixinApi, section, None)
        if mixinItems:
            ScriptItems = getattr(classApi, section, None)
            if not ScriptItems:
                ScriptItems = {}
                setattr(classApi, section, ScriptItems)

            for name in mixinItems:

                # Overridden Check
                if name in ScriptItems:

                    # If it was included, just store another origin
                    if "origin" in ScriptItems[name]:
                        ScriptItems[name]["origin"].append({
                            "name":
                            mixinName,
                            "link":
                            "%s:%s~%s" % (sectionLink[pos], mixinName, name)
                        })

                    # Otherwise add it to the overridden list
                    else:
                        if "overridden" not in ScriptItems[name]:
                            ScriptItems[name]["overridden"] = []

                        ScriptItems[name]["overridden"].append({
                            "name":
                            mixinName,
                            "link":
                            "%s:%s~%s" % (sectionLink[pos], mixinName, name)
                        })

                # Remember where classes are included from
                else:
                    ScriptItems[name] = {}
                    ScriptItems[name].update(mixinItems[name])
                    if "origin" not in ScriptItems[name]:
                        ScriptItems[name]["origin"] = []

                    ScriptItems[name]["origin"].append({
                        "name":
                        mixinName,
                        "link":
                        "%s:%s~%s" % (sectionLink[pos], mixinName, name)
                    })
Exemple #46
0
 def __compileFilterExpr(self, classes):
     """Returns the regular expression object to use for filtering"""
     
     # Merge asset hints from all classes and remove duplicates
     hints = set()
     for classObj in classes:
         hints.update(classObj.getMetaData(self.__session.getCurrentPermutation()).assets)
     
     # Compile filter expressions
     matcher = "^%s$" % "|".join(["(%s)" % fnmatch.translate(hint) for hint in hints])
     Console.debug("Compiled asset matcher: %s" % matcher)
     
     return re.compile(matcher)
Exemple #47
0
    def getMetaData(self, permutation):
        """Returns the meta data of this stylesheet."""

        permutation = self.filterPermutation(permutation)

        field = "style:meta[%s]-%s" % (self.id, permutation)
        meta = self.project.getCache().read(field, self.mtime)
        if meta is None:
            Console.debug("Collecting meta data %s...", Console.colorize(self.id, "bold"))
            meta = MetaData.MetaData(self.__getPermutatedTree(permutation))
            self.project.getCache().store(field, meta, self.mtime)

        return meta
Exemple #48
0
def executeCommand(args, failmsg=None, path=None):
    """
    Executes the given process and outputs failmsg when errors happen.
    Returns the combined shell output (stdout and strerr combined).

    :param args: 
    :type args: str or list
    :param failmsg: Message for exception when command fails
    :type failmsg: str
    :param path: Directory path where the command should be executed
    :type path: str
    :raise Exception: Raises an exception whenever the shell command fails in execution
    """

    if type(args) == str:
        args = shlex.split(args)

    prevpath = os.getcwd()

    # Execute in custom directory
    if path:
        path = os.path.abspath(os.path.expanduser(path))
        os.chdir(path)

    Console.debug("Executing command: %s", " ".join(args))
    Console.indent()

    # Using shell on Windows to resolve binaries like "git"
    output = tempfile.TemporaryFile(mode="w+t")
    returnValue = subprocess.call(args,
                                  stdout=output,
                                  stderr=output,
                                  shell=sys.platform == "win32")

    output.seek(0)
    result = output.read().strip("\n\r")
    output.close()

    # Change back to previous path
    os.chdir(prevpath)

    if returnValue != 0:
        raise Exception("Error during executing shell command: %s (%s)" %
                        (failmsg, result))

    for line in result.splitlines():
        Console.debug(line)

    Console.outdent()

    return result
Exemple #49
0
def __processOperator(node, values):
    Console.debug("Process operator: %s", node.type)

    # Resolve first child of operation
    first = node[0]
    if first.type == "variable":
        first = values[first.name]

    # Resolve second child of operation
    second = node[1]
    if second.type == "variable":
        second = values[second.name]

    return __computeOperation(first, second, node, node.type, values)
Exemple #50
0
    def clear(self):
        """Removes all generated sprite files found in the base directory."""

        Console.info("Cleaning sprite files...")
        Console.indent()

        for dirPath, dirNames, fileNames in os.walk(self.base):
            for fileName in fileNames:
                if fileName.startswith("jasysprite"):
                    filePath = os.path.join(dirPath, fileName)
                    Console.debug("Removing file: %s", filePath)
                    os.remove(filePath)

        Console.outdent()
Exemple #51
0
    def getIncludes(self, permutation):
        """Returns the includes which are referenced by this stylesheet."""

        field = "style:includes[%s]" % self.id
        includes = self.project.getCache().read(field, self.mtime)
        if includes is None:
            Console.debug("Collecting includes %s...", Console.colorize(self.id, "bold"))
            includes = []
            for includeName, includeNode in includeGenerator(self.__getPermutatedTree(permutation)):
                includes.append(includeName)

            self.project.getCache().store(field, includes, self.mtime)

        return includes
Exemple #52
0
def __injectContent(node, call):
    """Inserts content section of call into prepared content area of mixin clone."""

    for child in reversed(list(node)):
        if child:
            __injectContent(child, call)

    if node.type == "content":
        if hasattr(call, "rules"):
            Console.debug(
                "Inserting content section from call into mixin clone")
            node.parent.insertAllReplace(node, copy.deepcopy(call.rules))
        else:
            Console.debug("Removing unused content section from mixin clone")
            node.parent.remove(node)
Exemple #53
0
    def __addRuntimeData(self, runtime):
        assets = self.__assets
        data = self.__data

        for fileId in runtime:
            if not fileId in assets:
                Console.debug("Unknown asset: %s" % fileId)
                continue

            if not fileId in data:
                data[fileId] = {}

            data[fileId].update(runtime[fileId])

        return self
Exemple #54
0
    def close(self):
        """Closes the session and stores cache to the harddrive."""

        if not self.__projects:
            return

        Console.debug("Closing session...")
        Console.indent()

        for project in self.__projects:
            project.close()

        self.__projects = None

        Console.outdent()
Exemple #55
0
    def __addContent(self, content):
        Console.debug("Adding manual content")

        Console.indent()
        for fileId in content:
            fileContent = content[fileId]
            if len(fileContent) == 0:
                raise UserError("Empty content!")

            # If the user defines a file extension for JS public idenfiers
            # (which is not required) we filter them out
            if fileId.endswith(".js"):
                raise UserError(
                    "JavaScript files should define the exported name, not a file name: %s"
                    % fileId)

            fileExtension = os.path.splitext(fileContent[0])[1]

            # Support for joining text content
            if len(fileContent) == 1:
                filePath = os.path.join(self.__path, fileContent[0])
            else:
                filePath = [
                    os.path.join(self.__path, filePart)
                    for filePart in fileContent
                ]

            # Structure files
            if fileExtension in classExtensions:
                construct = jasy.item.Class.ClassItem
                dist = self.classes
            elif fileExtension in translationExtensions:
                construct = jasy.item.Translation.TranslationItem
                dist = self.translations
            else:
                construct = jasy.item.Asset.AssetItem
                dist = self.assets

            # Check for duplication
            if fileId in dist:
                raise UserError("Item ID was registered before: %s" % fileId)

            # Create instance
            item = construct(self, fileId).attach(filePath)
            Console.debug("Registering %s %s" % (item.kind, fileId))
            dist[fileId] = item

        Console.outdent()