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
This commit is contained in:
parent
a11ebef9c5
commit
95dcb4c1ba
146
cmake/recursive_include.py
Executable file
146
cmake/recursive_include.py
Executable file
@ -0,0 +1,146 @@
|
||||
#!/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()
|
Loading…
Reference in New Issue
Block a user