pymake/globrelative.py
author Benjamin Smedberg <benjamin@smedbergs.us>
Thu, 26 Feb 2009 20:32:19 -0500
branchresolve-perf
changeset 191 7a8dc41115d67e05b6da433da2f7685ca9941b02
parent 129 bc238aefc4d9405820d8f429534f2c86f2cf66dd
child 269 789e323581cd8777c19d3023addefd6813a43ae9
permissions -rw-r--r--
I noticed Expansion.resolve still comes up really high on perf charts. This patch makes it much easier to resolve expansions which are just literals, which is very common for variable names. Unfortunately, this makes the code a fair bit more complex, and doesn't help nearly as much as I'd like. I'm beginning to wonder if getting gmake performance parity is impossible, or at least improbable given the current architecture: but I can't think of an alternate architecture that is better.

"""
Filename globbing like the python glob module with minor differences:

* glob relative to an arbitrary directory
* include . and ..
* check that link targets exist, not just links
"""

import os, re, fnmatch

_globcheck = re.compile('[[*?]')

def hasglob(p):
    return _globcheck.search(p) is not None

def glob(fsdir, path):
    """
    Yield paths matching the path glob. Sorts as a bonus. Excludes '.' and '..'
    """

    dir, leaf = os.path.split(path)
    if dir == '':
        return globpattern(fsdir, leaf)

    if hasglob(dir):
        dirsfound = glob(fsdir, dir)
    else:
        dirsfound = [dir]

    r = []

    for dir in dirsfound:
        fspath = os.path.join(fsdir, dir)
        if not os.path.isdir(fspath):
            continue

        r.extend((os.path.join(dir, found) for found in globpattern(fspath, leaf)))

    return r

def globpattern(dir, pattern):
    """
    Return leaf names in the specified directory which match the pattern.
    """

    if not hasglob(pattern):
        if pattern == '':
            if os.path.isdir(dir):
                return ['']
            return []

        if os.path.exists(os.path.join(dir, pattern)):
            return [pattern]
        return []

    leaves = os.listdir(dir) + ['.', '..']

    # "hidden" filenames are a bit special
    if not pattern.startswith('.'):
        leaves = [leaf for leaf in leaves
                  if not leaf.startswith('.')]

    leaves = fnmatch.filter(leaves, pattern)
    leaves = filter(lambda l: os.path.exists(os.path.join(dir, l)), leaves)

    leaves.sort()
    return leaves