Exemple #1
0
def parse_install(ctx, tokens, breakstack):
  """
  The ``install()`` command has multiple different forms, implemented
  by different functions. The forms are indicated by the first token
  and are:

  * TARGETS
  * FILES
  * DIRECTORY
  * SCRIPT
  * CODE
  * EXPORT

  :see: https://cmake.org/cmake/help/v3.0/command/install.html
  """

  descriminator_token = get_first_semantic_token(tokens)
  if descriminator_token is None:
    logger.warning("Invalid install() command at %s", tokens[0].get_location())
    return StandardArgTree.parse(ctx, tokens, npargs='*', kwargs={}, flags=[],
                                 breakstack=breakstack)

  descriminator = descriminator_token.spelling.upper()
  parsemap = {
      "TARGETS": parse_install_targets,
      "FILES": parse_install_files,
      "PROGRAMS": parse_install_files,
      "DIRECTORY": parse_install_directory,
      "SCRIPT": parse_install_script,
      "CODE": parse_install_script,
      "EXPORT": parse_install_export
  }
  if descriminator not in parsemap:
    logger.warning("Invalid install form \"%s\" at %s", descriminator,
                   tokens[0].location())
    return StandardArgTree.parse(ctx, tokens, npargs='*', kwargs={}, flags=[],
                                 breakstack=breakstack)

  return parsemap[descriminator](ctx, tokens, breakstack)
Exemple #2
0
def parse_add_custom_command(ctx, tokens, breakstack):
    """
  There are two forms of `add_custom_command`. This is the dispatcher between
  the two forms.
  """
    descriminator_token = get_first_semantic_token(tokens)
    if (descriminator_token is None
            or descriminator_token.type is TokenType.RIGHT_PAREN):
        location = ()
        if tokens:
            location = tokens[0].get_location()
        logger.warning("Invalid empty file() command at %s", location)
        ctx.lint_ctx.record_lint("E1120", location=location)
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    if descriminator_token.type is TokenType.DEREF:
        ctx.lint_ctx.record_lint("C0114",
                                 location=descriminator_token.get_location())
        return parse_add_custom_command_standard(ctx, tokens, breakstack)

    descriminator_word = get_normalized_kwarg(descriminator_token)
    if descriminator_word == "TARGET":
        return parse_add_custom_command_events(ctx, tokens, breakstack)
    if descriminator_word == "OUTPUT":
        return parse_add_custom_command_standard(ctx, tokens, breakstack)

    logger.warning("Indeterminate form of add_custom_command \"%s\" at %s",
                   descriminator_word, descriminator_token.location())
    ctx.lint_ctx.record_lint("E1126",
                             location=descriminator_token.get_location())
    return parse_add_custom_command_standard(ctx, tokens, breakstack)
Exemple #3
0
def parse_file(ctx, tokens, breakstack):
    """
  The ``file()`` command has a lot of different forms, depending on the first
  argument. This function just dispatches the correct parse implementation for
  the given form::

    Reading
      file(READ <filename> <out-var> [...])
      file(STRINGS <filename> <out-var> [...])
      file(<HASH> <filename> <out-var>)
      file(TIMESTAMP <filename> <out-var> [...])

    Writing
      file({WRITE | APPEND} <filename> <content>...)
      file({TOUCH | TOUCH_NOCREATE} [<file>...])
      file(GENERATE OUTPUT <output-file> [...])

    Filesystem
      file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])
      file(RENAME <oldname> <newname>)
      file({REMOVE | REMOVE_RECURSE } [<files>...])
      file(MAKE_DIRECTORY [<dir>...])
      file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
      file(SIZE <filename> <out-var>)
      file(READ_SYMLINK <linkname> <out-var>)
      file(CREATE_LINK <original> <linkname> [...])

    Path Conversion
      file(RELATIVE_PATH <out-var> <directory> <file>)
      file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)

    Transfer
      file(DOWNLOAD <url> <file> [...])
      file(UPLOAD <file> <url> [...])

    Locking
      file(LOCK <path> [...])

  :see: https://cmake.org/cmake/help/v3.14/command/file.html
  """

    descriminator_token = get_first_semantic_token(tokens)
    if (descriminator_token is None
            or descriminator_token.type is TokenType.RIGHT_PAREN):
        location = ()
        if tokens:
            location = tokens[0].get_location()
        logger.warning("Invalid empty file() command at %s", location)
        ctx.lint_ctx.record_lint("E1120", location=location)
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    if descriminator_token.type is TokenType.DEREF:
        ctx.lint_ctx.record_lint("C0114",
                                 location=descriminator_token.get_location())
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    descriminator = descriminator_token.spelling.upper()
    parsemap = {
        "READ": parse_file_read,
        "STRINGS": parse_file_strings,
        "TIMESTAMP": parse_file_timestamp,
        "WRITE": parse_file_write,
        "APPEND": parse_file_write,
        "TOUCH": StandardParser('+', flags=["TOUCH"]),
        "TOUCH_NOCREATE": StandardParser('+', flags=["TOUCH_NOCREATE"]),
        "GENERATE": parse_file_generate_output,
        "GLOB": parse_file_glob,
        "GLOB_RECURSE": parse_file_glob,
        "RENAME": StandardParser(3, flags=["RENAME"]),
        "REMOVE": StandardParser('+', flags=["REMOVE"]),
        "REMOVE_RECURSE": StandardParser('+', flags=["REMOVE_RECURSE"]),
        "MAKE_DIRECTORY": StandardParser('+', flags=["MAKE_DIRECTORY"]),
        "COPY": parse_file_copy,
        "INSTALL": parse_file_copy,
        "SIZE": StandardParser(3, flags=["SIZE"]),
        "READ_SYMLINK": StandardParser(3, flags=["READ_SYMLINK"]),
        "CREATE_LINK": parse_file_create_link,
        "RELATIVE_PATH": StandardParser(4, flags=["RELATIVE_PATH"]),
        "TO_CMAKE_PATH": StandardParser(3, flags=["TO_CMAKE_PATH"]),
        "TO_NATIVE_PATH": StandardParser(3, flags=["TO_NATIVE_PATH"]),
        "DOWNLOAD": parse_file_xfer,
        "UPLOAD": parse_file_xfer,
        "LOCK": parse_file_lock
    }
    for hashname in HASH_STRINGS:
        parsemap[hashname] = parse_file_hash

    if descriminator not in parsemap:
        logger.warning("Indeterminate form of file() command \"%s\" at %s",
                       descriminator, tokens[0].location())
        ctx.lint_ctx.record_lint("E1126",
                                 location=descriminator_token.get_location())
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    return parsemap[descriminator](ctx, tokens, breakstack)
Exemple #4
0
def parse_list(ctx, tokens, breakstack):
    """
  Reading
    list(LENGTH <list> <out-var>)
    list(GET <list> <element index> [<index> ...] <out-var>)
    list(JOIN <list> <glue> <out-var>)
    list(SUBLIST <list> <begin> <length> <out-var>)

  Search
    list(FIND <list> <value> <out-var>)

  Modification
    list(APPEND <list> [<element>...])
    list(FILTER <list> {INCLUDE | EXCLUDE} REGEX <regex>)
    list(INSERT <list> <index> [<element>...])
    list(POP_BACK <list> [<out-var>...])
    list(POP_FRONT <list> [<out-var>...])
    list(PREPEND <list> [<element>...])
    list(REMOVE_ITEM <list> <value>...)
    list(REMOVE_AT <list> <index>...)
    list(REMOVE_DUPLICATES <list>)
    list(TRANSFORM <list> <ACTION> [...])

  Ordering
    list(REVERSE <list>)
    list(SORT <list> [...])

  TRANSFORM actions:
    list(TRANSFORM <list> <APPEND|PREPEND> <value> ...)
    list(TRANSFORM <list> <TOLOWER|TOUPPER> ...)
    list(TRANSFORM <list> STRIP ...)
    list(TRANSFORM <list> GENEX_STRIP ...)
    list(TRANSFORM <list> REPLACE <regular_expression>
                                  <replace_expression> ...)

  TRANSFORM selectors
    list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
    list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
    list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)

  :see: https://cmake.org/cmake/help/latest/command/list.html
  """

    descriminator_token = get_first_semantic_token(tokens)
    if (descriminator_token is None
            or descriminator_token.type is TokenType.RIGHT_PAREN):
        location = ()
        if tokens:
            location = tokens[0].get_location()
        logger.warning("Invalid empty list() command at %s", location)
        ctx.lint_ctx.record_lint("E1120", location=location)
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    if descriminator_token.type is TokenType.DEREF:
        ctx.lint_ctx.record_lint("C0114",
                                 location=descriminator_token.get_location())
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    descriminator = descriminator_token.spelling.upper()
    parsemap = {
        "LENGTH":
        StandardParser(3, flags=["LENGTH"]),
        "GET":
        StandardParser("4+", flags=["GET"]),
        "JOIN":
        StandardParser(4, flags=["JOIN"]),
        "SUBLIST":
        StandardParser(5, flags=["SUBLIST"]),
        "FIND":
        StandardParser(4, flags=["FIND"]),
        "APPEND":
        StandardParser("2+", flags=["APPEND"]),
        "FILTER":
        StandardParser(5, flags=["FILTER", "INCLUDE", "EXCLUDE", "REGEX"]),
        "INSERT":
        StandardParser("4+", flags=["INSERT"]),
        "POP_BACK":
        StandardParser("2+", flags=["POP_BACK"]),
        "POP_FRONT":
        StandardParser("2+", flags=["POP_FRONT"]),
        "PREPEND":
        StandardParser("2+", flags=["PREPEND"]),
        "REMOVE_ITEM":
        StandardParser("3+", flags=["REMOVE_ITEM"]),
        "REMOVE_AT":
        StandardParser("3+", flags=["REMOVE_AT"]),
        "REMOVE_DUPLICATES":
        StandardParser(2, flags=["REMOVE_DUPLICATES"]),
        "TRANSFORM":
        StandardParser(2, TRANSFORM_KWARGS),
        "REVERSE":
        StandardParser(2, flags=["REVERSE"]),
        "SORT":
        StandardParser(2, kwargs=SORT_KWARGS)
    }

    if descriminator not in parsemap:
        logger.warning("Indeterminate form of file() command \"%s\" at %s",
                       descriminator, tokens[0].location())
        ctx.lint_ctx.record_lint("E1126",
                                 location=descriminator_token.get_location())
        return StandardArgTree.parse(ctx,
                                     tokens,
                                     npargs='*',
                                     kwargs={},
                                     flags=[],
                                     breakstack=breakstack)

    return parsemap[descriminator](ctx, tokens, breakstack)
Exemple #5
0
def parse_add_custom_target(ctx, tokens, breakstack):
    """
  ::
    add_custom_target(Name [ALL] [command1 [args1...]]
                      [COMMAND command2 [args2...] ...]
                      [DEPENDS depend depend depend ... ]
                      [BYPRODUCTS [files...]]
                      [WORKING_DIRECTORY dir]
                      [COMMENT comment]
                      [JOB_POOL job_pool]
                      [VERBATIM] [USES_TERMINAL]
                      [COMMAND_EXPAND_LISTS]
                      [SOURCES src1 [src2...]])

  :see: https://cmake.org/cmake/help/latest/command/add_custom_target.html
  """
    kwargs = {
        "BYPRODUCTS": PositionalParser("+"),
        "COMMAND": ShellCommandNode.parse,
        "COMMENT": PositionalParser(1),
        "DEPENDS": PositionalParser("+"),
        "JOB_POOL": PositionalParser(1),
        "SOURCES": PositionalParser("+"),
        "WORKING_DIRECTORY": PositionalParser(1),
    }

    required_kwargs = {
        # Required by convention
        "COMMENT": "C0113"
    }

    flags = ("VERBATIM", "USES_TERMINAL", "COMMAND_EXPAND_LISTS")
    tree = ArgGroupNode()

    # If it is a whitespace token then put it directly in the parse tree at
    # the current depth
    while tokens and tokens[0].type in WHITESPACE_TOKENS:
        tree.children.append(tokens.pop(0))
        continue

    breaker = KwargBreaker(list(kwargs.keys()) + list(flags))
    child_breakstack = breakstack + [breaker]

    nametree = None
    state = "name"

    while tokens:
        # Break if the next token belongs to a parent parser, i.e. if it
        # matches a keyword argument of something higher in the stack, or if
        # it closes a parent group.
        if should_break(tokens[0], breakstack):
            break

        # If it is a whitespace token then put it directly in the parse tree at
        # the current depth
        if tokens[0].type in WHITESPACE_TOKENS:
            tree.children.append(tokens.pop(0))
            continue

        # If it's a comment, then add it at the current depth
        if tokens[0].type in (TokenType.COMMENT, TokenType.BRACKET_COMMENT):
            child = TreeNode(NodeType.COMMENT)
            tree.children.append(child)
            child.children.append(tokens.pop(0))
            continue

        ntokens = len(tokens)
        if state == "name":
            next_semantic = get_first_semantic_token(tokens[1:])
            if (next_semantic is not None
                    and get_normalized_kwarg(next_semantic) == "ALL"):
                npargs = 2
            else:
                npargs = 1

            nametree = PositionalGroupNode.parse(ctx, tokens, npargs, ["ALL"],
                                                 child_breakstack)
            assert len(tokens) < ntokens
            tree.children.append(nametree)
            state = "first-command"
            continue

        word = get_normalized_kwarg(tokens[0])
        if state == "first-command":
            if not (word in kwargs or word in flags):
                subtree = PositionalGroupNode.parse(ctx, tokens, '+', [],
                                                    child_breakstack)
                tree.children.append(subtree)
                assert len(tokens) < ntokens
            state = "kwargs"
            continue

        if word in flags:
            subtree = FlagGroupNode.parse(
                ctx, tokens, flags,
                breakstack + [KwargBreaker(list(kwargs.keys()))])
            assert len(tokens) < ntokens
            tree.children.append(subtree)
            continue

        if word in kwargs:
            required_kwargs.pop(word, None)
            subtree = KeywordGroupNode.parse(ctx, tokens, word, kwargs[word],
                                             child_breakstack)
            assert len(tokens) < ntokens
            tree.children.append(subtree)
            continue

        ctx.lint_ctx.record_lint("E1122", location=tokens[0].get_location())
        logger.warning("Unexpected positional argument %s at %s",
                       tokens[0].spelling, tokens[0].location())
        subtree = PositionalGroupNode.parse(ctx, tokens, '+', [],
                                            child_breakstack)
        assert len(tokens) < ntokens
        tree.children.append(subtree)
        continue

    if required_kwargs:
        location = ()
        for token in tree.get_semantic_tokens():
            location = token.get_location()
            break

        missing_kwargs = sorted(
            (lintid, word) for word, lintid in sorted(required_kwargs.items()))
        for lintid, word in missing_kwargs:
            ctx.lint_ctx.record_lint(lintid, word, location=location)
    return tree