예제 #1
0
def parse_add_library_object(ctx, tokens, breakstack):
  """
  ::
    add_library(<name> OBJECT <src>...)

    :see: https://cmake.org/cmake/help/latest/command/add_library.html#object-libraries
  """
  tree = TreeNode(NodeType.ARGGROUP)

  # 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

  ntokens = len(tokens)
  subtree = PositionalGroupNode.parse(ctx, tokens, 2, ["OBJECT"], breakstack)
  assert len(tokens) < ntokens
  tree.children.append(subtree)

  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):
      if not get_tag(tokens[0]) in ("sort", "sortable"):
        child = TreeNode(NodeType.COMMENT)
        tree.children.append(child)
        child.children.append(tokens.pop(0))
        continue

    ntokens = len(tokens)
    subtree = PositionalGroupNode.parse(ctx, tokens, '+', [], breakstack)
    assert len(tokens) < ntokens
    tree.children.append(subtree)
  return tree
예제 #2
0
def parse_file_write(ctx, tokens, breakstack):
    """
  ::

    file(WRITE <filename> <content>...)
    file(APPEND <filename> <content>...)

  :see: https://cmake.org/cmake/help/v3.14/command/file.html#writing
  """
    tree = TreeNode(NodeType.ARGGROUP)
    consume_whitespace_and_comments(ctx, tokens, tree)
    tree.children.append(
        PositionalGroupNode.parse(ctx, tokens, 2, ["WRITE", "APPEND"],
                                  breakstack))
    consume_whitespace_and_comments(ctx, tokens, tree)
    tree.children.append(
        PositionalGroupNode.parse(ctx, tokens, '+', [], breakstack))

    return tree
예제 #3
0
def parse_empty(ctx, tokens, breakstack):
  """
  ::

    break()
    continue()
    enable_testing()

  :see: https://cmake.org/cmake/help/latest/command/break.html
  :see: https://cmake.org/cmake/help/latest/command/continue.html
  :see: https://cmake.org/cmake/help/latest/command/enable_testing.html
  :see: https://cmake.org/cmake/help/latest/command/return.html
  """
  tree = ArgGroupNode()

  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 (lexer.TokenType.COMMENT,
                          lexer.TokenType.BRACKET_COMMENT):
      child = TreeNode(NodeType.COMMENT)
      tree.children.append(child)
      child.children.append(tokens.pop(0))
      continue

    ctx.lint_ctx.record_lint("E1121", location=tokens[0].get_location())
    ntokens = len(tokens)
    subtree = PositionalGroupNode.parse(ctx, tokens, "+", [], breakstack)
    assert len(tokens) < ntokens
    tree.children.append(subtree)
  return tree
예제 #4
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
예제 #5
0
def parse_set(ctx, tokens, breakstack):
    """
  ::

    set(<variable> <value>
        [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])

  :see: https://cmake.org/cmake/help/v3.0/command/set.html?
  """
    tree = SetFnNode()

    # 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

    kwargs = {"CACHE": PositionalParser('2+', flags=["FORCE"])}
    flags = ["PARENT_SCOPE"]
    kwarg_breakstack = breakstack + [KwargBreaker(list(kwargs.keys()) + flags)]
    positional_breakstack = breakstack + [KwargBreaker(list(kwargs.keys()))]

    ntokens = len(tokens)
    subtree = PositionalGroupNode.parse(ctx, tokens, 1, flags,
                                        positional_breakstack)
    assert len(tokens) < ntokens
    tree.children.append(subtree)
    for token in subtree.get_semantic_tokens():
        tree.varname = token
        break

    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 (lexer.TokenType.COMMENT,
                              lexer.TokenType.BRACKET_COMMENT):
            if not get_tag(tokens[0]) in ("sort", "sortable"):
                child = TreeNode(NodeType.COMMENT)
                tree.children.append(child)
                child.children.append(tokens.pop(0))
                continue

        ntokens = len(tokens)

        # NOTE(josh): each flag is also stored in kwargs as with a positional parser
        # of size zero. This is a legacy thing that should be removed, but for now
        # just make sure we check flags first.
        word = get_normalized_kwarg(tokens[0])
        if word == "CACHE":
            subtree = KeywordGroupNode.parse(ctx, tokens, word, kwargs[word],
                                             kwarg_breakstack)
            cache_tokens = subtree.get_semantic_tokens()
            cache_tokens.pop(0)
            cache_args = [None, None, False]
            for idx, token in enumerate(cache_tokens[:2]):
                cache_args[idx] = token.spelling

            if not cache_tokens:
                ctx.lint_ctx.record_lint("E1120",
                                         "<variable>",
                                         location=tokens[0].get_location())
            elif len(cache_tokens) < 2:
                ctx.lint_ctx.record_lint("E1120",
                                         "<value>",
                                         location=tokens[0].get_location())
            elif len(cache_tokens) < 3:
                pass
            elif len(cache_tokens) > 4:
                ctx.lint_ctx.record_lint(
                    "E1121", location=cache_tokens[2].get_location())
            else:
                if cache_tokens[2].spelling.upper() == "FORCE":
                    cache_args[2] = True
                else:
                    ctx.lint_ctx.record_lint(
                        "E1121", location=cache_tokens[2].get_location())
            tree.cache = CacheTuple(*cache_args)

        elif word == "PARENT_SCOPE":
            subtree = PositionalGroupNode.parse(ctx, tokens, '+',
                                                ["PARENT_SCOPE"],
                                                positional_breakstack)
            tree.parent_scope = True
        else:
            subtree = PositionalGroupNode.parse(ctx, tokens, '+', [],
                                                kwarg_breakstack)
            tree.value_group = subtree

        assert len(tokens) < ntokens
        tree.children.append(subtree)
    return tree
예제 #6
0
def parse_install_targets(ctx, tokens, breakstack):
  """
  ::

    install(TARGETS targets... [EXPORT <export-name>]
            [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
              PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
             [DESTINATION <dir>]
             [PERMISSIONS permissions...]
             [CONFIGURATIONS [Debug|Release|...]]
             [COMPONENT <component>]
             [NAMELINK_COMPONENT <component>]
             [OPTIONAL] [EXCLUDE_FROM_ALL]
             [NAMELINK_ONLY|NAMELINK_SKIP]
            ] [...]
            [INCLUDES DESTINATION [<dir> ...]]
            )

  :see: https://cmake.org/cmake/help/v3.14/command/install.html#targets
  """
  kwargs = {
      "TARGETS": PositionalParser('+'),
      "EXPORT": PositionalParser(1),
      "INCLUDES": PositionalParser('+', flags=["DESTINATION"]),
      # Common kwargs
      "DESTINATION": PositionalParser(1),
      "PERMISSIONS": PositionalParser('+'),
      "CONFIGURATIONS": PositionalParser('+'),
      "COMPONENT": PositionalParser(1),
      "NAMELINK_COMPONENT": PositionalParser(1),
  }
  flags = (
      "OPTIONAL",
      "EXCLUDE_FROM_ALL",
      "NAMELINK_ONLY",
      "NAMELINK_SKIP"
  )
  designated_kwargs = (
      "ARCHIVE", "LIBRARY", "RUNTIME", "OBJECTS", "FRAMEWORK",
      "BUNDLE", "PRIVATE_HEADER", "PUBLIC_HEADER", "RESOURCE"
  )

  # NOTE(josh): from here on, code is essentially StandardArgTree.parse(),
  # except that
  # we cannot break on the common subset of kwargs in the breakstack because
  # they are valid kwargs for the subtrees (ARCHIVE, LIBRARY, etc) as well as
  # the primary tree
  tree = TreeNode(NodeType.ARGGROUP)

  # 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

  # ARCHIVE, LIBRARY, RUNTIME, subtrees etc only break on the start of
  # another subtree, or on "INCLUDES DESTINATION"
  subtree_breakstack = breakstack + [KwargBreaker(
      list(designated_kwargs) + ["INCLUDES"]
  )]

  # kwargs at this tree depth break on other kwargs or flags
  kwarg_breakstack = breakstack + [KwargBreaker(
      list(kwargs.keys()) + list(designated_kwargs) + list(flags)
  )]

  # and flags at this depth break only on kwargs
  positional_breakstack = breakstack + [KwargBreaker(
      list(kwargs.keys()) + list(designated_kwargs)
  )]

  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 (lexer.TokenType.COMMENT,
                          lexer.TokenType.BRACKET_COMMENT):
      child = TreeNode(NodeType.COMMENT)
      tree.children.append(child)
      child.children.append(tokens.pop(0))
      continue

    ntokens = len(tokens)
    # NOTE(josh): each flag is also stored in kwargs as with a positional parser
    # of size zero. This is a legacy thing that should be removed, but for now
    # just make sure we check flags first.
    word = get_normalized_kwarg(tokens[0])
    if word in designated_kwargs:
      subtree = KeywordGroupNode.parse(
          ctx, tokens, word, parse_install_targets, subtree_breakstack)
    elif word in kwargs:
      subtree = KeywordGroupNode.parse(
          ctx, tokens, word, kwargs[word], kwarg_breakstack)
    else:
      subtree = PositionalGroupNode.parse(
          ctx, tokens, '+', flags, positional_breakstack)

    assert len(tokens) < ntokens
    tree.children.append(subtree)
  return tree