2aeef25881
Summary: database/GraphDbAccessor and storage/...Accessor now returns iterators over lazily created Accessors (filtered on visibility). Dependecies and forward declarations reorganized to support new code Reviewers: buda, teon.banek Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D83
179 lines
5.7 KiB
Python
Executable File
179 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
"""
|
|
A script for finding and [copying|printing] C++
|
|
headers that get recursively imported from one (or more)
|
|
starting points.
|
|
|
|
Supports absolute imports relative to some root folder
|
|
(project root) and relative imports relative to the
|
|
header that is doing the importing.
|
|
|
|
Does not support conditional imports (resulting from
|
|
#ifdef macros and such). All the #import statements
|
|
found in a header (one #import per line) are traversed.
|
|
|
|
Supports Python2 and Python3.
|
|
"""
|
|
|
|
__author__ = "Florijan Stamenkovic"
|
|
__copyright__ = "Copyright 2017, Memgraph"
|
|
|
|
import logging
|
|
import sys
|
|
import os
|
|
import re
|
|
import shutil
|
|
from argparse import ArgumentParser
|
|
|
|
# the prefix of an include directive
|
|
PREFIX = "#include"
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def parse_args():
|
|
argp = ArgumentParser(description=__doc__)
|
|
argp.add_argument("--logging", default="INFO", choices=["INFO", "DEBUG"],
|
|
help="Logging level")
|
|
argp.add_argument("--roots", required=True, nargs="+",
|
|
help="One or more paths in which headers are sought")
|
|
argp.add_argument("--start", required=True, nargs="+",
|
|
help="One or more headers from which to start scanning")
|
|
argp.add_argument("--stdout", action="store_true",
|
|
help="If found paths should be printed out to stdout")
|
|
argp.add_argument("--copy", default=None,
|
|
help="Prefix of the path where the headers should be copied")
|
|
return argp.parse_args()
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
logging.basicConfig(level=args.logging)
|
|
|
|
log.info("Recursively detecting used C/C++ headers in roots '%s' with starting point(s) '%s'",
|
|
args.roots, args.start)
|
|
|
|
args.roots = [os.path.abspath(p) for p in args.roots]
|
|
|
|
results = set()
|
|
for start in args.start:
|
|
find_recursive(start, args.roots, results)
|
|
|
|
results = list(sorted(results))
|
|
|
|
log.debug("Found %d paths:", len(results))
|
|
for r in results:
|
|
log.debug("\t%s", r)
|
|
|
|
# print out the results if required
|
|
if args.stdout:
|
|
for result in results:
|
|
print(result)
|
|
|
|
# copy the results if required
|
|
if args.copy is not None:
|
|
for root, path in results:
|
|
from_path = os.path.join(root, path)
|
|
to_path = os.path.join(args.copy, path)
|
|
log.debug("Copying '%s' to '%s'", from_path, to_path)
|
|
|
|
# create a directory if necessary, Py2 and Py3 compatible
|
|
to_dir = os.path.dirname(to_path)
|
|
if not os.path.exists(to_dir):
|
|
os.makedirs(to_dir)
|
|
|
|
shutil.copy(from_path, to_path)
|
|
|
|
|
|
def abs_to_relative(roots, path):
|
|
"""
|
|
Args:
|
|
roots: list of str, a list of possible prefixes
|
|
to the 'path'.
|
|
path: str, a path to a file.
|
|
Return:
|
|
A tuple (relative_path, root) where 'root' is one
|
|
of the given 'roots' and where
|
|
os.path.join(root, relative_path) equals the given
|
|
'path'
|
|
Raise:
|
|
An exception if none of the 'roots' is a prefix of
|
|
'path'
|
|
"""
|
|
for root in roots:
|
|
if path.startswith(root):
|
|
return (root, path[len(root) + 1:])
|
|
|
|
raise Exception("Failed to find prefix of '%s'in '%r'" % (
|
|
path, roots))
|
|
|
|
|
|
def find_recursive(path, roots, results):
|
|
"""
|
|
Recursivelly looks for headers and adds them to results.
|
|
|
|
Results are added as tuples of form (root, header_path)
|
|
where 'root' is one of the given roots: the one in which
|
|
the header was found, and 'header_path' is the found
|
|
header's path relative to 'root'.
|
|
|
|
Args:
|
|
path: str of tuple. If str, it's considered a path
|
|
that has one of the roots as prefix. If tuple
|
|
it's considered a (prefix, suffix) that defines
|
|
a path.
|
|
In both forms the path is to a header. This header is added
|
|
to results and scanned for #include statements of
|
|
other headers. For each #include (relative to current
|
|
`path` or to `project_root`) for which a file is found
|
|
this same function is called.
|
|
roots: list of str, List of folders in which headers are
|
|
sought. Must be absolute paths.
|
|
results: a collection into which the results are
|
|
added. The collection contains tuples of form
|
|
(root, path), see function description.
|
|
"""
|
|
log.debug("Processing path: %s", path)
|
|
|
|
if isinstance(path, str):
|
|
path = os.path.abspath(path)
|
|
path = abs_to_relative(roots, path)
|
|
|
|
# from this point onward 'path' is a tuple (root, suffix)
|
|
|
|
if path in results:
|
|
log.debug("Skipping already present path '%r'", path)
|
|
return
|
|
|
|
log.debug("Adding path '%r'", path)
|
|
results.add(path)
|
|
|
|
# go through files and look for include directives
|
|
with open(os.path.join(*path)) as f:
|
|
for line in filter(lambda l: l.startswith(PREFIX),
|
|
map(lambda l: l.strip(), f)):
|
|
|
|
include = line[len(PREFIX):].strip()
|
|
include = re.sub("[\"\'\>\<]", "", include)
|
|
|
|
log.debug("Processing include '%s'", include)
|
|
|
|
# search for the include relatively to the current header
|
|
include_rel = os.path.join(
|
|
os.path.dirname(os.path.join(*path)), include)
|
|
if os.path.exists(include_rel) and os.path.isfile(include_rel):
|
|
find_recursive(include_rel, roots, results)
|
|
continue
|
|
|
|
# search for file in roots
|
|
for root in roots:
|
|
include_abs = os.path.join(root, include)
|
|
if os.path.exists(include_abs) and os.path.isfile(include_abs):
|
|
find_recursive((root, include), roots, results)
|
|
continue
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|