1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
# PYTHON_ARGCOMPLETE_OK
"""
Parse cmake help to generate certain input files
TODO(josh): `cmake --help-module-list` to get a list of builtin find-modules
that we can search for more commands.
TODO(josh): `cmake --help-commandplist` to get a list of builtin commands
that we can generate parsers for.
"""
from __future__ import print_function, unicode_literals
import argparse
import io
import logging
import os
import re
import subprocess
import sys
import jinja2
logger = logging.getLogger(__name__)
def get_abspath(relpath):
return os.path.abspath(os.path.join(os.path.dirname(__file__), relpath))
def sub_callback(match):
# NOTE(josh): it would be handly to use named groups in regular expressions,
# but since we use | to join them together into a big regex... the names
# will collide so we cannot
return "(?P<{}>.*)".format(re.sub(r"\W", "_", match.group(1)))
GENERIC_LABEL = re.compile(r"<([^>]+)>")
def make_pattern(namestr):
"""
Look for any generic labels within a cmake property or variable name
(e.g. `<LANG>` in `<LANG>_CPPLINT`) and convert the pattern string to a
regular expression pattern (e.g. `.*_CPPLINT`)
"""
return GENERIC_LABEL.sub(sub_callback, namestr)
def strip_named_groups(pattern):
return re.sub(r"\(?P<[\w_]>", "(", pattern)
def get_properties(args, jenv):
proc = subprocess.Popen(
[args.cmake, "--help-property-list"],
stdout=subprocess.PIPE)
with proc.stdout as infile:
properties = [line.decode("utf-8").strip() for line in infile]
patterns = [make_pattern(namestr) for namestr in properties]
proc.wait()
template = jenv.get_template("properties.jinja.py")
content = template.render(patterns=patterns)
if args.outfile == "-":
args.outfile = os.dup(sys.stdout.fileno())
with io.open(args.outfile, "w", encoding="utf-8") as outfile:
outfile.write(content)
outfile.write("\n")
def get_variables(args, jenv):
proc = subprocess.Popen(
[args.cmake, "--help-variable-list"],
stdout=subprocess.PIPE)
with proc.stdout as infile:
variables = [line.decode("utf-8").strip() for line in infile]
patterns = [make_pattern(namestr) for namestr in variables]
proc.wait()
template = jenv.get_template("variables.jinja.py")
content = template.render(patterns=patterns)
if args.outfile == "-":
args.outfile = os.dup(sys.stdout.fileno())
with io.open(args.outfile, "w", encoding="utf-8") as outfile:
outfile.write(content)
outfile.write("\n")
def get_command_list(args):
"""
Get a list of all the builtin cmake commands (statement names).
"""
proc = subprocess.Popen(
[args.cmake, "--help-command-list"],
stdout=subprocess.PIPE)
with proc.stdout as infile:
return [line.decode("utf-8").strip() for line in infile]
def get_command_help(args, command_name):
"""
Get the help string for a specific command
"""
proc = subprocess.Popen(
[args.cmake, "--help-command", command_name],
stdout=subprocess.PIPE)
with proc.stdout as infile:
return infile.read().decode("utf-8")
def get_usages(helpstr):
"""
Parse the command help string and return a list of command usage strings.
"""
usage = []
active = False
buf = ""
lineiter = iter(helpstr.split("\n"))
for line in lineiter:
if active:
if not line.strip():
active = False
usage.append(buf)
buf = ""
else:
buf += line.rstrip() + "\n"
elif line.strip() == "::":
next(lineiter, None)
active = True
if buf:
usage.append(buf)
return usage
def cmd_get_usages(args, jenv): # pylint: disable=W0613
for command_name in get_command_list(args):
helpstr = get_command_help(args, command_name)
for usage in get_usages(helpstr):
print(usage)
def cmd_print_deprecated(args, jenv): # pylint: disable=W0613
for command_name in get_command_list(args):
helpstr = get_command_help(args, command_name)
if "Deprecated" in helpstr:
print(command_name)
def setup_argparse(parser):
"""Setup argument parser"""
parser.add_argument(
"--cmake", default="cmake",
help="Cmake binary path or command. Default is `cmake`")
parser.add_argument(
"--outfile", default="-",
help="output file, default is stdout")
subparsers = parser.add_subparsers(dest="command")
subparsers.add_parser("properties")
subparsers.add_parser("variables")
subparsers.add_parser("usages")
subparsers.add_parser("deprecated")
def main():
parser = argparse.ArgumentParser(description=__doc__)
setup_argparse(parser)
try:
import argcomplete
argcomplete.autocomplete(parser)
except ImportError:
pass
args = parser.parse_args()
thisdir = os.path.abspath(os.path.dirname(__file__))
jenv = jinja2.Environment(
loader=jinja2.FileSystemLoader(thisdir)
)
if args.command == "properties":
get_properties(args, jenv)
elif args.command == "variables":
get_variables(args, jenv)
elif args.command == "usages":
cmd_get_usages(args, jenv)
elif args.command == "deprecated":
cmd_print_deprecated(args, jenv)
else:
logger.warning("Unknown command %s", args.command)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
main()
|