Alien-SVN

 view release on metacpan or  search on metacpan

src/subversion/build/generator/gen_base.py  view on Meta::CPAN

  """Find files matching a space separated list of globs

  pats (string) is the list of glob patterns

  path (string), if specified, is a path that will be prepended to each
    glob pattern before it is evaluated

  If path is none the return value is a list of filenames, otherwise
  the return value is a list of 2-tuples. The first element in each tuple
  is a matching filename and the second element is the portion of the
  glob pattern which matched the file before its last forward slash (/)
  """
  result = [ ]
  for base_pat in pats.split():
    if path:
      pattern = build_path_join(path, base_pat)
    else:
      pattern = base_pat
    files = sorted(glob.glob(native_path(pattern))) or [pattern]

    if path is None:
      # just append the names to the result list
      for file in files:
        result.append(build_path(file))
    else:
      # if we have paths, then we need to record how each source is located
      # relative to the specified path
      reldir = build_path_dirname(base_pat)
      for file in files:
        result.append((build_path(file), reldir))

  return result

_re_public_include = re.compile(r'^subversion/include/(\w+)\.h$')
def _is_public_include(fname):
  return _re_public_include.match(build_path(fname))

def _swig_include_wrapper(fname):
  return native_path(_re_public_include.sub(
    r"subversion/bindings/swig/proxy/\1_h.swg", build_path(fname)))

def _path_endswith(path, subpath):
  """Check if SUBPATH is a true path suffix of PATH.
  """
  path_len = len(path)
  subpath_len = len(subpath)

  return (subpath_len > 0 and path_len >= subpath_len
          and path[-subpath_len:] == subpath
          and (path_len == subpath_len
               or (subpath[0] == os.sep and path[-subpath_len] == os.sep)
               or path[-subpath_len - 1] == os.sep))

class IncludeDependencyInfo:
  """Finds all dependencies between a named set of headers, and computes
  closure, so that individual C and SWIG source files can then be scanned, and
  the stored dependency data used to return all directly and indirectly
  referenced headers.

  Note that where SWIG is concerned, there are two different kinds of include:
  (1) those that include files in SWIG processing, and so matter to the
      generation of .c files. (These are %include, %import).
  (2) those that include references to C headers in the generated output,
      and so are not required at .c generation, only at .o generation.
      (These are %{ #include ... %}).

  This class works exclusively in native-style paths."""

  def __init__(self, filenames, fnames_nonexist):
    """Operation of an IncludeDependencyInfo instance is restricted to a
    'domain' - a set of header files which are considered interesting when
    following and reporting dependencies.  This is done to avoid creating any
    dependencies on system header files.  The domain is defined by three
    factors:
    (1) FILENAMES is a list of headers which are in the domain, and should be
        scanned to discover how they inter-relate.
    (2) FNAMES_NONEXIST is a list of headers which are in the domain, but will
        be created by the build process, and so are not available to be
        scanned - they will be assumed not to depend on any other interesting
        headers.
    (3) Files in subversion/bindings/swig/proxy/, which are based
        autogenerated based on files in subversion/include/, will be added to
        the domain when a file in subversion/include/ is processed, and
        dependencies will be deduced by special-case logic.
    """

    # This defines the domain (i.e. set of files) in which dependencies are
    # being located. Its structure is:
    # { 'basename.h': [ 'path/to/something/named/basename.h',
    #                   'path/to/another/named/basename.h', ] }
    self._domain = {}
    for fname in filenames + fnames_nonexist:
      bname = os.path.basename(fname)
      self._domain.setdefault(bname, []).append(fname)
      if _is_public_include(fname):
        swig_fname = _swig_include_wrapper(fname)
        swig_bname = os.path.basename(swig_fname)
        self._domain.setdefault(swig_bname, []).append(swig_fname)

    # This data structure is:
    # { 'full/path/to/header.h': { 'full/path/to/dependency.h': TYPECODE, } }
    # TYPECODE is '#', denoting a C include, or '%' denoting a SWIG include.
    self._deps = {}
    for fname in filenames:
      self._deps[fname] = self._scan_for_includes(fname)
      if _is_public_include(fname):
        hdrs = { self._domain["proxy.swg"][0]: '%',
                 self._domain["apr.swg"][0]: '%',
                 fname: '%' }
        for h in self._deps[fname].keys():
          if (_is_public_include(h)
              or h == os.path.join('subversion', 'include', 'private',
                                    'svn_debug.h')):
            hdrs[_swig_include_wrapper(h)] = '%'
          else:
            raise RuntimeError("Public include '%s' depends on '%s', " \
                "which is not a public include! What's going on?" % (fname, h))
        swig_fname = _swig_include_wrapper(fname)
        swig_bname = os.path.basename(swig_fname)
        self._deps[swig_fname] = hdrs
    for fname in fnames_nonexist:
      self._deps[fname] = {}

    # Keep recomputing closures until we see no more changes
    while True:
      changes = 0
      for fname in self._deps.keys():
        changes = self._include_closure(self._deps[fname]) or changes
      if not changes:
        break

  def query_swig(self, fname):
    """Scan the C or SWIG file FNAME, and return the full paths of each
    include file that is a direct or indirect dependency, as a 2-tuple:
      (C_INCLUDES, SWIG_INCLUDES)."""
    if fname in self._deps:
      hdrs = self._deps[fname]
    else:
      hdrs = self._scan_for_includes(fname)
      self._include_closure(hdrs)
    c_filenames = []
    swig_filenames = []
    for hdr, hdr_type in hdrs.items():



( run in 0.374 second using v1.01-cache-2.11-cpan-02777c243ea )