def parse_add_custom_command(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) descriminator_word = get_normalized_kwarg(descriminator_token) if descriminator_word == "TARGET": return parse_add_custom_command_events(tokens, breakstack) if descriminator_word == "OUTPUT": return parse_add_custom_command_standard(tokens, breakstack) logger.warning( "Indeterminate form of add_custom_command at %s", descriminator_token.location) return parse_add_custom_command_standard(tokens, breakstack)
def parse_install(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 parse_standard(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 parse_standard(tokens, npargs='*', kwargs={}, flags=[], breakstack=breakstack) return parsemap[descriminator](tokens, breakstack)
def parse_file(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: logger.warning("Invalid empty file() command at %s", tokens[0].get_location()) return parse_standard(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_NO_CREATE": StandardParser('+', flags=["TOUCH_NO_CREATE"]), "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("Invalid file() form \"%s\" at %s", descriminator, tokens[0].location()) return parse_standard(tokens, npargs='*', kwargs={}, flags=[], breakstack=breakstack) return parsemap[descriminator](tokens, breakstack)
def parse_add_custom_target(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": parse_shell_command, "COMMENT": PositionalParser(1), "DEPENDS": PositionalParser("+"), "JOB_POOL": PositionalParser(1), "SOURCES": PositionalParser("+"), "WORKING_DIRECTORY": PositionalParser(1), } flags = ("VERBATIM", "USES_TERMINAL", "COMMAND_EXPAND_LISTS") 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 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 (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) 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 = parse_positionals(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 = parse_positionals(tokens, '+', [], child_breakstack) tree.children.append(subtree) assert len(tokens) < ntokens state = "kwargs" continue if word in flags: subtree = parse_flags( tokens, flags, breakstack + [KwargBreaker(list(kwargs.keys()))]) assert len(tokens) < ntokens tree.children.append(subtree) continue if word in kwargs: subtree = parse_kwarg(tokens, word, kwargs[word], child_breakstack) assert len(tokens) < ntokens tree.children.append(subtree) continue logger.warning( "Unexpected positional argument %s at %s", tokens[0].spelling, tokens[0].location()) subtree = parse_positionals(tokens, '+', [], child_breakstack) assert len(tokens) < ntokens tree.children.append(subtree) continue return tree