memgraph/cmake/recursive_include.py
Florijan Stamenkovic 95dcb4c1ba Added recurisive_include.py to cmake folder
Summary: Added a script for recursive header search and copying.

Test Plan: No testing required.

Reviewers: buda

Reviewed By: buda

Subscribers: pullbot, florijan, buda

Differential Revision: https://phabricator.memgraph.io/D47
2017-02-17 10:17:16 +01:00

147 lines
4.6 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("--root", required=True,
help="Root path of the header tree (project root)")
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 root '%s' with starting point(s) '%s'",
args.root, args.start)
results = set()
for start in args.start:
find_recursive(start, args.root, 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 result in results:
from_path = os.path.join(args.root, result)
to_path = os.path.join(args.copy, result)
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 find_recursive(path, project_root, results):
"""
Recursivelly looks for headers and adds them to results.
The headers are added as paths relative to the given
`project_root`
Args:
path: str, path 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.
project_root: str, path to a project root. Used for
finding headers included using an absolute path
(that is actually relative to project_root).
results: a collection into which the results are
added.
"""
log.debug("Processing path: %s", path)
path_abs = os.path.abspath(path)
root_abs = os.path.abspath(project_root)
if not path_abs.startswith(root_abs):
log.warning("Project root '%s' not prefix of path '%s'",
root_abs, path_abs)
path_rel = path_abs[len(root_abs) + 1:]
log.debug("Rel path is '%s'", path_rel)
if path_rel in results:
log.debug("Skipping already present path '%s'", path_rel)
return
log.debug("Adding path '%s'", path_rel)
results.add(path_rel)
# go through files and look for include directives
with open(path_abs) 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)
# check if the file exists relative to this file
# or absolutely to project root
include_abs = os.path.join(project_root, include)
if os.path.exists(include_abs):
find_recursive(include_abs, project_root, results)
include_rel = os.path.join(os.path.dirname(path_abs), include)
if os.path.exists(include_rel):
find_recursive(include_rel, project_root, results)
if __name__ == '__main__':
main()