File: __main__.py

package info (click to toggle)
cmake-format 0.6.13-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,436 kB
  • sloc: python: 16,990; makefile: 14
file content (214 lines) | stat: -rw-r--r-- 6,450 bytes parent folder | download | duplicates (4)
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
203
204
205
206
207
208
209
210
211
212
213
214
# -*- coding: utf-8 -*-
# PYTHON_ARGCOMPLETE_OK
"""
Check cmake listfile for lint
"""
from __future__ import unicode_literals

import argparse
import io
import logging
import os
import sys

import cmakelang
from cmakelang import common
from cmakelang.format import __main__
from cmakelang import configuration
from cmakelang import lex
from cmakelang import parse

from cmakelang.lint import basic_checker
from cmakelang.lint import lint_util

logger = logging.getLogger(__name__)


def process_file(config, local_ctx, infile_content):
  """
  Parse the input cmake file, re-format it, and print to the output file.
  """

  if config.format.line_ending == 'auto':
    detected = __main__.detect_line_endings(infile_content)
    config = config.clone()
    config.set_line_ending(detected)

  checker = basic_checker.LintChecker(config, local_ctx)
  checker.check_basics(infile_content)
  tokens = lex.tokenize(infile_content)
  checker.check_tokens(tokens)

  parse_db = parse.funs.get_parse_db()
  parse_db.update(parse.funs.get_funtree(config.parse.fn_spec))
  ctx = parse.ParseContext(parse_db, local_ctx, config)
  parse_tree = parse.parse(tokens, ctx)
  parse_tree.build_ancestry()
  checker.check_parse_tree(parse_tree)


def setup_argparse(argparser):
  argparser.add_argument('-v', '--version', action='version',
                         version=cmakelang.__version__)
  argparser.add_argument(
      '-l', '--log-level', default="info",
      choices=["error", "warning", "info", "debug"])

  mutex = argparser.add_mutually_exclusive_group()
  mutex.add_argument('--dump-config', choices=['yaml', 'json', 'python'],
                     default=None, const='python', nargs='?',
                     help='If specified, print the default configuration to '
                          'stdout and exit')
  mutex.add_argument('-o', '--outfile-path', default=None,
                     help='Write errors to this file. '
                          'Default is stdout.')

  argparser.add_argument(
      "--no-help", action="store_false", dest="with_help",
      help="When used with --dump-config, will omit helptext comments in the"
           " output"
  )
  argparser.add_argument(
      "--no-default", action="store_false", dest="with_defaults",
      help="When used with --dump-config, will omit any unmodified "
           "configuration value."
  )

  argparser.add_argument(
      "--suppress-decorations", action="store_true",
      help="Suppress the file title decoration and summary statistics")

  argparser.add_argument(
      '-c', '--config-files', nargs='+',
      help='path to configuration file(s)')
  argparser.add_argument('infilepaths', nargs='*')

  configuration.Configuration().add_to_argparser(argparser)


USAGE_STRING = """
cmake-lint [-h]
           [--dump-config {yaml,json,python} | -o OUTFILE_PATH]
           [-c CONFIG_FILE]
           infilepath [infilepath ...]
"""


def get_argparser():
  arg_parser = argparse.ArgumentParser(
      prog='cmake-lint',
      description=__doc__,
      formatter_class=argparse.RawDescriptionHelpFormatter,
      usage=USAGE_STRING)

  setattr(arg_parser, 'man_short_description', __doc__.strip().splitlines()[0])

  setup_argparse(arg_parser)
  return arg_parser

def inner_main():
  """Parse arguments, open files, start work."""
  logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

  argparser = get_argparser()
  try:
    import argcomplete
    argcomplete.autocomplete(argparser)
  except ImportError:
    pass
  args = argparser.parse_args()
  logging.getLogger().setLevel(getattr(logging, args.log_level.upper()))

  if args.dump_config:
    config_dict = __main__.get_config(os.getcwd(), args.config_files)
    __main__.dump_config(args, config_dict, sys.stdout)
    sys.exit(0)

  if args.outfile_path is None:
    args.outfile_path = '-'

  if '-' in args.infilepaths:
    assert len(args.infilepaths) == 1, \
        "You cannot mix stdin as an input with other input files"

  if args.outfile_path == '-':
    outfile = io.open(os.dup(sys.stdout.fileno()),
                      mode='w', encoding="utf-8", newline='')
  else:
    outfile = io.open(args.outfile_path, 'w', encoding="utf-8", newline='')

  global_ctx = lint_util.GlobalContext(outfile)
  returncode = 0
  argdict = __main__.get_argdict(args)

  for infile_path in args.infilepaths:
    # NOTE(josh): have to load config once for every file, because we may pick
    # up a new config file location for each path
    if infile_path == '-':
      config_dict = __main__.get_config(os.getcwd(), args.config_files)
    else:
      config_dict = __main__.get_config(infile_path, args.config_files)
    config_dict.update(argdict)

    cfg = configuration.Configuration(**config_dict)
    if infile_path == '-':
      infile_path = os.dup(sys.stdin.fileno())

    try:
      infile = io.open(
          infile_path, mode='r', encoding=cfg.encode.input_encoding, newline='')
    except (IOError, OSError):
      logger.error("Failed to open %s for read", infile_path)
      returncode = 1
      continue

    try:
      with infile:
        intext = infile.read()
    except UnicodeDecodeError:
      logger.error(
          "Unable to read %s as %s", infile_path, cfg.encode.input_encoding)
      returncode = 1
      continue

    local_ctx = global_ctx.get_file_ctx(infile_path, cfg)
    process_file(cfg, local_ctx, intext)
    if not args.suppress_decorations:
      outfile.write("{}\n{}\n".format(infile_path, "=" * len(infile_path)))
    local_ctx.writeout(outfile)
    if not args.suppress_decorations:
      outfile.write("\n")
    if local_ctx.has_lint():
      returncode = 1

  if not args.suppress_decorations:
    global_ctx.write_summary(outfile)
  outfile.close()
  return returncode


def main():
  try:
    return inner_main()
  except SystemExit:
    return 0
  except common.UserError as ex:
    logger.fatal(ex.msg)
    return 1
  except common.InternalError as ex:
    logger.exception(ex.msg)
    return 2
  except AssertionError as ex:
    logger.exception(
        "An internal error occured. Please consider filing a bug report at "
        "github.com/cheshirekow/cmakelang/issues")
    return 2
  except:  # pylint: disable=bare-except
    logger.exception(
        "An internal error occured. Please consider filing a bug report at "
        "github.com/cheshirekow/cmakelang/issues")
    return 2


if __name__ == "__main__":
  sys.exit(main())