"""
Extension module has to follow the naming convention gersemi_{module_name} like
gersemi_example_module, gersemi_acme_corporation, gersemi_qt etc. or it has to be
path to file ending with .py extension like example_file.py, acme_corporation.py, qt.py
"""
"""
One can import builtin commands supported by gersemi to override some behaviours,
extend list of keywords etc. Check example (10) below.
"""
from gersemi.builtin_commands import builtin_commands
# mypy: disable-error-code="index"
modified_target_sources = builtin_commands["target_sources"]
modified_target_sources["keyword_preprocessors"] = {
key: "sort+unique" for key in modified_target_sources["multi_value_keywords"]
}
command_definitions = {
#
# Key defines canonical name. Usually builtin CMake commands are formatted
# with lower case. Among exception to this rule there are commands from
# ExternalProject module like ExternalProject_Add.
#
# In this example any variant of ExampleSpecificCanonicalName like:
# - examplespecificcanonicalname
# - EXAMPLESPECIFICCANONICALNAME
# - eXaMpLeSpEcIfIcCaNoNiCaLnAmE
# will be reformatted to ExampleSpecificCanonicalName.
#
"ExampleSpecificCanonicalName": {},
#
# Canonical name can have additional whitespace before parentheses.
#
"ExampleNameWithOneSpaceAtTheEnd ": {},
"ExampleNameWithThreeSpacesAtTheEnd ": {},
"ExampleNameWithOneTabAtTheEnd\t": {},
#
# In the simplest case commands can have one signature and such signature
# will have the following properties:
# 1) Front positional arguments: these are arguments appearing at the front
# of command invocation that aren't attached to any keyword.
# Example from builtin CMake commands:
#
# configure_file( ...)
#
# 2) Back positional arguments: these are arguments appearing at the back
# of command invocation that aren't attached to any keyword.
# Example:
#
# get_source_file_property(... )
#
# 3) Options: these are arguments that don't have any value following them.
# Example:
#
# include(... [OPTIONAL] [NO_POLICY_SCOPE] ...)
#
# 4) One value keywords: these are arguments that have one value following
# them. Example:
#
# add_test(NAME ... [WORKING_DIRECTORY ] ...)
#
# 5) Multi value keywords: these are arguments that have at least one value
# following them. Example:
#
# target_include_directories(
# ...
# [items1...]
# [ [items2...]]
# ...
# )
#
"example_pick_movie_to_watch": {
"front_positional_arguments": ["how_to_pick_movie"],
"options": ["EXCLUDE_ALREADY_WATCHED", "IS_FAMILY_FRIENDLY"],
"one_value_keywords": ["DIRECTOR"],
"multi_value_keywords": ["GENRES", "YEAR_RANGE", "RATING_RANGE"],
"back_positional_arguments": ["output_variable"],
},
#
# Names of positional arguments aren't used so they can represented as empty
# strings or gibberish names as long as number of these arguments correctly
# model the command. For documentation purposes it's better to name these
# arguments though.
#
"example_unnamed_positional_arguments": {
"front_positional_arguments": ["", "lorem-ipsum-dolor-sit-amet", ""],
"multi_value_keywords": ["THINGS"],
"back_positional_arguments": ["", ""],
},
#
# 6) Multi value keywords can form sections with their own positional
# arguments and keywords. Example from CMake builtins:
#
# export(
# SETUP
# [PACKAGE_DEPENDENCY
#
# [ENABLED (||AUTO)]
# [EXTRA_ARGS ...]
# ] [...]
# [TARGET
#
# [XCFRAMEWORK_LOCATION ]
# ] [...]
# )
#
"example_rate_movies": {
"front_positional_arguments": ["database"],
"options": ["RATE_IN_ONE_TRANSACTION"],
"multi_value_keywords": ["MOVIE"],
#
# Each multi value keyword defined in "sections" can be customized
# in the same way as base case presented in example_pick_movie_to_watch.
#
"sections": {
"MOVIE": {
"front_positional_arguments": ["original_title"],
"options": ["ROUND_UP", "ROUND_DOWN", "ROUND_TO_THE_NEAREST"],
"one_value_keywords": ["RATING"],
"multi_value_keywords": ["ALTERNATIVE_TITLES"],
}
},
},
#
# Sometimes keywords have broad scope when used standalone but narrow scope
# when used in section. Example:
#
# install(TARGETS ... [EXPORT ]
# [RUNTIME_DEPENDENCIES ...|RUNTIME_DEPENDENCY_SET ]
# [...]
# [ ...]...
# [INCLUDES DESTINATION [ ...]]
# )
#
# """
# The first ... group applies to target Output
# Artifacts that do not have a dedicated group specified later
# in the same call.
# """
#
"example_common_keywords_in_standalone_and_in_section": {
"front_positional_arguments": ["front_1", "front_2"],
"back_positional_arguments": ["back_1", "back_2", "back_3"],
"options": ["OPTION_1", "OPTION_2"],
"one_value_keywords": ["ONE_VALUE_KEYWORD_1", "ONE_VALUE_KEYWORD_2"],
"multi_value_keywords": [
"MULTI_VALUE_KEYWORD_1",
"SECTION_KEYWORD",
],
"sections": {
"SECTION_KEYWORD": {
"front_positional_arguments": ["section_front_1"],
"back_positional_arguments": ["section_back_1", "section_back_2"],
"options": ["OPTION_1", "OPTION_2"],
"one_value_keywords": ["ONE_VALUE_KEYWORD_1", "ONE_VALUE_KEYWORD_2"],
"multi_value_keywords": ["MULTI_VALUE_KEYWORD_1"],
}
},
},
#
# Nested sections are supported but perhaps it's better to avoid designing
# such commands.
#
"example_nested_sections": {
"front_positional_arguments": ["level_0_arg_1", "level_0_arg_2"],
"back_positional_arguments": ["level_0_arg_3", "level_0_arg_4"],
"options": [
"LEVEL_0___OPTION_1",
"LEVEL_0___OPTION_2",
"LEVEL_0___OPTION_3",
],
"one_value_keywords": ["LEVEL_0___ONE_VALUE_KEYWORD"],
"multi_value_keywords": ["LEVEL_0___MULTI_VALUE_KEYWORD"],
"sections": {
"LEVEL_0___MULTI_VALUE_KEYWORD": {
"front_positional_arguments": [
"level_1_arg_1",
"level_1_arg_2",
],
"back_positional_arguments": [
"level_1_arg_3",
"level_1_arg_4",
],
"options": [
"LEVEL_1___OPTION_1",
"LEVEL_1___OPTION_2",
"LEVEL_1___OPTION_3",
],
"one_value_keywords": ["LEVEL_1___ONE_VALUE_KEYWORD"],
"multi_value_keywords": ["LEVEL_1___MULTI_VALUE_KEYWORD"],
"sections": {
"LEVEL_1___MULTI_VALUE_KEYWORD": {
"front_positional_arguments": [
"level_2_arg_1",
"level_2_arg_2",
],
"back_positional_arguments": [
"level_2_arg_3",
"level_2_arg_4",
],
"options": [
"LEVEL_2___OPTION_1",
"LEVEL_2___OPTION_2",
"LEVEL_2___OPTION_3",
],
"one_value_keywords": ["LEVEL_2___ONE_VALUE_KEYWORD"],
"multi_value_keywords": ["LEVEL_2___MULTI_VALUE_KEYWORD"],
}
},
}
},
},
#
# 7) Another kind of specialized formatting available for multi value
# keywords is specifying "keyword_formatters" entry. Available kinds:
# - "pairs": values after the keyword will be grouped into pairs
# Example (PROPERTIES keyword):
#
# set_directory_properties(
# PROPERTIES
#
# [ ]
# ...
# )
#
# - "command_line": values after the keyword are treated like a sequence of
# words in command line and will flow to the next line once line length
# limit is reached.
# Example (COMMAND keyword):
#
# execute_process(
# COMMAND []
# [COMMAND []]...
# ...
# )
#
"example_add_movie_to_database": {
"front_positional_arguments": ["movie-name"],
"one_value_keywords": ["DIRECTOR"],
"multi_value_keywords": ["AVAILABLE_SUBTITLES", "CAST", "SUMMARY"],
"keyword_formatters": {
"CAST": "pairs",
"SUMMARY": "command_line",
},
},
#
# "sections" take precedence over "keyword_formatters" because these properties
# are mutually exclusive.
#
"example_keyword_cant_be_both_section_and_special_kind": {
"front_positional_arguments": ["something"],
"multi_value_keywords": ["CONFUSING_ARGUMENTS"],
"sections": {
"CONFUSING_ARGUMENTS": {"one_value_keywords": ["ARG1", "ARG2"]},
},
# dead property
"keyword_formatters": {
"CONFUSING_ARGUMENTS": "command_line",
},
},
#
# 8) Multi value keywords can be additionally preprocessed by specifying
# "keyword_preprocessors" entry. Available preprocessors:
# - "sort": arguments are sorted
# - "unique": only unique arguments are kept
# - "sort+unique": combination of "sort" and "unique"
#
"example_show_movie_credits": {
"front_positional_arguments": ["title"],
"multi_value_keywords": ["ACTORS", "WRITERS"],
"keyword_preprocessors": {
"ACTORS": "sort+unique",
"WRITERS": "unique",
},
},
#
# 9) Command can have multiple signatures which are selected
# through value of first argument. Example:
#
# install(TARGETS ... [...])
# install(IMPORTED_RUNTIME_ARTIFACTS ... [...])
# install({FILES | PROGRAMS} ... [...])
# install(DIRECTORY ... [...])
# install(SCRIPT [...])
# install(CODE [...])
# install(EXPORT [...])
# install(PACKAGE_INFO [...])
# install(RUNTIME_DEPENDENCY_SET [...])
#
# Signatures are specified through "signatures" entries
# and each signature can specify the same properties as in base case.
#
"example_compute_value": {
"signatures": {
"SUM": {
"front_positional_arguments": ["result-variable"],
"multi_value_keywords": ["VALUES"],
},
"PRODUCT": {
"front_positional_arguments": ["result-variable"],
"multi_value_keywords": ["VALUES"],
},
"MAP": {
"front_positional_arguments": ["result-variable"],
"one_value_keywords": ["FUNCTION"],
"multi_value_keywords": ["VALUES"],
},
}
},
#
# Since "signatures" property takes precedence over base case properties
# one should avoid specifying other properties.
#
"example_dead_properties": {
"signatures": {
"SOME_SIGNATURE": {
"options": [
"OPTION_KEYWORD_SIGNATURE___1",
"OPTION_KEYWORD_SIGNATURE___2",
],
"multi_value_keywords": ["THINGS"],
},
},
# dead property
"options": ["THIS_KEYWORD_IS_NOT_RECOGNIZED", "THAT_ONE_AS_WELL"],
},
#
# 10) Builtin commands can be completely overriden just as custom commands.
#
"target_link_libraries": {
"front_positional_arguments": [""],
# no keywords just for the sake of example
"multi_value_keywords": [],
"sections": {},
},
#
# Builtin commands can be also tweaked based on their original definition.
# In this case `target_sources` is tweaked in such a way that source files
# are sorted and unique.
#
"target_sources": modified_target_sources,
#
# In this case if/elseif/else/endif commands are changed in a way that
# whitespace is added after the name and all four commands have the same
# name length.
#
"if ": builtin_commands["if"],
"elseif ": builtin_commands["elseif"],
"else ": builtin_commands["else"],
"endif ": builtin_commands["endif"],
#
# 11) Commands can form block-like structure similar to pairs of builtin
# commands like function/endfunction, macro/endmacro and so on by specifying
# "block_end" property on command defining block beginning.
#
"example_movie_prologue": {
"block_end": "example_movie_epilogue",
"one_value_keywords": ["TITLE"],
"multi_value_keywords": ["MAIN_CAST"],
"keyword_formatters": {
"MAIN_CAST": "pairs",
},
},
"example_movie_epilogue": {
"multi_value_keywords": ["CREDITS"],
"keyword_formatters": {
"CREDITS": "pairs",
},
},
}