diff options
| author | Stefan Boberg <[email protected]> | 2025-11-07 14:49:13 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-11-07 14:49:13 +0100 |
| commit | 24e43a913f29ac3b314354e8ce5175f135bcc64f (patch) | |
| tree | ca442937ceeb63461012b33a4576e9835099f106 /thirdparty/ryml/ext/c4core/cmake | |
| parent | get oplog attachments (#622) (diff) | |
| download | zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.tar.xz zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.zip | |
switch to xmake for package management (#611)
This change removes our dependency on vcpkg for package management, in favour of bringing some code in-tree in the `thirdparty` folder as well as using the xmake build-in package management feature. For the latter, all the package definitions are maintained in the zen repo itself, in the `repo` folder.
It should now also be easier to build the project as it will no longer depend on having the right version of vcpkg installed, which has been a common problem for new people coming in to the codebase. Now you should only need xmake to build.
* Bumps xmake requirement on github runners to 2.9.9 to resolve an issue where xmake on Windows invokes cmake with `v144` toolchain which does not exist
* BLAKE3 is now in-tree at `thirdparty/blake3`
* cpr is now in-tree at `thirdparty/cpr`
* cxxopts is now in-tree at `thirdparty/cxxopts`
* fmt is now in-tree at `thirdparty/fmt`
* robin-map is now in-tree at `thirdparty/robin-map`
* ryml is now in-tree at `thirdparty/ryml`
* sol2 is now in-tree at `thirdparty/sol2`
* spdlog is now in-tree at `thirdparty/spdlog`
* utfcpp is now in-tree at `thirdparty/utfcpp`
* xmake package repo definitions is in `repo`
* implemented support for sanitizers. ASAN is supported on windows, TSAN, UBSAN, MSAN etc are supported on Linux/MacOS though I have not yet tested it extensively on MacOS
* the zencore encryption implementation also now supports using mbedTLS which is used on MacOS, though for now we still use openssl on Linux
* crashpad
* bumps libcurl to 8.11.0 (from 8.8.0) which should address a rare build upload bug
Diffstat (limited to 'thirdparty/ryml/ext/c4core/cmake')
43 files changed, 15858 insertions, 0 deletions
diff --git a/thirdparty/ryml/ext/c4core/cmake/.gitignore b/thirdparty/ryml/ext/c4core/cmake/.gitignore new file mode 100644 index 000000000..ed8ebf583 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/.gitignore @@ -0,0 +1 @@ +__pycache__
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake b/thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake new file mode 100644 index 000000000..45395ad1b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake @@ -0,0 +1,120 @@ + + +# this function works both with multiconfig and single-config generators. +function(set_default_build_type which) + # CMAKE_CONFIGURATION_TYPES is available only for multiconfig generators. + # so set the build type only if CMAKE_CONFIGURATION_TYPES does not exist. + if(NOT CMAKE_CONFIGURATION_TYPES) # not a multiconfig generator? + if(NOT CMAKE_BUILD_TYPE) + if(NOT which) + set(which RelWithDebInfo) + endif() + message("Defaulting to ${which} build.") + set(CMAKE_BUILD_TYPE ${which} CACHE STRING "") + endif() + endif() +endfunction() + + +# https://stackoverflow.com/questions/31546278/where-to-set-cmake-configuration-types-in-a-project-with-subprojects +function(setup_configuration_types) + set(options0arg + ) + set(options1arg + DEFAULT + ) + set(optionsnarg + TYPES + ) + cmake_parse_arguments("" "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + + if(NOT TYPES) + set(TYPES Release Debug RelWithDebInfo MinSizeRel) + endif() + + # make it safe to call repeatedly + if(NOT _setup_configuration_types_done) + set(_setup_configuration_types_done 1 CACHE INTERNAL "") + + # No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator + # Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator. + + if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator? + set(CMAKE_CONFIGURATION_TYPES "${TYPES}" CACHE STRING "") + else() # single-config generator + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build") + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${TYPES}") + # set the valid options for cmake-gui drop-down list + endif() + endif() +endfunction() + + +# https://stackoverflow.com/questions/31546278/where-to-set-cmake-configuration-types-in-a-project-with-subprojects +function(add_configuration_type name) + set(flag_vars + C_FLAGS + CXX_FLAGS + SHARED_LINKER_FLAGS + STATIC_LINKER_FLAGS + MODULE_LINKER_FLAGS + EXE_LINKER_FLAGS + RC_FLAGS + ) + + set(options0arg + PREPEND # when defaulting to a config, prepend to it instead of appending to it + SET_MAIN_FLAGS # eg, set CMAKE_CXX_FLAGS from CMAKE_CXX_FLAGS_${name} + ) + set(options1arg + DEFAULT_FROM # take the initial value of the flags from this config + ) + set(optionsnarg + C_FLAGS + CXX_FLAGS + SHARED_LINKER_FLAGS + STATIC_LINKER_FLAGS + MODULE_LINKER_FLAGS + EXE_LINKER_FLAGS + RC_FLAGS + ) + cmake_parse_arguments(_act "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + + string(TOUPPER ${name} UNAME) + + # make it safe to call repeatedly + if(NOT _add_configuration_type_${name}) + set(_add_configuration_type_${name} 1 CACHE INTERNAL "") + + setup_configuration_types() + + if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator? + set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES};${name}" CACHE STRING "" FORCE) + else() # single-config generator + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${CMAKE_BUILD_TYPES};${name}" FORCE) + # set the valid options for cmake-gui drop-down list + endif() + + # now set up the configuration + message(STATUS "config: CMAKE_${f}_${UNAME} --- ${val}") + foreach(f ${flag_vars}) + set(val ${_act_${f}}) + message(STATUS "config: ${name}: ${f} --- ${val}") + if(_act_DEFAULT_FROM) + if(_act_PREPEND) + set(val "${val} ${CMAKE_${f}_${_act_DEFAULT_FROM}}") + else() + set(val "${CMAKE_${f}_${_act_DEFAULT_FROM}} ${val}") + endif() + endif() + message(STATUS "config: CMAKE_${f}_${UNAME} --- ${val}") + set(CMAKE_${f}_${UNAME} "${val}" CACHE STRING "" FORCE) + mark_as_advanced(CMAKE_${f}_${UNAME}) + if(_act_SET_MAIN_FLAGS) + set(CMAKE_${f} "${CMAKE_${f}_${UNAME}}" CACHE STRING "" FORCE) + endif() + endforeach() + endif() + +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake b/thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake new file mode 100644 index 000000000..e8e144184 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake @@ -0,0 +1,31 @@ +# create hierarchical source groups based on a dir tree +# +# EXAMPLE USAGE: +# +# create_source_group("src" "${SRC_ROOT}" "${SRC_LIST}") +# +# Visual Studio usually has the equivalent to this: +# +# create_source_group("Header Files" ${PROJ_SRC_DIR} "${PROJ_HEADERS}") +# create_source_group("Source Files" ${PROJ_SRC_DIR} "${PROJ_SOURCES}") +# +# TODO: <jpmag> this was taken from a stack overflow answer. Need to find it +# and add a link here. + +macro(create_source_group GroupPrefix RootDir ProjectSources) + set(DirSources ${ProjectSources}) + foreach(Source ${DirSources}) + #message(STATUS "s=${Source}") + string(REGEX REPLACE "([\\^\\$*+?|])" "\\\\\\1" RootDirRegex "${RootDir}") + string(REGEX REPLACE "${RootDirRegex}" "" RelativePath "${Source}") + #message(STATUS " ${RelativePath}") + string(REGEX REPLACE "[\\\\/][^\\\\/]*$" "" RelativePath "${RelativePath}") + #message(STATUS " ${RelativePath}") + string(REGEX REPLACE "^[\\\\/]" "" RelativePath "${RelativePath}") + #message(STATUS " ${RelativePath}") + string(REGEX REPLACE "/" "\\\\\\\\" RelativePath "${RelativePath}") + #message(STATUS " ${RelativePath}") + source_group("${GroupPrefix}\\${RelativePath}" FILES ${Source}) + #message(STATUS " ${Source}") + endforeach(Source) +endmacro(create_source_group) diff --git a/thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in new file mode 100644 index 000000000..f444cb742 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in @@ -0,0 +1,2566 @@ +# Doxyfile 1.8.15 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @_PROJ@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = @_PROJ_BRIEF@ + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @_OUTPUT_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = @_STRIP_FROM_PATH@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = @_STRIP_FROM_INC_PATH@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 4 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @_INPUT@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice \ + @_FILE_PATTERNS@ + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = @_EXCLUDE@ + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = @_EXCLUDE_PATTERNS@ + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = @_EXCLUDE_SYMBOLS@ + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = @_EXAMPLE_PATH@ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = @_CLANG_DATABASE_PATH@ + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: \makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = \makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/thirdparty/ryml/ext/c4core/cmake/Doxyfile.in b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.in new file mode 100644 index 000000000..6b464bf40 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.in @@ -0,0 +1,2566 @@ +# Doxyfile 1.8.15 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @_PROJ@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = @_PROJ_BRIEF@ + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @_OUTPUT_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = @_STRIP_FROM_PATH@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = @_STRIP_FROM_INC_PATH@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 4 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @_INPUT@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice \ + @_FILE_PATTERNS@ + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = @_EXCLUDE@ + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = @_EXCLUDE_PATTERNS@ + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = @_EXCLUDE_SYMBOLS@ + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = @_EXAMPLE_PATH@ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = @_CLANG_DATABASE_PATH@ + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: \makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = \makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake b/thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake new file mode 100644 index 000000000..f1328c6df --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake @@ -0,0 +1,215 @@ +# (C) 2017 Joao Paulo Magalhaes <[email protected]> + +include(CMakeParseArguments) + +#------------------------------------------------------------------------------ +# Usage: +# +# ExternalProject_GetFwdArgs(output_var +# [NO_DEFAULTS] +# [VARS var1 var2 ...] +# [EXCLUDE xvar1 xvar2 ...] +# [QUIET] +# ) +# +# Get the current cmake environment in a sequence of -DVAR=${VAR} +# tokens so that the environment can be forwarded to an external +# cmake project through CMAKE_ARGS. +# +# Example: +# ExternalProject_GetFwdArgs(FWD_ARGS) +# ExternalProject_Add(foo SOURCE_DIR ../foo +# CMAKE_ARGS ${FWD_ARGS} +# ... etc) +# +# Use this function to enable forwarding the current cmake environment +# to an external project. It outputs all the needed variables in the +# form of a sequence of -DVAR=value, suitable for use in the CMAKE_ARGS +# clause of ExternalProject_Add(). +# +# This function uses ExternalProject_GetFwdVarNames() to find out the +# list of variables to export. If this behaviour does not fit your +# needs you can: +# +# * append more of your own variables (using the VARS +# argument). The vars specified in this option will each be +# added to the output in the form of -Dvar=${var} +# +# * you can also avoid any defaults obtained through usage of +# ExternalProject_GetFwdNames() by specifying NO_DEFAULTS. +# +# Example with custom variable names (adding more): +# ExternalProject_GetFwdVarNames(FWD_ARGS VARS USER_VAR1 USER_VAR2) +# ExternalProjectAdd(foo SOURCE_DIR ../foo CMAKE_ARGS ${FWD_ARGS}) +# +# Example with custom variable names (just your own): +# ExternalProject_GetFwdVarNames(FWD_ARGS NO_DEFAULTS VARS USER_VAR1 USER_VAR2) +# ExternalProjectAdd(foo SOURCE_DIR ../foo CMAKE_ARGS ${FWD_ARGS}) +# +function(ExternalProject_GetFwdArgs output_var) + set(options0arg + NO_DEFAULTS + QUIET + ) + set(options1arg + ) + set(optionsnarg + VARS + EXCLUDE + ) + cmake_parse_arguments(_epgfa "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + if(NOT _epfga_NO_DEFAULTS) + ExternalProject_GetFwdVarNames(_fwd_names) + endif() + if(_epgfa_VARS) + list(APPEND _fwd_names ${_epgfa_VARS}) + endif() + if(_epgfa_EXCLUDE) + list(REMOVE_ITEM _fwd_names ${_epgfa_EXCLUDE}) + endif() + set(_epgfa_args) + foreach(_f ${_fwd_names}) + if(${_f}) + list(APPEND _epgfa_args -D${_f}=${${_f}}) + if(NOT _epfga_QUIET) + message(STATUS "ExternalProject_GetFwdArgs: ${_f}=${${_f}}") + endif() + endif() + endforeach() + set(${output_var} "${_epgfa_args}" PARENT_SCOPE) +endfunction(ExternalProject_GetFwdArgs) + + +#------------------------------------------------------------------------------ +# Gets a default list with the names of variables to forward to an +# external project. This function creates a list of common cmake +# variable names which have an impact in the output binaries or their +# placement. +function(ExternalProject_GetFwdVarNames output_var) + # these common names are irrespective of build type + set(names + CMAKE_GENERATOR + CMAKE_INSTALL_PREFIX + CMAKE_ARCHIVE_OUTPUT_DIRECTORY + CMAKE_LIBRARY_OUTPUT_DIRECTORY + CMAKE_RUNTIME_OUTPUT_DIRECTORY + CMAKE_AR + CMAKE_BUILD_TYPE + CMAKE_INCLUDE_PATH + CMAKE_LIBRARY_PATH + #CMAKE_MODULE_PATH # this is dangerous as it can override the external project's build files. + CMAKE_PREFIX_PATH + BUILD_SHARED_LIBS + CMAKE_CXX_COMPILER + CMAKE_C_COMPILER + CMAKE_LINKER + CMAKE_MAKE_PROGRAM + CMAKE_NM + CMAKE_OBJCOPY + CMAKE_RANLIB + CMAKE_STRIP + CMAKE_TOOLCHAIN_FILE + #CMAKE_CONFIGURATION_TYPES # not this. external projects will have their own build configurations + ) + # these names have per-build type values; + # use CMAKE_CONFIGURATION_TYPES to construct the list + foreach(v + CMAKE_CXX_FLAGS + CMAKE_C_FLAGS + CMAKE_EXE_LINKER_FLAGS + CMAKE_MODULE_LINKER_FLAGS + CMAKE_SHARED_LINKER_FLAGS) + list(APPEND names ${v}) + foreach(t ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${t} u) + list(APPEND names ${v}_${u}) + endforeach() + endforeach() + set(${output_var} "${names}" PARENT_SCOPE) +endfunction(ExternalProject_GetFwdVarNames) + + +#------------------------------------------------------------------------------ +macro(ExternalProject_Import name) + set(options0arg + ) + set(options1arg + PREFIX # look only here when findind + ) + set(optionsnarg + INCLUDE_PATHS # use these dirs for searching includes + LIBRARY_PATHS # use these dirs for searching libraries + INCLUDES # find these includes and append them to ${name}_INCLUDE_DIRS + INCLUDE_DIR_SUFFIXES + LIBRARIES # find these libs and append them to ${name}_LIBRARIES + LIBRARY_DIR_SUFFIXES + ) + cmake_parse_arguments(_eep "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + + if(NOT _eep_PREFIX) + message(FATAL_ERROR "no prefix was given") + endif() + + include(FindPackageHandleStandardArgs) + + #---------------------------------------------------------------- + # includes + + # the list of paths to search for includes + set(_eep_ipaths ${_eep_PREFIX}) + foreach(_eep_i ${_eep_INCLUDE_DIRS}) + list(APPEND _eep_ipaths ${__eep_PREFIX}/${_eep_i}) + endforeach() + + # find the includes that were asked for, and add + # their paths to the includes list + set(_eep_idirs) + foreach(_eep_i ${_eep_INCLUDES}) + find_path(_eep_path_${_eep_i} ${_eep_i} + PATHS ${_eep_ipaths} + PATH_SUFFIXES include ${_eep_INCLUDE_DIR_SUFFIXES} + NO_DEFAULT_PATH + ) + if(NOT _eep_path_${_eep_i}) + message(FATAL_ERROR "could not find include: ${_eep_i}") + endif() + #message(STATUS "include: ${_eep_i} ---> ${_eep_path_${_eep_i}}") + list(APPEND _eep_idirs ${_eep_path_${_eep_i}}) + find_package_handle_standard_args(${_eep_i}_INCLUDE_DIR DEFAULT_MSG _eep_path_${_eep_i}) + endforeach() + if(_eep_idirs) + list(REMOVE_DUPLICATES _eep_idirs) + endif() + + # save the include list + set(${name}_INCLUDE_DIRS "${_eep_idirs}" CACHE STRING "" FORCE) + + #---------------------------------------------------------------- + # libraries + + # the list of paths to search for libraries + set(_eep_lpaths ${_eep_PREFIX}) + foreach(_eep_i ${_eep_LIBRARIES}) + list(APPEND _eep_lpaths ${__eep_PREFIX}/${_eep_i}) + endforeach() + + # find any libraries that were asked for + set(_eep_libs) + foreach(_eep_i ${_eep_LIBRARIES}) + find_library(_eep_lib_${_eep_i} ${_eep_i} + PATHS ${_eep_lpaths} + PATH_SUFFIXES lib ${_eep_LIBRARY_DIR_SUFFIXES} + NO_DEFAULT_PATH + ) + if(NOT _eep_lib_${_eep_i}) + message(FATAL_ERROR "could not find library: ${_eep_i}") + endif() + #message(STATUS "lib: ${_eep_i} ---> ${_eep_lib_${_eep_i}}") + list(APPEND _eep_libs ${_eep_lib_${_eep_i}}) + find_package_handle_standard_args(${_eep_i}_LIBRARY DEFAULT_MSG _eep_lib_${_eep_i}) + endforeach() + + # save the include list + set(${name}_LIBRARIES ${_eep_libs} CACHE STRING "") + +endmacro(ExternalProject_Import) diff --git a/thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake b/thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake new file mode 100644 index 000000000..01e7a3ae9 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake @@ -0,0 +1,75 @@ +# Find the win10 SDK path. +if ("$ENV{WIN10_SDK_PATH}$ENV{WIN10_SDK_VERSION}" STREQUAL "" ) + get_filename_component(WIN10_SDK_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;InstallationFolder]" ABSOLUTE CACHE) + get_filename_component(TEMP_WIN10_SDK_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;ProductVersion]" ABSOLUTE CACHE) + get_filename_component(WIN10_SDK_VERSION ${TEMP_WIN10_SDK_VERSION} NAME) +elseif(TRUE) + set (WIN10_SDK_PATH $ENV{WIN10_SDK_PATH}) + set (WIN10_SDK_VERSION $ENV{WIN10_SDK_VERSION}) +endif ("$ENV{WIN10_SDK_PATH}$ENV{WIN10_SDK_VERSION}" STREQUAL "" ) + +# WIN10_SDK_PATH will be something like C:\Program Files (x86)\Windows Kits\10 +# WIN10_SDK_VERSION will be something like 10.0.14393 or 10.0.14393.0; we need the +# one that matches the directory name. + +if (IS_DIRECTORY "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}.0") + set(WIN10_SDK_VERSION "${WIN10_SDK_VERSION}.0") +endif (IS_DIRECTORY "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}.0") + + +# Find the d3d12 and dxgi include path, it will typically look something like this. +# C:\Program Files (x86)\Windows Kits\10\Include\10.0.10586.0\um\d3d12.h +# C:\Program Files (x86)\Windows Kits\10\Include\10.0.10586.0\shared\dxgi1_4.h +find_path(D3D12_INCLUDE_DIR # Set variable D3D12_INCLUDE_DIR + d3d12.h # Find a path with d3d12.h + HINTS "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}/um" + DOC "path to WIN10 SDK header files" + HINTS + ) + +find_path(DXGI_INCLUDE_DIR # Set variable DXGI_INCLUDE_DIR + dxgi1_4.h # Find a path with dxgi1_4.h + HINTS "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}/shared" + DOC "path to WIN10 SDK header files" + HINTS + ) + +if ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x64 ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM") + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM64") + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm64 ) +elseif ("${DXC_BUILD_ARCH}" STREQUAL "Win32" ) + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x86 ) +endif ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + +if ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x64 ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM") + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM64") + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm64 ) +elseif ("${DXC_BUILD_ARCH}" STREQUAL "Win32" ) + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x86 ) +endif ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + +set(D3D12_LIBRARIES ${D3D12_LIBRARY} ${DXGI_LIBRARY}) +set(D3D12_INCLUDE_DIRS ${D3D12_INCLUDE_DIR} ${DXGI_INCLUDE_DIR}) + + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set D3D12_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(D3D12 DEFAULT_MSG + D3D12_INCLUDE_DIRS D3D12_LIBRARIES) + +mark_as_advanced(D3D12_INCLUDE_DIRS D3D12_LIBRARIES) diff --git a/thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake b/thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake new file mode 100644 index 000000000..5ae79ec07 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake @@ -0,0 +1,76 @@ +# Attempt to find the D3D12 libraries +# Defines: +# +# DX12_FOUND - system has DX12 +# DX12_INCLUDE_PATH - path to the DX12 headers +# DX12_LIBRARIES - path to the DX12 libraries +# DX12_LIB - d3d12.lib + +set(DX12_FOUND "NO") + +if(WIN32) + set(WIN10_SDK_DIR "C:/Program Files (x86)/Windows Kits/10") + #set(WIN10_SDK_VERSION "10.0.10069.0") + file(GLOB WIN10_SDK_VERSIONS + LIST_DIRECTORIES TRUE + RELATIVE "${WIN10_SDK_DIR}/Lib" + "${WIN10_SDK_DIR}/Lib/*") + list(SORT WIN10_SDK_VERSIONS) + list(GET WIN10_SDK_VERSIONS -1 WIN10_SDK_VERSION) + + if(CMAKE_CL_64) + set(w10ARCH x64) + elseif(CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM") + set(w10ARCH arm) + elseif(CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM64") + set(w10ARCH arm64) + else() + set(w10ARCH x86) + endif() + + # Look for the windows 8 sdk + find_path(DX12_INC_DIR + NAMES d3d12.h + PATHS "${WIN10_SDK_DIR}/Include/${WIN10_SDK_VERSION}/um" + DOC "Path to the d3d12.h file" + ) + find_path(DXGI_INC_DIR + NAMES dxgi1_4.h + PATHS "${WIN10_SDK_DIR}/Include/${WIN10_SDK_VERSION}/shared" + DOC "Path to the dxgi header file" + ) + + if(DX12_INC_DIR AND DXGI_INC_DIR) + find_library(DX12_LIB + NAMES d3d12 + PATHS "${WIN10_SDK_DIR}/Lib/${WIN10_SDK_VERSION}/um/${w10ARCH}" + NO_DEFAULT_PATH + DOC "Path to the d3d12.lib file" + ) + find_library(DXGI_LIB + NAMES dxgi + PATHS "${WIN10_SDK_DIR}/Lib/${WIN10_SDK_VERSION}/um/${w10ARCH}" + NO_DEFAULT_PATH + DOC "Path to the dxgi.lib file" + ) + if(DX12_LIB AND DXGI_LIB) + set(DX12_FOUND "YES") + set(DX12_LIBRARIES ${DX12_LIB} ${DXGI_LIB}) + mark_as_advanced(DX12_INC_DIR DX12_LIB) + mark_as_advanced(DXGI_INC_DIR DXGI_LIB) + endif() + endif() +endif(WIN32) + +if(DX12_FOUND) + if(NOT DX12_FIND_QUIETLY) + message(STATUS "DX12 headers found at ${DX12_INC_DIR}") + endif() +else() + if(DX12_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find Direct3D12") + endif() + if(NOT DX12_FIND_QUIETLY) + message(STATUS "Could NOT find Direct3D12") + endif() +endif() diff --git a/thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake b/thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake new file mode 100644 index 000000000..e7e9e5aa6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake @@ -0,0 +1,53 @@ + +function(_c4_intersperse_with_flag outvar flag) + if(MSVC AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # it may be clang as well + set(f "/${flag}") + else() + set(f "-${flag}") + endif() + set(out) + foreach(i ${ARGN}) + if(NOT "${i}" STREQUAL "") + set(out "${out} ${f} '${i}'") + + # ... Following this are several unsuccessful attempts to make + # sure that an empty generator expression passed as part of the + # arguments won't be expanded to nothing between successive + # flags. For example, -I /some/include -I -I /other/include, + # which is wrong as it misses an empty quote. This causes + # clang-tidy in particular to fail. Maybe this is happening + # because the result is passed to separate_arguments() which + # prevents the lists from being evaluated correctly. Also, note + # that add_custom_target() has the following options which may + # help: COMMAND_EXPAND_LISTS and VERBATIM. + + # Anyway -- for now it is working, but maybe the generator + # expression approach turns out to work while being much cleaner + # than the current approach. + + #set(c $<GENEX_EVAL,$<BOOL:${i}>>) + #set(c $<BOOL:${i}>) # i may be a generator expression the evaluates to empty + #set(s "${f} ${i}") + #set(e "${f} aaaaaaWTF") + #list(APPEND out $<IF:${c},${s},${e}>) + #list(APPEND out $<${c},${s}>) + #list(APPEND out $<GENEX_EVAL:${c},${s}>) + #list(APPEND out $<TARGET_GENEX_EVAL:${tgt},${c},${s}>) + endif() + endforeach() + ## https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#string-valued-generator-expressions + #if(ARGN) + # set(out "${f}$<JOIN:${ARGN},;${f}>") + #endif() + set(${outvar} ${out} PARENT_SCOPE) +endfunction() + +function(c4_get_define_flags outvar) + _c4_intersperse_with_flag(out D ${ARGN}) + set(${outvar} ${out} PARENT_SCOPE) +endfunction() + +function(c4_get_include_flags outvar) + _c4_intersperse_with_flag(out I ${ARGN}) + set(${outvar} ${out} PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/GetNames.cmake b/thirdparty/ryml/ext/c4core/cmake/GetNames.cmake new file mode 100644 index 000000000..a287372c0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/GetNames.cmake @@ -0,0 +1,51 @@ +function(get_lib_names lib_names base) + set(${lib_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.lib) + else() + set(__glnn lib${__glnname}.a) + endif() + list(APPEND ${lib_names} "${base}${__glnn}") + endforeach() + set(lib_names ${lib_names} PARENT_SCOPE) +endfunction() + +function(get_dll_names dll_names base) + set(${dll_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.dll) + else() + set(__glnn lib${__glnname}.so) + endif() + list(APPEND ${dll_names} "${base}${__glnn}") + endforeach() + set(dll_names ${dll_names} PARENT_SCOPE) +endfunction() + +function(get_script_names script_names base) + set(${script_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.bat) + else() + set(__glnn ${__glnname}.sh) + endif() + list(APPEND ${script_names} "${base}${__glnn}") + endforeach() + set(script_names ${script_names} PARENT_SCOPE) +endfunction() + +function(get_exe_names exe_names base) + set(${exe_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.exe) + else() + set(__glnn ${__glnname}) + endif() + list(APPEND ${exe_names} "${base}${__glnn}") + endforeach() + set(exe_names ${exe_names} PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/LICENSE.txt b/thirdparty/ryml/ext/c4core/cmake/LICENSE.txt new file mode 100644 index 000000000..47b6b4394 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018, Joao Paulo Magalhaes <[email protected]> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + diff --git a/thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake b/thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake new file mode 100644 index 000000000..f0d217c2b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake @@ -0,0 +1,275 @@ +# 2006-2008 (c) Viva64.com Team +# 2008-2016 (c) OOO "Program Verification Systems" +# +# Version 2 + +function (pvs_studio_relative_path VAR ROOT FILEPATH) + set("${VAR}" "${FILEPATH}" PARENT_SCOPE) + if ("${FILEPATH}" MATCHES "^/.*$") + file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}") + if (NOT "${RPATH}" MATCHES "^\\.\\..*$") + set("${VAR}" "${RPATH}" PARENT_SCOPE) + endif () + endif () +endfunction () + +function (pvs_studio_join_path VAR DIR1 DIR2) + if ("${DIR2}" MATCHES "^(/|~).*$" OR "${DIR1}" STREQUAL "") + set("${VAR}" "${DIR2}" PARENT_SCOPE) + else () + set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE) + endif () +endfunction () + +macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX) + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (PROP ${PROPERTY}) + pvs_studio_join_path(PROP "${DIR}" "${PROP}") + list(APPEND "${CXX}" "${PREFIX}${PROP}") + list(APPEND "${C}" "${PREFIX}${PROP}") + endforeach () + endif () +endmacro () + +macro (pvs_studio_append_standard_flag FLAGS STANDARD) + if ("${STANDARD}" MATCHES "^(99|11|14|17)$") + if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang") + list(APPEND "${FLAGS}" "-std=c++${STANDARD}") + endif () + endif () +endmacro () + +function (pvs_studio_set_directory_flags DIRECTORY CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_target_flags TARGET CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_target_property(PROPERTY "${TARGET}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_target_property(PROPERTY "${TARGET}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + get_target_property(PROPERTY "${TARGET}" CXX_STANDARD) + pvs_studio_append_standard_flag(CXX_FLAGS "${PROPERTY}") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_source_file_flags SOURCE) + set(LANGUAGE "") + + string(TOLOWER "${SOURCE}" SOURCE_LOWER) + if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$") + if ("${SOURCE}" MATCHES "^.*\\.c$") + set(LANGUAGE C) + else () + set(LANGUAGE CXX) + endif () + endif () + + if ("${LANGUAGE}" STREQUAL "C") + set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO) + elseif ("${LANGUAGE}" STREQUAL "CXX") + set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO) + endif () + + set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE) + set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR) + set(PLOGS ${PVS_STUDIO_PLOGS}) + pvs_studio_set_source_file_flags("${SOURCE}") + + get_filename_component(SOURCE "${SOURCE}" REALPATH) + pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}") + pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}") + + set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog") + get_filename_component(LOG "${LOG}" REALPATH) + get_filename_component(PARENT_DIR "${LOG}" DIRECTORY) + + if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "") + add_custom_command(OUTPUT "${LOG}" + COMMAND mkdir -p "${PARENT_DIR}" + COMMAND rm -f "${LOG}" + COMMAND "${PVS_STUDIO_BIN}" analyze + --output-file "${LOG}" + --source-file "${SOURCE}" + ${PVS_STUDIO_ARGS} + --cl-params ${PVS_STUDIO_CL_PARAMS} "${SOURCE}" + WORKING_DIRECTORY "${BINARY_DIR}" + DEPENDS "${SOURCE}" "${PVS_STUDIO_CONFIG}" + VERBATIM + COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}") + list(APPEND PLOGS "${LOG}") + endif () + set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_target TARGET DIR) + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + + get_target_property(PROPERTY "${TARGET}" SOURCES) + pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}") + if ("${BINARY_DIR}" MATCHES "^/.*$") + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}") + else () + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}") + endif () + + file(MAKE_DIRECTORY "${BINARY_DIR}") + + pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (SOURCE ${PROPERTY}) + pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}") + pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}") + endforeach () + endif () + + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_add_target) + macro (default VAR VALUE) + if ("${${VAR}}" STREQUAL "") + set("${VAR}" "${VALUE}") + endif () + endmacro () + + set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(DEFAULT_PREPROCESSOR "clang") + else () + set(DEFAULT_PREPROCESSOR "gcc") + endif () + + set(OPTIONAL OUTPUT ALL) + set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT) + set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE) + cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN}) + + if ("${PVS_STUDIO_CFG}" STREQUAL "" OR NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "") + set(PVS_STUDIO_EMPTY_CONFIG ON) + else () + set(PVS_STUDIO_EMPTY_CONFIG OFF) + endif () + + default(PVS_STUDIO_CFG_TEXT "analysis-mode=4") + default(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg") + default(PVS_STUDIO_C_FLAGS "") + default(PVS_STUDIO_CXX_FLAGS "") + default(PVS_STUDIO_TARGET "pvs") + default(PVS_STUDIO_LOG "PVS-Studio.log") + default(PVS_STUDIO_BIN "pvs-studio-analyzer") + default(PVS_STUDIO_CONVERTER "plog-converter") + default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}") + default(PVS_STUDIO_PLATFORM "linux64") + + if (PVS_STUDIO_EMPTY_CONFIG) + set(PVS_STUDIO_CONFIG_COMMAND echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}") + else () + set(PVS_STUDIO_CONFIG_COMMAND touch "${PVS_STUDIO_CONFIG}") + endif () + + add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}" + COMMAND ${PVS_STUDIO_CONFIG_COMMAND} + WORKING_DIRECTORY "${BINARY_DIR}" + COMMENT "Generating PVS-Studio.cfg") + + + if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$") + message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.") + endif () + + pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}") + pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS) + + if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "") + pvs_studio_join_path(PVS_STUDIO_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_LICENSE}") + list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}") + endif () + list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}" + --platform "${PVS_STUDIO_PLATFORM}" + --preprocessor "${PVS_STUDIO_PREPROCESSOR}") + + set(PVS_STUDIO_PLOGS "") + + foreach (TARGET ${PVS_STUDIO_ANALYZE}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + string(FIND "${TARGET}" ":" DELIM) + if ("${DELIM}" GREATER "-1") + math(EXPR DELIMI "${DELIM}+1") + string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR) + string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET) + pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}") + endif () + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + endforeach () + + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + foreach (SOURCE ${PVS_STUDIO_SOURCES}) + pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") + endforeach () + + pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}") + if (PVS_STUDIO_PLOGS) + set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} > "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}") + if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT) + if ("${PVS_STUDIO_FORMAT}" STREQUAL "") + set(PVS_STUDIO_FORMAT "errorfile") + endif () + list(APPEND COMMANDS + COMMAND mv "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${PVS_STUDIO_CONVERTER}" -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}" + COMMAND rm -f "${PVS_STUDIO_LOG}.pvs.raw") + endif () + else () + set(COMMANDS COMMAND touch "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}: no sources found") + endif () + + add_custom_command(OUTPUT "${PVS_STUDIO_LOG}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + + if (PVS_STUDIO_ALL) + set(ALL "ALL") + else () + set(ALL "") + endif () + + if (PVS_STUDIO_OUTPUT) + set(COMMANDS COMMAND cat "${PVS_STUDIO_LOG}" 1>&2) + else () + set(COMMANDS "") + endif () + + add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} ${COMMANDS} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS ${PVS_STUDIO_DEPENDS} "${PVS_STUDIO_LOG}") +endfunction () + diff --git a/thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake b/thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake new file mode 100644 index 000000000..164940e00 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake @@ -0,0 +1,25 @@ +# create a script that applies a patch (it's different in windows) + +# to generate a patch: +# subversion: svn diff --patch-compatible > path/to/the/patch.diff + + +function(apply_patch patch where mark) + if(NOT EXISTS "${mark}") + if(NOT Patch_EXECUTABLE) + find_package(Patch REQUIRED) + endif() + file(TO_NATIVE_PATH ${patch} patch_native) + get_filename_component(patch_name "${patch}" NAME) + message(STATUS "Applying patch: ${patch_name}") + execute_process( + COMMAND "${Patch_EXECUTABLE}" "-p0" "--input=${patch_native}" + WORKING_DIRECTORY "${where}" + RESULT_VARIABLE status) + if(NOT status STREQUAL "0") + message(FATAL_ERROR "could not apply patch: ${patch} ---> ${where}") + else() + file(TOUCH "${mark}") + endif() + endif() +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake b/thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake new file mode 100644 index 000000000..acd04e55b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake @@ -0,0 +1,27 @@ +function(status) + message(STATUS "${ARGV}") +endfunction() + +function(print_var var) + message(STATUS "${var}=${${var}} ${ARGN}") +endfunction() + +function(print_vars) + foreach(a ${ARGN}) + message(STATUS "${a}=${${a}}") + endforeach(a) +endfunction() + +function(debug_var debug var) + if(${debug}) + message(STATUS "${var}=${${var}} ${ARGN}") + endif() +endfunction() + +function(debug_vars debug) + if(${debug}) + foreach(a ${ARGN}) + message(STATUS "${a}=${${a}}") + endforeach(a) + endif() +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/README.md b/thirdparty/ryml/ext/c4core/cmake/README.md new file mode 100644 index 000000000..13341daa0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/README.md @@ -0,0 +1,25 @@ +# cmake project utilities + +Useful cmake scripts, at [c4Project.cmake](c4Project.cmake). + +## Project utilities + +## Adding targets + +### Target types + +## Downloading and configuring third-party projects at configure time + +## Setting up tests + +### Coverage +### Static analysis +### Valgrind + +## Setting up benchmarks + +## License + +MIT License + + diff --git a/thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake b/thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake new file mode 100644 index 000000000..57baf662c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake @@ -0,0 +1,180 @@ + + +function(c4_get_architecture_defines output_var) + c4_get_target_cpu_architecture(arch) + if("${arch}" STREQUAL "x86_64") + set(defines __x86_64__) + elseif("${arch}" STREQUAL "i386") + set(defines __i386__) + elseif("${arch}" STREQUAL "armv8_64") + set(defines __arm__ __aarch64__) + elseif("${arch}" STREQUAL "armv8") + set(defines __arm__ __ARM_ARCH_8__) + elseif("${arch}" STREQUAL "armv7") + set(defines __arm__ __ARM_ARCH_7__) + elseif("${arch}" STREQUAL "armv6") + set(defines __arm__ __ARM_ARCH_6__) + elseif("${arch}" STREQUAL "armv5") + set(defines __arm__ __ARM_ARCH_5__) + elseif("${arch}" STREQUAL "armv4") + set(defines __arm__ __ARM_ARCH_4T__) + elseif("${arch}" STREQUAL "ia64") + set(defines __ia64__) + elseif("${arch}" STREQUAL "ppc64") + set(defines __ppc64__) + elseif("${arch}" STREQUAL "ia64") + set(defines __ia64__) + elseif("${arch}" STREQUAL "riscv64") + set(defines __riscv64__) + elseif("${arch}" STREQUAL "riscv32") + set(defines __riscv32__) + elseif("${arch}" STREQUAL "s390x") + set(defines __s390x__) + else() + message(FATAL_ERROR "unknown target architecture: ${arch}") + endif() + set(${output_var} ${defines} PARENT_SCOPE) +endfunction() + + +# adapted from https://github.com/axr/solar-cmake/blob/master/TargetArch.cmake +# Set ppc_support to TRUE before including this file or ppc and ppc64 +# will be treated as invalid architectures since they are no longer supported by Apple +function(c4_get_target_cpu_architecture output_var) + # this should be more or less in line with c4core/cpu.hpp + set(archdetect_c_code " +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) + #error cmake_ARCH x86_64 +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) + #error cmake_ARCH i386 +#elif defined(__arm__) || defined(_M_ARM) \ + || defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64) + #if defined(__aarch64__) || defined(_M_ARM64) + #error cmake_ARCH armv8_64 + #else + #if defined(__ARM_ARCH_8__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8) + #error cmake_ARCH armv8 + #elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \ + || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \ + || (defined(_M_ARM) && _M_ARM >= 7) + #error cmake_ARCH armv7 + #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6) + #error cmake_ARCH armv6 + #elif defined(__ARM_ARCH_5TEJ__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5) + #error cmake_ARCH armv5 + #elif defined(__ARM_ARCH_4T__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4) + #error cmake_ARCH armv4 + #else + #error cmake_ARCH arm + #endif + #endif +#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + #error cmake_ARCH ia64 +#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \ + || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \ + || defined(_M_MPPC) || defined(_M_PPC) + #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) + #error cmake_ARCH ppc64 + #else + #error cmake_ARCH ppc32 + #endif +#elif defined(__riscv) + #if __riscv_xlen == 64 + #error cmake_ARCH riscv64 + #else + #error cmake_ARCH riscv32 + #endif +#elif defined(__s390x__) || defined(__zarch__) + #error cmake_ARCH s390x +#endif +#error cmake_ARCH unknown +") + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + # On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set + # First let's normalize the order of the values + + # Note that it's not possible to compile PowerPC applications if you are using + # the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we + # disable it by default + # See this page for more information: + # http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4 + + # Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime. + # On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise. + + foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) + if("${osx_arch}" STREQUAL "ppc" AND ppc_support) + set(osx_arch_ppc TRUE) + elseif("${osx_arch}" STREQUAL "i386") + set(osx_arch_i386 TRUE) + elseif("${osx_arch}" STREQUAL "x86_64") + set(osx_arch_x86_64 TRUE) + elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) + set(osx_arch_ppc64 TRUE) + else() + message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}") + endif() + endforeach() + + # Now add all the architectures in our normalized order + if(osx_arch_ppc) + list(APPEND ARCH ppc) + endif() + + if(osx_arch_i386) + list(APPEND ARCH i386) + endif() + + if(osx_arch_x86_64) + list(APPEND ARCH x86_64) + endif() + + if(osx_arch_ppc64) + list(APPEND ARCH ppc64) + endif() + else() + file(WRITE "${CMAKE_BINARY_DIR}/detect_cpu_arch.c" "${archdetect_c_code}") + + enable_language(C) + + # Detect the architecture in a rather creative way... + # This compiles a small C program which is a series of ifdefs that selects a + # particular #error preprocessor directive whose message string contains the + # target architecture. The program will always fail to compile (both because + # file is not a valid C program, and obviously because of the presence of the + # #error preprocessor directives... but by exploiting the preprocessor in this + # way, we can detect the correct target architecture even when cross-compiling, + # since the program itself never needs to be run (only the compiler/preprocessor) + try_run( + run_result_unused + compile_result_unused + "${CMAKE_BINARY_DIR}" + "${CMAKE_BINARY_DIR}/detect_cpu_arch.c" + COMPILE_OUTPUT_VARIABLE ARCH + CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + ) + + # Parse the architecture name from the compiler output + string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}") + + # Get rid of the value marker leaving just the architecture name + string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}") + + # If we are compiling with an unknown architecture this variable should + # already be set to "unknown" but in the case that it's empty (i.e. due + # to a typo in the code), then set it to unknown + if (NOT ARCH) + set(ARCH unknown) + endif() + endif() + + set(${output_var} "${ARCH}" PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake new file mode 100644 index 000000000..86d39dae1 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake @@ -0,0 +1,29 @@ +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_PROCESSOR arm) +SET(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_CROSSCOMPILING TRUE) + +find_program(CC_GCC arm-linux-gnueabihf-gcc REQUIRED) + +set(CMAKE_FIND_ROOT_PATH /usr/arm-gnueabihf) + +# Cross compiler +SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +set(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# Libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE) + +get_filename_component(TOOLCHAIN_DIR "${CC_GCC}" DIRECTORY) +get_filename_component(TOOLCHAIN_DIR "${TOOLCHAIN_DIR}" DIRECTORY) +set(TOOLCHAIN_SO_DIR "${TOOLCHAIN_DIR}/arm-linux-gnueabihf/") +#/home/jpmag/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L ${TOOLCHAIN_SO_DIR}) diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake new file mode 100644 index 000000000..7f6867126 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake @@ -0,0 +1,84 @@ +# taken from https://stackoverflow.com/a/49086560 + +# tested with the toolchain from ARM: +# gcc-arm-9.2-2019.12-mingw-w64-i686-arm-none-linux-gnueabihf.tar.xz +# found at +# https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads + +# see also: +# https://stackoverflow.com/questions/42371788/how-to-run-helloworld-on-arm +# https://dev.to/younup/cmake-on-stm32-the-beginning-3766 + +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_PROCESSOR arm) +SET(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_CROSSCOMPILING TRUE) + +find_program(CC_GCC arm-none-linux-gnueabihf-gcc REQUIRED) + +set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) + +# Cross compiler +SET(CMAKE_C_COMPILER arm-none-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER arm-none-linux-gnueabihf-g++) +set(CMAKE_LIBRARY_ARCHITECTURE arm-none-linux-gnueabihf) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# Libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE) + +get_filename_component(TOOLCHAIN_DIR "${CC_GCC}" DIRECTORY) +get_filename_component(TOOLCHAIN_DIR "${TOOLCHAIN_DIR}" DIRECTORY) +set(TOOLCHAIN_SO_DIR "${TOOLCHAIN_DIR}/arm-none-linux-gnueabihf/libc/") + +#/home/jpmag/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L ${TOOLCHAIN_SO_DIR}) + +return() + + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_CROSSCOMPILING 1) + +set(CMAKE_C_COMPILER "arm-none-eabi-gcc") +set(CMAKE_CXX_COMPILER "arm-none-eabi-g++") + + +set(CMAKE_FIND_ROOT_PATH /usr/arm-none-eabi) +set(CMAKE_EXE_LINKER_FLAGS "--specs=nosys.specs" CACHE INTERNAL "") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(COMPILER_FLAGS "-marm -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 -D_GNU_SOURCE") + +message(STATUS) +message(STATUS) +message(STATUS) + +if(NOT DEFINED CMAKE_C_FLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILER_FLAGS}" CACHE STRING "") +else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILER_FLAGS}") +endif() + +message(STATUS) +message(STATUS) +message(STATUS) +message(STATUS) + +if(NOT DEFINED CMAKE_CXX_FLAGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}" CACHE STRING "") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}") +endif() diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake new file mode 100644 index 000000000..260b9b078 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake @@ -0,0 +1,73 @@ +# Copyright 2017 Autodesk Inc. http://www.autodesk.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You may +# obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +# This module is shared; use include blocker. +if( _PS4_TOOLCHAIN_ ) + return() +endif() +set(_PS4_TOOLCHAIN_ 1) + +# PS4 SCE version requirement +set(REQUIRED_PS4_VERSION "4.000") + +# Get PS4 SCE environment +if( EXISTS "$ENV{SCE_ROOT_DIR}" AND IS_DIRECTORY "$ENV{SCE_ROOT_DIR}" ) + string(REGEX REPLACE "\\\\" "/" PS4_ROOT $ENV{SCE_ROOT_DIR}) + string(REGEX REPLACE "//" "/" PS4_ROOT ${PS4_ROOT}) + if( EXISTS "$ENV{SCE_ORBIS_SDK_DIR}" AND IS_DIRECTORY "$ENV{SCE_ORBIS_SDK_DIR}" ) + string(REGEX REPLACE "\\\\" "/" PS4_SDK $ENV{SCE_ORBIS_SDK_DIR}) + string(REGEX REPLACE "//" "/" PS4_SDK ${PS4_SDK}) + get_filename_component(SCE_VERSION "${PS4_SDK}" NAME) + endif() +endif() + +# Report and check version if it exist +if( NOT "${SCE_VERSION}" STREQUAL "" ) + message(STATUS "PS4 SCE version found: ${SCE_VERSION}") + if( NOT "${SCE_VERSION}" MATCHES "${REQUIRED_PS4_VERSION}+" ) + message(WARNING "Expected PS4 SCE version: ${REQUIRED_PS4_VERSION}") + if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + set(PS4_ROOT) + set(PS4_SDK) + endif() + endif() +endif() + +# If we only want the environment values, exit now +if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + return() +endif() + +# We are building PS4 platform, fail if PS4 SCE not found +if( NOT PS4_ROOT OR NOT PS4_SDK ) + message(FATAL_ERROR "Engine requires PS4 SCE SDK to be installed in order to build PS4 platform.") +endif() + +# Tell CMake we are cross-compiling to PS4 (Orbis) +set(CMAKE_SYSTEM_NAME Orbis) +set(PS4 True) + +# Set CMake system root search path +set(CMAKE_SYSROOT "${PS4_ROOT}") + +# Set compilers to the ones found in PS4 SCE SDK directory +set(CMAKE_C_COMPILER "${PS4_SDK}/host_tools/bin/orbis-clang.exe") +set(CMAKE_CXX_COMPILER "${PS4_SDK}/host_tools/bin/orbis-clang++.exe") +set(CMAKE_ASM_COMPILER "${PS4_SDK}/host_tools/bin/orbis-as.exe") + +# Only search the PS4 SCE SDK, not the remainder of the host file system +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake new file mode 100644 index 000000000..2ed9fad73 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake @@ -0,0 +1,93 @@ +# Copyright 2017 Autodesk Inc. http://www.autodesk.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You may +# obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +# This module is shared; use include blocker. +if( _XB1_TOOLCHAIN_ ) + return() +endif() +set(_XB1_TOOLCHAIN_ 1) + +# XB1 XDK version requirement +set(REQUIRED_XB1_TOOLCHAIN_VERSION "160305") + +# Get XDK environment +if( EXISTS "$ENV{DurangoXDK}" AND IS_DIRECTORY "$ENV{DurangoXDK}" ) + string(REGEX REPLACE "\\\\" "/" XDK_ROOT $ENV{DurangoXDK}) + string(REGEX REPLACE "//" "/" XDK_ROOT ${XDK_ROOT}) +endif() + +# Fail if XDK not found +if( NOT XDK_ROOT ) + if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + return() + endif() + message(FATAL_ERROR "Engine requires XB1 XDK to be installed in order to build XB1 platform.") +endif() + +# Get toolchain version +get_filename_component(XDK_TOOLCHAIN_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Durango XDK\\${REQUIRED_XB1_TOOLCHAIN_VERSION};EditionVersion]" NAME) + +if( XDK_TOOLCHAIN_VERSION STREQUAL REQUIRED_XB1_TOOLCHAIN_VERSION ) + message(STATUS "Found required XDK toolchain version (${XDK_TOOLCHAIN_VERSION})") +else() + get_filename_component(XDK_TOOLCHAIN_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Durango XDK;Latest]" NAME) + message(WARNING "Could not find required XDK toolchain version (${REQUIRED_XB1_TOOLCHAIN_VERSION}), using latest version instead (${XDK_TOOLCHAIN_VERSION})") +endif() + +# If we only want the environment values, exit now +if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + return() +endif() + +# Find XDK compiler directory +if( CMAKE_GENERATOR STREQUAL "Visual Studio 11 2012" ) + set(XDK_COMPILER_DIR "${XDK_ROOT}/${XDK_TOOLCHAIN_VERSION}/Compilers/dev11.1") +elseif( CMAKE_GENERATOR STREQUAL "Visual Studio 14 2015" ) + get_filename_component(XDK_COMPILER_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\14.0_Config\\Setup\\VC;ProductDir]" DIRECTORY) + if( DEFINED XDK_COMPILER_DIR ) + string(REGEX REPLACE "\\\\" "/" XDK_COMPILER_DIR ${XDK_COMPILER_DIR}) + string(REGEX REPLACE "//" "/" XDK_COMPILER_DIR ${XDK_COMPILER_DIR}) + endif() + if( NOT XDK_COMPILER_DIR ) + message(FATAL_ERROR "Can't find Visual Studio 2015 installation path.") + endif() +else() + message(FATAL_ERROR "Unsupported Visual Studio version!") +endif() + +# Tell CMake we are cross-compiling to XBoxOne (Durango) +set(CMAKE_SYSTEM_NAME Durango) +set(XBOXONE True) + +# Set CMake system root search path +set(CMAKE_SYSROOT "${XDK_COMPILER_DIR}") + +# Set the compilers to the ones found in XboxOne XDK directory +set(CMAKE_C_COMPILER "${XDK_COMPILER_DIR}/vc/bin/amd64/cl.exe") +set(CMAKE_CXX_COMPILER "${XDK_COMPILER_DIR}/vc/bin/amd64/cl.exe") +set(CMAKE_ASM_COMPILER "${XDK_COMPILER_DIR}/vc/bin/amd64/ml64.exe") + +# Force compilers to skip detecting compiler ABI info and compile features +set(CMAKE_C_COMPILER_FORCED True) +set(CMAKE_CXX_COMPILER_FORCED True) +set(CMAKE_ASM_COMPILER_FORCED True) + +# Only search the XBoxOne XDK, not the remainder of the host file system +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Global variables +set(XBOXONE_SDK_REFERENCES "Xbox Services API, Version=8.0;Xbox GameChat API, Version=8.0") diff --git a/thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py b/thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py new file mode 100644 index 000000000..d4115c974 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py @@ -0,0 +1,219 @@ +import re +import os + + +class cmtfile: + """commented file""" + def __init__(self, filename): + self.filename = filename + def __str__(self): + return self.filename + + +class cmttext: + """commented text""" + def __init__(self, text): + self.text = text + def __str__(self): + return self.text + + +class ignfile: + """ignore file""" + def __init__(self, filename): + self.filename = filename + def __str__(self): + return self.filename + + +class hdrfile: + """header file, with custom include guard""" + def __init__(self, filename, incpattern, include_guard=None): + self.filename = filename + self.incpattern = incpattern + self.include_guard = include_guard + def __str__(self): + return self.filename + + +class injfile: + """header file, to be injected at the first include point""" + def __init__(self, filename, incpattern): + self.filename = filename + self.incpattern = incpattern + def __str__(self): + return self.filename + + +class injcode: + """direct code to inject""" + def __init__(self, code): + self.code = code + def __str__(self): + return self.code + + +class onlyif: + def __init__(self, condition, obj): + self.condition = condition + self.obj = obj + + +def catfiles(filenames, rootdir, + include_regexes, + definition_macro, + repo, + result_incguard): + file_re = re.compile('[-./]') + sepb = "//" + ("**" * 40) + sepf = "//" + ("--" * 40) + to_inject = {} + custom_include_guards = {} + def banner(s): + return f"\n\n\n{sepb}\n{sepf}\n// {s}\n// {repo}/{s}\n{sepf}\n{sepb}\n\n" + def footer(s): + return f"\n\n// (end {repo}/{s})\n" + def incguard(filename): + return custom_include_guards.get(filename, + f"{file_re.sub('_', filename).upper()}_") + def replace_include(rx, match, line, guard): + line = line.rstrip() + incl = match.group(1) + if to_inject.get(incl) is None: + if guard is None: + guard = incguard(incl) + return f"""// amalgamate: removed include of +// {repo}/src/{incl} +//{line} +#if !defined({guard}) && !defined(_{guard}) +#error "amalgamate: file {incl} must have been included at this point" +#endif /* {guard} */\n +""" + else: + entry = to_inject[incl] + del to_inject[incl] + return append_file(entry.filename) + def append_file(filename, guard=None): + s = "" + with open(filename, encoding="utf8") as f: + for line in f.readlines(): + for rx in include_regexes: + match = rx.match(line) + if match: + line = replace_include(rx, match, line, guard) + s += line + return s + def append_cpp(filename): + return f"""#ifdef {definition_macro} +{append_file(filename)} +#endif /* {definition_macro} */ +""" + def is_src(filename): + return filename.endswith(".cpp") or filename.endswith(".c") + def cmtline(line, more=""): + if len(line.strip()) > 0: + return f"// {line}{more}" + else: + return "//\n" + out = "" + for entry in filenames: + if isinstance(entry, onlyif): + if entry.condition: + entry = entry.obj + else: + continue + if isinstance(entry, ignfile): + pass + elif isinstance(entry, cmttext): + for line in entry.text.split("\n"): + out += cmtline(line, "\n") + elif isinstance(entry, cmtfile): + filename = f"{rootdir}/{entry.filename}" + out += banner(entry.filename) + with open(filename, encoding="utf8") as file: + for line in file.readlines(): + out += cmtline(line) + elif isinstance(entry, injcode): + out += f"\n{entry.code}\n" + elif isinstance(entry, injfile): + entry.filename = f"{rootdir}/{entry.filename}" + to_inject[entry.incpattern] = entry + else: + filename = f"{rootdir}/{entry}" + out += banner(entry) + if isinstance(entry, hdrfile): + if entry.include_guard is not None: + custom_include_guards[entry.incpattern] = entry.include_guard + out += append_file(filename, entry.include_guard) + else: + assert isinstance(entry, str) + if is_src(filename): + out += append_cpp(filename) + else: + out += append_file(filename) + out += footer(entry) + return f"""#ifndef {result_incguard} +#define {result_incguard} + +{out} +#endif /* {result_incguard} */ +""" + +def include_only_first(file_contents: str): + rx = [ + re.compile(r'^\s*#\s*include "(.*?)".*'), + re.compile(r'^\s*#\s*include <(.*?)>.*'), + ] + already_included = {} + out = "" + for line in file_contents.split("\n"): + for expr in rx: + match = expr.match(line) + if match: + incl = match.group(1) + if already_included.get(incl) is None: + already_included[incl] = line + if incl.endswith(".h"): + cpp_version = f"c{incl[:-2]}" + already_included[cpp_version] = line + elif incl.startswith("c") and not (incl.endswith(".h") or incl.endswith(".hpp")): + c_version = f"{incl[1:]}.h" + already_included[c_version] = line + else: + line = f"//included above:\n//{line}" + break + out += line + out += "\n" + return out + + +def mkparser(**bool_args): + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("output", default=None, nargs='?', help="output file. defaults to stdout") + for k, (default, help) in bool_args.items(): + # https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse + feature = parser.add_mutually_exclusive_group(required=False) + yes = '--' + k + no = '--no-' + k + if default: + yes_default = "this is the default" + no_default = f"the default is {yes}" + else: + yes_default = f"the default is {no}" + no_default = "this is the default" + feature.add_argument(yes, dest=k, action='store_true', help=f"{help}. {yes_default}.") + feature.add_argument(no, dest=k, action='store_false', help=f"{help}. {no_default}.") + parser.set_defaults(**{k: default}) + return parser + + +def file_put_contents(filename: str, contents: str): + if filename is None: + print(contents) + else: + dirname = os.path.dirname(filename) + if dirname: + os.makedirs(dirname, exist_ok=True) + with open(filename, "w", encoding="utf8") as output: + output.write(contents) diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore b/thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore new file mode 100644 index 000000000..ccf5d016e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore @@ -0,0 +1 @@ +static/*
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md b/thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md new file mode 100644 index 000000000..485cd4edb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md @@ -0,0 +1,7 @@ +# Benchmark explorer + +You need to start a http server on this folder: + +```shellsession +$ python bm.py serve . +``` diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js new file mode 100644 index 000000000..402d6120f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js @@ -0,0 +1,475 @@ + +/* https://stackoverflow.com/questions/9050345/selecting-last-element-in-javascript-array */ +function last(arr) +{ + return arr[arr.length - 1]; +}; + +function dbg() +{ + /* pass ?dbg=1 to enable debug logs */ + /*if(!getParam('dbg', 0)){ + return; + }*/ + elm = $("#dbg"); + var s = ""; + for (var i = 0; i < arguments.length; i++) { + if(i > 0) s += ' '; + s += arguments[i].toString(); + } + console.log(s); + s+= "\n"; + elm.append(document.createTextNode(s)); +} + + +function iterArr(arr, fn) { + for (var key in arr) { + if (arr.hasOwnProperty(key)) { + fn(key, arr[key]); + } + } +} + + +function fileContents(file, onComplete) +{ + dbg(`${file}: requesting...`); + var data; + $.get(file, function(d) { + dbg(`${file}: got response! ${d.length}B...`); + if(onComplete) { + onComplete(d); + } + }, "text"); +} + + + +/* https://stackoverflow.com/questions/7394748/whats-the-right-way-to-decode-a-string-that-has-special-html-entities-in-it/7394787 */ +function decodeHtmlEntities(str) +{ + return str + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace(""", "\"") + .replace(/&#(\d+);/g, function(match, dec) { + return String.fromCharCode(dec); + }); +} +/* https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript */ +function escapeHtml(unsafe) +{ + return unsafe + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + + +/* URL params ----------------------------------------------------------------- */ + +var _curr_url_params = null; +function parseUrlParams() +{ + var keyvals = []; + var keys = document.location.search.substring(1).split('&'); + dbg("keys=", keys) + for(var i = 0; i < keys.length; i++) { + var key = keys[i].split('='); + dbg("i=", i, " key=", key); + keyvals.push(key[0]); + keyvals[key[0]] = key[1]; + } + _curr_url_params = keyvals; +} + +function dbgParams() { + iterArr(_curr_url_params, function(key, val){ dbg("url params:", key, "=", val); }) + +} +function getParam(name, fallback) +{ + if(_curr_url_params === null) { parseUrlParams(); } + if(name in _curr_url_params) { + return _curr_url_params[name]; + } + return fallback; +} + +function setParam(name, value) { + if(_curr_url_params === null) { parseUrlParams(); } + _curr_url_params[name] = value; + // https://stackoverflow.com/questions/486896/adding-a-parameter-to-the-url-with-javascript + document.location.search = joinParams(); +} + +function joinParams() { + if(_curr_url_params === null) { parseUrlParams(); } + var s = ""; + iterArr(_curr_url_params, function(key, val){ + if(s != ""){ s += '&'; } + s += `${key}=${val}`; + }); + return s; +} + + +/* ----------------------------------------------------------------------------- */ + +function colMax(data, col) +{ + var max = -1.e30; + data.forEach(function(item, index){ + max = item[col] > max ? item[col] : max; + }); + return max; +} + +function colMin(data, col) +{ + var min = 1.e30; + data.forEach(function(item, index){ + min = item[col] < min ? item[col] : min; + }); + return min; +} + +/* https://stackoverflow.com/questions/2283566/how-can-i-round-a-number-in-javascript-tofixed-returns-a-string */ +function toFixedNumber(num, digits, base) +{ + var pow = Math.pow(base||10, digits); + return Math.round(num*pow) / pow; +} + +function humanReadable(sz, base=1024, precision=3) +{ + var i = -1; + var units; + if(base == 1000) + { + units = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + } + else if(base == 1024) + { + units = ['ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + } + do + { + sz /= base; + i++; + } while (sz > base); + return sz.toFixed(precision) + units[i]; +}; + + +/* ----------------------------------------------------------------------------- */ + +class BmResults +{ + constructor(dict={}) + { + Object.assign(this, dict); + for(var i = 0; i < this.benchmarks.length; ++i) { + var bm = this.benchmarks[i]; + bm.name = decodeHtmlEntities(bm.name); + bm.run_name = decodeHtmlEntities(bm.run_name); + } + } +} + +var bmSpecs; +function iterBms(fn) +{ + iterArr(bmSpecs.bm, fn); +} + +function loadSpecs(specs) +{ + dbg("loading specs ...."); + iterArr(specs, function(k, v){dbg("k=", k, 'v=', v); }); + $("#heading-title").html(`Benchmarks: <a href="${specs.url}">${specs.projname}</a>`); + bmSpecs = specs; + var toc = $("#toc"); + /*toc.append(`<li><a href="#" onclick="setParam('bm', 'all');">Load all</a></li>`);*/ + iterBms(function(key, bm) { + toc.append(`<li><a href="#${key}" onclick="setParam('bm', '${key}');">${key}</a>: ${bm.specs.desc}</li>`) + bm.name = key; + }); + // load if required + currBm = getParam("bm", ""); + dbg("params=", _curr_url_params, currBm); + if(currBm != "") { + dbg("loading BM from URL:", currBm) + loadBm(currBm); + } +} + +function normalizeBy(results, column_name, best_fn) +{ + var best = best_fn(results.benchmarks, column_name); + results.benchmarks.forEach(function(item, index){ + item[`${column_name}_normalized`] = item[column_name] / best; + }); +} + + +function loadAll() +{ + var id = "#bm-results"; + $(id).empty(); + var i = 0; + iterBms(function(key, bm){ + if(i++ > 0) $(id).append("<div class='bm-sep'><hr/></div>"); + appendBm(key); + }); +} + + +function loadBm(key) +{ + dbg("loading-.....", key); + /*if(key == "all") { + loadAll(); + }*/ + $("#bm-results").empty(); + var bm = bmSpecs.bm[key]; + if(bm.src != "") { + fileContents(bm.src, function(data){ + dbg(`${key}: got src data!`) + bm.src_data = data; + }); + } + var latestRun = last(bm.entries); + var bmfile = `${latestRun}/${key}.json`; + dbg("bmfile=", bmfile); + fileContents("bm/"+bmfile, function(data){ + dbg(`${key}: got bm data!`) + bm.results_data = new BmResults(JSON.parse(data)); + bm.results_data.benchmarks.forEach(function(item, index){ + item.id = index; + }); + normalizeBy(bm.results_data, 'iterations', colMin); + normalizeBy(bm.results_data, 'real_time', colMin, ); + normalizeBy(bm.results_data, 'cpu_time', colMin); + normalizeBy(bm.results_data, 'bytes_per_second', colMin); + normalizeBy(bm.results_data, 'items_per_second', colMin); + appendBm(latestRun, key, bm); + }); +} + + +function appendBm(run_id, id, bm) +{ + if($(document).find(`bm-results-${id}`).length == 0) + { + $("#bm-results").append(` +<div id="bm-results-${id}"> + <h2 id="bm-title-${id}">${id}</h2> + + <h3 id="heading-details-table-${id}">Run details</h3><table id="table-details-${id}" class="datatable" width="800px"></table> + + <h3 id="heading-table-${id}">Result tables</h3> + <h4 id="heading-table-${id}_pretty">Results</h4><table id="table-${id}_pretty" class="datatable" width="800px"></table> + <h4 id="heading-table-${id}_normalized">Normalized by column min</h4><table id="table-${id}_normalized" class="datatable" width="800px"></table> + + <h3 id="heading-chart-${id}">Chart</h2> + <div id="chart-container-${id}"></div> + + <h3 id="heading-code-${id}">Code</h2> + <pre><code id="code-${id}" class="lang-c++"></code></pre> +</div> +`); + } + var results = bm.results_data; + var code = bm.src_data; + loadDetailsTable(run_id, id, bm, results); + loadTable(id, bm, results); + loadChart(id, bm, results); + loadCode(id, bm, code); +} + + +function loadCode(elmId, bm, code) +{ + var elm = $(`#code-${elmId}`); + elm.text(code); + /* hljs.highlightBlock(elm); // this doesn't work */ + /* ... and this is very inefficient: */ + document.querySelectorAll('pre code').forEach((block) => { + hljs.highlightBlock(block); + }); +} + +function parseRunId(run_id) +{ + // example: + // commit id / cpu id - system id - build id + // git20201204_202919-b3f7fa7/x86_64_b9db3176-linux_4e9326b4-64bit_Debug_gcc10.2.0_10c5d03c + // git20201203_193348-2974fb0/x86_64_16ac0500-win32_59f3579c-64bit_MinSizeRel_msvc19.28.29304.1_32f6fc66 + // to tune the regex: https://regex101.com/r/rdkPi8/1 + // commit / cpu - system - build + var rx = /^(.+?)-([0-9a-f]{7})\/(.+?)_([0-9a-f]{8})-(.+?)_([0-9a-f]{8})-(.+?)_([0-9a-f]{8})$/gim; + var tag = rx.exec(run_id); + dbg("fdx: run_id=", run_id); + dbg("fdx: tag=", tag); + dbg("fdx: len=", tag.length); + return { + commit_id: `${tag[2]}: ${tag[1]}`, + cpu_id: `${tag[4]}: ${tag[3]} `, + system_id: `${tag[6]}: ${tag[5]}`, + build_id: `${tag[8]}: ${tag[7]}`, + }; +} + +function getBuildId(run_id) +{ + return parseRunId(run_id).build_id; +} + +function loadDetailsTable(run_id, id, bm, results) +{ + var url = bmSpecs.url; + var run = bmSpecs.runs[run_id]; + var commit = bmSpecs.commit[run.commit].specs; + var cpu = bmSpecs.cpu[run.cpu].specs; + var system = bmSpecs.system[run.system].specs; + + let other_commit_entries = bmSpecs.commit[run.commit].entries.filter( + entry_run => entry_run != run_id + ).map(entry_run => getBuildId(entry_run)).join('<br>'); + + /* https://datatables.net/ */ + $(`#table-details-${id}`).DataTable({ + data: results.benchmarks, + info: false, + paging: false, + searching: false, + retrieve: false, + order: [], + columns: [ + {title: "", data: "desc"}, + {title: "", data: "contents"}, + ], + data: [ + {desc: "benchmark id" , contents: id}, + {desc: "commit" , contents: ahref(`${url}/commit/${commit.sha1}`, commit.sha1)}, + {desc: "commit date" , contents: ahref(`${url}/commit/${commit.sha1}`, commit.committed_datetime)}, + {desc: "commit summary", contents: ahref(`${url}/commit/${commit.sha1}`, commit.summary)}, + {desc: "source tree" , contents: ahref(`${url}/tree/${commit.sha1}`, `tree @ ${commit.sha1}`)}, + {desc: "benchmark" , contents: ahref(`${url}/tree/${commit.sha1}/${bm.specs.src}`, `source @ ${commit.sha1}`)}, + {desc: "cpu used" , contents: `${cpu.arch} ${cpu.brand_raw}`}, + {desc: "system used" , contents: `${system.uname.system} ${system.uname.release}`}, + {desc: "this build" , contents: `<pre>${getBuildId(run_id)}</pre>`}, + {desc: "commit builds" , contents: `<pre>${other_commit_entries}</pre>`}, + ] + }); + function ahref(url, txt) { return `<a href="${url}" target="_blank">${txt}</a>`; } +} + + +function loadTable(id, bm, results) +{ + function render_int(data, type, row, meta) { return toFixedNumber(data, 0); } + function render_megas(data, type, row, meta) { return toFixedNumber(data / 1.e6, 3); } + function render_fixed(data, type, row, meta) { return toFixedNumber(data, 3); } + function render_human(data, type, row, meta) { return humanReadable(data, 1000, 3); } + + addTable("_pretty" , "" , {ns: render_int, iters: render_megas, rates: render_megas}); + addTable("_normalized", "_normalized", {ns: render_fixed, iters: render_fixed, rates: render_fixed}); + + function addTable(suffix, data_suffix, renderers) { + /* https://datatables.net/ */ + var searching = (results.benchmarks.count > 20); + var ratePrefix = renderers.rates == render_megas ? "M" : ""; + var iterPrefix = renderers.iters == render_megas ? "M" : ""; + var clockSuffix = data_suffix == "_normalized" ? "" : "(ns)"; + $(`#table-${id}${suffix}`).DataTable( { + data: results.benchmarks, + info: false, + paging: false, + searching: searching, + retrieve: searching, + /* https://datatables.net/reference/option/columns.type */ + columns: [ + {title: "ID", data: "id", type: "num"}, + {title: "Name", data: "name", render: function(data, type, row, meta) { return escapeHtml(data); }}, + {title: `${ratePrefix}B/s` , data: `bytes_per_second${data_suffix}`, type: "num", className: "text-right", render: renderers.rates}, + {title: `${ratePrefix}items/s` , data: `items_per_second${data_suffix}`, type: "num", className: "text-right", render: renderers.rates}, + {title: `Clock${clockSuffix}` , data: `real_time${data_suffix}` , type: "num", className: "text-right", render: renderers.ns}, + {title: `CPU${clockSuffix}` , data: `cpu_time${data_suffix}` , type: "num", className: "text-right", render: renderers.ns}, + {title: `${ratePrefix}Iterations`, data: `iterations${data_suffix}` , type: "num", className: "text-right", render: renderers.iters}, + ]}); + } +} + +function loadChart(id, bm, results) +{ + + addChartFromColumn('bytes_per_second_normalized', "B/s", "(more is better)"); + addChartFromColumn('items_per_second_normalized', "items/s", "(more is better)"); + addChartFromColumn('iterations_normalized', "Iterations", "(more is better)"); + addChartFromColumn('real_time_normalized', "Clock time", "(less is better)"); + addChartFromColumn('cpu_time_normalized', "CPU time", "(less is better)"); + + function addChartFromColumn(column, column_name, obs) { + var elmId = `chart-${id}-${column}`; + var canvas = `${elmId}-canvas`; + + $(`#chart-container-${id}`).append(` +<div id="${elmId}" class="chart"> + <canvas id="${canvas}"></canvas> +</div> +`); + + var chart = new CanvasJS.Chart(elmId, { + animationEnabled: false, + title:{ + fontSize: 24, + /* text: `${id}: ${column_name}\n${obs}` */ + text: `${column_name}\n${obs}` + }, + axisX: { + labelFontSize: 12, + }, + data: [{ + type: "bar", + axisYType: "secondary", + color: "#eb7434",/*"#014D65",*/ + dataPoints: results.benchmarks.map(function(item){ + return { + indexLabelFormatter: function(e) { return e.dataPoint.indexLabel; }, + indexLabelFontSize: 16, + indexLabel: item.name, + /* label: item.name, */ + y: item[column], + /* save the result here: the tooltip will show the full thing */ + benchmark_results: item + }; + }), + }], + toolTip: { + /*content: "{indexLabel}: {y}",*/ + contentFormatter: function(e){ + function hr(val) { return humanReadable(val, 1000, 3); } + function fx(val) { return toFixedNumber(val, 3); } + function fxi(val) { return toFixedNumber(val, 0); } + function getRow(name, abs, rel) { return `<tr><td>${name}</td><td>${abs}</td><td>${rel}x min</td></tr>`; } + var r = e.entries[0].dataPoint.benchmark_results; + var hdrRow = `<tr><th></th><th>Absolute</th><th>Normalized</th></tr>`; + var bpsRow = getRow("B/s", hr(r.bytes_per_second), fx(r.bytes_per_second_normalized)); + var ipsRow = getRow("items/s", hr(r.items_per_second), fx(r.items_per_second_normalized)); + var cpuRow = getRow("CPU", fxi(r.cpu_time) + "ns", fx(r.cpu_time_normalized)); + var clockRow = getRow("Clock", fxi(r.real_time) + "ns", fx(r.real_time_normalized)); + var itersRow = getRow("Iterations", hr(r.iterations), fx(r.iterations_normalized)); + var table = `<table>${hdrRow}${bpsRow}${ipsRow}${cpuRow}${clockRow}${itersRow}</table>`; + return `<h4>${escapeHtml(r.name)}</h4>${table}`; + } + } + }); + chart.render(); + } +} diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py new file mode 100644 index 000000000..3bcab5f4d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py @@ -0,0 +1,746 @@ +import os +import sys +import copy +import re +import itertools +import typing +import enum + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +from dataclasses import dataclass + +from munch import Munch, munchify + +import bokeh.io as bki +import bokeh.models as bkm +import bokeh.plotting as bkp +import bokeh.transform as bkt +import bokeh.layouts as bkl +from bokeh.models.markers import marker_types as bk_markers +# https://docs.bokeh.org/en/latest/docs/reference/palettes.html +from bokeh.palettes import d3 as bk_palette_d3 +bk_palette = bk_palette_d3['Category20c'][20] + +# saving bokeh to png is not working, so we save png using matplotlib +import matplotlib.pyplot as plt +import matplotlib.ticker as plttck +plt_markers = [c for c in ".,ov^<>1234spP*hH+xXDdl"] + +from bm_util import _enum +from bm_util import * +from bm_run import BenchmarkRun, BenchmarkPanel + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +@dataclass +class BarChartSpecs: + horizontal: bool = True + bar_width: float = 0.9 + + +@dataclass +class LineChartSpecs: + width: int = 1000 + xlog: bool = False + ylog: bool = False + xlabel: str = "" + ylabel: str = "" + + +def _plt_save_png(name): + log(name) + plt.savefig(name, bbox_inches='tight', dpi=100) + + +def _plt_clear(): + plt.clf() + + +def _bokeh_save_html(name, p): + log(name) + bki.save(p, name) + + +def _bokeh_adjust_figure_props(p): + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + p.legend + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + + +def bokeh_plot_many(plots, name: str, ncols: int = 2): + layout = bkl.gridplot(plots, ncols=ncols) + _bokeh_save_html(name, layout) + #bkp.show(layout) + + +def plot_benchmark_run_as_bars(bm: BenchmarkRun, title: str, + bar_names, bar_values, bar_label, + **kwargs): + kwargs = BarChartSpecs(**kwargs) + # + palette = itertools.cycle(bk_palette) + colors = [next(palette) for _ in bar_names] + # + fig_args_bokeh = { + "title": title, + #"toolbar_location": None, + #"tools": "" + } + if kwargs.horizontal: + # + # plot with bokeh (interactive, but cannot export png) + rnames = list(reversed(bar_names)) + rvalues = list(reversed(bar_values)) + rcolors = list(reversed(colors)) + p = bkp.figure(y_range=rnames, **fig_args_bokeh) + p.hbar(y=rnames, right=rvalues, fill_color=rcolors, + line_color=rcolors, height=kwargs.bar_width) + p.ygrid.grid_line_color = None + p.x_range.start = 0 + p.xaxis.axis_label = bar_label + # + # plot with matplotlib (to export png) + p_ = plt.barh(y=rnames, width=rvalues, color=rcolors, + height=kwargs.bar_width) + plt.gca().xaxis.grid(True) + plt.gca().xaxis.set_minor_locator(plttck.AutoMinorLocator()) + plt.xlabel(bar_label, fontsize='small') + plt.yticks(fontsize='x-small') + plt.title(title) + else: + # + # plot with bokeh (interactive, but cannot export png) + p = bkp.figure(x_range=bar_names, **fig_args_bokeh) + p.vbar(x=bar_names, top=bar_values, fill_color=colors, + line_color=colors, width=kwargs.bar_width) + p.xaxis.major_label_orientation = 1 + p.xgrid.grid_line_color = None + p.y_range.start = 0 + p.yaxis.axis_label = bar_label + # + # plot with matplotlib (to export png) + p_ = plt.bar(x=bar_names, height=bar_values, color=colors, + width=kwargs.bar_width) + plt.gca().yaxis.grid(True) + plt.gca().yaxis.set_minor_locator(plttck.AutoMinorLocator()) + plt.ylabel(bar_label, fontsize='small') + plt.xticks(fontsize='x-small') + plt.title(title) + _bokeh_adjust_figure_props(p) + return p, p_ + + +def plot_benchmark_panel_as_lines(bm_panel: BenchmarkPanel, title: str, + xget, yget, nameget, + **kwargs): + kwargs = LineChartSpecs(**kwargs) + # + colors = itertools.cycle(bk_palette) + markers = itertools.cycle(bk_markers) + markers_ = itertools.cycle(plt_markers) + # + # plot with bokeh (interactive, but cannot export png) + p = bkp.figure(title=title, + x_axis_type="log" if kwargs.xlog else "linear", + y_axis_type="log" if kwargs.ylog else "linear", + #background_fill_color="#fafafa", + x_axis_label=kwargs.xlabel, + y_axis_label=kwargs.ylabel, + plot_width=kwargs.width, + ) + # plot with matplotlib (to export png) + plt.title(title) + for bm in bm_panel.runs: + x = xget(bm) + y = yget(bm) + line_name = nameget(bm) + color = next(colors) + marker = next(markers) + marker_ = next(markers_) + # plot with bokeh (interactive, but cannot export png) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=color, + legend_label=line_name) + p.line(x, y, color=color, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + # + # plot with matplotlib (to export png) + plt.plot(x, y, f'-{marker_}', color=color, label=line_name) + plt.gca().xaxis.grid(True) + plt.gca().yaxis.grid(True) + plt.xscale("log" if kwargs.xlog else "linear") + plt.yscale("log" if kwargs.ylog else "linear") + plt.xlabel(kwargs.xlabel, fontsize='small') + plt.ylabel(kwargs.ylabel, fontsize='small') + plt.gca().legend(loc='center left', bbox_to_anchor=(1, 0.5), fontsize='x-small') + _bokeh_adjust_figure_props(p) + return p + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +####### old code: to remove and tidy up + +@dataclass +class CharconvMeta: # also for atox + title: str + subject: str + function: str + data_type: FundamentalTypes + + @classmethod + def make(cls, bm_title: str): + # eg: + # xtoa_c4_write_dec<uint8_t> + # xtoa_c4_utoa<uint8_t> + # xtoa_c4_xtoa<uint8_t> + # xtoa_c4_to_chars<uint8_t> + # xtoa_std_to_chars<uint8_t> + # xtoa_std_to_string<uint8_t> + # xtoa_sprintf<uint8_t> + # xtoa_sstream_reuse<uint8_t> + # xtoa_sstream<uint8_t> + rx = re.compile(r'(atox|xtoa|xtoahex|xtoaoct|xtoabin)_(.*?)<(u?int\d+_t)>') + if not rx.fullmatch(bm_title): + raise Exception(f"cannot understand bm title: {bm_title}") + subject = rx.sub(r'\1', bm_title) + function = rx.sub(r'\2', bm_title) + data_type = rx.sub(r'\3', bm_title) + return cls( + title=bm_title, + subject=subject, + function=function.replace("c4_", "c4::").replace("std_", "std::"), + data_type=FundamentalTypes.make(data_type) + ) + + def checkbox_groups(self): + return { + 'data_type': [t for t in FundamentalTypes], + } + + @property + def shortname(self): + return self.function + + @property + def shortparams(self): + return str(self.data_type.short) + + @property + def shorttitle(self): + return f"{self.shortname}<{self.shortparams}>" + + +@dataclass +class CharconvThreadsMeta: + function: str + num_threads: int + + @classmethod + def make(cls, bm_title: str): + # eg: + # c4_itoa/real_time/threads:4 + rx = re.compile(r'(.*?)/real_time/threads:(\d+)') + if not rx.fullmatch(bm_title): + raise Exception(f"cannot understand bm title: {bm_title}") + function = rx.sub(r'\1', bm_title) + num_threads = int(rx.sub(r'\2', bm_title)) + return cls( + function=function.replace("c4_", "c4::").replace("std_", "std::"), + num_threads=num_threads + ) + + def checkbox_groups(self): + return {} + + @property + def shortname(self): + return self.function + + @property + def shorttitle(self): + return self.shortname + + +def plot_charconv_bars(bm_panel: BenchmarkPanel, panel_title_human: str, outputfile_prefix: str): + assert os.path.isabs(outputfile_prefix), outputfile_prefix + for prop in ("mega_bytes_per_second", "cpu_time_ms"): + ps, ps_ = [], [] + pd = bm_panel.first_run.property_plot_data(prop) + bar_label = f"{pd.human_name_short}{pd.what_is_better}" + outfilename = f"{outputfile_prefix}-{prop}" + for bm in bm_panel.runs: + bar_names = [m.shorttitle for m in bm.meta] + bar_values = list(getattr(bm, prop)) + data_type = first(bm.meta).data_type + # to save each bokeh plot separately and also + # a grid plot with all of them, we have to plot + # twice because bokeh does not allow saving twice + # the same plot from multiple pictures. + plotit = lambda: plot_benchmark_run_as_bars(bm, title=f"{panel_title_human}: {data_type}\n{bar_label}", + bar_names=bar_names, bar_values=bar_values, bar_label=bar_label) + # make one plot to save: + p, p_ = plotit() + _bokeh_save_html(f"{outfilename}-{data_type.short}.html", p) + _plt_save_png_and_clear(f"{outfilename}-{data_type.short}.png") + # and another to gather: + p, p_ = plotit() + ps.append(p) + ps_.append(p_) + layout = bkl.gridplot(ps, ncols=2) + _bokeh_save_html(f"{outfilename}.html", layout) + # now show + #bkp.show(layout) + + +def plot_charconv_threads_(bm_panel: BenchmarkPanel, panel_title_human: str, outputfile_prefix: str): + assert os.path.isabs(outputfile_prefix), outputfile_prefix + orig = lambda yprop: lambda bm: list(bm.extract_plot_series(yprop)) + divnt = lambda yprop: lambda bm: [v / n for v, n in bm.extract_plot_series_with_threads(yprop)] + mulnt = lambda yprop: lambda bm: [v * n for v, n in bm.extract_plot_series_with_threads(yprop)] + xprop = "threads" + xpd = bm_panel.first_run.property_plot_data(xprop) + xlabel = f"{xpd.human_name_short}" + for yprop, ylog, yget in ( + #("mega_items_per_second", False, orig), + ("mega_bytes_per_second", False, orig), + #("iterations", False, divnt), + #("real_time_ms", True, mulnt), + ("cpu_time_ms", True, orig),): + ypd = bm_panel.first_run.property_plot_data(yprop) + ylabel = f"{ypd.human_name_short}{ypd.what_is_better}" + p = plot_benchmark_panel_as_lines( + bm_panel, f"{panel_title_human}\n{ylabel}", + xget=orig("threads"), + yget=yget(yprop), + nameget=lambda bm: first(bm.meta).function, + ylog=ylog, + xlabel=xlabel, + ylabel=ylabel + ) + name = f"{outputfile_prefix}-lines-{yprop}" + # save png using matplotlib + _plt_save_png_and_clear(f"{name}.png") + # save html using bokeh + _bokeh_save_html(f"{name}.html", p) + #bkp.show(p) + return p + + +def plot_charconv_threads(json_files, case: str = ""): + case = f" [{case}]" if case else "" + dir_ = os.path.dirname(first(json_files)) + panel = BenchmarkPanel(json_files, CharconvThreadsMeta) + plot_charconv_threads_(panel, + f"itoa benchmark: convert 2M 32b integers to string{case}", + f"{dir_}/c4core-bm-charconv_threads") + + +def plot_charconv_xtoa(json_files, case: str = ""): + case = f" [{case}]" if case else "" + dir_ = os.path.dirname(first(json_files)) + panel = BenchmarkPanel(json_files, CharconvMeta) + plot_charconv_bars(panel, + f"xtoa benchmark: convert 2M numbers to strings{case}", + f"{dir_}/c4core-bm-charconv-xtoa") + + +def plot_charconv_atox(json_files, case: str = ""): + case = f" [{case}]" if case else "" + dir_ = os.path.dirname(first(json_files)) + panel = BenchmarkPanel(json_files, CharconvMeta) + plot_charconv_bars(panel, + f"atox benchmark: convert 2M strings to numbers{case}", + f"{dir_}/c4core-bm-charconv-atox") + + +def threads_data(dir_: str): + assert os.path.exists(dir_), dir_ + return [ + f"{dir_}/c4core-bm-charconv_threads-c4_write_dec.json", + f"{dir_}/c4core-bm-charconv_threads-c4_itoa.json", + f"{dir_}/c4core-bm-charconv_threads-c4_xtoa.json", + f"{dir_}/c4core-bm-charconv_threads-c4_to_chars.json", + f"{dir_}/c4core-bm-charconv_threads-fmtlib_format_to.json", + f"{dir_}/c4core-bm-charconv_threads-std_to_chars.json", + f"{dir_}/c4core-bm-charconv_threads-snprintf.json", + f"{dir_}/c4core-bm-charconv_threads-stb_snprintf.json", + f"{dir_}/c4core-bm-charconv_threads-sstream.json", + f"{dir_}/c4core-bm-charconv_threads-sstream_naive_reuse.json", + f"{dir_}/c4core-bm-charconv_threads-sstream_naive.json", + ] + + +def xtoa_data(dir_: str): + assert os.path.exists(dir_), dir_ + return [ + f"{dir_}/c4core-bm-charconv-xtoa-int8.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint8.json", + f"{dir_}/c4core-bm-charconv-xtoa-int16.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint16.json", + f"{dir_}/c4core-bm-charconv-xtoa-int32.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint32.json", + f"{dir_}/c4core-bm-charconv-xtoa-int64.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint64.json", + ] + + +def atox_data(dir_: str): + assert os.path.exists(dir_), dir_ + return [ + f"{dir_}/c4core-bm-charconv-atox-int8.json", + f"{dir_}/c4core-bm-charconv-atox-uint8.json", + f"{dir_}/c4core-bm-charconv-atox-int16.json", + f"{dir_}/c4core-bm-charconv-atox-uint16.json", + f"{dir_}/c4core-bm-charconv-atox-int32.json", + f"{dir_}/c4core-bm-charconv-atox-uint32.json", + f"{dir_}/c4core-bm-charconv-atox-int64.json", + f"{dir_}/c4core-bm-charconv-atox-uint64.json", + ] + + +def examples_dir(): + this_dir = os.path.dirname(os.path.abspath(__file__)) + exdir = f"{this_dir}/examples" + assert os.path.exists(exdir), exdir + return exdir + + +if __name__ == '__main__': + xdir = examples_dir() + # + plot_charconv_threads(threads_data(f"{xdir}/lines/gcc11.2"), "gcc11.2") + plot_charconv_threads(threads_data(f"{xdir}/lines/vs2022"), "vs2022") + # + plot_charconv_xtoa(xtoa_data(f"{xdir}/bars/xtoa/gcc11.2"), "gcc11.2") + plot_charconv_xtoa(xtoa_data(f"{xdir}/bars/xtoa/vs2022"), "vs2022") + # + plot_charconv_atox(atox_data(f"{xdir}/bars/atox/gcc11.2"), "gcc11.2") + plot_charconv_atox(atox_data(f"{xdir}/bars/atox/vs2022"), "vs2022") + # + exit() + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +def plot_benchmarks_as_lines(title, *bm, transform=None, + line_title_transform=None, + logx=True, logy=True): + import bokeh + from bokeh.plotting import figure, output_file, show + from bokeh.palettes import Dark2_5 as palette + from bokeh.layouts import row, column + from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div, + RadioGroup, Toggle, + ColumnDataSource, DataTable, TableColumn) + from bokeh.models.markers import marker_types + # + ids = entry_ids(*bm, transform=transform) + colors = itertools.cycle(palette) + markers = itertools.cycle(marker_types) + p = figure(title=title, + x_axis_type="log" if logx else "linear", + y_axis_type="log" if logy else "linear", + #background_fill_color="#fafafa", + plot_width=1000, + x_axis_label="Number of pixels", + y_axis_label="Throughput (MB/s)", + ) + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + # + def dft(v): return v if v else (lambda n: n) + tr = dft(transform) + lttr = dft(line_title_transform) + # + for results in bm: + x = [ids[name] for name in results.names] + y = [bps/1e6 for bps in results.bytes_per_second] + c = next(colors) + marker = next(markers) + next(markers) # advance two + line_name = lttr(results.first) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name) + p.line(x, y, + color=c, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + # + def input_title(title): + return Div(text=f"<h3>{title}</h3>") + inputs = [] + first = bm[0].first.meta + for k, g in first.checkbox_groups().items(): + cb = CheckboxGroup(labels=[str(v) for v in g], + active=[i for i in range(len(g))], + inline=True) + inputs.append(input_title(k)) + inputs.append(cb) + # + # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py + x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta] + table_sources = [] + for i, px in enumerate(x_axis_values): + c = ColumnDataSource(data={ + 'name': [nth(results.filtered_names, i) for results in bm], + 'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm], + 'items_per_second': [nth(results.items_per_second, i) for results in bm], + 'cpu_time': [nth(results.real_time, i) for results in bm], + 'real_time': [nth(results.real_time, i) for results in bm], + 'iterations': [nth(results.iterations, i) for results in bm], + 'threads': [nth(results.threads, i) for results in bm], + }) + table_sources.append(c) + selected_x_index = 8 # FIXME (currently 2000 pixels) + table_source = copy.deepcopy(table_sources[selected_x_index]) + relvalues = Toggle(label="Table: Relative values") + px_title = input_title("Table: number of pixels") + px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index) + table_inputs = [relvalues, px_title, px_radiogroup] + # + table_cols = [ + TableColumn(field='name', title='Name'), + TableColumn(field='bytes_per_second', title='Bytes/second'), + TableColumn(field='items_per_second', title='Items/second'), + TableColumn(field='cpu_time', title='CPU time'), + TableColumn(field='real_time', title='Real time'), + TableColumn(field='iterations', title='Iterations'), + TableColumn(field='threads', title='Threads'), + ] + data_table = DataTable(source=table_source, columns=table_cols, width=1200) + callback = CustomJS(args=dict( + radiogroup=px_radiogroup, + source=table_source, + table=table_sources + ), code=""" + console.log(`active=${radiogroup.active}`); + /*source.data=table[radiogroup.active];*/ + var nrows = source.data['name'].length; + var ts = table[radiogroup.active].data; + var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"]; + var ncols = names.length; + console.log(`names=${names} nrows=${nrows} ncols=${ncols}`); + for(var i = 0; i < nrows; i++) { + for(var j = 0; j < ncols; ++j) { + var name = names[j]; + /*console.log(`i=${i} j=${j} name=${name}`);*/ + source.data[name][i] = ts[name][i]; + } + } + source.change.emit(); + """) + px_radiogroup.js_on_change('active', callback) + # lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}")) + # + layout = column( + row(column(*inputs), p), + row(column(*table_inputs), data_table)) + show(layout) + + +def entry_ids(*bm, transform=None): + ids = {} + curr = 0 + for results in bm: + log(os.path.basename(results.filename), "------------------------------") + for entry in results.entries: + log(entry.name) + if transform is not None: + ids[entry.name] = transform(entry) + else: + if ids.get(entry.name) is None: + ids[entry.name] = curr + curr += 1 + return ids + + +class MatrixOrder(_enum): + row_major = "row_major" + col_major = "col_major" + @property + def short(self): + return "rm" if self is MatrixOrder.row_major else "cm" + @classmethod + def make(cls, s): + try: + return {"rm": cls.row_major, "cm": cls.col_major}[s] + except: + cls.err_unknown(s) + + +class MatrixLayout(_enum): + compact = "compact" + strided = "strided" + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + +class DimensionBinding(_enum): + compile_time = "compile_time" + run_time = "run_time" + @property + def short(self): + return "ct" if self is DimensionBinding.compile_time else "rt" + @classmethod + def make(cls, s): + try: + return {"ct": cls.compile_time, "rt": cls.run_time}[s] + except: + cls.err_unknown(s) + + +class MultType(_enum): + naive = "naive" + avx2 = "avx2" + avx2_unroll2 = "avx2_unroll2" + avx2_unroll4 = "avx2_unroll4" + avx2_unroll8 = "avx2_unroll8" + @classmethod + def make(cls, s): + try: + s = s.replace("dotprod_", "").replace("_naive", "") + return cls[s] + except: + cls.err_unknown(s) + + +class MatrixMult(typing.NamedTuple): + title: str + num_pixels: int + num_channels: int + num_features: int + mult_type: MultType + layout: MatrixLayout + dim_binding: DimensionBinding + ret_order: MatrixOrder + lhs_order: MatrixOrder + rhs_order: MatrixOrder + + @classmethod + def make(cls, bm_title: str): + # eg: + # mult_naive_strided_ct_rm_cmcm<250, 8, 16> + # mult_naive_compact_rt_rm_rmrm/4000/8/16 + rxline = r'mult_(.*)[</](\d+)(?:/|, )(\d+)(?:/|, )(\d+).*' + rxcase = r"(.*)_(compact|strided)_(ct|rt)_(rm|cm)_(rm|cm)(rm|cm)" + case = re.sub(rxline, r'\1', bm_title) + return cls( + title=case, + num_pixels=int(re.sub(rxline, r'\2', bm_title)), + num_channels=int(re.sub(rxline, r'\3', bm_title)), + num_features=int(re.sub(rxline, r'\4', bm_title)), + mult_type=MultType.make(re.sub(rxcase, r'\1', case)), + layout=MatrixLayout.make(re.sub(rxcase, r'\2', case)), + dim_binding=DimensionBinding.make(re.sub(rxcase, r'\3', case)), + ret_order=MatrixOrder.make(re.sub(rxcase, r'\4', case)), + lhs_order=MatrixOrder.make(re.sub(rxcase, r'\5', case)), + rhs_order=MatrixOrder.make(re.sub(rxcase, r'\6', case)) + ) + + def comparison_axes(self): + return ('num_pixels', 'num_channels', 'num_features') + + def checkbox_groups(self): + return { + 'multiplication method': [t for t in MultType], + 'layout': [t for t in MatrixLayout], + 'dimension': [d for d in DimensionBinding], + 'return matrix ordering': [o for o in MatrixOrder], + 'lhs matrix ordering': [o for o in MatrixOrder], + 'rhs matrix ordering': [o for o in MatrixOrder], + } + + @property + def matrix_size(self): + return self.num_pixels * self.num_channels + + @property + def classifier_size(self): + return self.num_channels * self.num_features + + @property + def shortname(self): + m = self + return f"{m.mult_type}/{m.layout}/{m.dim_binding.short}_{m.ret_order.short}_{m.lhs_order.short}{m.rhs_order.short}" + + @property + def shortparams(self): + m = self + return f"{m.num_pixels:04d}px{m.num_channels:02d}ch{m.num_features:02d}ft" + + @property + def shorttitle(self): + return f"{self.shortname}/{self.shortparams}" + + +def _test(): + def expect(v_, attr, val): + var = getattr(v_, attr) + if var != val: + raise Exception(f"{attr}: expected={val} actual={var}") + # + v = MatrixMult.make("mult_naive_strided_ct_rm_cmcm<250, 8, 16>") + expect(v, 'title', 'naive_strided_ct_rm_cmcm') + expect(v, 'num_pixels', 250) + expect(v, 'num_channels', 8) + expect(v, 'num_features', 16) + expect(v, 'mult_type', MultType.naive) + expect(v, 'layout', MatrixLayout.strided) + expect(v, 'dim_binding', DimensionBinding.compile_time) + expect(v, 'ret_order', MatrixOrder.row_major) + expect(v, 'lhs_order', MatrixOrder.col_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + v = MatrixMult.make("mult_dotprod_avx2_compact_rt_cm_rmcm/4000/16/8") + expect(v, 'title', 'dotprod_avx2_compact_rt_cm_rmcm') + expect(v, 'num_pixels', 4000) + expect(v, 'num_channels', 16) + expect(v, 'num_features', 8) + expect(v, 'mult_type', MultType.avx2) + expect(v, 'layout', MatrixLayout.compact) + expect(v, 'dim_binding', DimensionBinding.run_time) + expect(v, 'ret_order', MatrixOrder.col_major) + expect(v, 'lhs_order', MatrixOrder.row_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + +_test() + + + +def formatMBps(value): + return value / 1e6 + + + +if __name__ == '__main__': + bms = sorted(sys.argv[2:]) + log(bms) + bms = BenchmarkPanel(bms, bm_meta_cls=MatrixMult.make) + fm = bms.runs[0].first.meta + title = f"Classifier multiplication, {fm.num_channels} channels, {fm.num_features} features: throughput (MB/s)" + bms.plot_all_lines(title) + exit() + main() diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py new file mode 100644 index 000000000..812b64abe --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py @@ -0,0 +1,248 @@ +import copy +import os.path + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +from dataclasses import dataclass + +from munch import Munch + +from bm_util import load_json, first, _enum + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class QuantityType(_enum): + neutral = "" + more_is_better = "more is better" + less_is_better = "less is better" + + @property + def comment(self): + return f" ({self.value})" if self.name else "" + + +_more = QuantityType.more_is_better +_less = QuantityType.less_is_better + + +@dataclass +class BenchmarkPropertyPlotData: + human_name: str = "" + human_name_short: str = "" + qty_type: QuantityType = QuantityType.neutral + + +class BenchmarkRun(Munch): + """results of an individual run""" + + def __init__(self, json_file: str, meta_class): + """ + meta_class is a class to extract property values from the benchmark run + """ + self._filename = json_file + props = load_json(json_file) + assert hasattr(props, "context") + assert hasattr(props, "benchmarks") + super().__init__(**props) + setattr(self, 'property_names', list(__class__._properties.keys())) + for bm in self.benchmarks: + if meta_class is not None: + setattr(bm, 'meta', meta_class.make(bm.name)) + else: + setattr(bm, 'meta', None) + + _properties = { + 'filename': None, + 'basename': None, + 'dirname': None, + 'meta': None, + 'shorttitle': None, + 'name': None, + 'run_name': None, + 'run_type': None, + 'repetitions': None, + 'repetition_index': None, + 'threads': BenchmarkPropertyPlotData('number of threads', 'threads'), + 'iterations': BenchmarkPropertyPlotData('number of iterations', 'iterations', _more), + 'real_time': BenchmarkPropertyPlotData('real time', 'real time', _less), + 'cpu_time': BenchmarkPropertyPlotData('CPU time', 'cpu time', _less), + 'real_time_ms': BenchmarkPropertyPlotData('real time', 'real time', _less), + 'cpu_time_ms': BenchmarkPropertyPlotData('CPU time', 'cpu time', _less), + 'time_unit': None, + 'bytes_per_second': BenchmarkPropertyPlotData('Bytes/s', 'B/s', _more), + 'items_per_second': BenchmarkPropertyPlotData('items/s', 'items/s', _more), + 'mega_bytes_per_second': BenchmarkPropertyPlotData('MBytes/s', 'MB/s', _more), + 'mega_items_per_second': BenchmarkPropertyPlotData('Mega items/s', 'Mega items/s', _more), + 'counters': None, + } + + def property_plot_data(self, property_name: str): + pd = copy.deepcopy(__class__._properties.get(property_name, BenchmarkPropertyPlotData())) + if property_name.endswith('_time'): + time_unit = first(self.entries).time_unit + pd.human_name += f' ({time_unit})' + pd.human_name_short += f' ({time_unit})' + elif property_name.endswith('_time_ms'): + pd.human_name += ' (ms)' + pd.human_name_short += ' (ms)' + return pd + + def extract_plot_series(self, property_name_or_getter, + relative_to_entry = None, + percent_of_entry = None, + ): + if isinstance(property_name_or_getter, str): + series = getattr(self, property_name_or_getter) + else: + series = property_name_or_getter(self) + series = list(series) + def getrefval(ref): + assert ref in self.entries, ref.name + pos = self.pos(ref) + assert pos in range(len(series)), (pos, len(series)) + return series[pos] + if relative_to_entry: + refval = getrefval(relative_to_entry) + for v in series: + yield v / refval + elif percent_of_entry: + refval = getrefval(percent_of_entry) + for v in series: + yield 100.0 * ((v - refval) / refval) + else: + for v in series: + yield v + + def extract_plot_series_with_threads(self, property_name_or_getter, + relative_to: str = None, + percent_of: str = None, + ): + series = self.extract_plot_series(property_name_or_getter, relative_to=relative_to, percent_of=percent_of) + for y, n in zip(series, self.threads): + yield y, n + + def pos(self, entry): + for i, e in enumerate(self.entries): + if e == entry: + return i + raise Exception("entry not found") + + @property + def filename(self): + return self._filename + + @property + def basename(self): + return os.path.basename(self._filename) + + @property + def dirname(self): + return os.path.dirname(self._filename) + + @property + def entries(self): + for entry in self.benchmarks: + yield entry + + @property + def meta(self): + for entry in self.benchmarks: + yield entry.meta + + @property + def names(self): + for entry in self.benchmarks: + yield entry.name + + @property + def run_names(self): + for entry in self.benchmarks: + yield entry.run_name + + @property + def run_types(self): + for entry in self.benchmarks: + yield entry.run_type + + @property + def repetitions(self): + for entry in self.benchmarks: + yield entry.repetitions + + @property + def repetition_indices(self): + for entry in self.benchmarks: + yield entry.repetition_index + + @property + def threads(self): + for entry in self.benchmarks: + yield entry.threads + + @property + def iterations(self): + for entry in self.benchmarks: + yield entry.iterations + + @property + def real_time(self): + for entry in self.benchmarks: + yield entry.real_time + + @property + def cpu_time(self): + for entry in self.benchmarks: + yield entry.cpu_time + + @property + def real_time_ms(self): + for entry in self.benchmarks: + assert entry.time_unit == "ns" + yield entry.real_time / 1e6 + + @property + def cpu_time_ms(self): + for entry in self.benchmarks: + assert entry.time_unit == "ns" + yield entry.cpu_time / 1e6 + + @property + def time_unit(self): + for entry in self.benchmarks: + yield entry.time_unit + + @property + def bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second + + @property + def items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second + + @property + def mega_bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second / 1e6 + + @property + def mega_items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second / 1e6 + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkPanel: + + def __init__(self, runs, bm_meta_cls=None): + self.runs = [BenchmarkRun(a, bm_meta_cls) for a in runs] + + @property + def first_run(self): + return first(self.runs) diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py new file mode 100644 index 000000000..6fc683b84 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py @@ -0,0 +1,502 @@ +import os +import sys +import argparse +import copy +import requests +import flask +import json +import re +import yaml +import shutil +import mmh3 +import itertools +import typing +import enum + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +from dataclasses import dataclass + +from munch import Munch, munchify +from flask import render_template, redirect, url_for, send_from_directory +from markupsafe import escape + +from bm_util import * + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkCollection: + + @staticmethod + def create_new(args): + dir = args.target + filename = os.path.join(dir, "bm.yml") + manifest = os.path.join(dir, "manifest.yml") + if not os.path.exists(dir): + os.makedirs(dir) + shutil.copyfile(args.filename, filename) + dump_yml(load_yml("""{runs: {}, bm: {}}"""), manifest) + return __class__(dir) + + def __init__(self, dir): + if not os.path.exists(dir): + raise Exception(f"not found: {dir}") + self.dir = os.path.abspath(dir) + self.runs_dir = os.path.join(self.dir, "runs") + self.manifest = os.path.join(self.dir, "manifest.yml") + self.filename = os.path.join(self.dir, "bm.yml") + self.specs = munchify(load_yml_file(self.filename)) + self.manif = munchify(load_yml_file(self.manifest)) + + def add(self, results_dir): + results_dir = os.path.abspath(results_dir) + dst_dir, meta = self._read_run(results_dir) + self._add_run(results_dir, dst_dir, meta) + dump_yml(self.manif, self.manifest) + + def _read_run(self, results_dir): + log("adding run...") + id = f"{len(self.manif.runs.keys()):05d}" + log(f"adding run: id={id}") + meta = ResultMeta.load(results_dir) + dst_dir = os.path.join(self.runs_dir, meta.name) + return dst_dir, meta + + def _add_run(self, results_dir, dst_dir, meta): + cats = self._add_meta_categories(meta) + for filename in ("meta.yml", + "CMakeCCompiler.cmake", + "CMakeCXXCompiler.cmake", + "CMakeSystem.cmake", + "compile_commands.json"): + filename = os.path.join(results_dir, filename) + if os.path.exists(filename): + copy_file_to_dir(filename, dst_dir) + else: + if not filename.endswith("compile_commands.json"): + raise Exception(f"wtf???? {filename}") + for name, specs in self.specs.bm.items(): + if not hasattr(specs, 'variants'): + filename = chk(f"{results_dir}/{name}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(name, specs, meta) + else: + for t in specs.variants: + tname = f"{name}-{t}" + filename = chk(f"{results_dir}/{tname}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(tname, specs, meta) + + def _add_bm_run(self, name, specs, meta): + if name not in self.manif.bm.keys(): + self.manif.bm[name] = Munch(specs=specs, entries=[]) + entry = self.manif.bm[name] + entry.specs = specs + if meta.name not in entry.entries: + entry.entries.append(meta.name) + + def _add_meta_categories(self, meta): + run = Munch() + for catname in ('commit', 'cpu', 'system', 'build'): + meta_item = getattr(meta, catname) + self._add_item_to_category(meta.name, catname, meta_item) + run[catname] = meta_item.storage_id + # build specs are too verbose; remove them + self.manif.build[meta.build.storage_id].specs = Munch() + self.manif.runs[meta.name] = run + + def _add_item_to_category(self, run, category_name, item): + if not hasattr(self.manif, category_name): + setattr(self.manif, category_name, Munch()) + category = getattr(self.manif, category_name) + if item.storage_id not in category.keys(): + category[item.storage_id] = Munch(specs=item, entries=[]) + entry = category[item.storage_id] + entry.specs = item + if run not in entry.entries: + entry.entries.append(run) + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class ResultMeta(Munch): + + def __init__(self, results_dir, cmakecache, build_type): + super().__init__(self) + self.date = __class__.get_date() + self.commit = __class__.get_commit(results_dir) + self.cpu = __class__.get_cpu_info() + self.system = __class__.get_sys_info() + self.build = __class__.get_build_info(cmakecache, build_type) + self.name = self._get_name() + + @staticmethod + def load(results_dir): + results_dir = os.path.join(os.path.abspath(results_dir), "meta.yml") + data = load_yml_file(results_dir) + return munchify(data) + + def save(self, results_dir): + out = os.path.join(results_dir, "meta.yml") + log("saving meta:", out) + dump_yml(self, out) + self.build.save(results_dir) + + @staticmethod + def get_date(): + import datetime + now = datetime.datetime.now() + return now.strftime("%Y%m%d-%H%M%S") + + def _get_name(self): + commit = self.commit.storage_name + cpu = self.cpu.storage_name + sys = self.system.storage_name + build = self.build.storage_name + name = f"{commit}/{cpu}-{sys}-{build}" + return name + + @staticmethod + def get_commit(results_dir): + import git + repo = git.Repo(results_dir, search_parent_directories=True) + commit = repo.head.commit + commit = {p: str(getattr(commit, p)) + for p in ('message', 'summary', 'name_rev', + 'author', + 'authored_datetime', + 'committer', + 'committed_datetime',)} + commit = Munch(commit) + commit.message = commit.message.strip() + commit.sha1 = commit.name_rev[:7] + spl = commit.authored_datetime.split(" ") + date = re.sub(r'-', '', spl[0]) + time = re.sub(r'(\d+):(\d+):(\d+).*', r'\1\2\3', spl[1]) + commit.storage_id = commit.sha1 + commit.storage_name = f"git{date}_{time}-{commit.sha1}" + return commit + + @staticmethod + def get_cpu_info(): + import cpuinfo + nfo = cpuinfo.get_cpu_info() + nfo = Munch(nfo) + for a in ('cpu_version', 'cpu_version_string', 'python_version'): + if hasattr(nfo, a): + delattr(nfo, a) + for a in ('arch_string_raw', 'brand_raw', 'hardware_raw', 'vendor_id_raw'): + if not hasattr(nfo, a): + setattr(nfo, a, '') + nfo.storage_id = myhash( + nfo.arch_string_raw, nfo.brand_raw, nfo.hardware_raw, nfo.vendor_id_raw, + nfo.arch, nfo.bits, nfo.count, nfo.family, nfo.model, nfo.stepping, + ",".join(nfo.flags), nfo.hz_advertised_friendly, + nfo.l2_cache_associativity, + nfo.l2_cache_line_size, + nfo.l2_cache_size, + nfo.l3_cache_size, + *optionals('l1_data_cache_size', 'l1_instruction_cache_size') + ) + nfo.storage_name = f"{nfo.arch.lower()}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_sys_info(): + import platform + uname = platform.uname() + nfo = Munch( + sys_platform=sys.platform, + sys=platform.system(), + uname=Munch( + machine=uname.machine, + node=uname.node, + release=uname.release, + system=uname.system, + version=uname.version, + ) + ) + nfo.storage_id = myhash( + nfo.sys_platform, + nfo.uname.machine, + ) + nfo.storage_name = f"{nfo.sys_platform}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_build_info(cmakecache_txt, buildtype): + nfo = CMakeCache(cmakecache_txt) + def _btflags(name): + return (getattr(nfo, name), getattr(nfo, f"{name}_{buildtype.upper()}")) + nfo.storage_id = myhash( + buildtype, + nfo.CMAKE_CXX_COMPILER_ID, + nfo.CMAKE_CXX_COMPILER_VERSION, + nfo.CMAKE_CXX_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_CXX_COMPILER_ABI, + nfo.CMAKE_CXX_SIZEOF_DATA_PTR, + nfo.CMAKE_C_COMPILER_ID, + nfo.CMAKE_C_COMPILER_VERSION, + nfo.CMAKE_C_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_C_COMPILER_ABI, + nfo.CMAKE_C_SIZEOF_DATA_PTR, + *_btflags("CMAKE_CXX_FLAGS"), + *_btflags("CMAKE_C_FLAGS"), + *_btflags("CMAKE_STATIC_LINKER_FLAGS"), + *_btflags("CMAKE_SHARED_LINKER_FLAGS"), + ) + # + ccname = nfo.CMAKE_CXX_COMPILER_ID.lower() + if ccname == "gnu": + ccname = "gcc" + ccname += nfo.CMAKE_CXX_COMPILER_VERSION.lower() + # + if nfo.CMAKE_C_SIZEOF_DATA_PTR == "4": + bits = "32bit" + elif nfo.CMAKE_C_SIZEOF_DATA_PTR == "8": + bits = "64bit" + else: + raise Exception("unknown architecture") + # + nfo.storage_name = f"{bits}_{buildtype}_{ccname}_{nfo.storage_id}" + return nfo + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class CMakeCache(Munch): + + def __init__(self, cmakecache_txt): + import glob + for line in iter_cmake_lines(cmakecache_txt): + spl = line.split("=") + if len(spl) < 2: + continue + k, ty = spl[0].split(":") + v = "=".join(spl[1:]).strip() + setattr(self, k, v) + bdir = os.path.dirname(os.path.abspath(cmakecache_txt)) + self._c_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCCompiler.cmake"))[-1] # get the last + self._cxx_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCXXCompiler.cmake"))[-1] # get the last + self._system_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeSystem.cmake"))[-1] # get the last + self._load_cmake_file(self._c_compiler_file) + self._load_cmake_file(self._cxx_compiler_file) + ccomfile = f"{bdir}/compile_commands.json" + self._compile_commands_file = ccomfile if os.path.exists(ccomfile) else None + + def _load_cmake_file(self, filename): + for line in iter_cmake_lines(filename): + if not line.startswith("set("): + continue + k = re.sub(r"set\((.*)\ +(.*)\)", r"\1", line) + v = re.sub(r"set\((.*)\ +(.*)\)", r"\2", line) + v = v.strip('"').strip("'").strip() + setattr(self, k, v) + + def save(self, results_dir): + copy_file_to_dir(self._c_compiler_file, results_dir) + copy_file_to_dir(self._cxx_compiler_file, results_dir) + copy_file_to_dir(self._system_file, results_dir) + if self._compile_commands_file is not None: + copy_file_to_dir(self._compile_commands_file, results_dir) + + +def iter_cmake_lines(filename): + with open(filename) as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#") or line.startswith("//") or len(line) == 0: + continue + yield line + + +# -------------------------------------------------------- + + +def get_manifest(args): + bmdir = os.path.abspath(args.bmdir) + if not args.manifest: + manifest_yml = os.path.join(bmdir, "manifest.yml") + else: + if not os.path.isabs(args.manifest): + manifest_yml = os.path.join(os.getcwd(), args.manifest) + manifest_json = os.path.join(os.path.dirname(manifest.yml), "manifest.json") + manifest = load_yml_file(manifest_yml) + dump_json(manifest, manifest_json) + return manifest + + +def add_results(args): + log("adding results:", args.results) + col = BenchmarkCollection(args.target) + col.add(args.results) + + +def add_meta(args): + log("adding bm run metadata to results dir:", args.results) + meta = ResultMeta(results_dir=args.results, + cmakecache=args.cmakecache, + build_type=args.build_type) + meta.save(args.results) + log("adding bm run metadata to results dir: success!") + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +app = flask.Flask(__name__, template_folder='template') + +def _setup_app(args): + def _s(prop, val): + assert not hasattr(app, prop), prop + setattr(app, prop, val) + _s('args', args) + _s('manifest', get_manifest(args)) + if args.debug: + app.config["DEBUG"] = True + + +def freeze(args): + "https://pythonhosted.org/Frozen-Flask/" + from flask_frozen import Freezer + _setup_app(args) + freezer = Freezer(app) + freezer.freeze(debug=args.debug) + + +def serve(args): + _setup_app(args) + app.run(host=args.host, port=args.port, debug=args.debug) + + [email protected]("/") +def home(): + log("requested home") + return render_template("index.html") + + [email protected]("/<path>") +def other_(path): + path = escape(path) + d = app.args.bmdir + log("requested other path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path) + + [email protected]("/static/<path>") +def static_(path): + path = escape(path) + d = os.path.join(app.args.bmdir, "static") + log("requested static path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path, cache_timeout=1) # timeout in seconds + + [email protected]("/bm/<commit>/<run>/<resultjson>") +def bm_(commit, run, resultjson): + commit = escape(commit) + run = escape(run) + resultjson = escape(resultjson) + d = os.path.join(app.args.bmdir, "runs", commit, run) + log("requested result:", os.path.join(d, resultjson)) + return send_from_directory(d, resultjson, cache_timeout=1) # timeout in seconds + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def download_deps(): + deps = [ + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/ui/1.12.1/jquery-ui.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css", + "https://www.chartjs.org/dist/2.9.1/Chart.min.js", + #("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.css", "highlight.github.css"), + ("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.min.css", "highlight.github.min.css"), + #"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.js", + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js", + ] + for src in deps: + if type(src) == str: + base = os.path.basename(src) + else: + src, base = src + dst = f"{os.getcwd()}/static/{base}" + download_url(src, dst) + + +def download_url(url, dst): + log("download url:", url, "--->", dst) + req = requests.get(url, stream=True) + if req.status_code == 200: + sz = 0 + with open(dst, 'wb') as f: + for chunk in req: + f.write(chunk) + sz += len(chunk) + log(f"........ finished: {sz}B") + else: + log(f" error:", req.status_code, url) + + + +def main(): + def _common_args(parser): + parser.add_argument("-m", "--manifest", type=str, default="", help="enable debug mode") + parser.add_argument("--debug", action="store_true", help="enable debug mode") + # + parser = argparse.ArgumentParser(description="Browse benchmark results", prog="bm") + _common_args(parser) + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("create", help="create benchmark collection") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("filename", type=str, help="the YAML file with the benchmark specs") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("meta", help="get the required meta-information: cpu info, commit data") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("cmakecache", type=str, help="the path to the CMakeCache.txt file used to build the benchmark binaries") + sp.add_argument("build_type", type=str, help="the build type, eg Release Debug MinSizeRel RelWithDebInfo") + # + sp = subparsers.add_parser("add", help="add benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("serve", help="serve benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + sp.add_argument("-H", "--host", type=str, default="localhost", help="host. default=%(default)s") + sp.add_argument("-p", "--port", type=int, default=8000, help="port. default=%(default)s") + # + sp = subparsers.add_parser("export", help="export static html") + sp.set_defaults(func=freeze) + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + _common_args(sp) + # + sp = subparsers.add_parser("deps", help="install server dependencies") + sp.set_defaults(func=lambda _: download_deps()) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + _common_args(sp) + # + args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["serve"]) + if args.debug: + log(args) + args.func(args) diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py new file mode 100644 index 000000000..7c7c2891e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py @@ -0,0 +1,147 @@ +import os +import json +import yaml +import shutil +import mmh3 +import itertools +import munch +import enum + + +# -------------------------------------------------------- + +class _enum(enum.Enum): + def __str__(self): + return str(self.name) + + @property + def short(self): + return self.name + + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + @classmethod + def err_unknown(cls, s): + raise Exception(f"unknown {__class__.__name__}: {s}") + + +class FundamentalTypes(_enum): + float = "float" + double = "double" + int8_t = "int8_t" + uint8_t = "uint8_t" + int16_t = "int16_t" + uint16_t = "uint16_t" + int32_t = "int32_t" + uint32_t = "uint32_t" + int64_t = "int64_t" + uint64_t = "uint64_t" + @property + def short(self): + return self.name.replace("uint", "u").replace("int", "i").replace("_t", "") + + +# -------------------------------------------------------- + +def log(*args, **kwargs): + print(*args, **kwargs, flush=True) + + +def myhash_combine(curr, value): + return curr ^ (value + 0x9e3779b9 + (curr << 6) + (curr >> 2)) + + +def first(iterable): + """Returns the first item""" + if isinstance(iterable, list): + return iterable[0] + return next(iterable) + + +def chain(*iterables): + for it in iterables: + for elm in it: + yield elm + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value""" + return next(itertools.islice(iterable, n, None), default) + + +def optionals(obj, *attrs): + ret = [] + for attr in attrs: + if not hasattr(obj, attr): + log("attr not present:", attr) + continue + ret.append(getattr(obj, attr)) + return ret + + +def myhash(*args): + h = 137597 + for a in args: + if isinstance(a, str): + if a == "": + continue + b = bytes(a, "utf8") + else: + b = bytes(a) + hb = mmh3.hash(b, signed=False) + h = myhash_combine(h, hb) + s = hex(h) + return s[2:min(10, len(s))] + + +def copy_file_to_dir(file, dir): + dir = os.path.abspath(dir) + src = os.path.abspath(file) + dst = f"{dir}/{os.path.basename(src)}" + if not os.path.exists(dir): + os.makedirs(dir) + if os.path.exists(dst): + os.remove(dst) + log("copy:", src, "-->", dst) + shutil.copy(src, dst) + return dst + + +def chk(f): + log("looking for file:", f) + assert os.path.exists(f), f + return f + + +def load_yml_file(filename): + if not os.path.exists(filename): + raise Exception(f"not found: {filename}") + with open(filename) as f: + return load_yml(f.read()) + + +def dump_yml(data, filename): + with open(filename, "w") as f: + yaml.safe_dump(data, f) + + +def load_yml(yml): + return munch.munchify(yaml.safe_load(yml)) + + +def dump_json(data, filename): + with open(filename, "w") as f: + f.write(json.dumps(data, indent=2, sort_keys=True)) + + +def load_json(filename): + with open(filename, "r") as f: + try: + return munch.munchify(json.load(f)) + except Exception as exc: + raise Exception(f"could not load file: {filename}: {exc}") diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py new file mode 100644 index 000000000..3db4175cb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py @@ -0,0 +1,1061 @@ +import os +import sys +import argparse +import copy +import requests +import flask +import json +import re +import yaml +import shutil +import mmh3 +from itertools import islice + +from munch import Munch, munchify +from flask import render_template, redirect, url_for, send_from_directory +from markupsafe import escape + + +def log(*args, **kwargs): + print(*args, **kwargs, flush=True) + + +def myhash_combine(curr, value): + return curr ^ (value + 0x9e3779b9 + (curr<<6) + (curr>>2)) + + +def nth(iterable, n, default=None): + "Returns the nth item or a default value" + return next(islice(iterable, n, None), default) + + +def optionals(obj, *attrs): + ret = [] + for attr in attrs: + if not hasattr(obj, attr): + log("attr not present:", attr) + continue + ret.append(getattr(obj, attr)) + return ret + + +def myhash(*args): + h = 137597 + for a in args: + if isinstance(a, str): + if a == "": + continue + b = bytes(a, "utf8") + else: + b = bytes(a) + hb = mmh3.hash(b, signed=False) + h = myhash_combine(h, hb) + s = hex(h) + return s[2:min(10, len(s))] + + +def copy_file_to_dir(file, dir): + dir = os.path.abspath(dir) + src = os.path.abspath(file) + dst = f"{dir}/{os.path.basename(src)}" + if not os.path.exists(dir): + os.makedirs(dir) + if os.path.exists(dst): + os.remove(dst) + log("copy:", src, "-->", dst) + shutil.copy(src, dst) + return dst + + +def chk(f): + log(f"looking for file:", f) + assert os.path.exists(f), f + return f + + +def load_yml_file(filename): + if not os.path.exists(filename): + raise Exception(f"not found: {filename}") + with open(filename) as f: + return load_yml(f.read()) + + +def dump_yml(data, filename): + with open(filename, "w") as f: + yaml.safe_dump(data, f) + + +def load_yml(yml): + return munchify(yaml.safe_load(yml)) + + +def dump_json(data, filename): + with open(filename, "w") as f: + f.write(json.dumps(data, indent=2, sort_keys=True)) + + +def load_json(filename): + with open(filename, "r") as f: + try: + return munchify(json.load(f)) + except Exception as exc: + raise Exception(f"could not load file: {filename}: {exc}") + + +def main(): + def _common_args(parser): + parser.add_argument("-m", "--manifest", type=str, default="", help="enable debug mode") + parser.add_argument("--debug", action="store_true", help="enable debug mode") + # + parser = argparse.ArgumentParser(description="Browse benchmark results", prog="bm") + _common_args(parser) + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("create", help="create benchmark collection") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("filename", type=str, help="the YAML file with the benchmark specs") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("meta", help="get the required meta-information: cpu info, commit data") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("cmakecache", type=str, help="the path to the CMakeCache.txt file used to build the benchmark binaries") + sp.add_argument("build_type", type=str, help="the build type, eg Release Debug MinSizeRel RelWithDebInfo") + # + sp = subparsers.add_parser("add", help="add benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("serve", help="serve benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + sp.add_argument("-H", "--host", type=str, default="localhost", help="host. default=%(default)s") + sp.add_argument("-p", "--port", type=int, default=8000, help="port. default=%(default)s") + # + sp = subparsers.add_parser("export", help="export static html") + sp.set_defaults(func=freeze) + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + _common_args(sp) + # + sp = subparsers.add_parser("deps", help="install server dependencies") + sp.set_defaults(func=lambda _: download_deps()) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + _common_args(sp) + # + args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["serve"]) + if args.debug: + log(args) + args.func(args) + + +def get_manifest(args): + bmdir = os.path.abspath(args.bmdir) + if not args.manifest: + manifest_yml = os.path.join(bmdir, "manifest.yml") + else: + if not os.path.isabs(args.manifest): + manifest_yml = os.path.join(os.getcwd(), args.manifest) + manifest_json = os.path.join(os.path.dirname(manifest.yml), "manifest.json") + manifest = load_yml_file(manifest_yml) + dump_json(manifest, manifest_json) + return manifest + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +app = flask.Flask(__name__, + template_folder='template') + + +def _setup_app(args): + def _s(prop, val): + assert not hasattr(app, prop), prop + setattr(app, prop, val) + _s('args', args) + _s('manifest', get_manifest(args)) + if args.debug: + app.config["DEBUG"] = True + + +def freeze(args): + "https://pythonhosted.org/Frozen-Flask/" + from flask_frozen import Freezer + _setup_app(args) + freezer = Freezer(app) + freezer.freeze(debug=args.debug) + + +def serve(args): + _setup_app(args) + app.run(host=args.host, port=args.port, debug=args.debug) + + [email protected]("/") +def home(): + log("requested home") + return render_template("index.html") + + [email protected]("/<path>") +def other_(path): + path = escape(path) + d = app.args.bmdir + log("requested other path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path) + + [email protected]("/static/<path>") +def static_(path): + path = escape(path) + d = os.path.join(app.args.bmdir, "static") + log("requested static path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path, cache_timeout=1) # timeout in seconds + + [email protected]("/bm/<commit>/<run>/<resultjson>") +def bm_(commit, run, resultjson): + commit = escape(commit) + run = escape(run) + resultjson = escape(resultjson) + d = os.path.join(app.args.bmdir, "runs", commit, run) + log("requested result:", os.path.join(d, resultjson)) + return send_from_directory(d, resultjson, cache_timeout=1) # timeout in seconds + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def download_deps(): + deps = [ + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/ui/1.12.1/jquery-ui.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css", + "https://www.chartjs.org/dist/2.9.1/Chart.min.js", + #("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.css", "highlight.github.css"), + ("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.min.css", "highlight.github.min.css"), + #"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.js", + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js", + ] + for src in deps: + if type(src) == str: + base = os.path.basename(src) + else: + src, base = src + dst = f"{os.getcwd()}/static/{base}" + download_url(src, dst) + + +def download_url(url, dst): + log("download url:", url, "--->", dst) + req = requests.get(url, stream=True) + if req.status_code == 200: + sz = 0 + with open(dst, 'wb') as f: + for chunk in req: + f.write(chunk) + sz += len(chunk) + log(f"........ finished: {sz}B") + else: + log(f" error:", req.status_code, url) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkCollection: + + @staticmethod + def create_new(args): + dir = args.target + filename = os.path.join(dir, "bm.yml") + manifest = os.path.join(dir, "manifest.yml") + if not os.path.exists(dir): + os.makedirs(dir) + shutil.copyfile(args.filename, filename) + dump_yml(load_yml("""{runs: {}, bm: {}}"""), manifest) + return __class__(dir) + + def __init__(self, dir): + if not os.path.exists(dir): + raise Exception(f"not found: {dir}") + self.dir = os.path.abspath(dir) + self.runs_dir = os.path.join(self.dir, "runs") + self.manifest = os.path.join(self.dir, "manifest.yml") + self.filename = os.path.join(self.dir, "bm.yml") + self.specs = munchify(load_yml_file(self.filename)) + self.manif = munchify(load_yml_file(self.manifest)) + + def add(self, results_dir): + results_dir = os.path.abspath(results_dir) + dst_dir, meta = self._read_run(results_dir) + self._add_run(results_dir, dst_dir, meta) + dump_yml(self.manif, self.manifest) + + def _read_run(self, results_dir): + log("adding run...") + id = f"{len(self.manif.runs.keys()):05d}" + log(f"adding run: id={id}") + meta = ResultMeta.load(results_dir) + dst_dir = os.path.join(self.runs_dir, meta.name) + return dst_dir, meta + + def _add_run(self, results_dir, dst_dir, meta): + cats = self._add_meta_categories(meta) + for filename in ("meta.yml", + "CMakeCCompiler.cmake", + "CMakeCXXCompiler.cmake", + "CMakeSystem.cmake", + "compile_commands.json"): + filename = os.path.join(results_dir, filename) + if os.path.exists(filename): + copy_file_to_dir(filename, dst_dir) + else: + if not filename.endswith("compile_commands.json"): + raise Exception(f"wtf???? {filename}") + for name, specs in self.specs.bm.items(): + if not hasattr(specs, 'variants'): + filename = chk(f"{results_dir}/{name}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(name, specs, meta) + else: + for t in specs.variants: + tname = f"{name}-{t}" + filename = chk(f"{results_dir}/{tname}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(tname, specs, meta) + + def _add_bm_run(self, name, specs, meta): + if name not in self.manif.bm.keys(): + self.manif.bm[name] = Munch(specs=specs, entries=[]) + entry = self.manif.bm[name] + entry.specs = specs + if meta.name not in entry.entries: + entry.entries.append(meta.name) + + def _add_meta_categories(self, meta): + run = Munch() + for catname in ('commit', 'cpu', 'system', 'build'): + meta_item = getattr(meta, catname) + self._add_item_to_category(meta.name, catname, meta_item) + run[catname] = meta_item.storage_id + # build specs are too verbose; remove them + self.manif.build[meta.build.storage_id].specs = Munch() + self.manif.runs[meta.name] = run + + def _add_item_to_category(self, run, category_name, item): + if not hasattr(self.manif, category_name): + setattr(self.manif, category_name, Munch()) + category = getattr(self.manif, category_name) + if item.storage_id not in category.keys(): + category[item.storage_id] = Munch(specs=item, entries=[]) + entry = category[item.storage_id] + entry.specs = item + if run not in entry.entries: + entry.entries.append(run) + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class ResultMeta(Munch): + + def __init__(self, results_dir, cmakecache, build_type): + super().__init__(self) + self.date = __class__.get_date() + self.commit = __class__.get_commit(results_dir) + self.cpu = __class__.get_cpu_info() + self.system = __class__.get_sys_info() + self.build = __class__.get_build_info(cmakecache, build_type) + self.name = self._get_name() + + @staticmethod + def load(results_dir): + results_dir = os.path.join(os.path.abspath(results_dir), "meta.yml") + data = load_yml_file(results_dir) + return munchify(data) + + def save(self, results_dir): + out = os.path.join(results_dir, "meta.yml") + log("saving meta:", out) + dump_yml(self, out) + self.build.save(results_dir) + + @staticmethod + def get_date(): + import datetime + now = datetime.datetime.now() + return now.strftime("%Y%m%d-%H%M%S") + + def _get_name(self): + commit = self.commit.storage_name + cpu = self.cpu.storage_name + sys = self.system.storage_name + build = self.build.storage_name + name = f"{commit}/{cpu}-{sys}-{build}" + return name + + @staticmethod + def get_commit(results_dir): + import git + repo = git.Repo(results_dir, search_parent_directories=True) + commit = repo.head.commit + commit = {p: str(getattr(commit, p)) + for p in ('message', 'summary', 'name_rev', + 'author', + 'authored_datetime', + 'committer', + 'committed_datetime',)} + commit = Munch(commit) + commit.message = commit.message.strip() + commit.sha1 = commit.name_rev[:7] + spl = commit.authored_datetime.split(" ") + date = re.sub(r'-', '', spl[0]) + time = re.sub(r'(\d+):(\d+):(\d+).*', r'\1\2\3', spl[1]) + commit.storage_id = commit.sha1 + commit.storage_name = f"git{date}_{time}-{commit.sha1}" + return commit + + @staticmethod + def get_cpu_info(): + import cpuinfo + nfo = cpuinfo.get_cpu_info() + nfo = Munch(nfo) + for a in ('cpu_version', 'cpu_version_string', 'python_version'): + if hasattr(nfo, a): + delattr(nfo, a) + for a in ('arch_string_raw', 'brand_raw', 'hardware_raw', 'vendor_id_raw'): + if not hasattr(nfo, a): + setattr(nfo, a, '') + nfo.storage_id = myhash( + nfo.arch_string_raw, nfo.brand_raw, nfo.hardware_raw, nfo.vendor_id_raw, + nfo.arch, nfo.bits, nfo.count, nfo.family, nfo.model, nfo.stepping, + ",".join(nfo.flags), nfo.hz_advertised_friendly, + nfo.l2_cache_associativity, + nfo.l2_cache_line_size, + nfo.l2_cache_size, + nfo.l3_cache_size, + *optionals('l1_data_cache_size', 'l1_instruction_cache_size') + ) + nfo.storage_name = f"{nfo.arch.lower()}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_sys_info(): + import platform + uname = platform.uname() + nfo = Munch( + sys_platform=sys.platform, + sys=platform.system(), + uname=Munch( + machine=uname.machine, + node=uname.node, + release=uname.release, + system=uname.system, + version=uname.version, + ) + ) + nfo.storage_id = myhash( + nfo.sys_platform, + nfo.uname.machine, + ) + nfo.storage_name = f"{nfo.sys_platform}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_build_info(cmakecache_txt, buildtype): + nfo = CMakeCache(cmakecache_txt) + def _btflags(name): + return (getattr(nfo, name), getattr(nfo, f"{name}_{buildtype.upper()}")) + nfo.storage_id = myhash( + buildtype, + nfo.CMAKE_CXX_COMPILER_ID, + nfo.CMAKE_CXX_COMPILER_VERSION, + nfo.CMAKE_CXX_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_CXX_COMPILER_ABI, + nfo.CMAKE_CXX_SIZEOF_DATA_PTR, + nfo.CMAKE_C_COMPILER_ID, + nfo.CMAKE_C_COMPILER_VERSION, + nfo.CMAKE_C_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_C_COMPILER_ABI, + nfo.CMAKE_C_SIZEOF_DATA_PTR, + *_btflags("CMAKE_CXX_FLAGS"), + *_btflags("CMAKE_C_FLAGS"), + *_btflags("CMAKE_STATIC_LINKER_FLAGS"), + *_btflags("CMAKE_SHARED_LINKER_FLAGS"), + ) + # + ccname = nfo.CMAKE_CXX_COMPILER_ID.lower() + if ccname == "gnu": + ccname = "gcc" + ccname += nfo.CMAKE_CXX_COMPILER_VERSION.lower() + # + if nfo.CMAKE_C_SIZEOF_DATA_PTR == "4": + bits = "32bit" + elif nfo.CMAKE_C_SIZEOF_DATA_PTR == "8": + bits = "64bit" + else: + raise Exception("unknown architecture") + # + nfo.storage_name = f"{bits}_{buildtype}_{ccname}_{nfo.storage_id}" + return nfo + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class CMakeCache(Munch): + + def __init__(self, cmakecache_txt): + import glob + for line in iter_cmake_lines(cmakecache_txt): + spl = line.split("=") + if len(spl) < 2: + continue + k, ty = spl[0].split(":") + v = "=".join(spl[1:]).strip() + setattr(self, k, v) + bdir = os.path.dirname(os.path.abspath(cmakecache_txt)) + self._c_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCCompiler.cmake"))[-1] # get the last + self._cxx_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCXXCompiler.cmake"))[-1] # get the last + self._system_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeSystem.cmake"))[-1] # get the last + self._load_cmake_file(self._c_compiler_file) + self._load_cmake_file(self._cxx_compiler_file) + ccomfile = f"{bdir}/compile_commands.json" + self._compile_commands_file = ccomfile if os.path.exists(ccomfile) else None + + def _load_cmake_file(self, filename): + for line in iter_cmake_lines(filename): + if not line.startswith("set("): + continue + k = re.sub(r"set\((.*)\ +(.*)\)", r"\1", line) + v = re.sub(r"set\((.*)\ +(.*)\)", r"\2", line) + v = v.strip('"').strip("'").strip() + setattr(self, k, v) + + def save(self, results_dir): + copy_file_to_dir(self._c_compiler_file, results_dir) + copy_file_to_dir(self._cxx_compiler_file, results_dir) + copy_file_to_dir(self._system_file, results_dir) + if self._compile_commands_file is not None: + copy_file_to_dir(self._compile_commands_file, results_dir) + + +def iter_cmake_lines(filename): + with open(filename) as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#") or line.startswith("//") or len(line) == 0: + continue + yield line + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkRun(Munch): + "results of an individual run" + + def __init__(self, json_file, meta_class): + props = load_json(json_file) + setattr(self, "filename", json_file) + assert hasattr(props, "context") + assert hasattr(props, "benchmarks") + super().__init__(**props) + for e in self.benchmarks: + setattr(e, 'meta', meta_class(e.name)) + setattr(self, 'property_names', ( + 'meta', + 'shorttitle', + 'name', + 'run_name', + 'run_type', + 'repetitions', + 'repetition_index', + 'repetition_index', + 'threads', + 'iterations', + 'real_time', + 'cpu_time', + 'time_unit', + 'bytes_per_second', + 'items_per_second', + 'counters', + )) + + @property + def first(self): + return self.benchmarks[0] + + @property + def entries(self): + for entry in self.benchmarks: + yield entry + + @property + def meta(self): + for entry in self.benchmarks: + yield entry.meta + + @property + def filtered_names(self): + for entry in self.benchmarks: + yield entry.meta.shorttitle + + @property + def names(self): + for entry in self.benchmarks: + yield entry.name + + @property + def run_names(self): + for entry in self.benchmarks: + yield entry.run_name + + @property + def run_types(self): + for entry in self.benchmarks: + yield entry.run_type + + @property + def repetitions(self): + for entry in self.benchmarks: + yield entry.repetitions + + @property + def repetition_indices(self): + for entry in self.benchmarks: + yield entry.repetition_index + + @property + def threads(self): + for entry in self.benchmarks: + yield entry.threads + + @property + def iterations(self): + for entry in self.benchmarks: + yield entry.iterations + + @property + def real_time(self): + for entry in self.benchmarks: + yield entry.real_time + + @property + def cpu_time(self): + for entry in self.benchmarks: + yield entry.cpu_time + + @property + def time_unit(self): + for entry in self.benchmarks: + yield entry.time_unit + + @property + def bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second + + @property + def items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkPanel: + + def __init__(self, runs, bm_meta_cls=None): + self.runs = [BenchmarkRun(a, bm_meta_cls) for a in runs] + + def plot_bars(self, title): + plot_benchmarks_as_lines(title, *self.runs) + + + def plot_all_lines(self, title): + plot_benchmarks_as_lines(title, *self.runs, + transform=lambda r: r.meta.num_pixels, + line_title_transform=lambda r: r.meta.shortname) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +def plot_benchmarks_as_bars(title, *bm, transform=None): + from bokeh.models import ColumnDataSource, FactorRange + from bokeh.plotting import figure, show + from bokeh.transform import factor_cmap + pass + + + + +def plot_benchmarks_as_lines(title, *bm, transform=None, + line_title_transform=None, + logx=True, logy=True): + import bokeh + from bokeh.plotting import figure, output_file, show + from bokeh.palettes import Dark2_5 as palette + from bokeh.layouts import row, column + from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div, + RadioGroup, Toggle, + ColumnDataSource, DataTable, TableColumn) + from bokeh.models.markers import marker_types + import itertools + # + ids = entry_ids(*bm, transform=transform) + colors = itertools.cycle(palette) + markers = itertools.cycle(marker_types) + p = figure(title=title, + x_axis_type="log" if logx else "linear", + y_axis_type="log" if logy else "linear", + #background_fill_color="#fafafa", + plot_width=1000, + x_axis_label="Number of pixels", + y_axis_label="Throughput (MB/s)", + ) + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + # + def dft(v): return v if v else (lambda n: n) + tr = dft(transform) + lttr = dft(line_title_transform) + # + for results in bm: + x = [ids[name] for name in results.names] + y = [bps/1e6 for bps in results.bytes_per_second] + c = next(colors) + marker = next(markers) + next(markers) # advance two + line_name = lttr(results.first) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name) + p.line(x, y, + color=c, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + # + def input_title(title): + return Div(text=f"<h3>{title}</h3>") + inputs = [] + first = bm[0].first.meta + for k, g in first.checkbox_groups().items(): + cb = CheckboxGroup(labels=[str(v) for v in g], + active=[i for i in range(len(g))], + inline=True) + inputs.append(input_title(k)) + inputs.append(cb) + # + # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py + x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta] + table_sources = [] + for i, px in enumerate(x_axis_values): + c = ColumnDataSource(data={ + 'name': [nth(results.filtered_names, i) for results in bm], + 'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm], + 'items_per_second': [nth(results.items_per_second, i) for results in bm], + 'cpu_time': [nth(results.real_time, i) for results in bm], + 'real_time': [nth(results.real_time, i) for results in bm], + 'iterations': [nth(results.iterations, i) for results in bm], + 'threads': [nth(results.threads, i) for results in bm], + }) + table_sources.append(c) + selected_x_index = 8 # FIXME (currently 2000 pixels) + table_source = copy.deepcopy(table_sources[selected_x_index]) + relvalues = Toggle(label="Table: Relative values") + px_title = input_title("Table: number of pixels") + px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index) + table_inputs = [relvalues, px_title, px_radiogroup] + # + table_cols = [ + TableColumn(field='name', title='Name'), + TableColumn(field='bytes_per_second', title='Bytes/second'), + TableColumn(field='items_per_second', title='Items/second'), + TableColumn(field='cpu_time', title='CPU time'), + TableColumn(field='real_time', title='Real time'), + TableColumn(field='iterations', title='Iterations'), + TableColumn(field='threads', title='Threads'), + ] + data_table = DataTable(source=table_source, columns=table_cols, width=1200) + callback = CustomJS(args=dict( + radiogroup=px_radiogroup, + source=table_source, + table=table_sources + ), code=""" + console.log(`active=${radiogroup.active}`); + /*source.data=table[radiogroup.active];*/ + var nrows = source.data['name'].length; + var ts = table[radiogroup.active].data; + var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"]; + var ncols = names.length; + console.log(`names=${names} nrows=${nrows} ncols=${ncols}`); + for(var i = 0; i < nrows; i++) { + for(var j = 0; j < ncols; ++j) { + var name = names[j]; + /*console.log(`i=${i} j=${j} name=${name}`);*/ + source.data[name][i] = ts[name][i]; + } + } + source.change.emit(); + """) + px_radiogroup.js_on_change('active', callback) + # lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}")) + # + layout = column( + row(column(*inputs), p), + row(column(*table_inputs), data_table)) + show(layout) + + +def chain(*iterables): + for it in iterables: + for elm in it: + yield elm + + +def entry_ids(*bm, transform=None): + ids = {} + curr = 0 + for results in bm: + log(os.path.basename(results.filename), "------------------------------") + for entry in results.entries: + log(entry.name) + if transform is not None: + ids[entry.name] = transform(entry) + else: + if ids.get(entry.name) is None: + ids[entry.name] = curr + curr += 1 + return ids + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def add_results(args): + log("adding results:", args.results) + col = BenchmarkCollection(args.target) + col.add(args.results) + + +def add_meta(args): + log("adding bm run metadata to results dir:", args.results) + meta = ResultMeta(results_dir=args.results, + cmakecache=args.cmakecache, + build_type=args.build_type) + meta.save(args.results) + log("adding bm run metadata to results dir: success!") + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +import typing +import enum + + +class _enum(enum.Enum): + def __str__(self): + return str(self.name) + @classmethod + def err_unknown(cls, s): + raise Exception(f"unknown {__class__.__name__}: {s}") + + +class MatrixOrder(_enum): + row_major = "row_major" + col_major = "col_major" + @property + def short(self): + return "rm" if self is MatrixOrder.row_major else "cm" + @classmethod + def make(cls, s): + try: + return {"rm": cls.row_major, "cm": cls.col_major}[s] + except: + cls.err_unknown(s) + + +class MatrixLayout(_enum): + compact = "compact" + strided = "strided" + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + +class DimensionBinding(_enum): + compile_time = "compile_time" + run_time = "run_time" + @property + def short(self): + return "ct" if self is DimensionBinding.compile_time else "rt" + @classmethod + def make(cls, s): + try: + return {"ct": cls.compile_time, "rt": cls.run_time}[s] + except: + cls.err_unknown(s) + + +class MultType(_enum): + naive = "naive" + avx2 = "avx2" + avx2_unroll2 = "avx2_unroll2" + avx2_unroll4 = "avx2_unroll4" + avx2_unroll8 = "avx2_unroll8" + @classmethod + def make(cls, s): + try: + s = s.replace("dotprod_", "").replace("_naive", "") + return cls[s] + except: + cls.err_unknown(s) + + +class MatrixMult(typing.NamedTuple): + title: str + num_pixels: int + num_channels: int + num_features: int + mult_type: MultType + layout: MatrixLayout + dim_binding: DimensionBinding + ret_order: MatrixOrder + lhs_order: MatrixOrder + rhs_order: MatrixOrder + + @classmethod + def make(cls, bm_title: str): + # eg: + # mult_naive_strided_ct_rm_cmcm<250, 8, 16> + # mult_naive_compact_rt_rm_rmrm/4000/8/16 + rxline = r'mult_(.*)[</](\d+)(?:/|, )(\d+)(?:/|, )(\d+).*' + rxcase = r"(.*)_(compact|strided)_(ct|rt)_(rm|cm)_(rm|cm)(rm|cm)" + case = re.sub(rxline, r'\1', bm_title) + return cls( + title=case, + num_pixels=int(re.sub(rxline, r'\2', bm_title)), + num_channels=int(re.sub(rxline, r'\3', bm_title)), + num_features=int(re.sub(rxline, r'\4', bm_title)), + mult_type=MultType.make(re.sub(rxcase, r'\1', case)), + layout=MatrixLayout.make(re.sub(rxcase, r'\2', case)), + dim_binding=DimensionBinding.make(re.sub(rxcase, r'\3', case)), + ret_order=MatrixOrder.make(re.sub(rxcase, r'\4', case)), + lhs_order=MatrixOrder.make(re.sub(rxcase, r'\5', case)), + rhs_order=MatrixOrder.make(re.sub(rxcase, r'\6', case)) + ) + + def comparison_axes(self): + return ('num_pixels', 'num_channels', 'num_features') + + def checkbox_groups(self): + return { + 'multiplication method': [t for t in MultType], + 'layout': [t for t in MatrixLayout], + 'dimension': [d for d in DimensionBinding], + 'return matrix ordering': [o for o in MatrixOrder], + 'lhs matrix ordering': [o for o in MatrixOrder], + 'rhs matrix ordering': [o for o in MatrixOrder], + } + + @property + def matrix_size(self): + return self.num_pixels * self.num_channels + + @property + def classifier_size(self): + return self.num_channels * self.num_features + + @property + def shortname(self): + m = self + return f"{m.mult_type}/{m.layout}/{m.dim_binding.short}_{m.ret_order.short}_{m.lhs_order.short}{m.rhs_order.short}" + + @property + def shortparams(self): + m = self + return f"{m.num_pixels:04d}px{m.num_channels:02d}ch{m.num_features:02d}ft" + + @property + def shorttitle(self): + return f"{self.shortname}/{self.shortparams}" + + +def _test(): + def expect(v_, attr, val): + var = getattr(v_, attr) + if var != val: + raise Exception(f"{attr}: expected={val} actual={var}") + # + v = MatrixMult.make("mult_naive_strided_ct_rm_cmcm<250, 8, 16>") + expect(v, 'title', 'naive_strided_ct_rm_cmcm') + expect(v, 'num_pixels', 250) + expect(v, 'num_channels', 8) + expect(v, 'num_features', 16) + expect(v, 'mult_type', MultType.naive) + expect(v, 'layout', MatrixLayout.strided) + expect(v, 'dim_binding', DimensionBinding.compile_time) + expect(v, 'ret_order', MatrixOrder.row_major) + expect(v, 'lhs_order', MatrixOrder.col_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + v = MatrixMult.make("mult_dotprod_avx2_compact_rt_cm_rmcm/4000/16/8") + expect(v, 'title', 'dotprod_avx2_compact_rt_cm_rmcm') + expect(v, 'num_pixels', 4000) + expect(v, 'num_channels', 16) + expect(v, 'num_features', 8) + expect(v, 'mult_type', MultType.avx2) + expect(v, 'layout', MatrixLayout.compact) + expect(v, 'dim_binding', DimensionBinding.run_time) + expect(v, 'ret_order', MatrixOrder.col_major) + expect(v, 'lhs_order', MatrixOrder.row_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + +_test() + + + +def formatMBps(value): + return value / 1e6 + + + +if __name__ == '__main__': + bms = sorted(sys.argv[2:]) + log(bms) + bms = BenchmarkPanel(bms, bm_meta_cls=MatrixMult.make) + fm = bms.runs[0].first.meta + title = f"Classifier multiplication, {fm.num_channels} channels, {fm.num_features} features: throughput (MB/s)" + bms.plot_all_lines(title) + exit() + main() diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py new file mode 100644 index 000000000..3db4175cb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py @@ -0,0 +1,1061 @@ +import os +import sys +import argparse +import copy +import requests +import flask +import json +import re +import yaml +import shutil +import mmh3 +from itertools import islice + +from munch import Munch, munchify +from flask import render_template, redirect, url_for, send_from_directory +from markupsafe import escape + + +def log(*args, **kwargs): + print(*args, **kwargs, flush=True) + + +def myhash_combine(curr, value): + return curr ^ (value + 0x9e3779b9 + (curr<<6) + (curr>>2)) + + +def nth(iterable, n, default=None): + "Returns the nth item or a default value" + return next(islice(iterable, n, None), default) + + +def optionals(obj, *attrs): + ret = [] + for attr in attrs: + if not hasattr(obj, attr): + log("attr not present:", attr) + continue + ret.append(getattr(obj, attr)) + return ret + + +def myhash(*args): + h = 137597 + for a in args: + if isinstance(a, str): + if a == "": + continue + b = bytes(a, "utf8") + else: + b = bytes(a) + hb = mmh3.hash(b, signed=False) + h = myhash_combine(h, hb) + s = hex(h) + return s[2:min(10, len(s))] + + +def copy_file_to_dir(file, dir): + dir = os.path.abspath(dir) + src = os.path.abspath(file) + dst = f"{dir}/{os.path.basename(src)}" + if not os.path.exists(dir): + os.makedirs(dir) + if os.path.exists(dst): + os.remove(dst) + log("copy:", src, "-->", dst) + shutil.copy(src, dst) + return dst + + +def chk(f): + log(f"looking for file:", f) + assert os.path.exists(f), f + return f + + +def load_yml_file(filename): + if not os.path.exists(filename): + raise Exception(f"not found: {filename}") + with open(filename) as f: + return load_yml(f.read()) + + +def dump_yml(data, filename): + with open(filename, "w") as f: + yaml.safe_dump(data, f) + + +def load_yml(yml): + return munchify(yaml.safe_load(yml)) + + +def dump_json(data, filename): + with open(filename, "w") as f: + f.write(json.dumps(data, indent=2, sort_keys=True)) + + +def load_json(filename): + with open(filename, "r") as f: + try: + return munchify(json.load(f)) + except Exception as exc: + raise Exception(f"could not load file: {filename}: {exc}") + + +def main(): + def _common_args(parser): + parser.add_argument("-m", "--manifest", type=str, default="", help="enable debug mode") + parser.add_argument("--debug", action="store_true", help="enable debug mode") + # + parser = argparse.ArgumentParser(description="Browse benchmark results", prog="bm") + _common_args(parser) + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("create", help="create benchmark collection") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("filename", type=str, help="the YAML file with the benchmark specs") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("meta", help="get the required meta-information: cpu info, commit data") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("cmakecache", type=str, help="the path to the CMakeCache.txt file used to build the benchmark binaries") + sp.add_argument("build_type", type=str, help="the build type, eg Release Debug MinSizeRel RelWithDebInfo") + # + sp = subparsers.add_parser("add", help="add benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("serve", help="serve benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + sp.add_argument("-H", "--host", type=str, default="localhost", help="host. default=%(default)s") + sp.add_argument("-p", "--port", type=int, default=8000, help="port. default=%(default)s") + # + sp = subparsers.add_parser("export", help="export static html") + sp.set_defaults(func=freeze) + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + _common_args(sp) + # + sp = subparsers.add_parser("deps", help="install server dependencies") + sp.set_defaults(func=lambda _: download_deps()) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + _common_args(sp) + # + args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["serve"]) + if args.debug: + log(args) + args.func(args) + + +def get_manifest(args): + bmdir = os.path.abspath(args.bmdir) + if not args.manifest: + manifest_yml = os.path.join(bmdir, "manifest.yml") + else: + if not os.path.isabs(args.manifest): + manifest_yml = os.path.join(os.getcwd(), args.manifest) + manifest_json = os.path.join(os.path.dirname(manifest.yml), "manifest.json") + manifest = load_yml_file(manifest_yml) + dump_json(manifest, manifest_json) + return manifest + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +app = flask.Flask(__name__, + template_folder='template') + + +def _setup_app(args): + def _s(prop, val): + assert not hasattr(app, prop), prop + setattr(app, prop, val) + _s('args', args) + _s('manifest', get_manifest(args)) + if args.debug: + app.config["DEBUG"] = True + + +def freeze(args): + "https://pythonhosted.org/Frozen-Flask/" + from flask_frozen import Freezer + _setup_app(args) + freezer = Freezer(app) + freezer.freeze(debug=args.debug) + + +def serve(args): + _setup_app(args) + app.run(host=args.host, port=args.port, debug=args.debug) + + [email protected]("/") +def home(): + log("requested home") + return render_template("index.html") + + [email protected]("/<path>") +def other_(path): + path = escape(path) + d = app.args.bmdir + log("requested other path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path) + + [email protected]("/static/<path>") +def static_(path): + path = escape(path) + d = os.path.join(app.args.bmdir, "static") + log("requested static path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path, cache_timeout=1) # timeout in seconds + + [email protected]("/bm/<commit>/<run>/<resultjson>") +def bm_(commit, run, resultjson): + commit = escape(commit) + run = escape(run) + resultjson = escape(resultjson) + d = os.path.join(app.args.bmdir, "runs", commit, run) + log("requested result:", os.path.join(d, resultjson)) + return send_from_directory(d, resultjson, cache_timeout=1) # timeout in seconds + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def download_deps(): + deps = [ + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/ui/1.12.1/jquery-ui.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css", + "https://www.chartjs.org/dist/2.9.1/Chart.min.js", + #("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.css", "highlight.github.css"), + ("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.min.css", "highlight.github.min.css"), + #"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.js", + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js", + ] + for src in deps: + if type(src) == str: + base = os.path.basename(src) + else: + src, base = src + dst = f"{os.getcwd()}/static/{base}" + download_url(src, dst) + + +def download_url(url, dst): + log("download url:", url, "--->", dst) + req = requests.get(url, stream=True) + if req.status_code == 200: + sz = 0 + with open(dst, 'wb') as f: + for chunk in req: + f.write(chunk) + sz += len(chunk) + log(f"........ finished: {sz}B") + else: + log(f" error:", req.status_code, url) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkCollection: + + @staticmethod + def create_new(args): + dir = args.target + filename = os.path.join(dir, "bm.yml") + manifest = os.path.join(dir, "manifest.yml") + if not os.path.exists(dir): + os.makedirs(dir) + shutil.copyfile(args.filename, filename) + dump_yml(load_yml("""{runs: {}, bm: {}}"""), manifest) + return __class__(dir) + + def __init__(self, dir): + if not os.path.exists(dir): + raise Exception(f"not found: {dir}") + self.dir = os.path.abspath(dir) + self.runs_dir = os.path.join(self.dir, "runs") + self.manifest = os.path.join(self.dir, "manifest.yml") + self.filename = os.path.join(self.dir, "bm.yml") + self.specs = munchify(load_yml_file(self.filename)) + self.manif = munchify(load_yml_file(self.manifest)) + + def add(self, results_dir): + results_dir = os.path.abspath(results_dir) + dst_dir, meta = self._read_run(results_dir) + self._add_run(results_dir, dst_dir, meta) + dump_yml(self.manif, self.manifest) + + def _read_run(self, results_dir): + log("adding run...") + id = f"{len(self.manif.runs.keys()):05d}" + log(f"adding run: id={id}") + meta = ResultMeta.load(results_dir) + dst_dir = os.path.join(self.runs_dir, meta.name) + return dst_dir, meta + + def _add_run(self, results_dir, dst_dir, meta): + cats = self._add_meta_categories(meta) + for filename in ("meta.yml", + "CMakeCCompiler.cmake", + "CMakeCXXCompiler.cmake", + "CMakeSystem.cmake", + "compile_commands.json"): + filename = os.path.join(results_dir, filename) + if os.path.exists(filename): + copy_file_to_dir(filename, dst_dir) + else: + if not filename.endswith("compile_commands.json"): + raise Exception(f"wtf???? {filename}") + for name, specs in self.specs.bm.items(): + if not hasattr(specs, 'variants'): + filename = chk(f"{results_dir}/{name}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(name, specs, meta) + else: + for t in specs.variants: + tname = f"{name}-{t}" + filename = chk(f"{results_dir}/{tname}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(tname, specs, meta) + + def _add_bm_run(self, name, specs, meta): + if name not in self.manif.bm.keys(): + self.manif.bm[name] = Munch(specs=specs, entries=[]) + entry = self.manif.bm[name] + entry.specs = specs + if meta.name not in entry.entries: + entry.entries.append(meta.name) + + def _add_meta_categories(self, meta): + run = Munch() + for catname in ('commit', 'cpu', 'system', 'build'): + meta_item = getattr(meta, catname) + self._add_item_to_category(meta.name, catname, meta_item) + run[catname] = meta_item.storage_id + # build specs are too verbose; remove them + self.manif.build[meta.build.storage_id].specs = Munch() + self.manif.runs[meta.name] = run + + def _add_item_to_category(self, run, category_name, item): + if not hasattr(self.manif, category_name): + setattr(self.manif, category_name, Munch()) + category = getattr(self.manif, category_name) + if item.storage_id not in category.keys(): + category[item.storage_id] = Munch(specs=item, entries=[]) + entry = category[item.storage_id] + entry.specs = item + if run not in entry.entries: + entry.entries.append(run) + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class ResultMeta(Munch): + + def __init__(self, results_dir, cmakecache, build_type): + super().__init__(self) + self.date = __class__.get_date() + self.commit = __class__.get_commit(results_dir) + self.cpu = __class__.get_cpu_info() + self.system = __class__.get_sys_info() + self.build = __class__.get_build_info(cmakecache, build_type) + self.name = self._get_name() + + @staticmethod + def load(results_dir): + results_dir = os.path.join(os.path.abspath(results_dir), "meta.yml") + data = load_yml_file(results_dir) + return munchify(data) + + def save(self, results_dir): + out = os.path.join(results_dir, "meta.yml") + log("saving meta:", out) + dump_yml(self, out) + self.build.save(results_dir) + + @staticmethod + def get_date(): + import datetime + now = datetime.datetime.now() + return now.strftime("%Y%m%d-%H%M%S") + + def _get_name(self): + commit = self.commit.storage_name + cpu = self.cpu.storage_name + sys = self.system.storage_name + build = self.build.storage_name + name = f"{commit}/{cpu}-{sys}-{build}" + return name + + @staticmethod + def get_commit(results_dir): + import git + repo = git.Repo(results_dir, search_parent_directories=True) + commit = repo.head.commit + commit = {p: str(getattr(commit, p)) + for p in ('message', 'summary', 'name_rev', + 'author', + 'authored_datetime', + 'committer', + 'committed_datetime',)} + commit = Munch(commit) + commit.message = commit.message.strip() + commit.sha1 = commit.name_rev[:7] + spl = commit.authored_datetime.split(" ") + date = re.sub(r'-', '', spl[0]) + time = re.sub(r'(\d+):(\d+):(\d+).*', r'\1\2\3', spl[1]) + commit.storage_id = commit.sha1 + commit.storage_name = f"git{date}_{time}-{commit.sha1}" + return commit + + @staticmethod + def get_cpu_info(): + import cpuinfo + nfo = cpuinfo.get_cpu_info() + nfo = Munch(nfo) + for a in ('cpu_version', 'cpu_version_string', 'python_version'): + if hasattr(nfo, a): + delattr(nfo, a) + for a in ('arch_string_raw', 'brand_raw', 'hardware_raw', 'vendor_id_raw'): + if not hasattr(nfo, a): + setattr(nfo, a, '') + nfo.storage_id = myhash( + nfo.arch_string_raw, nfo.brand_raw, nfo.hardware_raw, nfo.vendor_id_raw, + nfo.arch, nfo.bits, nfo.count, nfo.family, nfo.model, nfo.stepping, + ",".join(nfo.flags), nfo.hz_advertised_friendly, + nfo.l2_cache_associativity, + nfo.l2_cache_line_size, + nfo.l2_cache_size, + nfo.l3_cache_size, + *optionals('l1_data_cache_size', 'l1_instruction_cache_size') + ) + nfo.storage_name = f"{nfo.arch.lower()}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_sys_info(): + import platform + uname = platform.uname() + nfo = Munch( + sys_platform=sys.platform, + sys=platform.system(), + uname=Munch( + machine=uname.machine, + node=uname.node, + release=uname.release, + system=uname.system, + version=uname.version, + ) + ) + nfo.storage_id = myhash( + nfo.sys_platform, + nfo.uname.machine, + ) + nfo.storage_name = f"{nfo.sys_platform}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_build_info(cmakecache_txt, buildtype): + nfo = CMakeCache(cmakecache_txt) + def _btflags(name): + return (getattr(nfo, name), getattr(nfo, f"{name}_{buildtype.upper()}")) + nfo.storage_id = myhash( + buildtype, + nfo.CMAKE_CXX_COMPILER_ID, + nfo.CMAKE_CXX_COMPILER_VERSION, + nfo.CMAKE_CXX_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_CXX_COMPILER_ABI, + nfo.CMAKE_CXX_SIZEOF_DATA_PTR, + nfo.CMAKE_C_COMPILER_ID, + nfo.CMAKE_C_COMPILER_VERSION, + nfo.CMAKE_C_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_C_COMPILER_ABI, + nfo.CMAKE_C_SIZEOF_DATA_PTR, + *_btflags("CMAKE_CXX_FLAGS"), + *_btflags("CMAKE_C_FLAGS"), + *_btflags("CMAKE_STATIC_LINKER_FLAGS"), + *_btflags("CMAKE_SHARED_LINKER_FLAGS"), + ) + # + ccname = nfo.CMAKE_CXX_COMPILER_ID.lower() + if ccname == "gnu": + ccname = "gcc" + ccname += nfo.CMAKE_CXX_COMPILER_VERSION.lower() + # + if nfo.CMAKE_C_SIZEOF_DATA_PTR == "4": + bits = "32bit" + elif nfo.CMAKE_C_SIZEOF_DATA_PTR == "8": + bits = "64bit" + else: + raise Exception("unknown architecture") + # + nfo.storage_name = f"{bits}_{buildtype}_{ccname}_{nfo.storage_id}" + return nfo + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class CMakeCache(Munch): + + def __init__(self, cmakecache_txt): + import glob + for line in iter_cmake_lines(cmakecache_txt): + spl = line.split("=") + if len(spl) < 2: + continue + k, ty = spl[0].split(":") + v = "=".join(spl[1:]).strip() + setattr(self, k, v) + bdir = os.path.dirname(os.path.abspath(cmakecache_txt)) + self._c_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCCompiler.cmake"))[-1] # get the last + self._cxx_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCXXCompiler.cmake"))[-1] # get the last + self._system_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeSystem.cmake"))[-1] # get the last + self._load_cmake_file(self._c_compiler_file) + self._load_cmake_file(self._cxx_compiler_file) + ccomfile = f"{bdir}/compile_commands.json" + self._compile_commands_file = ccomfile if os.path.exists(ccomfile) else None + + def _load_cmake_file(self, filename): + for line in iter_cmake_lines(filename): + if not line.startswith("set("): + continue + k = re.sub(r"set\((.*)\ +(.*)\)", r"\1", line) + v = re.sub(r"set\((.*)\ +(.*)\)", r"\2", line) + v = v.strip('"').strip("'").strip() + setattr(self, k, v) + + def save(self, results_dir): + copy_file_to_dir(self._c_compiler_file, results_dir) + copy_file_to_dir(self._cxx_compiler_file, results_dir) + copy_file_to_dir(self._system_file, results_dir) + if self._compile_commands_file is not None: + copy_file_to_dir(self._compile_commands_file, results_dir) + + +def iter_cmake_lines(filename): + with open(filename) as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#") or line.startswith("//") or len(line) == 0: + continue + yield line + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkRun(Munch): + "results of an individual run" + + def __init__(self, json_file, meta_class): + props = load_json(json_file) + setattr(self, "filename", json_file) + assert hasattr(props, "context") + assert hasattr(props, "benchmarks") + super().__init__(**props) + for e in self.benchmarks: + setattr(e, 'meta', meta_class(e.name)) + setattr(self, 'property_names', ( + 'meta', + 'shorttitle', + 'name', + 'run_name', + 'run_type', + 'repetitions', + 'repetition_index', + 'repetition_index', + 'threads', + 'iterations', + 'real_time', + 'cpu_time', + 'time_unit', + 'bytes_per_second', + 'items_per_second', + 'counters', + )) + + @property + def first(self): + return self.benchmarks[0] + + @property + def entries(self): + for entry in self.benchmarks: + yield entry + + @property + def meta(self): + for entry in self.benchmarks: + yield entry.meta + + @property + def filtered_names(self): + for entry in self.benchmarks: + yield entry.meta.shorttitle + + @property + def names(self): + for entry in self.benchmarks: + yield entry.name + + @property + def run_names(self): + for entry in self.benchmarks: + yield entry.run_name + + @property + def run_types(self): + for entry in self.benchmarks: + yield entry.run_type + + @property + def repetitions(self): + for entry in self.benchmarks: + yield entry.repetitions + + @property + def repetition_indices(self): + for entry in self.benchmarks: + yield entry.repetition_index + + @property + def threads(self): + for entry in self.benchmarks: + yield entry.threads + + @property + def iterations(self): + for entry in self.benchmarks: + yield entry.iterations + + @property + def real_time(self): + for entry in self.benchmarks: + yield entry.real_time + + @property + def cpu_time(self): + for entry in self.benchmarks: + yield entry.cpu_time + + @property + def time_unit(self): + for entry in self.benchmarks: + yield entry.time_unit + + @property + def bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second + + @property + def items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkPanel: + + def __init__(self, runs, bm_meta_cls=None): + self.runs = [BenchmarkRun(a, bm_meta_cls) for a in runs] + + def plot_bars(self, title): + plot_benchmarks_as_lines(title, *self.runs) + + + def plot_all_lines(self, title): + plot_benchmarks_as_lines(title, *self.runs, + transform=lambda r: r.meta.num_pixels, + line_title_transform=lambda r: r.meta.shortname) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +def plot_benchmarks_as_bars(title, *bm, transform=None): + from bokeh.models import ColumnDataSource, FactorRange + from bokeh.plotting import figure, show + from bokeh.transform import factor_cmap + pass + + + + +def plot_benchmarks_as_lines(title, *bm, transform=None, + line_title_transform=None, + logx=True, logy=True): + import bokeh + from bokeh.plotting import figure, output_file, show + from bokeh.palettes import Dark2_5 as palette + from bokeh.layouts import row, column + from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div, + RadioGroup, Toggle, + ColumnDataSource, DataTable, TableColumn) + from bokeh.models.markers import marker_types + import itertools + # + ids = entry_ids(*bm, transform=transform) + colors = itertools.cycle(palette) + markers = itertools.cycle(marker_types) + p = figure(title=title, + x_axis_type="log" if logx else "linear", + y_axis_type="log" if logy else "linear", + #background_fill_color="#fafafa", + plot_width=1000, + x_axis_label="Number of pixels", + y_axis_label="Throughput (MB/s)", + ) + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + # + def dft(v): return v if v else (lambda n: n) + tr = dft(transform) + lttr = dft(line_title_transform) + # + for results in bm: + x = [ids[name] for name in results.names] + y = [bps/1e6 for bps in results.bytes_per_second] + c = next(colors) + marker = next(markers) + next(markers) # advance two + line_name = lttr(results.first) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name) + p.line(x, y, + color=c, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + # + def input_title(title): + return Div(text=f"<h3>{title}</h3>") + inputs = [] + first = bm[0].first.meta + for k, g in first.checkbox_groups().items(): + cb = CheckboxGroup(labels=[str(v) for v in g], + active=[i for i in range(len(g))], + inline=True) + inputs.append(input_title(k)) + inputs.append(cb) + # + # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py + x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta] + table_sources = [] + for i, px in enumerate(x_axis_values): + c = ColumnDataSource(data={ + 'name': [nth(results.filtered_names, i) for results in bm], + 'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm], + 'items_per_second': [nth(results.items_per_second, i) for results in bm], + 'cpu_time': [nth(results.real_time, i) for results in bm], + 'real_time': [nth(results.real_time, i) for results in bm], + 'iterations': [nth(results.iterations, i) for results in bm], + 'threads': [nth(results.threads, i) for results in bm], + }) + table_sources.append(c) + selected_x_index = 8 # FIXME (currently 2000 pixels) + table_source = copy.deepcopy(table_sources[selected_x_index]) + relvalues = Toggle(label="Table: Relative values") + px_title = input_title("Table: number of pixels") + px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index) + table_inputs = [relvalues, px_title, px_radiogroup] + # + table_cols = [ + TableColumn(field='name', title='Name'), + TableColumn(field='bytes_per_second', title='Bytes/second'), + TableColumn(field='items_per_second', title='Items/second'), + TableColumn(field='cpu_time', title='CPU time'), + TableColumn(field='real_time', title='Real time'), + TableColumn(field='iterations', title='Iterations'), + TableColumn(field='threads', title='Threads'), + ] + data_table = DataTable(source=table_source, columns=table_cols, width=1200) + callback = CustomJS(args=dict( + radiogroup=px_radiogroup, + source=table_source, + table=table_sources + ), code=""" + console.log(`active=${radiogroup.active}`); + /*source.data=table[radiogroup.active];*/ + var nrows = source.data['name'].length; + var ts = table[radiogroup.active].data; + var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"]; + var ncols = names.length; + console.log(`names=${names} nrows=${nrows} ncols=${ncols}`); + for(var i = 0; i < nrows; i++) { + for(var j = 0; j < ncols; ++j) { + var name = names[j]; + /*console.log(`i=${i} j=${j} name=${name}`);*/ + source.data[name][i] = ts[name][i]; + } + } + source.change.emit(); + """) + px_radiogroup.js_on_change('active', callback) + # lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}")) + # + layout = column( + row(column(*inputs), p), + row(column(*table_inputs), data_table)) + show(layout) + + +def chain(*iterables): + for it in iterables: + for elm in it: + yield elm + + +def entry_ids(*bm, transform=None): + ids = {} + curr = 0 + for results in bm: + log(os.path.basename(results.filename), "------------------------------") + for entry in results.entries: + log(entry.name) + if transform is not None: + ids[entry.name] = transform(entry) + else: + if ids.get(entry.name) is None: + ids[entry.name] = curr + curr += 1 + return ids + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def add_results(args): + log("adding results:", args.results) + col = BenchmarkCollection(args.target) + col.add(args.results) + + +def add_meta(args): + log("adding bm run metadata to results dir:", args.results) + meta = ResultMeta(results_dir=args.results, + cmakecache=args.cmakecache, + build_type=args.build_type) + meta.save(args.results) + log("adding bm run metadata to results dir: success!") + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +import typing +import enum + + +class _enum(enum.Enum): + def __str__(self): + return str(self.name) + @classmethod + def err_unknown(cls, s): + raise Exception(f"unknown {__class__.__name__}: {s}") + + +class MatrixOrder(_enum): + row_major = "row_major" + col_major = "col_major" + @property + def short(self): + return "rm" if self is MatrixOrder.row_major else "cm" + @classmethod + def make(cls, s): + try: + return {"rm": cls.row_major, "cm": cls.col_major}[s] + except: + cls.err_unknown(s) + + +class MatrixLayout(_enum): + compact = "compact" + strided = "strided" + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + +class DimensionBinding(_enum): + compile_time = "compile_time" + run_time = "run_time" + @property + def short(self): + return "ct" if self is DimensionBinding.compile_time else "rt" + @classmethod + def make(cls, s): + try: + return {"ct": cls.compile_time, "rt": cls.run_time}[s] + except: + cls.err_unknown(s) + + +class MultType(_enum): + naive = "naive" + avx2 = "avx2" + avx2_unroll2 = "avx2_unroll2" + avx2_unroll4 = "avx2_unroll4" + avx2_unroll8 = "avx2_unroll8" + @classmethod + def make(cls, s): + try: + s = s.replace("dotprod_", "").replace("_naive", "") + return cls[s] + except: + cls.err_unknown(s) + + +class MatrixMult(typing.NamedTuple): + title: str + num_pixels: int + num_channels: int + num_features: int + mult_type: MultType + layout: MatrixLayout + dim_binding: DimensionBinding + ret_order: MatrixOrder + lhs_order: MatrixOrder + rhs_order: MatrixOrder + + @classmethod + def make(cls, bm_title: str): + # eg: + # mult_naive_strided_ct_rm_cmcm<250, 8, 16> + # mult_naive_compact_rt_rm_rmrm/4000/8/16 + rxline = r'mult_(.*)[</](\d+)(?:/|, )(\d+)(?:/|, )(\d+).*' + rxcase = r"(.*)_(compact|strided)_(ct|rt)_(rm|cm)_(rm|cm)(rm|cm)" + case = re.sub(rxline, r'\1', bm_title) + return cls( + title=case, + num_pixels=int(re.sub(rxline, r'\2', bm_title)), + num_channels=int(re.sub(rxline, r'\3', bm_title)), + num_features=int(re.sub(rxline, r'\4', bm_title)), + mult_type=MultType.make(re.sub(rxcase, r'\1', case)), + layout=MatrixLayout.make(re.sub(rxcase, r'\2', case)), + dim_binding=DimensionBinding.make(re.sub(rxcase, r'\3', case)), + ret_order=MatrixOrder.make(re.sub(rxcase, r'\4', case)), + lhs_order=MatrixOrder.make(re.sub(rxcase, r'\5', case)), + rhs_order=MatrixOrder.make(re.sub(rxcase, r'\6', case)) + ) + + def comparison_axes(self): + return ('num_pixels', 'num_channels', 'num_features') + + def checkbox_groups(self): + return { + 'multiplication method': [t for t in MultType], + 'layout': [t for t in MatrixLayout], + 'dimension': [d for d in DimensionBinding], + 'return matrix ordering': [o for o in MatrixOrder], + 'lhs matrix ordering': [o for o in MatrixOrder], + 'rhs matrix ordering': [o for o in MatrixOrder], + } + + @property + def matrix_size(self): + return self.num_pixels * self.num_channels + + @property + def classifier_size(self): + return self.num_channels * self.num_features + + @property + def shortname(self): + m = self + return f"{m.mult_type}/{m.layout}/{m.dim_binding.short}_{m.ret_order.short}_{m.lhs_order.short}{m.rhs_order.short}" + + @property + def shortparams(self): + m = self + return f"{m.num_pixels:04d}px{m.num_channels:02d}ch{m.num_features:02d}ft" + + @property + def shorttitle(self): + return f"{self.shortname}/{self.shortparams}" + + +def _test(): + def expect(v_, attr, val): + var = getattr(v_, attr) + if var != val: + raise Exception(f"{attr}: expected={val} actual={var}") + # + v = MatrixMult.make("mult_naive_strided_ct_rm_cmcm<250, 8, 16>") + expect(v, 'title', 'naive_strided_ct_rm_cmcm') + expect(v, 'num_pixels', 250) + expect(v, 'num_channels', 8) + expect(v, 'num_features', 16) + expect(v, 'mult_type', MultType.naive) + expect(v, 'layout', MatrixLayout.strided) + expect(v, 'dim_binding', DimensionBinding.compile_time) + expect(v, 'ret_order', MatrixOrder.row_major) + expect(v, 'lhs_order', MatrixOrder.col_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + v = MatrixMult.make("mult_dotprod_avx2_compact_rt_cm_rmcm/4000/16/8") + expect(v, 'title', 'dotprod_avx2_compact_rt_cm_rmcm') + expect(v, 'num_pixels', 4000) + expect(v, 'num_channels', 16) + expect(v, 'num_features', 8) + expect(v, 'mult_type', MultType.avx2) + expect(v, 'layout', MatrixLayout.compact) + expect(v, 'dim_binding', DimensionBinding.run_time) + expect(v, 'ret_order', MatrixOrder.col_major) + expect(v, 'lhs_order', MatrixOrder.row_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + +_test() + + + +def formatMBps(value): + return value / 1e6 + + + +if __name__ == '__main__': + bms = sorted(sys.argv[2:]) + log(bms) + bms = BenchmarkPanel(bms, bm_meta_cls=MatrixMult.make) + fm = bms.runs[0].first.meta + title = f"Classifier multiplication, {fm.num_channels} channels, {fm.num_features} features: throughput (MB/s)" + bms.plot_all_lines(title) + exit() + main() diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt b/thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt new file mode 100644 index 000000000..5a1b395ef --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt @@ -0,0 +1,14 @@ +munch +pyyaml +py-cpuinfo +psutil +gitpython +flask +markupsafe +Frozen-Flask +requests +mmh3 +bokeh<3.0 +selenium +matplotlib +prettytable diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html b/thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html new file mode 100644 index 000000000..fb52b8ba0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> + + <head> + <meta charset="UTF-8"> + <link rel="shortcut icon" href="#"> + <link rel="stylesheet" type="text/css" href="/static/jquery-ui.min.css"/> + <link rel="stylesheet" type="text/css" href="/static/jquery.dataTables.min.css"/> + <link rel="stylesheet" type="text/css" href="/static/highlight.github.min.css"/> + <style> + body { + font-family: "Trebuchet MS", sans-serif; + margin: 50px; + } + .chart { + height: 700px; max-width: 920px; margin: 0px auto; + } + </style> + </head> + + <body> + <h1 id="heading-title">Title</h1> + <div> + Available benchmarks: + <ul id="toc"></ul> + </div> + <div id="bm-results"></div> + <div><pre id="dbg"></pre></div> + <!-- scripts --> + <script type="text/javascript" src="/static/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/static/jquery-ui.js"></script> + <script type="text/javascript" src="/static/jquery.canvasjs.min.js"></script> + <script type="text/javascript" src="/static/Chart.min.js"></script> + <script type="text/javascript" src="/static/jquery.dataTables.min.js"></script> + <script type="text/javascript" src="/static/highlight.min.js"></script> + <script type="text/javascript" src="/bm.js"></script> + <script type="text/javascript"> + $(document).ready(function() { + $.getJSON('/manifest.json', function(specs){ + loadSpecs(specs); + }) + }); + </script> + </body> +</html> diff --git a/thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake b/thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake new file mode 100644 index 000000000..1633989af --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake @@ -0,0 +1,105 @@ +if(NOT _c4CatSourcesIncluded) +set(_c4CatSourcesIncluded ON) + + +#------------------------------------------------------------------------------ +# concatenate the source files to an output file, adding preprocessor adjustment +# for correct file/line reporting +function(c4_cat_sources files output umbrella_target) + _c4_cat_sources_create_cat(cat) + c4_to_full_path("${files}" full_files) # we must work with full paths + c4_separate_list("${full_files}" sepfiles) # and use a string instead of a list + c4_dbg("${_c4_prefix}: catting sources to ${output}") + if(NOT EXISTS "${output}") + # the cat command is executed at build time, but we need the output + # file to exist to be able to create the target. so to bootstrap, just + # run the command now + c4_dbg("${_c4_prefix}: creating ${output} for the first time") + execute_process( + COMMAND ${cat} "${sepfiles}" "${output}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + ) + else() + c4_dbg("output exists: ${output}") + endif() + # add a custom command invoking our cat script for the input files + add_custom_command(OUTPUT ${output} + COMMAND ${cat} "${sepfiles}" "${output}" + DEPENDS ${files} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + COMMENT "concatenating sources to ${output}") + if(NOT TARGET ${umbrella_target}) + add_custom_target(${umbrella_target} DEPENDS ${output} ${files}) + endif() +endfunction(c4_cat_sources) + + +#------------------------------------------------------------------------------ +# get a cat script +function(_c4_cat_sources_create_cat catfile) + # create a script to concatenate the sources + if(WIN32) + set(cat ${CMAKE_BINARY_DIR}/_c4catfiles.bat) + set(cattmp ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/_c4catfiles.bat) + else() + set(cat ${CMAKE_BINARY_DIR}/_c4catfiles.sh) + set(cattmp ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/_c4catfiles.sh) + endif() + set(${catfile} ${cat} PARENT_SCOPE) + if(NOT EXISTS ${cat}) + if(WIN32) + file(WRITE ${cattmp} " +setlocal EnableDelayedExpansion +set \"src_files=%1\" +set \"out_file=%2\" +echo.>\"out_file%\" +for %%f in (%src_files%) do ( + echo.>>\"%out_file%\" + echo.>>\"%out_file%\" + echo \"/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/\".>>\"%out_file%\" + echo \"/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/\".>>\"%out_file%\" + echo \"/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/\".>>\"%out_file%\" + echo \"#line 1 \\\"%%f\\\" // reset __LINE__ and __FILE__ to the correct value\".>>\"%out_file%\" + type %%f>>\"%out_file%\" +) +") + else() + file(WRITE ${cattmp} "#!/bin/sh + +src_files=$1 +out_file=$2 +#echo \"src_files $src_files\" +#echo \"out_file $out_file\" + +cat > $out_file << EOF +// DO NOT EDIT. +// this is an auto-generated file, and will be overwritten +EOF +for f in $src_files ; do + cat >> $out_file <<EOF + + +/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ +/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ +/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ +#line 1 \"$f\" +EOF + cat $f >> $out_file +done + +echo \"Wrote output to $out_file\" +") + endif() + # add execute permissions + get_filename_component(catdir ${cat} DIRECTORY) + file(COPY ${cattmp} DESTINATION ${catdir} + FILE_PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + ) + endif() +endfunction() + + +endif(NOT _c4CatSourcesIncluded) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake b/thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake new file mode 100644 index 000000000..b5e018872 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake @@ -0,0 +1,121 @@ +# (C) 2019 Joao Paulo Magalhaes <[email protected]> +if(NOT _c4_doxygen_included) +set(_c4_doxygen_included ON) + + +#------------------------------------------------------------------------------ +# TODO use customizations from https://cmake.org/cmake/help/v3.9/module/FindDoxygen.html +function(c4_setup_doxygen umbrella_option) + cmake_dependent_option(${_c4_uprefix}BUILD_DOCS "Enable targets to build documentation for ${prefix}" ON "${umbrella_option}" OFF) + if(${_c4_uprefix}BUILD_DOCS) + find_package(Doxygen QUIET) + if(DOXYGEN_FOUND) + c4_log("enabling documentation targets") + else() + c4_dbg("doxygen not found") + endif() + endif() +endfunction() + +#------------------------------------------------------------------------------ +function(c4_add_doxygen doc_name) + if(NOT ${_c4_uprefix}BUILD_DOCS) + return() + endif() + # + set(opt0 + ) + set(opt1 + DOXYFILE DOXYFILE_IN + PROJ + PROJ_BRIEF + VERSION + OUTPUT_DIR + CLANG_DATABASE_PATH + ) + set(optN + INPUT + FILE_PATTERNS + EXCLUDE + EXCLUDE_PATTERNS + EXCLUDE_SYMBOLS + STRIP_FROM_PATH + STRIP_FROM_INC_PATH + EXAMPLE_PATH + ) + cmake_parse_arguments("" "${opt0}" "${opt1}" "${optN}" ${ARGN}) + # + if(NOT _PROJ) + set(_PROJ ${_c4_ucprefix}) + endif() + if(NOT _DOXYFILE AND NOT _DOXYFILE_IN) + set(_DOXYFILE_IN ${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in) + endif() + if(NOT _OUTPUT_DIR) + if("${doc_name}" MATCHES "^[Dd]oc") + set(_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${doc_name}) + else() + set(_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doc/${doc_name}) + endif() + endif() + # + _c4_doxy_fwd_to_cmd(_PROJ OFF) + _c4_doxy_fwd_to_cmd(_PROJ_BRIEF OFF) + _c4_doxy_fwd_to_cmd(_VERSION OFF) + _c4_doxy_fwd_to_cmd(_OUTPUT_DIR OFF) + _c4_doxy_fwd_to_cmd(_CLANG_DATABASE_PATH OFF) + _c4_doxy_fwd_to_cmd(_INPUT ON) + _c4_doxy_fwd_to_cmd(_FILE_PATTERNS ON) + _c4_doxy_fwd_to_cmd(_EXCLUDE ON) + _c4_doxy_fwd_to_cmd(_EXCLUDE_PATTERNS ON) + _c4_doxy_fwd_to_cmd(_EXCLUDE_SYMBOLS ON) + _c4_doxy_fwd_to_cmd(_STRIP_FROM_PATH ON) + _c4_doxy_fwd_to_cmd(_STRIP_FROM_INC_PATH ON) + _c4_doxy_fwd_to_cmd(_EXAMPLE_PATH ON) + # + if("${doc_name}" MATCHES "^[Dd]oc") + set(tgt ${_c4_lcprefix}-${doc_name}) + else() + set(tgt ${_c4_lcprefix}-doc-${doc_name}) + endif() + # + if(_DOXYFILE) + set(doxyfile_out ${_DOXYFILE}) + elseif(_DOXYFILE_IN) + set(doxyfile_out ${_OUTPUT_DIR}/Doxyfile) + set(config_script ${_c4_project_dir}/c4DoxygenConfig.cmake) + add_custom_command(OUTPUT ${doxyfile_out} + COMMAND ${CMAKE_COMMAND} -E remove -f ${doxyfile_out} + COMMAND ${CMAKE_COMMAND} -DDOXYFILE_IN=${_DOXYFILE_IN} -DDOXYFILE_OUT=${doxyfile_out} ${defs} '-DALLVARS=${allvars}' '-DLISTVARS=${listvars}' -P ${config_script} + DEPENDS ${_DOXYFILE_IN} ${config_script} + COMMENT "${tgt}: generating ${doxyfile_out}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() + # + add_custom_target(${tgt} + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile_out} + DEPENDS ${doxyfile_out} + WORKING_DIRECTORY ${_OUTPUT_DIR} + COMMENT "${tgt}: docs will be placed in ${_OUTPUT_DIR}" + VERBATIM) + _c4_set_target_folder(${tgt} doc) +endfunction() + + +macro(_c4_doxy_fwd_to_cmd varname is_list) + if(NOT ("${${varname}}" STREQUAL "")) + if("${defs}" STREQUAL "") + set(li "-D${varname}=${${varname}}") + else() + set(li ${defs}) + list(APPEND li "-D${varname}='${${varname}}'") + endif() + set(defs ${li}) + endif() + set(allvars "${allvars};${varname}") + if(${is_list}) + set(listvars "${listvars};${varname}") + endif() +endmacro() + +endif(NOT _c4_doxygen_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake b/thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake new file mode 100644 index 000000000..b472cab88 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake @@ -0,0 +1,24 @@ +function(_c4_doxy_list_to_str var) + set(il) + foreach(i ${${var}}) + if("${il}" STREQUAL "") + set(il "${i}") + else() + set(il "${il} ${i}") + endif() + endforeach() + set(${var} "${il}" PARENT_SCOPE) +endfunction() + +string(REPLACE " " ";" ALLVARS ${ALLVARS}) +string(REPLACE " " ";" LISTVARS ${LISTVARS}) + +foreach(var ${LISTVARS}) + _c4_doxy_list_to_str(${var}) +endforeach() + +foreach(var ${ALLVARS}) + message(STATUS "${var}='${${var}}'") +endforeach() + +configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake b/thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake new file mode 100644 index 000000000..45a74aa9e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake @@ -0,0 +1,186 @@ +if(NOT _c4_GTPR_included) +set(_c4_GTPR_included ON) + +function(c4_get_target_property_recursive outputvar target property) + # + # helps for debugging + if(_stack) + set(_stack "${_stack}/${target}") + else() + set(_stack "${property}:${target}") + endif() + # + # what type of target is this? + get_target_property(_rec_target_type ${target} TYPE) + c4_dbg("${_stack} [type=${_rec_target_type}]: get property ${property}") + # + # adjust the property names for interface targets + set(_ept_prop_ll LINK_LIBRARIES) + if(_rec_target_type STREQUAL "INTERFACE_LIBRARY") + set(_ept_prop_ll INTERFACE_LINK_LIBRARIES) + if(property STREQUAL "INCLUDE_DIRECTORIES") + c4_dbg("${_stack} [type=${_rec_target_type}]: property ${property} ---> INTERFACE_INCLUDE_DIRECTORIES") + set(property INTERFACE_INCLUDE_DIRECTORIES) + elseif(property STREQUAL "LINK_LIBRARIES") + c4_dbg("${_stack} [type=${_rec_target_type}]: property ${property} ---> INTERFACE_LINK_LIBRARIES") + set(property INTERFACE_LINK_LIBRARIES) + endif() + endif() + # + get_target_property(_ept_li ${target} ${property}) + c4_dbg("${_stack} [type=${_rec_target_type}]: property ${property}=${_ept_li}") + if(NOT _ept_li) # the property may not be set (ie foo-NOTFOUND) + set(_ept_li) # so clear it in that case + endif() + # + # now descend and append the property for each of the linked libraries + get_target_property(_ept_deps ${target} ${_ept_prop_ll}) + if(_ept_deps) + foreach(_ept_ll ${_ept_deps}) + if(TARGET ${_ept_ll}) + c4_get_target_property_recursive(_ept_out ${_ept_ll} ${property}) + list(APPEND _ept_li ${_ept_out}) + endif() + endforeach() + endif() + # + foreach(le_ ${_ept_li}) + string(STRIP "${le_}" le) + if(NOT le) + elseif("${le}" STREQUAL "") + else() + list(APPEND _ept_li_f ${le}) + endif() + endforeach() + c4_dbg("${_stack} [type=${_rec_target_type}]: final=${_ept_li_f}") + set(${outputvar} ${_ept_li_f} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + + +function(c4_set_transitive_property target prop_name prop_value) + set_target_properties(${target} PROPERTIES "${prop_name}" "${prop_value}") +endfunction() + + +function(c4_append_transitive_property target prop_name prop_value) + get_target_property(curr_value ${target} "${prop_name}") + if(curr_value) + list(APPEND curr_value "${prop_value}") + else() + set(curr_value "${prop_value}") + endif() + c4_set_transitive_property(${target} "${prop_name}" "${curr_value}") +endfunction() + + +# TODO: maybe we can use c4_get_target_property_recursive()? +function(c4_get_transitive_property target prop_name out) + if(NOT TARGET ${target}) + return() + endif() + # these will be the names of the variables we'll use to cache the result + set(_trval _C4_TRANSITIVE_${prop_name}) + set(_trmark _C4_TRANSITIVE_${prop_name}_DONE) + # + get_target_property(cached ${target} ${_trmark}) # is it cached already + if(cached) + get_target_property(p ${target} _C4_TRANSITIVE_${prop_name}) + set(${out} ${p} PARENT_SCOPE) + #c4_dbg("${target}: c4_get_transitive_property ${target} ${prop_name}: cached='${p}'") + else() + #c4_dbg("${target}: gathering transitive property: ${prop_name}...") + set(interleaved) + get_target_property(lv ${target} ${prop_name}) + if(lv) + list(APPEND interleaved ${lv}) + endif() + c4_get_transitive_libraries(${target} LINK_LIBRARIES libs) + c4_get_transitive_libraries(${target} INTERFACE_LINK_LIBRARIES ilibs) + list(APPEND libs ${ilibs}) + foreach(lib ${libs}) + #c4_dbg("${target}: considering ${lib}...") + if(NOT lib) + #c4_dbg("${target}: considering ${lib}: not found, skipping...") + continue() + endif() + if(NOT TARGET ${lib}) + #c4_dbg("${target}: considering ${lib}: not a target, skipping...") + continue() + endif() + get_target_property(lv ${lib} ${prop_name}) + if(lv) + list(APPEND interleaved ${lv}) + endif() + c4_get_transitive_property(${lib} ${prop_name} v) + if(v) + list(APPEND interleaved ${v}) + endif() + #c4_dbg("${target}: considering ${lib}---${interleaved}") + endforeach() + #c4_dbg("${target}: gathering transitive property: ${prop_name}: ${interleaved}") + set(${out} ${interleaved} PARENT_SCOPE) + get_target_property(aliased_target ${target} ALIASED_TARGET) + if(NOT aliased_target) + set_target_properties(${target} PROPERTIES + ${_trmark} ON + ${_trval} "${interleaved}") + endif() + endif() +endfunction() + + +function(c4_get_transitive_libraries target prop_name out) + if(NOT TARGET ${target}) + return() + endif() + # these will be the names of the variables we'll use to cache the result + set(_trval _C4_TRANSITIVE_${prop_name}) + set(_trmark _C4_TRANSITIVE_${prop_name}_DONE) + # + get_target_property(cached ${target} ${_trmark}) + if(cached) + get_target_property(p ${target} ${_trval}) + set(${out} ${p} PARENT_SCOPE) + #c4_dbg("${target}: c4_get_transitive_libraries ${target} ${prop_name}: cached='${p}'") + else() + #c4_dbg("${target}: gathering transitive libraries: ${prop_name}...") + get_target_property(target_type ${target} TYPE) + set(interleaved) + if(NOT ("${target_type}" STREQUAL "INTERFACE_LIBRARY") + AND ("${prop_name}" STREQUAL LINK_LIBRARIES)) + get_target_property(l ${target} ${prop_name}) + foreach(ll ${l}) + #c4_dbg("${target}: considering ${ll}...") + if(NOT ll) + #c4_dbg("${target}: considering ${ll}: not found, skipping...") + continue() + endif() + if(NOT ll) + #c4_dbg("${target}: considering ${ll}: not a target, skipping...") + continue() + endif() + list(APPEND interleaved ${ll}) + c4_get_transitive_libraries(${ll} ${prop_name} v) + if(v) + list(APPEND interleaved ${v}) + endif() + #c4_dbg("${target}: considering ${ll}---${interleaved}") + endforeach() + endif() + #c4_dbg("${target}: gathering transitive libraries: ${prop_name}: result='${interleaved}'") + set(${out} ${interleaved} PARENT_SCOPE) + get_target_property(aliased_target ${target} ALIASED_TARGET) + if(NOT aliased_target) + set_target_properties(${target} PROPERTIES + ${_trmark} ON + ${_trval} "${interleaved}") + endif() + endif() +endfunction() + +endif(NOT _c4_GTPR_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake b/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake new file mode 100644 index 000000000..60c8717fe --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake @@ -0,0 +1,3691 @@ +if(NOT _c4_project_included) +set(_c4_project_included ON) +set(_c4_project_file ${CMAKE_CURRENT_LIST_FILE}) +set(_c4_project_dir ${CMAKE_CURRENT_LIST_DIR}) + + +# "I didn't have time to write a short letter, so I wrote a long one +# instead." -- Mark Twain +# +# ... Eg, hopefully this code will be cleaned up. There's a lot of +# code here that can be streamlined into a more intuitive arrangement. + + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +include(ConfigurationTypes) +include(CreateSourceGroup) +include(c4SanitizeTarget) +include(c4StaticAnalysis) +include(PrintVar) +include(c4CatSources) +include(c4Doxygen) +include(PatchUtils) + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# define c4 project settings + +set(C4_EXTERN_DIR "$ENV{C4_EXTERN_DIR}" CACHE PATH "the directory where imported projects should be looked for (or cloned in when not found)") +set(C4_DBG_ENABLED OFF CACHE BOOL "enable detailed cmake logs in c4Project code") +set(C4_LIBRARY_TYPE "" CACHE STRING "default library type: either \"\"(defer to BUILD_SHARED_LIBS),INTERFACE,STATIC,SHARED,MODULE") +set(C4_SOURCE_TRANSFORM NONE CACHE STRING "global source transform method") +set(C4_HDR_EXTS "h;hpp;hh;h++;hxx" CACHE STRING "list of header extensions for determining which files are headers") +set(C4_SRC_EXTS "c;cpp;cc;c++;cxx;cu;" CACHE STRING "list of compilation unit extensions for determining which files are sources") +set(C4_ADD_EXTS "natvis" CACHE STRING "list of additional file extensions that might be added as sources to targets") +set(C4_GEN_SRC_EXT "cpp" CACHE STRING "the extension of the output source files resulting from concatenation") +set(C4_GEN_HDR_EXT "hpp" CACHE STRING "the extension of the output header files resulting from concatenation") +set(C4_CXX_STANDARDS "20;17;14;11" CACHE STRING "list of CXX standards") +set(C4_CXX_STANDARD_DEFAULT "11" CACHE STRING "the default CXX standard for projects not specifying one") + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +macro(c4_log) + message(STATUS "${_c4_prefix}: ${ARGN}") +endmacro() + + +macro(c4_err) + message(FATAL_ERROR "${_c4_prefix}: ${ARGN}") +endmacro() + + +macro(c4_dbg) + if(C4_DBG_ENABLED) + message(STATUS "${_c4_prefix}: ${ARGN}") + endif() +endmacro() + + +macro(c4_log_var varname) + c4_log("${varname}=${${varname}} ${ARGN}") +endmacro() +macro(c4_log_vars) + set(____s____) + foreach(varname ${ARGN}) + set(____s____ "${____s____}${varname}=${${varname}} ") + endforeach() + c4_log("${____s____}") +endmacro() +macro(c4_dbg_var varname) + c4_dbg("${varname}=${${varname}} ${ARGN}") +endmacro() +macro(c4_log_var_if varname) + if(${varname}) + c4_log("${varname}=${${varname}} ${ARGN}") + endif() +endmacro() +macro(c4_dbg_var_if varname) + if(${varname}) + c4_dbg("${varname}=${${varname}} ${ARGN}") + endif() +endmacro() + + +macro(_c4_show_pfx_vars) + if(NOT ("${ARGN}" STREQUAL "")) + c4_log("prefix vars: ${ARGN}") + endif() + print_var(_c4_prefix) + print_var(_c4_ocprefix) + print_var(_c4_ucprefix) + print_var(_c4_lcprefix) + print_var(_c4_oprefix) + print_var(_c4_uprefix) + print_var(_c4_lprefix) +endmacro() + + +function(c4_zero_pad padded size str) + string(LENGTH "${str}" len) + math(EXPR numchars "${size} - ${len}") + if(numchars EQUAL 0) + set(${padded} "${str}" PARENT_SCOPE) + else() + set(out "${str}") + math(EXPR ncm1 "${numchars} - 1") + foreach(z RANGE ${ncm1}) + set(out "0${out}") + endforeach() + set(${padded} "${out}" PARENT_SCOPE) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# handy macro for dealing with arguments in one single statement. +# look for example usage cases below. +macro(_c4_handle_args) + set(opt0arg + ) + set(opt1arg + _PREFIX + ) + set(optNarg + _ARGS0 + _ARGS1 + _ARGSN + _ARGS + _DEPRECATE + ) + # parse the arguments to this macro to find out the required arguments + cmake_parse_arguments("__c4ha" "${opt0arg}" "${opt1arg}" "${optNarg}" ${ARGN}) + # now parse the required arguments + cmake_parse_arguments("${__c4ha__PREFIX}" "${__c4ha__ARGS0}" "${__c4ha__ARGS1}" "${__c4ha__ARGSN}" ${__c4ha__ARGS}) + # raise an error on deprecated arguments + foreach(a ${__c4ha__DEPRECATE}) + list(FIND __c4ha__ARGS ${a} contains) + if(NOT (${contains} EQUAL -1)) + c4err("${a} is deprecated") + endif() + endforeach() +endmacro() + +# fallback to provided default(s) if argument is not set +macro(_c4_handle_arg argname) + if("${_${argname}}" STREQUAL "") + set(_${argname} "${ARGN}") + else() + set(_${argname} "${_${argname}}") + endif() +endmacro() +macro(_c4_handle_arg_no_pfx argname) + if("${${argname}}" STREQUAL "") + set(${argname} "${ARGN}") + else() + set(${argname} "${${argname}}") + endif() +endmacro() + + +# if ${_${argname}} is non empty, return it +# otherwise, fallback to ${_c4_uprefix}${argname} +# otherwise, fallback to C4_${argname} +# otherwise, fallback to provided default through ${ARGN} +macro(_c4_handle_arg_or_fallback argname) + if(NOT ("${_${argname}}" STREQUAL "")) + c4_dbg("handle arg ${argname}: picking explicit value _${argname}=${_${argname}}") + else() + foreach(_c4haf_varname "${_c4_uprefix}${argname}" "C4_${argname}" "${argname}" "CMAKE_${argname}") + set(v ${${_c4haf_varname}}) + if("${v}" STREQUAL "") + c4_dbg("handle arg ${argname}: ${_c4haf_varname}: empty, continuing") + else() + c4_dbg("handle arg ${argname}: ${_c4haf_varname}=${v} not empty!") + c4_setg(_${argname} "${v}") + break() + endif() + endforeach() + if("${_${argname}}" STREQUAL "") + c4_dbg("handle arg ${argname}: picking default: ${ARGN}") + c4_setg(_${argname} "${ARGN}") + endif() + endif() +endmacro() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_get_config var name) + c4_dbg("get_config: ${var} ${name}") + c4_get_from_first_of(config ${ARGN} VARS ${_c4_uprefix}${name} C4_${name} ${name}) + c4_dbg("get_config: ${var} ${name}=${config}") + set(${var} ${config} PARENT_SCOPE) +endfunction() + + +function(c4_get_from_first_of var) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + REQUIRED # raise an error if no set variable was found + ENV # if none of the provided vars is given, + # then search next on environment variables + # of the same name, using the same sequence + _ARGS1 + DEFAULT + _ARGSN + VARS + ) + c4_dbg("get_from_first_of(): searching ${var}") + foreach(_var ${_VARS}) + set(val ${${_var}}) + c4_dbg("${var}: searching ${_var}=${val}") + if(NOT ("${val}" STREQUAL "")) + set(${var} "${val}" PARENT_SCOPE) + return() + endif() + endforeach() + if(_ENV) + foreach(_envvar ${_VARS}) + set(val $ENV{${_envvar}}) + c4_dbg("${var}: searching environment variable ${_envvar}=${val}") + if(NOT ("${val}" STREQUAL "")) + c4_dbg("${var}: picking ${val} from ${_envvar}") + set(${var} "${val}" PARENT_SCOPE) + return() + endif() + endforeach() + endif() + if(_REQUIRED) + c4_err("could not find a value for the variable ${var}") + endif() + set(${var} ${_DEFAULT} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# assumes a prior call to project() +function(c4_project) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + STANDALONE # Declare that targets from this project MAY be + # compiled in standalone mode. In this mode, any + # designated libraries on which a target depends + # will be incorporated into the target instead of + # being linked with it. The effect is to "flatten" + # those libraries into the requesting library, with + # their sources now becoming part of the requesting + # library; their dependencies are transitively handled. + # Note that requesting targets must explicitly + # opt-in to this behavior via the INCORPORATE + # argument to c4_add_library() or + # c4_add_executable(). Note also that this behavior + # is only enabled if this project's option + # ${prefix}_STANDALONE or C4_STANDALONE is set to ON. + _ARGS1 # one-value macro arguments + AUTHOR # specify author(s); used in cpack + VERSION # cmake does not accept semantic versioning so we provide + # that here (see https://gitlab.kitware.com/cmake/cmake/-/issues/16716) + CXX_STANDARD # one of latest;${C4_VALID_CXX_STANDARDS} + # if this is not provided, falls back on + # ${uprefix}CXX_STANDARD, then C4_CXX_STANDARD, + # then CXX_STANDARD. if none are provided, + # defaults to 11 + _ARGSN # multi-value macro arguments + ) + # get the prefix from the call to project() + set(prefix ${PROJECT_NAME}) + string(TOUPPER "${prefix}" ucprefix) # ucprefix := upper case prefix + string(TOLOWER "${prefix}" lcprefix) # lcprefix := lower case prefix + if(NOT _c4_prefix) + c4_setg(_c4_is_root_proj ON) + c4_setg(_c4_root_proj ${prefix}) + c4_setg(_c4_root_uproj ${ucprefix}) + c4_setg(_c4_root_lproj ${lcprefix}) + c4_setg(_c4_curr_path "") + else() + c4_setg(_c4_is_root_proj OFF) + if(_c4_curr_path) + c4_setg(_c4_curr_path "${_c4_curr_path}/${prefix}") + else() + c4_setg(_c4_curr_path "${prefix}") + endif() + endif() + c4_setg(_c4_curr_subproject ${prefix}) + # get the several prefix flavors + c4_setg(_c4_ucprefix ${ucprefix}) + c4_setg(_c4_lcprefix ${lcprefix}) + c4_setg(_c4_ocprefix ${prefix}) # ocprefix := original case prefix + c4_setg(_c4_prefix ${prefix}) # prefix := original prefix + c4_setg(_c4_oprefix ${prefix}) # oprefix := original prefix + c4_setg(_c4_uprefix ${_c4_ucprefix}) # upper prefix: for variables + c4_setg(_c4_lprefix ${_c4_lcprefix}) # lower prefix: for targets + if(_c4_oprefix) + c4_setg(_c4_oprefix "${_c4_oprefix}_") + endif() + if(_c4_uprefix) + c4_setg(_c4_uprefix "${_c4_uprefix}_") + endif() + if(_c4_lprefix) + c4_setg(_c4_lprefix "${_c4_lprefix}-") + endif() + # + if(_STANDALONE) + option(${_c4_uprefix}STANDALONE + "Enable compilation of opting-in targets from ${_c4_lcprefix} in standalone mode (ie, incorporate subprojects as specified in the INCORPORATE clause to c4_add_library/c4_add_target)" + ${_c4_is_root_proj}) + c4_setg(_c4_root_proj_standalone ${_c4_uprefix}STANDALONE) + endif() + _c4_handle_arg_or_fallback(CXX_STANDARD ${C4_CXX_STANDARD_DEFAULT}) + _c4_handle_arg(VERSION 0.0.0-pre0) + _c4_handle_arg(AUTHOR "") + _c4_handle_semantic_version(${_VERSION}) + # + # make sure project-wide settings are defined -- see cmake's + # documentation for project(), which defines these and other + # variables + if("${PROJECT_DESCRIPTION}" STREQUAL "") + c4_setg(PROJECT_DESCRIPTION "${prefix}") + c4_setg(${prefix}_DESCRIPTION "${prefix}") + endif() + if("${PROJECT_HOMEPAGE_URL}" STREQUAL "") + c4_setg(PROJECT_HOMEPAGE_URL "") + c4_setg(${prefix}_HOMEPAGE_URL "") + endif() + # other specific c4_project properties + c4_setg(PROJECT_AUTHOR "${_AUTHOR}") + c4_setg(${prefix}_AUTHOR "${_AUTHOR}") + + # CXX standard + if("${_CXX_STANDARD}" STREQUAL "latest") + _c4_find_latest_supported_cxx_standard(_CXX_STANDARD) + endif() + c4_log("using C++ standard: C++${_CXX_STANDARD}") + c4_set_proj_prop(CXX_STANDARD "${_CXX_STANDARD}") + c4_setg(${_c4_uprefix}CXX_STANDARD "${_CXX_STANDARD}") + if(${_CXX_STANDARD}) + c4_set_cxx(${_CXX_STANDARD}) + endif() + + # we are opinionated with respect to directory structure + c4_setg(${_c4_uprefix}SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src) + c4_setg(${_c4_uprefix}EXT_DIR ${CMAKE_CURRENT_LIST_DIR}/ext) + c4_setg(${_c4_uprefix}API_DIR ${CMAKE_CURRENT_LIST_DIR}/api) + # opionionated also for directory test + # opionionated also for directory bm (benchmarks) + + if("${C4_DEV}" STREQUAL "") + option(C4_DEV "enable development targets for all c4 projects" OFF) + endif() + option(${_c4_uprefix}DEV "enable development targets: tests, benchmarks, sanitize, static analysis, coverage" ${C4_DEV}) + + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/test") + cmake_dependent_option(${_c4_uprefix}BUILD_TESTS "build unit tests" ON ${_c4_uprefix}DEV OFF) + else() + c4_dbg("no tests: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/test") + endif() + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/bm") + cmake_dependent_option(${_c4_uprefix}BUILD_BENCHMARKS "build benchmarks" ON ${_c4_uprefix}DEV OFF) + else() + c4_dbg("no benchmarks: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/bm") + endif() + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/api") + cmake_dependent_option(${_c4_uprefix}BUILD_API "build API" OFF ${_c4_uprefix}DEV OFF) + else() + c4_dbg("no API generation: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/api") + endif() + if(_c4_is_root_proj) + c4_setup_coverage() + endif() + c4_setup_valgrind(${_c4_uprefix}DEV) + c4_setup_sanitize(${_c4_uprefix}DEV) + c4_setup_static_analysis(${_c4_uprefix}DEV) + c4_setup_doxygen(${_c4_uprefix}DEV) + + # option to use libc++ + option(${_c4_uprefix}USE_LIBCXX "use libc++ instead of the default standard library" OFF) + if(${_c4_uprefix}USE_LIBCXX) + if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + c4_log("using libc++") + list(APPEND CMAKE_CXX_FLAGS -stdlib=libc++) + list(APPEND CMAKE_EXE_LINKER_FLAGS -lc++) + list(APPEND CMAKE_MODULE_LINKER_FLAGS -lc++) + list(APPEND CMAKE_SHARED_LINKER_FLAGS -lc++) + list(APPEND CMAKE_STATIC_LINKER_FLAGS -lc++) + else() + c4_err("libc++ can only be used with clang") + endif() + endif() + + # default compilation flags + set(${_c4_uprefix}CXX_FLAGS "${${_c4_uprefix}CXX_FLAGS_FWD}" CACHE STRING "compilation flags for ${_c4_prefix} targets") + set(${_c4_uprefix}CXX_LINKER_FLAGS "${${_c4_uprefix}CXX_LINKER_FLAGS_FWD}" CACHE STRING "linker flags for ${_c4_prefix} targets") + c4_dbg_var_if(${_c4_uprefix}CXX_LINKER_FLAGS_FWD) + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_FWD) + c4_dbg_var_if(${_c4_uprefix}CXX_LINKER_FLAGS) + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS) + + # Dev compilation flags, appended to the project's flags. They + # are enabled when in dev mode, but provided as a (default-disabled) + # option when not in dev mode + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_OPT_FWD) + c4_setg(${_c4_uprefix}CXX_FLAGS_OPT "${${_c4_uprefix}CXX_FLAGS_OPT_FWD}") + c4_optional_compile_flags_dev(WERROR "Compile with warnings as errors" + GCC_CLANG -Werror -pedantic-errors + MSVC /WX + ) + c4_optional_compile_flags_dev(STRICT_ALIASING "Enable strict aliasing" + GCC_CLANG -fstrict-aliasing + MSVC # does it have this? + ) + c4_optional_compile_flags_dev(PEDANTIC "Compile in pedantic mode" + GCC ${_C4_PEDANTIC_FLAGS_GCC} + CLANG ${_C4_PEDANTIC_FLAGS_CLANG} + MSVC ${_C4_PEDANTIC_FLAGS_MSVC} + ) + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_OPT) +endfunction(c4_project) + + +# cmake: VERSION argument in project() does not accept semantic versioning +# see: https://gitlab.kitware.com/cmake/cmake/-/issues/16716 +macro(_c4_handle_semantic_version version) + # https://stackoverflow.com/questions/18658233/split-string-to-3-variables-in-cmake + string(REPLACE "." ";" version_list ${version}) + list(GET version_list 0 _major) + list(GET version_list 1 _minor) + list(GET version_list 2 _patch) + if("${_patch}" STREQUAL "") + set(_patch 1) + set(_tweak) + else() + string(REGEX REPLACE "([0-9]+)[-_.]?(.*)" "\\2" _tweak ${_patch}) # do this first + string(REGEX REPLACE "([0-9]+)[-_.]?(.*)" "\\1" _patch ${_patch}) # ... because this replaces _patch + endif() + # because cmake handles only numeric tweak fields, make sure to skip our + # semantic tweak field if it is not numeric + if(${_tweak} MATCHES "^[0-9]+$") + set(_safe_tweak ${_tweak}) + set(_safe_version ${_major}.${_minor}.${_patch}.${_tweak}) + else() + set(_safe_tweak) + set(_safe_version ${_major}.${_minor}.${_patch}) + endif() + c4_setg(PROJECT_VERSION_FULL ${version}) + c4_setg(PROJECT_VERSION ${_safe_version}) + c4_setg(PROJECT_VERSION_MAJOR ${_major}) + c4_setg(PROJECT_VERSION_MINOR ${_minor}) + c4_setg(PROJECT_VERSION_PATCH ${_patch}) + c4_setg(PROJECT_VERSION_TWEAK "${_safe_tweak}") + c4_setg(PROJECT_VERSION_TWEAK_FULL "${_tweak}") + c4_setg(${prefix}_VERSION_FULL ${version}) + c4_setg(${prefix}_VERSION ${_safe_version}) + c4_setg(${prefix}_VERSION_MAJOR ${_major}) + c4_setg(${prefix}_VERSION_MINOR ${_minor}) + c4_setg(${prefix}_VERSION_PATCH ${_patch}) + c4_setg(${prefix}_VERSION_TWEAK "${_safe_tweak}") + c4_setg(${prefix}_VERSION_TWEAK_FULL "${_tweak}") +endmacro() + + +# Add targets for testing (dir=./test), benchmark (dir=./bm) and API (dir=./api). +# Call this macro towards the end of the project's main CMakeLists.txt. +# Experimental feature: docs. +function(c4_add_dev_targets) + if(NOT CMAKE_CURRENT_LIST_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + c4_err("this macro needs to be called on the project's main CMakeLists.txt file") + endif() + # + if(${_c4_uprefix}BUILD_TESTS) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/test") + c4_dbg("adding tests: ${CMAKE_CURRENT_LIST_DIR}/test") + enable_testing() # this must be done here (and not inside the + # test dir) so that the cmake-generated test + # targets are available at the top level + add_subdirectory(test) + endif() + endif() + # + if(${_c4_uprefix}BUILD_BENCHMARKS) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/bm") + c4_dbg("adding benchmarks: ${CMAKE_CURRENT_LIST_DIR}/bm") + add_subdirectory(bm) + endif() + endif() + # + if(${_c4_uprefix}BUILD_API) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/api") + c4_dbg("adding API: ${d}") + add_subdirectory(api) + endif() + endif() + # + # FIXME + c4_add_doxygen(doc DOXYFILE_IN ${_c4_project_dir}/Doxyfile.in + PROJ c4core + INPUT ${${_c4_uprefix}SRC_DIR} + EXCLUDE ${${_c4_uprefix}EXT_DIR} ${${_c4_uprefix}SRC_DIR}/c4/ext + STRIP_FROM_PATH ${${_c4_uprefix}SRC_DIR} + STRIP_FROM_INC_PATH ${${_c4_uprefix}SRC_DIR} + CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR} + ) + c4_add_doxygen(doc-full DOXYFILE_IN ${_c4_project_dir}/Doxyfile.full.in + PROJ c4core + INPUT ${${_c4_uprefix}SRC_DIR} + EXCLUDE ${${_c4_uprefix}EXT_DIR} ${${_c4_uprefix}SRC_DIR}/c4/ext + STRIP_FROM_PATH ${${_c4_uprefix}SRC_DIR} + STRIP_FROM_INC_PATH ${${_c4_uprefix}SRC_DIR} + CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR} + ) +endfunction() + + +function(_c4_get_san_targets target result) + _c4_get_tgt_prop(san_targets ${target} C4_SAN_TARGETS) + if(NOT san_targets) + #c4_err("${target} must have at least itself in its sanitized target list") + set(${result} ${target} PARENT_SCOPE) + else() + set(${result} ${san_targets} PARENT_SCOPE) + endif() +endfunction() + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +# utilities for compilation flags and defines + +# flags enabled only on dev mode +macro(c4_optional_compile_flags_dev tag desc) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + _ARGS1 + _ARGSN + MSVC # flags for Visual Studio compilers + GCC # flags for gcc compilers + CLANG # flags for clang compilers + GCC_CLANG # flags common to gcc and clang + _DEPRECATE + ) + cmake_dependent_option(${_c4_uprefix}${tag} "${desc}" ON ${_c4_uprefix}DEV OFF) + set(optname ${_c4_uprefix}${tag}) + if(${optname}) + c4_dbg("${optname} is enabled. Adding flags...") + if(MSVC) + set(flags ${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_GCC_CLANG};${_GCC}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) # FIXME + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) # FIXME + else() + c4_err("unknown compiler") + endif() + else() + c4_dbg("${optname} is disabled.") + endif() + if(flags) + c4_log("${tag} flags [${desc}]: ${flags}") + c4_setg(${_c4_uprefix}CXX_FLAGS_OPT "${${_c4_uprefix}CXX_FLAGS_OPT};${flags}") + endif() +endmacro() + + +function(c4_target_compile_flags target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + PUBLIC + PRIVATE + INTERFACE + AFTER # this is the default + BEFORE + _ARGS1 + _ARGSN + ALL # flags for all compilers + MSVC # flags for Visual Studio compilers + GCC # flags for gcc compilers + CLANG # flags for clang compilers + GCC_CLANG # flags common to gcc and clang + _DEPRECATE + ) + if(MSVC) + set(flags ${_ALL};${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) # FIXME + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) # FIXME + else() + c4_err("unknown compiler") + endif() + if(NOT flags) + c4_dbg("no compile flags to be set") + return() + endif() + if(_AFTER OR (NOT _BEFORE)) + set(mode) + c4_log("${target}: adding compile flags AFTER: ${flags}") + elseif(_BEFORE) + set(mode BEFORE) + c4_log("${target}: adding compile flags BEFORE: ${flags}") + endif() + _c4_get_san_targets(${target} san_targets) + foreach(st ${san_targets}) + if(_PUBLIC) + target_compile_options(${st} ${mode} PUBLIC ${flags}) + elseif(_PRIVATE) + target_compile_options(${st} ${mode} PRIVATE ${flags}) + elseif(_INTERFACE) + target_compile_options(${st} ${mode} INTERFACE ${flags}) + else() + c4_err("${target}: must have one of PUBLIC, PRIVATE or INTERFACE") + endif() + endforeach() +endfunction() + + +function(c4_target_definitions target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + PUBLIC + PRIVATE + INTERFACE + _ARGS1 + _ARGSN + ALL # defines for all compilers + MSVC # defines for Visual Studio compilers + GCC # defines for gcc compilers + CLANG # defines for clang compilers + GCC_CLANG # defines common to gcc and clang + _DEPRECATE + ) + if(MSVC) + set(flags ${_ALL};${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) + else() + c4_err("unknown compiler") + endif() + if(NOT flags) + c4_dbg("no compile flags to be set") + return() + endif() + if(_AFTER OR (NOT _BEFORE)) + set(mode) + c4_log("${target}: adding definitions AFTER: ${flags}") + elseif(_BEFORE) + set(mode BEFORE) + c4_log("${target}: adding definitions BEFORE: ${flags}") + endif() + _c4_get_san_targets(${target} san_targets) + foreach(st ${san_targets}) + if(_PUBLIC) + target_compile_definitions(${st} ${mode} PUBLIC ${flags}) + elseif(_PRIVATE) + target_compile_definitions(${st} ${mode} PRIVATE ${flags}) + elseif(_INTERFACE) + target_compile_definitions(${st} ${mode} INTERFACE ${flags}) + else() + c4_err("${target}: must have one of PUBLIC, PRIVATE or INTERFACE") + endif() + endforeach() +endfunction() + + +function(c4_target_remove_compile_flags target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + PUBLIC # remove only from public compile options + INTERFACE # remove only from interface compile options + _ARGS1 + _ARGSN + MSVC # flags for Visual Studio compilers + GCC # flags for gcc compilers + CLANG # flags for clang compilers + GCC_CLANG # flags common to gcc and clang + _DEPRECATE + ) + if(MSVC) + set(flags ${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_GCC_CLANG};${_GCC}) + else() + c4_err("unknown compiler") + endif() + if(NOT flags) + return() + endif() + _c4_get_san_targets(${target} san_targets) + foreach(st ${san_targets}) + if(_PUBLIC OR (NOT _INTERFACE)) + get_target_property(co ${st} COMPILE_OPTIONS) + if(co) + _c4_remove_entries_from_list("${flags}" co) + set_target_properties(${st} PROPERTIES COMPILE_OPTIONS "${co}") + endif() + endif() + if(_INTERFACE OR (NOT _PUBLIC)) + get_target_property(ico ${st} INTERFACE_COMPILE_OPTIONS) + if(ico) + _c4_remove_entries_from_list("${flags}" ico) + set_target_properties(${st} PROPERTIES INTERFACE_COMPILE_OPTIONS "${ico}") + endif() + endif() + endforeach() +endfunction() + + +function(_c4_remove_entries_from_list entries_to_remove list) + set(str ${${list}}) + string(REPLACE ";" "==?==" str "${str}") + foreach(entry ${entries_to_remove}) + string(REPLACE "${entry}" "" str "${str}") + endforeach() + string(REPLACE "==?==" ";" str "${str}") + string(REPLACE ";;" ";" str "${str}") + set(${list} "${str}" PARENT_SCOPE) +endfunction() + + + +# pedantic flags... +# default pedantic flags taken from: +# https://github.com/lefticus/cpp_starter_project/blob/master/cmake/CompilerWarnings.cmake +set(_C4_PEDANTIC_FLAGS_MSVC + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + $<$<VERSION_GREATER:${MSVC_VERSION},1900>:/permissive-> # standards conformance mode for MSVC compiler (only vs2017+) + ) + +set(_C4_PEDANTIC_FLAGS_CLANG + -Wall + -Wextra + -pedantic + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + #-Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wdouble-promotion # warn if float is implicit promoted to double + -Wfloat-equal # warn if comparing floats + -Wformat=2 # warn on security issues around functions that format output (ie printf) + ) + +set(_C4_PEDANTIC_FLAGS_GCC ${_C4_PEDANTIC_FLAGS_CLANG} + -Wlogical-op # where logical operations are used where bitwise were probably wanted + -Wuseless-cast # where you perform a cast to the same type + ) + +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)) + list(APPEND _C4_PEDANTIC_FLAGS_GCC + -Wnull-dereference # warn if a null dereference is detected + -Wmisleading-indentation # where indentation implies blocks where blocks do not exist + -Wduplicated-cond # where if-else chain has duplicated conditions + ) +endif() + +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0)) + list(APPEND _C4_PEDANTIC_FLAGS_GCC + -Wduplicated-branches # where if-else branches have duplicated code + ) +endif() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_pack_project) + # if this is the top-level project... pack it. + if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + c4_log("packing the project: ${ARGN}") + c4_set_default_pack_properties(${ARGN}) + include(CPack) + endif() +endfunction() + + +# [WIP] set convenient defaults for the properties used by CPack +function(c4_set_default_pack_properties) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + TYPE # one of LIBRARY, EXECUTABLE + _ARGSN # multi-value macro arguments + ) + set(pd "${PROJECT_SOURCE_DIR}") + _c4_handle_arg(TYPE EXECUTABLE) # default to EXECUTABLE + # + _c4_get_platform_tag(platform_tag) + if("${_TYPE}" STREQUAL "LIBRARY") + if(BUILD_SHARED_LIBS) + set(build_tag "-shared") + else() + set(build_tag "-static") + endif() + get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(multi_config) + # doesn't work because generators are not evaluated: set(build_tag "${build_tag}-$<CONFIG>") + # doesn't work because generators are not evaluated: set(build_tag "${build_tag}$<$<CONFIG:Debug>:-Debug>$<$<CONFIG:MinSizeRel>:-MinSizeRel>$<$<CONFIG:Release>:-Release>$<$<CONFIG:RelWithDebInfo>:-RelWithDebInfo>") + # see also https://stackoverflow.com/questions/44153730/how-to-change-cpack-package-file-name-based-on-configuration + if(CMAKE_BUILD_TYPE) # in the off-chance it was explicitly set + set(build_tag "${build_tag}-${CMAKE_BUILD_TYPE}") + endif() + else() + set(build_tag "${build_tag}-${CMAKE_BUILD_TYPE}") + endif() + elseif("${_TYPE}" STREQUAL "EXECUTABLE") + set(build_tag) + elseif() + c4_err("unknown TYPE: ${_TYPE}") + endif() + # + c4_setg(CPACK_VERBATIM_VARIABLES true) + c4_setg(CPACK_PACKAGE_VENDOR "${${_c4_prefix}_HOMEPAGE_URL}") + c4_setg(CPACK_PACKAGE_CONTACT "${${_c4_prefix}_AUTHOR}") + c4_setg(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${${_c4_prefix}_DESCRIPTION}") + if(EXISTS "${pd}/README.md") + c4_setg(CPACK_PACKAGE_DESCRIPTION_FILE "${pd}/README.md") + c4_setg(CPACK_PACKAGE_DESCRIPTION_README "${pd}/README.md") + c4_setg(CPACK_PACKAGE_DESCRIPTION_WELCOME "${pd}/README.md") + elseif(EXISTS "${pd}/README.txt") + c4_setg(CPACK_PACKAGE_DESCRIPTION_FILE "${pd}/README.txt") + c4_setg(CPACK_PACKAGE_DESCRIPTION_README "${pd}/README.txt") + c4_setg(CPACK_PACKAGE_DESCRIPTION_WELCOME "${pd}/README.txt") + endif() + if(EXISTS "${pd}/LICENSE.md") + c4_setg(CPACK_RESOURCE_FILE_LICENSE "${pd}/LICENSE.md") + elseif(EXISTS "${pd}/LICENSE.txt") + c4_setg(CPACK_RESOURCE_FILE_LICENSE "${pd}/LICENSE.txt") + endif() + c4_proj_get_version("${pd}" version_tag full major minor patch tweak) + c4_setg(CPACK_PACKAGE_VERSION "${full}") + c4_setg(CPACK_PACKAGE_VERSION_MAJOR "${major}") + c4_setg(CPACK_PACKAGE_VERSION_MINOR "${minor}") + c4_setg(CPACK_PACKAGE_VERSION_PATCH "${patch}") + c4_setg(CPACK_PACKAGE_VERSION_TWEAK "${tweak}") + c4_setg(CPACK_PACKAGE_INSTALL_DIRECTORY "${_c4_prefix}-${version_tag}") + c4_setg(CPACK_PACKAGE_FILE_NAME "${_c4_prefix}-${version_tag}-${platform_tag}${build_tag}") + if(WIN32 AND NOT UNIX) + # There is a bug in NSI that does not handle full UNIX paths properly. + # Make sure there is at least one set of four backlashes. + #c4_setg(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") + #c4_setg(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\MyExecutable.exe") + c4_setg(CPACK_NSIS_DISPLAY_NAME "${_c4_prefix} ${version_tag}") + c4_setg(CPACK_NSIS_HELP_LINK "${${_c4_prefix}_HOMEPAGE_URL}") + c4_setg(CPACK_NSIS_URL_INFO_ABOUT "${${_c4_prefix}_HOMEPAGE_URL}") + c4_setg(CPACK_NSIS_CONTACT "${${_c4_prefix}_AUTHOR}") + c4_setg(CPACK_NSIS_MODIFY_PATH ON) + else() + #c4_setg(CPACK_STRIP_FILES "bin/MyExecutable") + #c4_setg(CPACK_SOURCE_STRIP_FILES "") + c4_setg(CPACK_DEBIAN_PACKAGE_MAINTAINER "${${_c4_prefix}_AUTHOR}") + endif() + #c4_setg(CPACK_PACKAGE_EXECUTABLES "MyExecutable" "My Executable") +endfunction() + + +function(_c4_get_platform_tag tag_) + if(WIN32 AND NOT UNIX) + set(tag win) + elseif(APPLE) + set(tag apple) + elseif(UNIX) + set(tag unix) + else() + set(tag ${CMAKE_SYSTEM_NAME}) + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64 bits + set(tag ${tag}64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 bits + set(tag ${tag}32) + else() + c4_err("not implemented") + endif() + set(${tag_} ${tag} PARENT_SCOPE) +endfunction() + + +function(_c4_extract_version_tag tag_) + # git describe --tags <commit-id> for unannotated tags + # git describe --contains <commit> +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# set project-wide property +function(c4_set_proj_prop prop value) + c4_dbg("set ${prop}=${value}") + set(C4PROJ_${_c4_prefix}_${prop} ${value}) +endfunction() + +# set project-wide property +function(c4_get_proj_prop prop var) + c4_dbg("get ${prop}=${C4PROJ_${_c4_prefix}_${prop}}") + set(${var} ${C4PROJ_${_c4_prefix}_${prop}} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# set target-wide c4 property +function(c4_set_target_prop target prop value) + _c4_set_tgt_prop(${target} C4_TGT_${prop} "${value}") +endfunction() +function(c4_append_target_prop target prop value) + _c4_append_tgt_prop(${target} C4_TGT_${prop} "${value}") +endfunction() + +# get target-wide c4 property +function(c4_get_target_prop target prop var) + _c4_get_tgt_prop(val ${target} C4_TGT_${prop}) + set(${var} ${val} PARENT_SCOPE) +endfunction() + + +# get target-wide property +function(_c4_get_tgt_prop out tgt prop) + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "INTERFACE_LIBRARY") + get_property(val GLOBAL PROPERTY C4_TGT_${tgt}_${prop}) + else() + get_target_property(val ${tgt} ${prop}) + endif() + c4_dbg("target ${tgt}: get ${prop}=${val}") + set(${out} "${val}" PARENT_SCOPE) +endfunction() + +# set target-wide property +function(_c4_set_tgt_prop tgt prop propval) + c4_dbg("target ${tgt}: set ${prop}=${propval}") + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "INTERFACE_LIBRARY") + set_property(GLOBAL PROPERTY C4_TGT_${tgt}_${prop} "${propval}") + else() + set_target_properties(${tgt} PROPERTIES ${prop} "${propval}") + endif() +endfunction() +function(_c4_append_tgt_prop tgt prop propval) + c4_dbg("target ${tgt}: appending ${prop}=${propval}") + _c4_get_tgt_prop(curr ${tgt} ${prop}) + if(curr) + list(APPEND curr "${propval}") + else() + set(curr "${propval}") + endif() + _c4_set_tgt_prop(${tgt} ${prop} "${curr}") +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_set_var_tmp var value) + c4_dbg("tmp-setting ${var} to ${value} (was ${${value}})") + set(_c4_old_val_${var} ${${var}}) + set(${var} ${value} PARENT_SCOPE) +endfunction() + +function(c4_clean_var_tmp var) + c4_dbg("cleaning ${var} to ${_c4_old_val_${var}} (tmp was ${${var}})") + set(${var} ${_c4_old_val_${var}} PARENT_SCOPE) +endfunction() + +macro(c4_override opt val) + set(${opt} ${val} CACHE BOOL "" FORCE) +endmacro() + + +macro(c4_setg var val) + set(${var} ${val}) + set(${var} ${val} PARENT_SCOPE) +endmacro() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_proj_get_version dir tag_o full_o major_o minor_o patch_o tweak_o) + if("${dir}" STREQUAL "") + set(dir ${CMAKE_CURRENT_LIST_DIR}) + endif() + find_program(GIT git REQUIRED) + function(_c4pgv_get_cmd outputvar) + execute_process(COMMAND ${ARGN} + WORKING_DIRECTORY ${dir} + ERROR_VARIABLE error + ERROR_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE output + OUTPUT_STRIP_TRAILING_WHITESPACE) + c4_dbg("output of ${ARGN}: ${outputvar}=${output} [@${dir}]") + set(${outputvar} ${output} PARENT_SCOPE) + endfunction() + # do we have any tags yet? + _c4pgv_get_cmd(head_desc ${GIT} describe HEAD) + _c4pgv_get_cmd(branch ${GIT} rev-parse --abbrev-ref HEAD) + if(NOT head_desc) + c4_dbg("the repo does not have any tags yet") + _c4pgv_get_cmd(commit_hash ${GIT} rev-parse --short HEAD) + set(otag "${commit_hash}-${branch}") + else() + c4_dbg("there are tags!") + # is the current commit tagged? + _c4pgv_get_cmd(commit_hash_full ${GIT} rev-parse HEAD) + _c4pgv_get_cmd(commit_desc ${GIT} describe --exact-match ${commit_hash_full}) + if(commit_desc) + c4_dbg("current commit is tagged") + # is the tag a version tag? + _c4_parse_version_tag(${commit_desc} is_version major minor patch tweak more) + if(is_version) + c4_dbg("current commit's tag is a version tag") + # is the tag the current version tag? + if("${is_version}" VERSION_EQUAL "${${_c4_prefix}_VERSION_FULL}") + c4_dbg("this is the official version commit") + else() + c4_dbg("this is a different version") + endif() + set(otag "${commit_desc}") + else() + c4_dbg("this is a non-version tag") + set(otag "${commit_desc}-${branch}") + endif() + else(commit_desc) + # is the latest tag in the head_desc a version tag? + string(REGEX REPLACE "(.*)-[0-9]+-[0-9a-f]+" "\\1" latest_tag "${head_desc}") + c4_dbg("current commit is NOT tagged. latest tag=${latest_tag}") + _c4_parse_version_tag(${latest_tag} latest_tag_is_a_version major minor patch tweak more) + if(latest_tag_is_a_version) + c4_dbg("latest tag is a version. stick to the head description") + set(otag "${head_desc}-${branch}") + set(full "${latest_tag_is_a_version}") + else() + c4_dbg("latest tag is NOT a version. Use the current project version from cmake + the output of git describe") + set(otag "v${full}-${head_desc}-${branch}") + set(full "${${_c4_prefix}_VERSION_FULL}") + set(major "${${_c4_prefix}_VERSION_MAJOR}") + set(minor "${${_c4_prefix}_VERSION_MINOR}") + set(patch "${${_c4_prefix}_VERSION_PATCH}") + set(tweak "${${_c4_prefix}_VERSION_TWEAK}") + endif() + endif(commit_desc) + endif(NOT head_desc) + c4_log("cpack tag: ${otag}") + set(${tag_o} "${otag}" PARENT_SCOPE) + set(${full_o} "${full}" PARENT_SCOPE) + set(${major_o} "${major}" PARENT_SCOPE) + set(${minor_o} "${minor}" PARENT_SCOPE) + set(${patch_o} "${patch}" PARENT_SCOPE) + set(${tweak_o} "${tweak}" PARENT_SCOPE) + # also: dirty index? + # https://stackoverflow.com/questions/2657935/checking-for-a-dirty-index-or-untracked-files-with-git +endfunction() + + +function(_c4_parse_version_tag tag is_version major minor patch tweak more) + # does the tag match a four-part version? + string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)([\._][0-9]+)([\._][0-9]+)(.*)" match "${tag}") + function(_triml arg out) # trim the leading [\._] from the left + if("${arg}" STREQUAL "") + set(${out} "" PARENT_SCOPE) + else() + string(REGEX REPLACE "[\._](.*)" "\\1" ret "${arg}") + set("${out}" "${ret}" PARENT_SCOPE) + endif() + endfunction() + if(match) + set(${is_version} ${tag} PARENT_SCOPE) + _triml("${CMAKE_MATCH_1}" major_v) + _triml("${CMAKE_MATCH_2}" minor_v) + _triml("${CMAKE_MATCH_3}" patch_v) + _triml("${CMAKE_MATCH_4}" tweak_v) + _triml("${CMAKE_MATCH_5}" more_v) + else() + # does the tag match a three-part version? + string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)([\._][0-9]+)(.*)" match "${tag}") + if(match) + set(${is_version} ${tag} PARENT_SCOPE) + _triml("${CMAKE_MATCH_1}" major_v) + _triml("${CMAKE_MATCH_2}" minor_v) + _triml("${CMAKE_MATCH_3}" patch_v) + _triml("${CMAKE_MATCH_4}" more_v) + else() + # does the tag match a two-part version? + string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)(.*)" match "${tag}") + if(match) + set(${is_version} ${tag} PARENT_SCOPE) + _triml("${CMAKE_MATCH_1}" major_v) + _triml("${CMAKE_MATCH_2}" minor_v) + _triml("${CMAKE_MATCH_3}" more_v) + else() + # not a version! + set(${is_version} FALSE PARENT_SCOPE) + endif() + endif() + endif() + set(${major} "${major_v}" PARENT_SCOPE) + set(${minor} "${minor_v}" PARENT_SCOPE) + set(${patch} "${patch_v}" PARENT_SCOPE) + set(${tweak} "${tweak_v}" PARENT_SCOPE) + set(${more} "${more_v}" PARENT_SCOPE) +endfunction() + + +#function(testvtag) +# set(err FALSE) +# function(cmp value expected) +# if(NOT ("${${value}}" STREQUAL "${expected}")) +# c4_log("${tag}: error: expected ${value}=='${expected}': '${${value}}'=='${expected}'") +# set(err TRUE PARENT_SCOPE) +# else() +# c4_log("${tag}: ok: expected ${value}=='${expected}': '${${value}}'=='${expected}'") +# endif() +# endfunction() +# function(verify tag is_version_e major_e minor_e patch_e tweak_e more_e) +# _c4_parse_version_tag(${tag} is_version major minor patch tweak more) +# cmp(is_version ${is_version_e}) +# cmp(major "${major_e}") +# cmp(minor "${minor_e}") +# cmp(patch "${patch_e}") +# cmp(tweak "${tweak_e}") +# cmp(more "${more_e}") +# set(err ${err} PARENT_SCOPE) +# endfunction() +# verify(v12.34.567.89-rcfoo TRUE 12 34 567 89 -rcfoo) +# verify(v12_34_567_89-rcfoo TRUE 12 34 567 89 -rcfoo) +# verify(v12.34.567.89 TRUE 12 34 567 89 "") +# verify(v12_34_567_89 TRUE 12 34 567 89 "") +# verify(v12.34.567-rcfoo TRUE 12 34 567 "" -rcfoo) +# verify(v12_34_567-rcfoo TRUE 12 34 567 "" -rcfoo) +# verify(v12.34.567 TRUE 12 34 567 "" "") +# verify(v12_34_567 TRUE 12 34 567 "" "") +# verify(v12_34 TRUE 12 34 "" "" "") +# verify(v12.34 TRUE 12 34 "" "" "") +# if(err) +# c4_err("test failed") +# endif() +#endfunction() +#testvtag() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + + +macro(_c4_handle_cxx_standard_args) + # EXTENSIONS: + # enable compiler extensions eg, prefer gnu++11 to c++11 + if(EXTENSIONS IN_LIST ARGN) + set(_EXTENSIONS ON) + else() + c4_get_from_first_of(_EXTENSIONS + ENV + DEFAULT OFF + VARS ${_c4_uprefix}CXX_EXTENSIONS C4_CXX_EXTENSIONS CMAKE_CXX_EXTENSIONS) + endif() + # + # OPTIONAL + if(OPTIONAL IN_LIST ARGN) + set(_REQUIRED OFF) + else() + c4_get_from_first_of(_REQUIRED + ENV + DEFAULT ON + VARS ${_c4_uprefix}CXX_STANDARD_REQUIRED C4_CXX_STANDARD_REQUIRED CMAKE_CXX_STANDARD_REQUIRED) + endif() +endmacro() + + +# set the global cxx standard for the project. +# +# examples: +# c4_set_cxx(latest) # find the latest standard supported by the compiler, and use that +# c4_set_cxx(11) # required, no extensions (eg c++11) +# c4_set_cxx(14) # required, no extensions (eg c++14) +# c4_set_cxx(11 EXTENSIONS) # opt-in to extensions (eg, gnu++11) +# c4_set_cxx(14 EXTENSIONS) # opt-in to extensions (eg, gnu++14) +# c4_set_cxx(11 OPTIONAL) # not REQUIRED. no extensions +# c4_set_cxx(11 OPTIONAL EXTENSIONS) # not REQUIRED. with extensions. +macro(c4_set_cxx standard) + _c4_handle_cxx_standard_args(${ARGN}) + if(NOT DEFINED CMAKE_CXX_STANDARD) + c4_log("setting C++ standard: ${standard}") + c4_setg(CMAKE_CXX_STANDARD ${standard}) + endif() + if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) + c4_log("setting C++ standard required: ${_REQUIRED}") + c4_setg(CMAKE_CXX_STANDARD_REQUIRED ${_REQUIRED}) + endif() + if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) + c4_log("setting C++ standard extensions: ${_EXTENSIONS}") + c4_setg(CMAKE_CXX_EXTENSIONS ${_EXTENSIONS}) + endif() +endmacro() + + +# set the cxx standard for a target. +# +# examples: +# c4_target_set_cxx(target latest) # find the latest standard supported by the compiler, and use that +# c4_target_set_cxx(target 11) # required, no extensions (eg c++11) +# c4_target_set_cxx(target 14) # required, no extensions (eg c++14) +# c4_target_set_cxx(target 11 EXTENSIONS) # opt-in to extensions (eg, gnu++11) +# c4_target_set_cxx(target 14 EXTENSIONS) # opt-in to extensions (eg, gnu++14) +# c4_target_set_cxx(target 11 OPTIONAL) # not REQUIRED. no extensions +# c4_target_set_cxx(target 11 OPTIONAL EXTENSIONS) +function(c4_target_set_cxx target standard) + c4_dbg("setting C++ standard for target ${target}: ${standard}") + _c4_handle_cxx_standard_args(${ARGN}) + set_target_properties(${target} PROPERTIES + CXX_STANDARD ${standard} + CXX_STANDARD_REQUIRED ${_REQUIRED} + CXX_EXTENSIONS ${_EXTENSIONS}) + target_compile_features(${target} PUBLIC cxx_std_${standard}) +endfunction() + + +# set the cxx standard for a target based on the global project settings +function(c4_target_inherit_cxx_standard target) + c4_dbg("inheriting C++ standard for target ${target}: ${CMAKE_CXX_STANDARD}") + set_target_properties(${target} PROPERTIES + CXX_STANDARD "${CMAKE_CXX_STANDARD}" + CXX_STANDARD_REQUIRED "${CMAKE_CXX_STANDARD_REQUIRED}" + CXX_EXTENSIONS "${CMAKE_CXX_EXTENSIONS}") + target_compile_features(${target} PUBLIC cxx_std_${CMAKE_CXX_STANDARD}) +endfunction() + + +function(_c4_find_latest_supported_cxx_standard out) + if(NOT c4_latest_supported_cxx_standard) + include(CheckCXXCompilerFlag) + # make sure CMAKE_CXX_FLAGS is clean here + # see https://cmake.org/cmake/help/v3.16/module/CheckCXXCompilerFlag.html + # Note: since this is a function, we don't need to reset CMAKE_CXX_FLAGS + # back to its previous value + set(CMAKE_CXX_FLAGS) + set(standard 11) # default to C++11 if everything fails + foreach(s ${C4_CXX_STANDARDS}) + if(MSVC) + set(flag /std:c++${s}) + else() + # assume GNU-style compiler + set(flag -std=c++${s}) + endif() + c4_log("checking CXX standard: C++${s} flag=${flag}") + check_cxx_compiler_flag(${flag} has${s}) + if(has${s}) + c4_log("checking CXX standard: C++${s} is supported! flag=${flag}") + set(standard ${s}) + break() + else() + c4_log("checking CXX standard: C++${s}: no support for flag=${flag} no") + endif() + endforeach() + set(c4_latest_supported_cxx_standard ${standard} CACHE INTERNAL "") + endif() + set(${out} ${c4_latest_supported_cxx_standard} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# examples: +# +# # require subproject c4core, as a subdirectory. c4core will be used +# # as a separate library +# c4_require_subproject(c4core SUBDIRECTORY ${C4OPT_EXT_DIR}/c4core) +# +# # require subproject c4core, as a remote proj +# c4_require_subproject(c4core REMOTE +# GIT_REPOSITORY https://github.com/biojppm/c4core +# GIT_TAG master +# ) +function(c4_require_subproject subproj) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + INCORPORATE + EXCLUDE_FROM_ALL + _ARGS1 + SUBDIRECTORY # the subproject is located in the given directory name and + # will be added via add_subdirectory() + _ARGSN + REMOTE # the subproject is located in a remote repo/url + # and will be added via c4_import_remote_proj(), + # forwarding all the arguments in here. + OVERRIDE # a list of variable name+value pairs + # these variables will be set with c4_override() + # before calling add_subdirectory() + SET_FOLDER_TARGETS # Set the folder of the given targets using + # c4_set_folder_remote_project_targets(). + # The first expected argument is the folder, + # and the remaining arguments are the targets + # which we want to set the folder. + _DEPRECATE + INTERFACE + ) + list(APPEND _${_c4_uprefix}_deps ${subproj}) + c4_setg(_${_c4_uprefix}_deps ${_${_c4_uprefix}_deps}) + c4_dbg("-----------------------------------------------") + c4_dbg("requires subproject ${subproj}!") + if(_INCORPORATE) + c4_dbg("requires subproject ${subproj} in INCORPORATE mode!") + c4_dbg_var(${_c4_root_uproj}_STANDALONE) + if(${_c4_root_uproj}_STANDALONE) + c4_dbg("${_c4_root_uproj} is STANDALONE: honoring INCORPORATE mode...") + else() + c4_dbg("${_c4_root_uproj} is not STANDALONE: ignoring INCORPORATE mode...") + set(_INCORPORATE OFF) + endif() + endif() + # + _c4_get_subproject_property(${subproj} AVAILABLE _available) + if(_available) + c4_dbg("required subproject ${subproj} was already imported:") + c4_dbg_subproject(${subproj}) + # TODO check version compatibility + else() #elseif(NOT _${subproj}_available) + c4_dbg("required subproject ${subproj} is unknown. Importing...") + if(_EXCLUDE_FROM_ALL) + set(excl EXCLUDE_FROM_ALL) + endif() + # forward c4 compile flags + string(TOUPPER ${subproj} usubproj) + c4_setg(${usubproj}_CXX_FLAGS_FWD "${${_c4_uprefix}CXX_FLAGS}") + c4_setg(${usubproj}_CXX_FLAGS_OPT_FWD "${${_c4_uprefix}CXX_FLAGS_OPT}") + c4_setg(${usubproj}_CXX_LINKER_FLAGS_FWD "${${_c4_uprefix}CXX_LINKER_FLAGS}") + # root dir + set(_r ${CMAKE_CURRENT_BINARY_DIR}/subprojects/${subproj}) + if(_REMOTE) + c4_log("importing subproject ${subproj} (REMOTE)... ${_REMOTE}") + _c4_mark_subproject_imported(${subproj} ${_r}/src ${_r}/build ${_INCORPORATE}) + c4_import_remote_proj(${subproj} ${_r} REMOTE ${_REMOTE} OVERRIDE ${_OVERRIDE} ${excl}) + _c4_get_subproject_property(${subproj} SRC_DIR _srcdir) + c4_dbg("finished importing subproject ${subproj} (REMOTE, SRC_DIR=${_srcdir}).") + elseif(_SUBDIRECTORY) + c4_log("importing subproject ${subproj} (SUBDIRECTORY)... ${_SUBDIRECTORY}") + _c4_mark_subproject_imported(${subproj} ${_SUBDIRECTORY} ${_r}/build ${_INCORPORATE}) + c4_add_subproj(${subproj} ${_SUBDIRECTORY} ${_r}/build OVERRIDE ${_OVERRIDE} ${excl}) + set(_srcdir ${_SUBDIRECTORY}) + c4_dbg("finished importing subproject ${subproj} (SUBDIRECTORY=${_SUBDIRECTORY}).") + else() + c4_err("subproject type must be either REMOTE or SUBDIRECTORY") + endif() + endif() + # + if(_SET_FOLDER_TARGETS) + c4_set_folder_remote_project_targets(${_SET_FOLDER_TARGETS}) + endif() +endfunction(c4_require_subproject) + + +function(c4_add_subproj proj dir bindir) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + EXCLUDE_FROM_ALL # forward to add_subdirectory() + _ARGS1 + _ARGSN + OVERRIDE # a list of variable name+value pairs + # these variables will be set with c4_override() + # before calling add_subdirectory() + ) + # push the subproj into the current path + set(prev_subproject ${_c4_curr_subproject}) + set(prev_path ${_c4_curr_path}) + set(_c4_curr_subproject ${proj}) + string(REGEX MATCH ".*/${proj}\$" pos "${_c4_curr_path}") + if(pos EQUAL -1) + string(REGEX MATCH "^${proj}\$" pos "${_c4_curr_path}") + if(pos EQUAL -1) + set(_c4_curr_path ${_c4_curr_path}/${proj}) + endif() + endif() + # + while(_OVERRIDE) + list(POP_FRONT _OVERRIDE varname) + list(POP_FRONT _OVERRIDE varvalue) + c4_override(${varname} ${varvalue}) + endwhile() + # + if(_EXCLUDE_FROM_ALL) + set(excl EXCLUDE_FROM_ALL) + endif() + # + c4_dbg("adding subproj: ${prev_subproject}->${_c4_curr_subproject}. path=${_c4_curr_path}") + add_subdirectory(${dir} ${bindir} ${excl}) + # pop the subproj from the current path + set(_c4_curr_subproject ${prev_subproject}) + set(_c4_curr_path ${prev_path}) +endfunction() + + +function(_c4_mark_subproject_imported subproject_name subproject_src_dir subproject_bin_dir incorporate) + c4_dbg("marking subproject imported: ${subproject_name} (imported by ${_c4_prefix}). src=${subproject_src_dir}") + _c4_append_subproject_property(${_c4_prefix} DEPENDENCIES ${subproject_name}) + _c4_get_folder(folder ${_c4_prefix} ${subproject_name}) + _c4_set_subproject_property(${subproject_name} AVAILABLE ON) + _c4_set_subproject_property(${subproject_name} IMPORTER "${_c4_prefix}") + _c4_set_subproject_property(${subproject_name} SRC_DIR "${subproject_src_dir}") + _c4_set_subproject_property(${subproject_name} BIN_DIR "${subproject_bin_dir}") + _c4_set_subproject_property(${subproject_name} FOLDER "${folder}") + _c4_set_subproject_property(${subproject_name} INCORPORATE "${incorporate}") +endfunction() + + +function(_c4_get_subproject_property subproject property var) + get_property(v GLOBAL PROPERTY _c4_subproject-${subproject}-${property}) + set(${var} "${v}" PARENT_SCOPE) +endfunction() + + +function(_c4_set_subproject_property subproject property value) + c4_dbg("setting subproj prop: ${subproject}: ${property}=${value}") + set_property(GLOBAL PROPERTY _c4_subproject-${subproject}-${property} "${value}") +endfunction() +function(_c4_append_subproject_property subproject property value) + _c4_get_subproject_property(${subproject} ${property} cval) + if(cval) + list(APPEND cval ${value}) + else() + set(cval ${value}) + endif() + _c4_set_subproject_property(${subproject} ${property} ${cval}) +endfunction() + + +function(_c4_is_incorporated subproj out) + if("${subproj}" STREQUAL "${_c4_root_proj}") + c4_dbg("${subproj} is incorporated? root proj, no") + set(${out} OFF PARENT_SCOPE) + else() + _c4_get_subproject_property(${subproj} INCORPORATE inc) + c4_dbg("${subproj} is incorporated? not root proj, incorporate=${inc}") + set(${out} ${inc} PARENT_SCOPE) + endif() +endfunction() + + +function(c4_dbg_subproject subproject) + set(props AVAILABLE IMPORTER SRC_DIR BIN_DIR DEPENDENCIES FOLDER INCORPORATE) + foreach(p ${props}) + _c4_get_subproject_property(${subproject} ${p} pv) + c4_dbg("${subproject}: ${p}=${pv}") + endforeach() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# +# +function(c4_import_remote_proj name dir) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + EXCLUDE_FROM_ALL + _ARGS1 + SUBDIR # path to the subdirectory where the CMakeLists file is to be found. + _ARGSN + OVERRIDE # a list of variable name+value pairs + # these variables will be set with c4_override() + # before calling add_subdirectory() + REMOTE # to specify url, repo, tag, or branch, + # pass the needed arguments after dir. + # These arguments will be forwarded to ExternalProject_Add() + SET_FOLDER_TARGETS # Set the folder of the given targets using + # c4_set_folder_remote_project_targets(). + # The first expected argument is the folder, + # and the remaining arguments are the targets + # which we want to set the folder. + ) + set(srcdir_in_out "${dir}") + c4_download_remote_proj(${name} srcdir_in_out ${_REMOTE}) + if(_SUBDIR) + set(srcdir_in_out "${srcdir_in_out}/${_SUBDIR}") + endif() + _c4_set_subproject_property(${name} SRC_DIR "${srcdir_in_out}") + if(_EXCLUDE_FROM_ALL) + set(excl EXCLUDE_FROM_ALL) + endif() + c4_add_subproj(${name} "${srcdir_in_out}" "${dir}/build" OVERRIDE ${_OVERRIDE} ${excl}) + # + if(_SET_FOLDER_TARGETS) + c4_set_folder_remote_project_targets(${_SET_FOLDER_TARGETS}) + endif() +endfunction() + + +# download remote projects while running cmake +# to specify url, repo, tag, or branch, +# pass the needed arguments after dir. +# These arguments will be forwarded to ExternalProject_Add() +function(c4_download_remote_proj name candidate_dir) + # https://crascit.com/2015/07/25/cmake-gtest/ + # (via https://stackoverflow.com/questions/15175318/cmake-how-to-build-external-projects-and-include-their-targets) + set(dir ${${candidate_dir}}) + if("${dir}" STREQUAL "") + set(dir "${CMAKE_BINARY_DIR}/extern/${name}") + endif() + set(cvar _${_c4_uprefix}_DOWNLOAD_${name}_LOCATION) + set(cval ${${cvar}}) + # + # was it already downloaded in this project? + if(NOT ("${cval}" STREQUAL "")) + if(EXISTS "${cval}") + c4_log("${name} was previously imported into this project - found at \"${cval}\"!") + set(${candidate_dir} "${cval}" PARENT_SCOPE) + return() + else() + c4_log("${name} was previously imported into this project - but was NOT found at \"${cval}\"!") + endif() + endif() + # + # try to find an existing version (downloaded by some other project) + set(out "${dir}") + _c4_find_cached_proj(${name} out) + if(NOT ("${out}" STREQUAL "${dir}")) + c4_log("using ${name} from \"${out}\"...") + set(${cvar} "${out}" CACHE INTERNAL "") + set(${candidate_dir} "${out}" PARENT_SCOPE) + return() + endif() + # + # no version was found; need to download. + c4_log("downloading ${name}: not in cache...") + # check for a global place to download into + set(srcdir) + _c4_get_cached_srcdir_global_extern(${name} srcdir) + if("${srcdir}" STREQUAL "") + # none found; default to the given dir + set(srcdir "${dir}/src") + endif() + # + # do it + #if((EXISTS ${dir}/dl) AND (EXISTS ${dir}/dl/CMakeLists.txt)) + # return() + #endif() + c4_log("downloading remote project: ${name} -> \"${srcdir}\" (dir=${dir})...") + # + file(WRITE ${dir}/dl/CMakeLists.txt " +cmake_minimum_required(VERSION 2.8.2) +project(${_c4_lcprefix}-download-${name} NONE) + +# this project only downloads ${name} +# (ie, no configure, build or install step) +include(ExternalProject) + +ExternalProject_Add(${name}-dl + ${ARGN} + SOURCE_DIR \"${srcdir}\" + BINARY_DIR \"${dir}/build\" + CONFIGURE_COMMAND \"\" + BUILD_COMMAND \"\" + INSTALL_COMMAND \"\" + TEST_COMMAND \"\" +) +") + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY ${dir}/dl) + execute_process(COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${dir}/dl) + # + set(${candidate_dir} "${srcdir}" PARENT_SCOPE) + set(_${_c4_uprefix}_DOWNLOAD_${name}_LOCATION "${srcdir}" CACHE INTERNAL "") +endfunction() + + +# checks if the project was already downloaded. If it was, then dir_in_out is +# changed to the directory where the project was found at. +function(_c4_find_cached_proj name dir_in_out) + c4_log("downloading ${name}: searching cached project...") + # + # 1. search in the per-import variable, eg RYML_CACHE_DOWNLOAD_GTEST + string(TOUPPER ${name} uname) + set(var ${_c4_uprefix}CACHE_DOWNLOAD_${uname}) + set(val "${${var}}") + if(NOT ("${val}" STREQUAL "")) + c4_log("downloading ${name}: searching in ${var}=${val}") + if(EXISTS "${val}") + c4_log("downloading ${name}: picked ${sav} instead of ${${dir_in_out}}") + set(${dir_in_out} ${sav} PARENT_SCOPE) + endif() + endif() + # + # 2. search in the global directory (if there is one) + _c4_get_cached_srcdir_global_extern(${name} sav) + if(NOT ("${sav}" STREQUAL "")) + c4_log("downloading ${name}: searching in C4_EXTERN_DIR: ${sav}") + if(EXISTS "${sav}") + c4_log("downloading ${name}: picked ${sav} instead of ${${dir_in_out}}") + set(${dir_in_out} ${sav} PARENT_SCOPE) + endif() + endif() +endfunction() + + +function(_c4_get_cached_srcdir_global_extern name out) + set(${out} "" PARENT_SCOPE) + if("${C4_EXTERN_DIR}" STREQUAL "") + set(C4_EXTERN_DIR "$ENV{C4_EXTERN_DIR}") + endif() + if(NOT ("${C4_EXTERN_DIR}" STREQUAL "")) + set(${out} "${C4_EXTERN_DIR}/${name}" PARENT_SCOPE) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(_c4_get_folder output importer_subproject subproject_name) + _c4_get_subproject_property(${importer_subproject} FOLDER importer_folder) + if("${importer_folder}" STREQUAL "") + set(folder ${importer_subproject}) + else() + set(folder "${importer_folder}/deps/${subproject_name}") + endif() + set(${output} ${folder} PARENT_SCOPE) +endfunction() + + +function(_c4_set_target_folder target subfolder) + string(FIND "${subfolder}" "/" pos) + if(pos EQUAL 0) + if("${_c4_curr_path}" STREQUAL "") + string(SUBSTRING "${subfolder}" 1 -1 sf) + set_target_properties(${target} PROPERTIES + FOLDER "${sf}") + else() + set_target_properties(${target} PROPERTIES + FOLDER "${subfolder}") + endif() + elseif("${subfolder}" STREQUAL "") + set_target_properties(${target} PROPERTIES + FOLDER "${_c4_curr_path}") + else() + if("${_c4_curr_path}" STREQUAL "") + set_target_properties(${target} PROPERTIES + FOLDER "${subfolder}") + else() + set_target_properties(${target} PROPERTIES + FOLDER "${_c4_curr_path}/${subfolder}") + endif() + endif() +endfunction() + + +function(c4_set_folder_remote_project_targets subfolder) + foreach(target ${ARGN}) + if(TARGET ${target}) + _c4_set_target_folder(${target} "${subfolder}") + endif() + endforeach() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# a convenience alias to c4_add_target() +function(c4_add_executable target) + c4_add_target(${target} EXECUTABLE ${ARGN}) +endfunction(c4_add_executable) + + +# a convenience alias to c4_add_target() +function(c4_add_library target) + c4_add_target(${target} LIBRARY ${ARGN}) +endfunction(c4_add_library) + + +# example: c4_add_target(ryml LIBRARY SOURCES ${SRC}) +function(c4_add_target target) + c4_dbg("adding target: ${target}: ${ARGN}") + set(opt0arg + LIBRARY # the target is a library + EXECUTABLE # the target is an executable + WIN32 # the executable is WIN32 + SANITIZE # deprecated + ) + set(opt1arg + LIBRARY_TYPE # override global setting for C4_LIBRARY_TYPE + SHARED_MACRO # the name of the macro to turn on export/import symbols + # for compiling the library as a windows DLL. + # defaults to ${_c4_uprefix}SHARED. + SHARED_EXPORTS # the name of the macro to turn on export of symbols + # for compiling the library as a windows DLL. + # defaults to ${_c4_uprefix}EXPORTS. + SOURCE_ROOT # the directory where relative source paths + # should be resolved. when empty, + # use CMAKE_CURRENT_SOURCE_DIR + FOLDER # IDE folder to group the target in + SANITIZERS # outputs the list of sanitize targets in this var + SOURCE_TRANSFORM # WIP + ) + set(optnarg + INCORPORATE # incorporate these libraries into this target, + # subject to ${_c4_uprefix}STANDALONE and C4_STANDALONE + SOURCES PUBLIC_SOURCES INTERFACE_SOURCES PRIVATE_SOURCES + HEADERS PUBLIC_HEADERS INTERFACE_HEADERS PRIVATE_HEADERS + INC_DIRS PUBLIC_INC_DIRS INTERFACE_INC_DIRS PRIVATE_INC_DIRS + LIBS PUBLIC_LIBS INTERFACE_LIBS PRIVATE_LIBS + DEFS PUBLIC_DEFS INTERFACE_DEFS PRIVATE_DEFS # defines + CFLAGS PUBLIC_CFLAGS INTERFACE_CFLAGS PRIVATE_CFLAGS # compiler flags. TODO: linker flags + DLLS # DLLs required by this target + MORE_ARGS + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + # + if(_SANITIZE) + c4_err("SANITIZE is deprecated") + endif() + + if(${_LIBRARY}) + set(_what LIBRARY) + elseif(${_EXECUTABLE}) + set(_what EXECUTABLE) + else() + c4_err("must be either LIBRARY or EXECUTABLE") + endif() + + _c4_handle_arg(SHARED_MACRO ${_c4_uprefix}MACRO) + _c4_handle_arg(SHARED_EXPORTS ${_c4_uprefix}EXPORTS) + _c4_handle_arg_or_fallback(SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}") + function(_c4_transform_to_full_path list all) + set(l) + foreach(f ${${list}}) + if(NOT IS_ABSOLUTE "${f}") + set(f "${_SOURCE_ROOT}/${f}") + endif() + list(APPEND l "${f}") + endforeach() + set(${list} "${l}" PARENT_SCOPE) + set(cp ${${all}}) + list(APPEND cp ${l}) + set(${all} ${cp} PARENT_SCOPE) + endfunction() + _c4_transform_to_full_path( _SOURCES allsrc) + _c4_transform_to_full_path( _HEADERS allsrc) + _c4_transform_to_full_path( _PUBLIC_SOURCES allsrc) + _c4_transform_to_full_path(_INTERFACE_SOURCES allsrc) + _c4_transform_to_full_path( _PRIVATE_SOURCES allsrc) + _c4_transform_to_full_path( _PUBLIC_HEADERS allsrc) + _c4_transform_to_full_path(_INTERFACE_HEADERS allsrc) + _c4_transform_to_full_path( _PRIVATE_HEADERS allsrc) + create_source_group("" "${_SOURCE_ROOT}" "${allsrc}") + # is the target name prefixed with the project prefix? + string(REGEX MATCH "${_c4_prefix}::.*" target_is_prefixed "${target}") + if(NOT ${_c4_uprefix}SANITIZE_ONLY) + if(${_EXECUTABLE}) + c4_dbg("adding executable: ${target}") + if(WIN32) + if(${_WIN32}) + list(APPEND _MORE_ARGS WIN32) + endif() + endif() + add_executable(${target} ${_MORE_ARGS}) + if(NOT target_is_prefixed) + add_executable(${_c4_prefix}::${target} ALIAS ${target}) + endif() + set(src_mode PRIVATE) + set(tgt_type PUBLIC) + set(compiled_target ON) + set_target_properties(${target} PROPERTIES VERSION ${${_c4_prefix}_VERSION}) + elseif(${_LIBRARY}) + c4_dbg("adding library: ${target}") + set(_blt ${C4_LIBRARY_TYPE}) # build library type + if(NOT "${_LIBRARY_TYPE}" STREQUAL "") + set(_blt ${_LIBRARY_TYPE}) + endif() + if("${_blt}" STREQUAL "") + endif() + # + if("${_blt}" STREQUAL "INTERFACE") + c4_dbg("adding interface library ${target}") + add_library(${target} INTERFACE) + set(src_mode INTERFACE) + set(tgt_type INTERFACE) + set(compiled_target OFF) + else() + if("${_blt}" STREQUAL "") + # obey BUILD_SHARED_LIBS (ie, either static or shared library) + c4_dbg("adding library ${target} (defer to BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}) --- ${_MORE_ARGS}") + add_library(${target} ${_MORE_ARGS}) + if(BUILD_SHARED_LIBS) + set(_blt SHARED) + else() + set(_blt STATIC) + endif() + else() + c4_dbg("adding library ${target} with type ${_blt}") + add_library(${target} ${_blt} ${_MORE_ARGS}) + endif() + # libraries + set(src_mode PRIVATE) + set(tgt_type PUBLIC) + set(compiled_target ON) + set_target_properties(${target} PROPERTIES VERSION ${${_c4_prefix}_VERSION}) + if("${_blt}" STREQUAL SHARED) + set_target_properties(${target} PROPERTIES SOVERSION ${${_c4_prefix}_VERSION}) + endif() + # exports for shared libraries + if(WIN32) + if("${_blt}" STREQUAL SHARED) + set_target_properties(${target} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + target_compile_definitions(${target} PUBLIC ${_SHARED_MACRO}) + target_compile_definitions(${target} PRIVATE $<BUILD_INTERFACE:${_SHARED_EXPORTS}>) + # save the name of the macro for later use when(if) incorporating this library + c4_set_target_prop(${target} SHARED_EXPORTS ${_SHARED_EXPORTS}) + endif() # shared lib + endif() # win32 + endif() # interface or lib + if(NOT target_is_prefixed) + add_library(${_c4_prefix}::${target} ALIAS ${target}) + endif() + endif(${_EXECUTABLE}) + + if(src_mode STREQUAL "PUBLIC") + c4_add_target_sources(${target} + PUBLIC "${_SOURCES};${_HEADERS};${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}" + INTERFACE "${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}" + PRIVATE "${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}") + elseif(src_mode STREQUAL "INTERFACE") + c4_add_target_sources(${target} + PUBLIC "${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}" + INTERFACE "${_SOURCES};${_HEADERS};${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}" + PRIVATE "${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}") + elseif(src_mode STREQUAL "PRIVATE") + c4_add_target_sources(${target} + PUBLIC "${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}" + INTERFACE "${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}" + PRIVATE "${_SOURCES};${_HEADERS};${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}") + elseif() + c4_err("${target}: adding sources: invalid source mode") + endif() + _c4_set_tgt_prop(${target} C4_SOURCE_ROOT "${_SOURCE_ROOT}") + + if(_INC_DIRS) + c4_dbg("${target}: adding include dirs ${_INC_DIRS} [from target: ${tgt_type}]") + target_include_directories(${target} "${tgt_type}" ${_INC_DIRS}) + endif() + if(_PUBLIC_INC_DIRS) + c4_dbg("${target}: adding PUBLIC include dirs ${_PUBLIC_INC_DIRS}") + target_include_directories(${target} PUBLIC ${_PUBLIC_INC_DIRS}) + endif() + if(_INTERFACE_INC_DIRS) + c4_dbg("${target}: adding INTERFACE include dirs ${_INTERFACE_INC_DIRS}") + target_include_directories(${target} INTERFACE ${_INTERFACE_INC_DIRS}) + endif() + if(_PRIVATE_INC_DIRS) + c4_dbg("${target}: adding PRIVATE include dirs ${_PRIVATE_INC_DIRS}") + target_include_directories(${target} PRIVATE ${_PRIVATE_INC_DIRS}) + endif() + + if(_LIBS) + _c4_link_with_libs(${target} "${tgt_type}" "${_LIBS}" "${_INCORPORATE}") + endif() + if(_PUBLIC_LIBS) + _c4_link_with_libs(${target} PUBLIC "${_PUBLIC_LIBS}" "${_INCORPORATE}") + endif() + if(_INTERFACE_LIBS) + _c4_link_with_libs(${target} INTERFACE "${_INTERFACE_LIBS}" "${_INCORPORATE}") + endif() + if(_PRIVATE_LIBS) + _c4_link_with_libs(${target} PRIVATE "${_PRIVATE_LIBS}" "${_INCORPORATE}") + endif() + + if(compiled_target) + if(_FOLDER) + _c4_set_target_folder(${target} "${_FOLDER}") + else() + _c4_set_target_folder(${target} "") + endif() + # cxx standard + c4_target_inherit_cxx_standard(${target}) + # compile flags + set(_more_flags + ${${_c4_uprefix}CXX_FLAGS} + ${${_c4_uprefix}C_FLAGS} + ${${_c4_uprefix}CXX_FLAGS_OPT}) + if(_more_flags) + get_target_property(_flags ${target} COMPILE_OPTIONS) + if(_flags) + set(_more_flags ${_flags};${_more_flags}) + endif() + c4_dbg("${target}: COMPILE_FLAGS=${_more_flags}") + target_compile_options(${target} PRIVATE "${_more_flags}") + endif() + # linker flags + set(_link_flags ${${_c4_uprefix}CXX_LINKER_FLAGS}) + if(_link_flags) + get_target_property(_flags ${target} LINK_OPTIONS) + if(_flags) + set(_link_flags ${_flags};${_more_flags}) + endif() + c4_dbg("${target}: LINKER_FLAGS=${_link_flags}") + target_link_options(${target} PUBLIC "${_link_flags}") + endif() + # static analysis + if(${_c4_uprefix}LINT) + c4_static_analysis_target(${target} "${_FOLDER}" lint_targets) + endif() + endif(compiled_target) + + if(_DEFS) + target_compile_definitions(${target} "${tgt_type}" ${_DEFS}) + endif() + if(_PUBLIC_DEFS) + target_compile_definitions(${target} PUBLIC ${_PUBLIC_DEFS}) + endif() + if(_INTERFACE_DEFS) + target_compile_definitions(${target} INTERFACE ${_INTERFACE_DEFS}) + endif() + if(_PRIVATE_DEFS) + target_compile_definitions(${target} PRIVATE ${_PRIVATE_DEFS}) + endif() + + if(_CFLAGS) + target_compile_options(${target} "${tgt_type}" ${_CFLAGS}) + endif() + if(_PUBLIC_CFLAGS) + target_compile_options(${target} PUBLIC ${_PUBLIC_CFLAGS}) + endif() + if(_INTERFACE_CFLAGS) + target_compile_options(${target} INTERFACE ${_INTERFACE_CFLAGS}) + endif() + if(_PRIVATE_CFLAGS) + target_compile_options(${target} PRIVATE ${_PRIVATE_CFLAGS}) + endif() + + if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND + (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND + (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)) + c4_dbg("${target}: adding compat include path") + target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${_c4_project_dir}/compat>) + endif() + + endif(NOT ${_c4_uprefix}SANITIZE_ONLY) + + if(compiled_target) + if(${_c4_uprefix}SANITIZE) + c4_sanitize_target(${target} + ${_what} # LIBRARY or EXECUTABLE + SOURCES ${allsrc} + INC_DIRS ${_INC_DIRS} ${_PUBLIC_INC_DIRS} ${_INTERFACE_INC_DIRS} ${_PRIVATE_INC_DIRS} + LIBS ${_LIBS} ${_PUBLIC_LIBS} ${_INTERFACE_LIBS} ${_PRIVATE_LIBS} + DEFS ${_DEFS} ${_PUBLIC_DEFS} ${_INTERFACE_DEFS} ${_PRIVATE_DEFS} + CFLAGS ${_CFLAGS} ${_PUBLIC_CFLAGS} ${_INTERFACE_CFLAGS} ${_PRIVATE_CFLAGS} + OUTPUT_TARGET_NAMES san_targets + FOLDER "${_FOLDER}" + ) + endif() + + if(NOT ${_c4_uprefix}SANITIZE_ONLY) + list(INSERT san_targets 0 ${target}) + endif() + + if(_SANITIZERS) + set(${_SANITIZERS} ${san_targets} PARENT_SCOPE) + endif() + + _c4_set_tgt_prop(${target} C4_SAN_TARGETS "${san_targets}") + else() + _c4_set_tgt_prop(${target} C4_SAN_TARGETS "${target}") + endif() + + # gather dlls so that they can be automatically copied to the target directory + if(_DLLS) + c4_append_transitive_property(${target} _C4_DLLS "${_DLLS}") + endif() + + if(${_EXECUTABLE}) + if(WIN32) + c4_get_transitive_property(${target} _C4_DLLS transitive_dlls) + list(REMOVE_DUPLICATES transitive_dlls) + foreach(_dll ${transitive_dlls}) + if(_dll) + c4_dbg("enable copy of dll to target file dir: ${_dll} ---> $<TARGET_FILE_DIR:${target}>") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_dll}" "$<TARGET_FILE_DIR:${target}>" + ) + else() + message(WARNING "dll required by ${_c4_prefix}/${target} was not found, so cannot copy: ${_dll}") + endif() + endforeach() + endif() + endif() +endfunction() # add_target + + +function(_c4_link_with_libs target link_type libs incorporate) + foreach(lib ${libs}) + # add targets that are DLLs + if(WIN32) + if(TARGET ${lib}) + get_target_property(lib_type ${lib} TYPE) + if(lib_type STREQUAL SHARED_LIBRARY) + c4_append_transitive_property(${target} _C4_DLLS "$<TARGET_FILE:${lib}>") + endif() + endif() + endif() + _c4_lib_is_incorporated(${lib} isinc) + if(isinc OR (incorporate AND ${_c4_uprefix}STANDALONE)) + c4_log("-----> target ${target} ${link_type} incorporating lib ${lib}") + _c4_incorporate_lib(${target} ${link_type} ${lib}) + else() + c4_dbg("${target} ${link_type} linking with lib ${lib}") + target_link_libraries(${target} ${link_type} ${lib}) + endif() + endforeach() +endfunction() + + +function(_c4_lib_is_incorporated lib ret) + c4_dbg("${lib}: is incorporated?") + if(NOT TARGET ${lib}) + c4_dbg("${lib}: no, not a target") + set(${ret} OFF PARENT_SCOPE) + else() + c4_get_target_prop(${lib} INCORPORATING_TARGETS inc) + if(inc) + c4_dbg("${lib}: is incorporated!") + set(${ret} ON PARENT_SCOPE) + else() + c4_dbg("${lib}: is not incorporated!") + set(${ret} OFF PARENT_SCOPE) + endif() + endif() +endfunction() + + +function(_c4_incorporate_lib target link_type lib) + c4_dbg("target ${target}: incorporating lib ${lib} [${link_type}]") + _c4_get_tgt_prop(srcroot ${lib} C4_SOURCE_ROOT) + # + c4_append_target_prop(${lib} INCORPORATING_TARGETS ${target}) + c4_append_target_prop(${target} INCORPORATED_TARGETS ${lib}) + # + _c4_get_tgt_prop(lib_src ${lib} SOURCES) + if(lib_src) + create_source_group("${lib}" "${srcroot}" "${lib_src}") + c4_add_target_sources(${target} INCORPORATED_FROM ${lib} PRIVATE ${lib_src}) + endif() + # + _c4_get_tgt_prop(lib_isrc ${lib} INTERFACE_SOURCES) + if(lib_isrc) + create_source_group("${lib}" "${srcroot}" "${lib_isrc}") + c4_add_target_sources(${target} INCORPORATED_FROM ${lib} INTERFACE ${lib_isrc}) + endif() + # + _c4_get_tgt_prop(lib_psrc ${lib} PRIVATE_SOURCES) + if(lib_psrc) + create_source_group("${lib}" "${srcroot}" "${lib_psrc}") + c4_add_target_sources(${target} INCORPORATED_FROM ${lib} INTERFACE ${lib_psrc}) + endif() + # + # + _c4_get_tgt_prop(lib_incs ${lib} INCLUDE_DIRECTORIES) + if(lib_incs) + target_include_directories(${target} PUBLIC ${lib_incs}) + endif() + # + _c4_get_tgt_prop(lib_iincs ${lib} INTERFACE_INCLUDE_DIRECTORIES) + if(lib_iincs) + target_include_directories(${target} INTERFACE ${lib_iincs}) + endif() + # + # + _c4_get_tgt_prop(lib_lib ${lib} LINK_LIBRARIES) + if(lib_lib) + target_link_libraries(${target} PUBLIC ${lib_lib}) + endif() + _c4_get_tgt_prop(lib_ilib ${lib} INTERFACE_LIBRARY) + if(lib_ilib) + target_link_libraries(${target} INTERFACE ${lib_ilib}) + endif() + # + # + c4_get_target_prop(${lib} SHARED_EXPORTS lib_exports) + if(lib_exports) + target_compile_definitions(${target} PRIVATE $<BUILD_INTERFACE:${lib_exports}>) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# +# +function(c4_add_target_sources target) + # https://steveire.wordpress.com/2016/08/09/opt-in-header-only-libraries-with-cmake/ + _c4_handle_args(_ARGS ${ARGN} + _ARGS1 # one-value macro arguments + INCORPORATED_FROM + TRANSFORM # Transform types: + # * NONE - do not transform the sources + # * UNITY + # * UNITY_HDR + # * SINGLE_HDR + # * SINGLE_UNIT + _ARGSN # multi-value macro arguments + PUBLIC + INTERFACE + PRIVATE + ) + if(("${_TRANSFORM}" STREQUAL "GLOBAL") OR ("${_TRANSFORM}" STREQUAL "")) + set(_TRANSFORM ${C4_SOURCE_TRANSFORM}) + endif() + if("${_TRANSFORM}" STREQUAL "") + set(_TRANSFORM NONE) + endif() + # + # is this target an interface? + set(_is_iface FALSE) + _c4_get_tgt_prop(target_type ${target} TYPE) + if("${target_type}" STREQUAL "INTERFACE_LIBRARY") + set(_is_iface TRUE) + elseif("${prop_name}" STREQUAL "LINK_LIBRARIES") + set(_is_iface FALSE) + endif() + # + set(out) + set(umbrella ${_c4_lprefix}transform-src) + # + if("${_TRANSFORM}" STREQUAL "NONE") + c4_dbg("target ${target}: source transform: NONE!") + # + # do not transform the sources + # + if(_PUBLIC) + c4_dbg("target=${target} PUBLIC sources: ${_PUBLIC}") + c4_append_target_prop(${target} PUBLIC_SRC "${_PUBLIC}") + if(_INCORPORATED_FROM) + c4_append_target_prop(${target} PUBLIC_SRC_${_INCORPORATED_FROM} "${_PUBLIC}") + else() + c4_append_target_prop(${target} PUBLIC_SRC_${target} "${_PUBLIC}") + endif() + target_sources(${target} PUBLIC "${_PUBLIC}") + endif() + if(_INTERFACE) + c4_dbg("target=${target} INTERFACE sources: ${_INTERFACE}") + c4_append_target_prop(${target} INTERFACE_SRC "${_INTERFACE}") + if(_INCORPORATED_FROM) + c4_append_target_prop(${target} INTERFACE_SRC_${_INCORPORATED_FROM} "${_INTERFACE}") + else() + c4_append_target_prop(${target} INTERFACE_SRC_${target} "${_INTERFACE}") + endif() + target_sources(${target} INTERFACE "${_INTERFACE}") + endif() + if(_PRIVATE) + c4_dbg("target=${target} PRIVATE sources: ${_PRIVATE}") + c4_append_target_prop(${target} PRIVATE_SRC "${_PRIVATE}") + if(_INCORPORATED_FROM) + c4_append_target_prop(${target} PRIVATE_SRC_${_INCORPORATED_FROM} "${_PRIVATE}") + else() + c4_append_target_prop(${target} PRIVATE_SRC_${target} "${_PRIVATE}") + endif() + target_sources(${target} PRIVATE "${_PRIVATE}") + endif() + # + elseif("${_TRANSFORM}" STREQUAL "UNITY") + c4_dbg("target ${target}: source transform: UNITY!") + c4_err("source transformation not implemented") + # + # concatenate all compilation unit files (excluding interface) + # into a single compilation unit + # + _c4cat_filter_srcs("${_PUBLIC}" cpublic) + _c4cat_filter_hdrs("${_PUBLIC}" hpublic) + _c4cat_filter_srcs("${_INTERFACE}" cinterface) + _c4cat_filter_hdrs("${_INTERFACE}" hinterface) + _c4cat_filter_srcs("${_PRIVATE}" cprivate) + _c4cat_filter_hdrs("${_PRIVATE}" hprivate) + if(cpublic OR cinterface OR cprivate) + _c4cat_get_outname(${target} "src" ${C4_GEN_SRC_EXT} out) + c4_dbg("${target}: output unit: ${out}") + c4_cat_sources("${cpublic};${cinterface};${cprivate}" "${out}" ${umbrella}) + add_dependencies(${target} ${out}) + endif() + if(_PUBLIC) + c4_append_target_prop(${target} PUBLIC_SRC + $<BUILD_INTERFACE:${hpublic};${out}> + $<INSTALL_INTERFACE:${hpublic};${out}>) + target_sources(${target} PUBLIC + $<BUILD_INTERFACE:${hpublic};${out}> + $<INSTALL_INTERFACE:${hpublic};${out}>) + endif() + if(_INTERFACE) + c4_append_target_prop(${target} INTERFACE_SRC + $<BUILD_INTERFACE:${hinterface}> + $<INSTALL_INTERFACE:${hinterface}>) + target_sources(${target} INTERFACE + $<BUILD_INTERFACE:${hinterface}> + $<INSTALL_INTERFACE:${hinterface}>) + endif() + if(_PRIVATE) + c4_append_target_prop(${target} PRIVATE_SRC + $<BUILD_INTERFACE:${hprivate}> + $<INSTALL_INTERFACE:${hprivate}>) + target_sources(${target} PRIVATE + $<BUILD_INTERFACE:${hprivate}> + $<INSTALL_INTERFACE:${hprivate}>) + endif() + elseif("${_TRANSFORM}" STREQUAL "UNITY_HDR") + c4_dbg("target ${target}: source transform: UNITY_HDR!") + c4_err("target ${target}: source transformation not implemented") + # + # like unity, but concatenate compilation units into + # a header file, leaving other header files untouched + # + _c4cat_filter_srcs("${_PUBLIC}" cpublic) + _c4cat_filter_hdrs("${_PUBLIC}" hpublic) + _c4cat_filter_srcs("${_INTERFACE}" cinterface) + _c4cat_filter_hdrs("${_INTERFACE}" hinterface) + _c4cat_filter_srcs("${_PRIVATE}" cprivate) + _c4cat_filter_hdrs("${_PRIVATE}" hprivate) + if(c) + _c4cat_get_outname(${target} "src" ${C4_GEN_HDR_EXT} out) + c4_dbg("${target}: output hdr: ${out}") + _c4cat_filter_srcs_hdrs("${_PUBLIC}" c_h) + c4_cat_sources("${c}" "${out}" ${umbrella}) + add_dependencies(${target} ${out}) + add_dependencies(${target} ${_c4_lprefix}cat) + endif() + set(${src} ${out} PARENT_SCOPE) + set(${hdr} ${h} PARENT_SCOPE) + # + elseif("${_TRANSFORM}" STREQUAL "SINGLE_HDR") + c4_dbg("target ${target}: source transform: SINGLE_HDR!") + c4_err("target ${target}: source transformation not implemented") + # + # concatenate everything into a single header file + # + _c4cat_get_outname(${target} "all" ${C4_GEN_HDR_EXT} out) + _c4cat_filter_srcs_hdrs("${_c4al_SOURCES}" ch) + c4_cat_sources("${ch}" "${out}" ${umbrella}) + # + elseif("${_TRANSFORM}" STREQUAL "SINGLE_UNIT") + c4_dbg("target ${target}: source transform: SINGLE_UNIT!") + c4_err("target ${target}: source transformation not implemented") + # + # concatenate: + # * all compilation units into a single compilation unit + # * all headers into a single header + # + _c4cat_get_outname(${target} "src" ${C4_GEN_SRC_EXT} out) + _c4cat_get_outname(${target} "hdr" ${C4_GEN_SRC_EXT} out) + _c4cat_filter_srcs_hdrs("${_c4al_SOURCES}" ch) + c4_cat_sources("${ch}" "${out}" ${umbrella}) + else() + c4_err("unknown transform type: ${transform_type}. Must be one of GLOBAL;NONE;UNITY;TO_HEADERS;SINGLE_HEADER") + endif() +endfunction() + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# WIP, under construction (still incomplete) +# see: https://github.com/pr0g/cmake-examples +# see: https://cliutils.gitlab.io/modern-cmake/ + + +function(c4_install_target target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS1 # one-value macro arguments + EXPORT # the name of the export target. default: see below. + ) + _c4_handle_arg(EXPORT "${_c4_prefix}-export") + # + c4_dbg("installing target: ${target} ${ARGN}") + #_c4_is_incorporated(${_c4_prefix} inc) + #if(inc) + # c4_dbg("this project is INCORPORATEd. skipping install of targets") + # return() + #endif() + # + _c4_setup_install_vars() + install(TARGETS ${target} + EXPORT ${_EXPORT} + RUNTIME DESTINATION ${_RUNTIME_INSTALL_DIR} #COMPONENT runtime + BUNDLE DESTINATION ${_RUNTIME_INSTALL_DIR} #COMPONENT runtime + LIBRARY DESTINATION ${_LIBRARY_INSTALL_DIR} #COMPONENT runtime + ARCHIVE DESTINATION ${_ARCHIVE_INSTALL_DIR} #COMPONENT development + OBJECTS DESTINATION ${_OBJECTS_INSTALL_DIR} #COMPONENT development + INCLUDES DESTINATION ${_INCLUDE_INSTALL_DIR} #COMPONENT development + PUBLIC_HEADER DESTINATION ${_INCLUDE_INSTALL_DIR} #COMPONENT development + ) + c4_install_sources(${target} include) + # + # on windows, install also required DLLs + if(WIN32) + get_target_property(target_type ${target} TYPE) + if("${target_type}" STREQUAL "EXECUTABLE") + c4_get_transitive_property(${target} _C4_DLLS transitive_dlls) + if(transitive_dlls) + c4_dbg("${target}: installing dlls: ${transitive_dlls} to ${_RUNTIME_INSTALL_DIR}") + list(REMOVE_DUPLICATES transitive_dlls) + install(FILES ${transitive_dlls} + DESTINATION ${_RUNTIME_INSTALL_DIR} # shouldn't it be _LIBRARY_INSTALL_DIR? + #COMPONENT runtime + ) + endif() + endif() + endif() + # + set(l ${${_c4_prefix}_TARGETS}) + list(APPEND l ${target}) + set(${_c4_prefix}_TARGETS ${l} PARENT_SCOPE) + # +# # pkgconfig (WIP) +# set(pc ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${target}.pc) +# file(WRITE ${pc} "# pkg-config: ${target} +# +#prefix=\"${CMAKE_INSTALL_PREFIX}\" +#exec_prefix=\"\${_c4_prefix}\" +#libdir=\"\${_c4_prefix}/${CMAKE_INSTALL_LIBDIR}\" +#includedir=\"\${_c4_prefix}/include\" +# +#Name: ${target} +#Description: A library for xyzzying frobnixes +#URL: https://github.com/me/mylibrary +#Version: 0.0.0 +#Requires: @PKGCONF_REQ_PUB@ +#Requires.private: @PKGCONF_REQ_PRIV@ +#Cflags: -I\"${includedir}\" +#Libs: -L\"${libdir}\" -lmylibrary +#Libs.private: -L\"${libdir}\" -lmylibrary @PKGCONF_LIBS_PRIV@ +#") +# _c4_setup_install_vars() +# install(FILES ${pc} DESTINATION "${_ARCHIVE_INSTALL_DIR}/pkgconfig/") +endfunction() + + +function(c4_install_sources target destination) + c4_dbg("target ${target}: installing sources to ${destination}") + # executables have no sources requiring install + _c4_get_tgt_prop(target_type ${target} TYPE) + if(target_type STREQUAL "EXECUTABLE") + c4_dbg("target ${target}: is executable, skipping source install") + return() + endif() + # install source from the target and incorporated targets + c4_get_target_prop(${target} INCORPORATED_TARGETS inctargets) + if(inctargets) + set(targets "${inctargets};${target}") + else() + set(targets "${target}") + endif() + foreach(t ${targets}) + _c4_get_tgt_prop(srcroot ${t} C4_SOURCE_ROOT) + # get the sources from the target + # + c4_get_target_prop(${t} PUBLIC_SRC_${t} src) + if(src) + _c4cat_filter_hdrs("${src}" srcf) + _c4cat_filter_additional_exts("${src}" add) + c4_install_files("${srcf}" "${destination}" "${srcroot}") + c4_install_files("${add}" "${destination}" "${srcroot}") + endif() + # + c4_get_target_prop(${t} PRIVATE_SRC_${t} psrc) + if(psrc) + _c4cat_filter_hdrs("${psrc}" psrcf) + _c4cat_filter_additional_exts("${psrc}" add) + c4_install_files("${psrcf}" "${destination}" "${srcroot}") + c4_install_files("${add}" "${destination}" "${srcroot}") + endif() + # + c4_get_target_prop(${t} INTERFACE_SRC_${t} isrc) + if(isrc) + _c4cat_filter_srcs_hdrs("${isrc}" isrcf) + _c4cat_filter_additional_exts("${isrc}" add) + c4_install_files("${isrcf}" "${destination}" "${srcroot}") + c4_install_files("${add}" "${destination}" "${srcroot}") + endif() + # + c4_get_target_prop(${t} ADDFILES addfiles) + if(addfiles) + foreach(af ${addfiles}) + string(REGEX REPLACE "(.*)!!(.*)!!(.*)" "\\1;\\2;\\3" li "${af}") + list(GET li 0 files) + list(GET li 1 dst) + list(GET li 2 relative_to) + string(REPLACE "%%%" ";" files "${files}") + c4_install_files("${files}" "${dst}" "${relative_to}") + endforeach() + endif() + # + c4_get_target_prop(${t} ADDDIRS adddirs) + if(adddirs) + foreach(af ${adddirs}) + string(REGEX REPLACE "(.*)!!(.*)!!(.*)" "\\1;\\2;\\3" li "${af}") + list(GET li 0 dirs) + list(GET li 1 dst) + list(GET li 2 relative_to) + string(REPLACE "%%%" ";" dirs "${files}") + c4_install_dirs("${dirs}" "${dst}" "${relative_to}") + endforeach() + endif() + endforeach() +endfunction() + + +function(c4_install_target_add_files target files destination relative_to) + c4_dbg("installing additional files for target ${target}, destination=${destination}: ${files}") + string(REPLACE ";" "%%%" rfiles "${files}") + c4_append_target_prop(${target} ADDFILES "${rfiles}!!${destination}!!${relative_to}") + # + _c4_is_incorporated(${_c4_prefix} inc) + if(inc) + c4_dbg("this project is INCORPORATEd. skipping install of targets") + return() + endif() + c4_install_files("${files}" "${destination}" "${relative_to}") +endfunction() + + +function(c4_install_target_add_dirs target dirs destination relative_to) + c4_dbg("installing additional dirs for target ${target}, destination=${destination}: ${dirs}") + string(REPLACE ";" "%%%" rdirs "${dirs}") + c4_append_target_prop(${target} ADDDIRS "${rdirs}!!${destination}!!${relative_to}") + # + _c4_is_incorporated(${_c4_prefix} inc) + if(inc) + c4_dbg("this project is INCORPORATEd. skipping install of targets") + return() + endif() + c4_install_dirs("${dirs}" "${destination}" "${relative_to}") +endfunction() + + +function(c4_install_files files destination relative_to) + c4_dbg("adding files to install list, destination ${destination}: ${files}") + foreach(f ${files}) + file(RELATIVE_PATH rf "${relative_to}" ${f}) + get_filename_component(rd "${rf}" DIRECTORY) + install(FILES ${f} DESTINATION "${destination}/${rd}" ${ARGN}) + endforeach() +endfunction() + + +function(c4_install_directories directories destination relative_to) + c4_dbg("adding directories to install list, destination ${destination}: ${directories}") + foreach(d ${directories}) + file(RELATIVE_PATH rf "${relative_to}" ${d}) + get_filename_component(rd "${rf}" DIRECTORY) + install(DIRECTORY ${d} DESTINATION "${destination}/${rd}" ${ARGN}) + endforeach() +endfunction() + + +function(c4_install_exports) + _c4_handle_args(_ARGS ${ARGN} + _ARGS1 # one-value macro arguments + PREFIX # override the c4 project-wide prefix. This will be used in the cmake + TARGET # the name of the exports target + NAMESPACE # the namespace for the targets + _ARGSN # multi-value macro arguments + DEPENDENCIES + ) + # + _c4_handle_arg(PREFIX "${_c4_prefix}") + _c4_handle_arg(TARGET "${_c4_prefix}-export") + _c4_handle_arg(NAMESPACE "${_c4_prefix}::") + # + c4_dbg("installing exports: ${ARGN}") + #_c4_is_incorporated(${_c4_prefix} inc) + #if(inc) + # c4_dbg("this project is INCORPORATEd. skipping install of exports") + # return() + #endif() + # + _c4_setup_install_vars() + # + list(GET ${_c4_prefix}_TARGETS 0 target) + set(exported_target "${_NAMESPACE}${target}") + set(targets_file "${_PREFIX}Targets.cmake") + # + set(deps) + if(_DEPENDENCIES) + set(deps "#----------------------------- +include(CMakeFindDependencyMacro) +") + foreach(d ${_DEPENDENCIES}) + _c4_is_incorporated(${d} inc) + if(inc) + c4_dbg("install: dependency ${d} is INCORPORATEd, skipping check") + continue() + endif() + c4_dbg("install: adding dependency check for ${d}") + set(deps "${deps}find_dependency(${d} REQUIRED) +") + endforeach() + set(deps "${deps}#-----------------------------") + endif() + # + # cfg_dst is the path relative to install root where the export + # should be installed; cfg_dst_rel is the path from there to + # the install root + macro(__c4_install_exports cfg_dst cfg_dst_rel) + # make sure that different exports are staged in different directories + set(case ${CMAKE_CURRENT_BINARY_DIR}/export_cases/${cfg_dst}) + file(MAKE_DIRECTORY ${case}) + # + install(EXPORT "${_TARGET}" + FILE "${targets_file}" + NAMESPACE "${_NAMESPACE}" + DESTINATION "${cfg_dst}") + export(EXPORT ${_TARGET} + FILE "${targets_file}" + NAMESPACE "${_NAMESPACE}") + # + # Config files + # the module below has nice docs in it; do read them + # to understand the macro calls below + include(CMakePackageConfigHelpers) + set(cfg ${case}/${_PREFIX}Config.cmake) + set(cfg_ver ${case}/${_PREFIX}ConfigVersion.cmake) + # + file(WRITE ${cfg}.in "${deps} +set(${_c4_uprefix}VERSION ${${_c4_uprefix}VERSION}) + +@PACKAGE_INIT@ + +if(NOT TARGET ${exported_target}) + include(\${PACKAGE_PREFIX_DIR}/${targets_file}) +endif() + +# HACK: PACKAGE_PREFIX_DIR is obtained from the PACKAGE_INIT macro above; +# When used below in the calls to set_and_check(), +# it points at the location of this file. So point it instead +# to the CMAKE_INSTALL_PREFIX, in relative terms +get_filename_component(PACKAGE_PREFIX_DIR + \"\${PACKAGE_PREFIX_DIR}/${cfg_dst_rel}\" ABSOLUTE) + +set_and_check(${_c4_uprefix}INCLUDE_DIR \"@PACKAGE__INCLUDE_INSTALL_DIR@\") +set_and_check(${_c4_uprefix}LIB_DIR \"@PACKAGE__LIBRARY_INSTALL_DIR@\") +#set_and_check(${_c4_uprefix}SYSCONFIG_DIR \"@PACKAGE__SYSCONFIG_INSTALL_DIR@\") + +check_required_components(${_c4_lcprefix}) +") + configure_package_config_file(${cfg}.in ${cfg} + INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" # defaults to CMAKE_INSTALL_PREFIX + INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}" + PATH_VARS + _INCLUDE_INSTALL_DIR + _LIBRARY_INSTALL_DIR + _SYSCONFIG_INSTALL_DIR + #NO_SET_AND_CHECK_MACRO + #NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + write_basic_package_version_file( + ${cfg_ver} + VERSION ${${_c4_uprefix}VERSION} + COMPATIBILITY AnyNewerVersion + ) + install(FILES ${cfg} ${cfg_ver} DESTINATION ${cfg_dst}) + endmacro(__c4_install_exports) + # + # To install the exports: + # + # Windows: + # <prefix>/ + # <prefix>/(cmake|CMake)/ + # <prefix>/<name>*/ + # <prefix>/<name>*/(cmake|CMake)/ + # + # Unix: + # <prefix>/(lib/<arch>|lib|share)/cmake/<name>*/ + # <prefix>/(lib/<arch>|lib|share)/<name>*/ + # <prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/ + # + # Apple: + # <prefix>/<name>.framework/Resources/ + # <prefix>/<name>.framework/Resources/CMake/ + # <prefix>/<name>.framework/Versions/*/Resources/ + # <prefix>/<name>.framework/Versions/*/Resources/CMake/ + # <prefix>/<name>.app/Contents/Resources/ + # <prefix>/<name>.app/Contents/Resources/CMake/ + # + # (This was taken from the find_package() documentation) + if(WIN32) + __c4_install_exports(cmake/ "..") + elseif(APPLE) + __c4_install_exports(${_ARCHIVE_INSTALL_DIR}/cmake/${_c4_prefix} "../../..") + #__c4_install_exports(${_ARCHIVE_INSTALL_DIR}/${_c4_prefix}.framework/Resources/ "../../..") + elseif(UNIX OR (CMAKE_SYSTEM_NAME STREQUAL UNIX) OR (CMAKE_SYSTEM_NAME STREQUAL Linux) OR (CMAKE_SYSTEM_NAME STREQUAL Generic)) + __c4_install_exports(${_ARCHIVE_INSTALL_DIR}/cmake/${_c4_prefix} "../../..") + else() + c4_err("unknown platform. CMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}") + endif() +endfunction() + + +macro(_c4_setup_install_vars) + set(_RUNTIME_INSTALL_DIR bin/) + set(_ARCHIVE_INSTALL_DIR lib/) + set(_LIBRARY_INSTALL_DIR lib/) # TODO on Windows, ARCHIVE and LIBRARY dirs must be different to prevent name clashes + set(_INCLUDE_INSTALL_DIR include/) + set(_OBJECTS_INSTALL_DIR obj/) + set(_SYSCONFIG_INSTALL_DIR etc/${_c4_lcprefix}/) +endmacro() + + +function(c4_get_target_installed_headers target out) + c4_get_target_prop(${target} INCORPORATED_TARGETS inctargets) + if(inctargets) + set(targets "${inctargets};${target}") + else() + set(targets "${target}") + endif() + set(hdrs) + foreach(t ${targets}) + _c4_get_tgt_prop(srcroot ${t} C4_SOURCE_ROOT) + # + c4_get_target_prop(${t} PUBLIC_SRC_${t} src) + if(src) + _c4cat_filter_hdrs("${src}" srcf) + if(thdrs) + set(thdrs "${thdrs};${srcf}") + else() + set(thdrs "${srcf}") + endif() + endif() + # + c4_get_target_prop(${t} PRIVATE_SRC_${t} psrc) + if(src) + _c4cat_filter_hdrs("${psrc}" psrcf) + if(thdrs) + set(thdrs "${thdrs};${psrcf}") + else() + set(thdrs "${psrcf}") + endif() + endif() + # + c4_get_target_prop(${t} INTERFACE_SRC_${t} isrc) + if(src) + _c4cat_filter_hdrs("${isrc}" isrcf) + if(thdrs) + set(thdrs "${thdrs};${isrcf}") + else() + set(thdrs "${isrcf}") + endif() + endif() + # + foreach(h ${thdrs}) + file(RELATIVE_PATH rf "${srcroot}" "${h}") + list(APPEND hdrs "${rf}") + endforeach() + endforeach() + set(${out} ${hdrs} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +function(c4_setup_testing) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + GTEST # download and import googletest + DOCTEST # download and import doctest + _ARGS1 + _ARGSN + ) + #include(GoogleTest) # this module requires at least cmake 3.9 + c4_dbg("enabling tests") + # umbrella target for building test binaries + add_custom_target(${_c4_lprefix}test-build) + _c4_set_target_folder(${_c4_lprefix}test-build test) + # umbrella targets for running tests + if(NOT TARGET test-build) + add_custom_target(test-build) + add_custom_target(test-verbose) + _c4_set_target_folder(test-build "/test") + _c4_set_target_folder(test-verbose "/test") + endif() + if(NOT TARGET test) + # add a test target. To prevent a warning, we need to set up a policy, + # and also suppress the resulting warning from suppressing the warning. + set(_depr_old_val ${CMAKE_WARN_DEPRECATED}) + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) # https://stackoverflow.com/questions/67432538/cannot-set-cmake-warn-deprecated-inside-the-cmakelists-txt + cmake_policy(PUSH) + cmake_policy(SET CMP0037 OLD) # target name "test" is reserved for CTesting + add_custom_target(test) + _c4_set_target_folder(test "/test") + cmake_policy(POP) + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "${_depr_old_val}" FORCE) + unset(_depr_old_val) + endif() + function(_def_runner runner) + set(echo " +CWD=${CMAKE_CURRENT_BINARY_DIR} +---------------------------------- +${ARGN} +---------------------------------- +") + add_custom_target(${runner} + #${CMAKE_COMMAND} -E echo "${echo}" + COMMAND ${ARGN} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${_c4_lprefix}test-build + ) + _c4_set_target_folder(${runner} test) + endfunction() + _def_runner(${_c4_lprefix}test-run ${CMAKE_CTEST_COMMAND} --output-on-failure ${${_c4_uprefix}CTEST_OPTIONS} -C $<CONFIG>) + _def_runner(${_c4_lprefix}test-run-verbose ${CMAKE_CTEST_COMMAND} -VV ${${_c4_uprefix}CTEST_OPTIONS} -C $<CONFIG>) + add_dependencies(test ${_c4_lprefix}test-run) + add_dependencies(test-verbose ${_c4_lprefix}test-run-verbose) + add_dependencies(test-build ${_c4_lprefix}test-build) + # + # import required libraries + if(_GTEST) + c4_log("testing requires googletest") + if(NOT TARGET gtest) + c4_import_remote_proj(gtest ${CMAKE_CURRENT_BINARY_DIR}/ext/gtest + REMOTE + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 #GIT_SHALLOW ON + OVERRIDE + BUILD_GTEST ON + BUILD_GMOCK OFF + gtest_force_shared_crt ON + gtest_build_samples OFF + gtest_build_tests OFF + SET_FOLDER_TARGETS ext gtest gtest_main + EXCLUDE_FROM_ALL + ) + # old gcc-4.8 support + if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND + (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND + (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)) + _c4_get_subproject_property(gtest SRC_DIR _gtest_patch_src_dir) + apply_patch("${_c4_project_dir}/compat/gtest_gcc-4.8.patch" + "${_gtest_patch_src_dir}" + "${_gtest_patch_src_dir}/.gtest_gcc-4.8.patch") + unset(_gtest_patch_src_dir) + target_compile_options(gtest PUBLIC -include ${_c4_project_dir}/compat/c4/gcc-4.8.hpp) + endif() + endif() + endif() + if(_DOCTEST) + c4_log("testing requires doctest") + if(NOT TARGET doctest) + c4_import_remote_proj(doctest ${CMAKE_CURRENT_BINARY_DIR}/ext/doctest + REMOTE + GIT_REPOSITORY https://github.com/onqtam/doctest.git + GIT_TAG 2.4.6 #GIT_SHALLOW ON + OVERRIDE + DOCTEST_WITH_TESTS OFF + DOCTEST_WITH_MAIN_IN_STATIC_LIB ON + SET_FOLDER_TARGETS ext doctest_with_main + EXCLUDE_FROM_ALL + ) + endif() + endif() +endfunction(c4_setup_testing) + + +function(c4_add_test target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + WORKING_DIRECTORY + _ARGSN # multi-value macro arguments + ARGS + ) + # + if(_WORKING_DIRECTORY) + set(_WORKING_DIRECTORY WORKING_DIRECTORY ${_WORKING_DIRECTORY}) + endif() + set(cmd_pfx) + if(CMAKE_CROSSCOMPILING) + set(cmd_pfx ${CMAKE_CROSSCOMPILING_EMULATOR}) + endif() + if(NOT ${uprefix}SANITIZE_ONLY) + if(${CMAKE_VERSION} VERSION_LESS "3.16.0") + add_test(NAME ${target} + COMMAND ${cmd_pfx} "$<TARGET_FILE:${target}>" ${_ARGS} + ${_WORKING_DIRECTORY}) + else() + add_test(NAME ${target} + COMMAND ${cmd_pfx} "$<TARGET_FILE:${target}>" ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() + endif() + # + if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") + add_dependencies(${_c4_lprefix}test-build ${target}) + return() + endif() + # + set(sanitized_targets) + foreach(s asan msan tsan ubsan) + set(t ${target}-${s}) + if(TARGET ${t}) + list(APPEND sanitized_targets ${s}) + endif() + endforeach() + if(sanitized_targets) + add_custom_target(${target}-all) + add_dependencies(${target}-all ${target}) + add_dependencies(${_c4_lprefix}test-build ${target}-all) + _c4_set_target_folder(${target}-all test/${target}) + else() + add_dependencies(${_c4_lprefix}test-build ${target}) + endif() + if(sanitized_targets) + foreach(s asan msan tsan ubsan) + set(t ${target}-${s}) + if(TARGET ${t}) + add_dependencies(${target}-all ${t}) + c4_sanitize_get_target_command("${cmd_pfx};$<TARGET_FILE:${t}>" ${s} cmd) + #c4_log("adding test: ${t}") + add_test(NAME ${t} + COMMAND ${cmd} ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() + endforeach() + endif() + if(NOT CMAKE_CROSSCOMPILING) + if(NOT ${_c4_uprefix}SANITIZE_ONLY) + c4_add_valgrind(${target} ${ARGN}) + endif() + endif() + if(${_c4_uprefix}LINT) + c4_static_analysis_add_tests(${target}) # this will not actually run the executable + endif() +endfunction(c4_add_test) + + +# every excess argument is passed on to set_target_properties() +function(c4_add_test_fail_build name srccontent_or_srcfilename) + # + set(sdir ${CMAKE_CURRENT_BINARY_DIR}/test_fail_build) + set(src ${srccontent_or_srcfilename}) + if("${src}" STREQUAL "") + c4_err("must be given an existing source file name or a non-empty string") + endif() + # + if(EXISTS ${src}) + set(fn ${src}) + else() + if(NOT EXISTS ${sdir}) + file(MAKE_DIRECTORY ${sdir}) + endif() + set(fn ${sdir}/${name}.cpp) + file(WRITE ${fn} "${src}") + endif() + # + # https://stackoverflow.com/questions/30155619/expected-build-failure-tests-in-cmake + add_executable(${name} ${fn}) + # don't build this target + set_target_properties(${name} PROPERTIES + EXCLUDE_FROM_ALL TRUE + EXCLUDE_FROM_DEFAULT_BUILD TRUE + # and pass on further properties given by the caller + ${ARGN}) + add_test(NAME ${name} + COMMAND ${CMAKE_COMMAND} --build . --target ${name} --config $<CONFIGURATION> + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE) +endfunction() + + +# add a test ensuring that a target linking and using code from a library +# successfully compiles and runs against the installed library +function(c4_add_install_link_test library namespace exe_source_code) + if(CMAKE_CROSSCOMPILING) + c4_log("cross-compiling: skip install link test") + return() + endif() + if("${library}" STREQUAL "${_c4_prefix}") + set(testname ${_c4_lprefix}test-install-link) + else() + set(testname ${_c4_lprefix}test-install-link-${library}) + endif() + _c4_add_library_client_test(${library} "${namespace}" "${testname}" "${exe_source_code}") +endfunction() + + +# add a test ensuring that a target consuming every header in a library +# successfully compiles and runs against the installed library +function(c4_add_install_include_test library namespace) + if(CMAKE_CROSSCOMPILING) + c4_log("cross-compiling: skip install include test") + return() + endif() + c4_get_target_installed_headers(${library} incfiles) + set(incblock) + foreach(i ${incfiles}) + set(incblock "${incblock} +#include <${i}>") + endforeach() + set(src "${incblock} + +int main() +{ + return 0; +} +") + if("${library}" STREQUAL "${_c4_prefix}") + set(testname ${_c4_lprefix}test-install-include) + else() + set(testname ${_c4_lprefix}test-install-include-${library}) + endif() + _c4_add_library_client_test(${library} "${namespace}" "${testname}" "${src}") +endfunction() + + +function(_c4_add_library_client_test library namespace pname source_code) + if("${CMAKE_BUILD_TYPE}" STREQUAL Coverage) + add_test(NAME ${pname} + COMMAND ${CMAKE_COMMAND} -E echo "skipping this test in coverage builds" + ) + return() + endif() + set(pdir "${CMAKE_CURRENT_BINARY_DIR}/${pname}") + set(bdir "${pdir}/build") + if(NOT EXISTS "${pdir}") + file(MAKE_DIRECTORY "${pdir}") + endif() + if(NOT EXISTS "${bdir}/build") + file(MAKE_DIRECTORY "${bdir}/build") + endif() + set(psrc "${pdir}/${pname}.cpp") + set(tsrc "${pdir}/${pname}-run.cmake") + set(tout "${pdir}/${pname}-run-out") + # generate the source file + file(WRITE "${psrc}" "${source_code}") + # generate the cmake project consuming this library + file(WRITE "${pdir}/CMakeLists.txt" " +cmake_minimum_required(VERSION 3.12) +project(${pname} LANGUAGES CXX) + +find_package(${library} REQUIRED) + +message(STATUS \" +found ${library}: + ${_c4_uprefix}INCLUDE_DIR=\${${_c4_uprefix}INCLUDE_DIR} + ${_c4_uprefix}LIB_DIR=\${${_c4_uprefix}LIB_DIR} +\") + +add_executable(${pname} ${pname}.cpp) +# this must be the only required setup to link with ${library} +target_link_libraries(${pname} PUBLIC ${namespace}${library}) + +get_target_property(lib_type ${namespace}${library} TYPE) +if(WIN32 AND (lib_type STREQUAL SHARED_LIBRARY)) + # add the directory containing the DLL to the path + get_target_property(imported_configs ${namespace}${library} IMPORTED_CONFIGURATIONS) + message(STATUS \"${namespace}${library}: it's a shared library. imported configs: \${imported_configs}\") + foreach(cfg \${imported_configs}) + get_target_property(implib ${namespace}${library} IMPORTED_IMPLIB_\${cfg}) + get_target_property(location ${namespace}${library} IMPORTED_LOCATION_\${cfg}) + message(STATUS \"${namespace}${library}: implib_\${cfg}=\${implib}\") + message(STATUS \"${namespace}${library}: location_\${cfg}=\${location}\") + break() + endforeach() + get_filename_component(dlldir \"\${location}\" DIRECTORY) + message(STATUS \"${namespace}${library}: dlldir=\${dlldir}\") + add_custom_target(${pname}-run + COMMAND \${CMAKE_COMMAND} -E echo \"cd \${dlldir} && \$<TARGET_FILE:${pname}>\" + COMMAND \$<TARGET_FILE:${pname}> + DEPENDS ${pname} + WORKING_DIRECTORY \${dlldir}) +else() + add_custom_target(${pname}-run + COMMAND \$<TARGET_FILE:${pname}> + DEPENDS ${pname}) +endif() +") + # The test consists in running the script generated below. + # We force evaluation of the configuration generator expression + # by receiving its result via the command line. + add_test(NAME ${pname} + COMMAND ${CMAKE_COMMAND} -DCFG_IN=$<CONFIG> -P "${tsrc}" + ) + # NOTE: in the cmake configure command, be sure to NOT use quotes + # in -DCMAKE_PREFIX_PATH=\"${CMAKE_INSTALL_PREFIX}\". Use + # -DCMAKE_PREFIX_PATH=${CMAKE_INSTALL_PREFIX} instead. + # So here we add a check to make sure the install path has no spaces + string(FIND "${CMAKE_INSTALL_PREFIX}" " " has_spaces) + if(NOT (has_spaces EQUAL -1)) + c4_err("install tests will fail if the install path has spaces: '${CMAKE_INSTALL_PREFIX}' : ... ${has_spaces}") + endif() + # make sure the test project uses the same architecture + # CMAKE_VS_PLATFORM_NAME is available only since cmake 3.9 + # see https://cmake.org/cmake/help/v3.9/variable/CMAKE_GENERATOR_PLATFORM.html + if(WIN32) + set(cfg_opt "--config \${cfg}") + if(CMAKE_GENERATOR_PLATFORM OR CMAKE_VS_PLATFORM_NAME) + set(arch "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" "-DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}") + else() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(arch -A x64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(arch -A Win32) + else() + c4_err("not implemented") + endif() + endif() + elseif(ANDROID OR IOS OR WINCE OR WINDOWS_PHONE) + c4_err("not implemented") + elseif(IOS) + c4_err("not implemented") + elseif(UNIX) + if(CMAKE_GENERATOR_PLATFORM OR CMAKE_VS_PLATFORM_NAME) + set(arch "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" "-DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}") + else() + if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) + else() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(arch "-DCMAKE_CXX_FLAGS=-m64") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(arch "-DCMAKE_CXX_FLAGS=-m32") + else() + c4_err("not implemented") + endif() + endif() + endif() + endif() + # generate the cmake script with the test content + file(WRITE "${tsrc}" " +# run a command and check its return status +function(runcmd id) + set(cmdout \"${tout}-\${id}.log\") + message(STATUS \"Running command: \${ARGN}\") + message(STATUS \"Running command: output goes to \${cmdout}\") + execute_process( + COMMAND \${ARGN} + RESULT_VARIABLE retval + OUTPUT_FILE \"\${cmdout}\" + ERROR_FILE \"\${cmdout}\" + # COMMAND_ECHO STDOUT # only available from cmake-3.15 + ) + message(STATUS \"Running command: exit status was \${retval}\") + file(READ \"\${cmdout}\" output) + if(\"\${cmdout}\" STREQUAL \"\") + message(STATUS \"Running command: no output\") + else() + message(STATUS \"Running command: output: +-------------------- +\${output}--------------------\") + endif() + if(NOT (\${retval} EQUAL 0)) + message(FATAL_ERROR \"Command failed with exit status \${retval}: \${ARGN}\") + endif() +endfunction() + +set(cmk \"${CMAKE_COMMAND}\") +set(pfx \"${CMAKE_INSTALL_PREFIX}\") +set(idir \"${CMAKE_BINARY_DIR}\") +set(pdir \"${pdir}\") +set(bdir \"${bdir}\") + +# force evaluation of the configuration generator expression +# by receiving its result via the command line +set(cfg \${CFG_IN}) + +# remove any existing library install +if(EXISTS \"\${pfx}\") + runcmd(0_rmdir \"\${cmk}\" -E remove_directory \"\${pfx}\") +else() + message(STATUS \"does not exist: \${pfx}\") +endif() + +# install the library +#runcmd(1_install_lib \"\${cmk}\" --install \"\${idir}\" ${cfg_opt}) # requires cmake>3.13 (at least) +runcmd(1_install_lib \"\${cmk}\" --build \"\${idir}\" ${cfg_opt} --target install) + +# configure the client project +runcmd(2_config \"\${cmk}\" -S \"\${pdir}\" -B \"\${bdir}\" \"-DCMAKE_PREFIX_PATH=\${pfx}\" \"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}\" ${arch} \"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\" \"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\") + +# build the client project +runcmd(3_build \"\${cmk}\" --build \"\${bdir}\" ${cfg_opt}) + +# run the client executable +runcmd(4_install \"\${cmk}\" --build \"\${bdir}\" --target \"${pname}-run\" ${cfg_opt}) +") +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +function(c4_setup_valgrind umbrella_option) + if(UNIX AND (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")) + if("${C4_VALGRIND}" STREQUAL "") + option(C4_VALGRIND "enable valgrind tests (all subprojects)" ON) + endif() + if("${C4_VALGRIND_SGCHECK}" STREQUAL "") + option(C4_VALGRIND_SGCHECK "enable valgrind tests with the exp-sgcheck tool (all subprojects)" OFF) + endif() + cmake_dependent_option(${_c4_uprefix}VALGRIND "enable valgrind tests" ${C4_VALGRIND} ${umbrella_option} OFF) + cmake_dependent_option(${_c4_uprefix}VALGRIND_SGCHECK "enable valgrind tests with the exp-sgcheck tool" ${C4_VALGRIND_SGCHECK} ${umbrella_option} OFF) + set(${_c4_uprefix}VALGRIND_OPTIONS "--gen-suppressions=all --error-exitcode=10101" CACHE STRING "options for valgrind tests") + endif() +endfunction(c4_setup_valgrind) + + +function(c4_add_valgrind target_name) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + WORKING_DIRECTORY + _ARGSN # multi-value macro arguments + ARGS + ) + # + if(_WORKING_DIRECTORY) + set(_WORKING_DIRECTORY WORKING_DIRECTORY ${_WORKING_DIRECTORY}) + endif() + # @todo: consider doing this for valgrind: + # http://stackoverflow.com/questions/40325957/how-do-i-add-valgrind-tests-to-my-cmake-test-target + # for now we explicitly run it: + if(${_c4_uprefix}VALGRIND) + separate_arguments(_vg_opts UNIX_COMMAND "${${_c4_uprefix}VALGRIND_OPTIONS}") + add_test(NAME ${target_name}-valgrind + COMMAND valgrind ${_vg_opts} $<TARGET_FILE:${target_name}> ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() + if(${_c4_uprefix}VALGRIND_SGCHECK) + # stack and global array overrun detector + # http://valgrind.org/docs/manual/sg-manual.html + separate_arguments(_sg_opts UNIX_COMMAND "--tool=exp-sgcheck ${${_c4_uprefix}VALGRIND_OPTIONS}") + add_test(NAME ${target_name}-sgcheck + COMMAND valgrind ${_sg_opts} $<TARGET_FILE:${target_name}> ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() +endfunction(c4_add_valgrind) + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_setup_coverage) + if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")) + return() + endif() + # + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + _ARGSN # multi-value macro arguments + COVFLAGS # coverage compilation flags + INCLUDE # patterns to include in the coverage, relative to CMAKE_SOURCE_DIR + EXCLUDE # patterns to exclude in the coverage, relative to CMAKE_SOURCE_DIR + EXCLUDE_ABS # absolute paths to exclude in the coverage + GENHTML_ARGS # options to pass to genhtml + ) + # defaults for the macro arguments + set(_genhtml_args "--title ${_c4_lcprefix} --demangle-cpp --sort --function-coverage --branch-coverage --prefix '${CMAKE_SOURCE_DIR}' --prefix '${CMAKE_BINARY_DIR}'") + set(covflags "-g -O0 --coverage") #set(covflags "-g -O0 -fprofile-arcs -ftest-coverage") + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(covflags "${covflags} -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline") + endif() + set(${_c4_uprefix}COVERAGE_FLAGS "${covflags}" CACHE STRING "coverage compilation flags") + set(${_c4_uprefix}COVERAGE_GENHTML_ARGS "${_genhtml_args}" CACHE STRING "arguments to pass to genhtml") + set(${_c4_uprefix}COVERAGE_INCLUDE src CACHE STRING "relative paths to include in the coverage, relative to CMAKE_SOURCE_DIR") + set(${_c4_uprefix}COVERAGE_EXCLUDE bm;build;extern;ext;src/c4/ext;test CACHE STRING "relative paths to exclude from the coverage, relative to CMAKE_SOURCE_DIR") + set(${_c4_uprefix}COVERAGE_EXCLUDE_ABS /usr CACHE STRING "absolute paths to exclude from the coverage") + _c4_handle_arg(COVFLAGS ${${_c4_uprefix}COVERAGE_FLAGS}) + _c4_handle_arg(INCLUDE ${${_c4_uprefix}COVERAGE_INCLUDE}) + _c4_handle_arg(EXCLUDE ${${_c4_uprefix}COVERAGE_EXCLUDE}) + _c4_handle_arg(EXCLUDE_ABS ${${_c4_uprefix}COVERAGE_EXCLUDE_ABS} "${CMAKE_BINARY_DIR}") + _c4_handle_arg(GENHTML_ARGS ${${_c4_uprefix}COVERAGE_GENHTML_ARGS}) + # + function(_c4cov_transform_filters var reldir) + set(_filters) + foreach(pat ${${var}}) + list(APPEND _filters "'${reldir}${pat}/*'") + endforeach() + set(${var} ${_filters} PARENT_SCOPE) + endfunction() + _c4cov_transform_filters(_INCLUDE "${CMAKE_SOURCE_DIR}/") + _c4cov_transform_filters(_EXCLUDE "${CMAKE_SOURCE_DIR}/") + _c4cov_transform_filters(_EXCLUDE_ABS "") + # + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + c4_err("coverage: clang version must be 3.0.0 or greater") + endif() + elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + c4_err("coverage: compiler is not GNUCXX") + endif() + # + find_program(GCOV gcov) + find_program(LCOV lcov) + find_program(GENHTML genhtml) + find_program(CTEST ctest) + if(NOT (GCOV AND LCOV AND GENHTML AND CTEST)) + c4_err("Coverage tools not available: + gcov: ${GCOV} + lcov: ${LCOV} + genhtml: ${GENHTML} + ctest: ${CTEST} + --coverage flags: ${_COVFLAGS}") + endif() + # + add_configuration_type(Coverage + DEFAULT_FROM DEBUG + C_FLAGS ${_COVFLAGS} + CXX_FLAGS ${_COVFLAGS} + ) + # + option(${_c4_uprefix}COVERAGE_CODECOV "enable target to submit coverage to codecov.io" OFF) + option(${_c4_uprefix}COVERAGE_COVERALLS "enable target to submit coverage to coveralls.io" OFF) + # + c4_dbg("adding coverage targets") + # + set(sd "${CMAKE_SOURCE_DIR}") + set(bd "${CMAKE_BINARY_DIR}") + set(coverage_result ${bd}/lcov/index.html) + set(lcov_result ${bd}/coverage3-final_filtered.lcov) + separate_arguments(_GENHTML_ARGS NATIVE_COMMAND ${_GENHTML_ARGS}) + add_custom_command(OUTPUT ${coverage_result} ${lcov_result} + COMMAND echo "cd ${CMAKE_BINARY_DIR}" + COMMAND ${LCOV} -q --zerocounters --directory . + COMMAND ${LCOV} -q --no-external --capture --base-directory "${sd}" --directory . --output-file ${bd}/coverage0-before.lcov --initial + COMMAND ${CMAKE_COMMAND} --build . --target ${_c4_lprefix}test-run || echo "Failed running the tests. Proceeding with coverage, but results may be affected or even empty." + COMMAND ${LCOV} -q --no-external --capture --base-directory "${sd}" --directory . --output-file ${bd}/coverage1-after.lcov + COMMAND ${LCOV} -q --add-tracefile ${bd}/coverage0-before.lcov --add-tracefile ${bd}/coverage1-after.lcov --output-file ${bd}/coverage2-final.lcov + COMMAND ${LCOV} -q --remove ${bd}/coverage2-final.lcov ${_EXCLUDE} ${EXCLUDE_ABS} --output-file ${bd}/coverage3-final_filtered.lcov + COMMAND ${GENHTML} ${bd}/coverage3-final_filtered.lcov -o ${bd}/lcov ${_GENHTML_ARGS} + COMMAND echo "Coverage report: ${coverage_result}" + DEPENDS ${_c4_lprefix}test-build + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "${_c4_prefix} coverage: LCOV report at ${coverage_result}" + #VERBATIM + ) + add_custom_target(${_c4_lprefix}coverage SOURCES ${coverage_result} ${lcov_result}) + # + if(${_c4_uprefix}COVERAGE_CODECOV) + set(_subm ${_c4_lprefix}coverage-submit-codecov) + _c4cov_get_service_token(codecov _token) + if(NOT ("${_token}" STREQUAL "")) + set(_token -t "${_token}") + endif() + set(_silent_codecov) + if(${_c4_uprefix}COVERAGE_CODECOV_SILENT) + set(_silent_codecov >${CMAKE_BINARY_DIR}/codecov.log 2>&1) + endif() + # + c4_log("coverage: enabling submission of results to https://codecov.io: ${_subm}") + set(submitcc "${CMAKE_BINARY_DIR}/submit_codecov.sh") + c4_download_file("https://codecov.io/bash" "${submitcc}") + set(submit_cmd bash ${submitcc} -Z ${_token} -X gcov -X gcovout -p ${CMAKE_SOURCE_DIR} -f ${lcov_result} ${_silent_codecov}) + string(REPLACE ";" " " submit_cmd_str "${submit_cmd}") + add_custom_target(${_subm} + SOURCES ${lcov_result} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND echo "cd ${CMAKE_BINARY_DIR} && ${submit_cmd_str}" + COMMAND ${submit_cmd} + VERBATIM + COMMENT "${_c4_lcprefix} coverage: submit to codecov" + ) + c4_add_umbrella_target(coverage-submit-codecov coverage-submit) # uses the current prefix + endif() + # + if(${_c4_uprefix}COVERAGE_COVERALLS) + set(_subm ${_c4_lprefix}coverage-submit-coveralls) + _c4cov_get_service_token(coveralls _token) + if(NOT ("${_token}" STREQUAL "")) + set(_token --repo-token "${_token}") + endif() + set(_silent_coveralls) + if(${_c4_uprefix}COVERAGE_COVERALLS_SILENT) + set(_silent_coveralls >${CMAKE_BINARY_DIR}/coveralls.log 2>&1) + endif() + # + c4_log("coverage: enabling submission of results to https://coveralls.io: ${_subm}") + set(submit_cmd coveralls ${_token} --build-root ${CMAKE_BINARY_DIR} --root ${CMAKE_SOURCE_DIR} --no-gcov --lcov-file ${lcov_result} ${_silent_coveralls}) + string(REPLACE ";" " " submit_cmd_str "${submit_cmd}") + add_custom_target(${_subm} + SOURCES ${lcov_result} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND echo "cd ${CMAKE_BINARY_DIR} && ${submit_cmd_str}" + COMMAND ${submit_cmd} + VERBATIM + COMMENT "${_c4_lcprefix} coverage: submit to coveralls" + ) + c4_add_umbrella_target(coverage-submit-coveralls coverage-submit) # uses the current prefix + endif() +endfunction(c4_setup_coverage) + + +# 1. try cmake or environment variables +# 2. try local file +function(_c4cov_get_service_token service out) + # try cmake var + string(TOUPPER ${service} uservice) + c4_get_from_first_of(token COVERAGE_${uservice}_TOKEN ENV) + if(NOT ("${token}" STREQUAL "")) + c4_dbg("${service}: found token from variable: ${token}") + else() + # try local file + set(service_token_file ${CMAKE_SOURCE_DIR}/.ci/${service}.token) + if(EXISTS ${service_token_file}) + file(READ ${service_token_file} token) + c4_dbg("found token file for ${service} coverage report: ${service_token_file}") + else() + c4_dbg("could not find token for ${service} coverage report") + endif() + endif() + set(${out} ${token} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_add_umbrella_target target umbrella_target) + _c4_handle_args(_ARGS ${ARGN} + # zero-value macro arguments + _ARGS0 + ALWAYS # Add the umbrella target even if this is the only one under it. + # The default behavior is to add the umbrella target only if + # there is more than one target under it. + # one-value macro arguments + _ARGS1 + PREFIX # The project prefix. Defaults to ${_c4_lprefix} + # multi-value macro arguments + _ARGSN + ARGS # more args to add_custom_target() + ) + if(NOT _PREFIX) + set(_PREFIX "${_c4_lprefix}") + endif() + set(t ${_PREFIX}${target}) + set(ut ${_PREFIX}${umbrella_target}) + # if the umbrella target already exists, just add the dependency + if(TARGET ${ut}) + add_dependencies(${ut} ${t}) + else() + if(_ALWAYS) + add_custom_target(${ut} ${_ARGS}) + add_dependencies(${ut} ${t}) + else() + # check if there is more than one under the same umbrella + c4_get_proj_prop(${ut}_subtargets sub) + if(sub) + add_custom_target(${ut} ${_ARGS}) + add_dependencies(${ut} ${sub}) + add_dependencies(${ut} ${t}) + else() + c4_set_proj_prop(${ut}_subtargets ${t}) + endif() + endif() + endif() +endfunction() + + + +function(c4_download_file url dstpath) + c4_dbg("downloading file: ${url} ---> ${dstpath}") + get_filename_component(abspath ${dstpath} ABSOLUTE) + if(NOT EXISTS ${abspath}) + c4_dbg("downloading file: does not exist: ${dstpath}") + file(DOWNLOAD ${url} ${abspath} LOG dl_log STATUS status ${ARGN}) + if((NOT (status EQUAL 0)) OR (NOT EXISTS ${abspath})) + c4_err("error downloading file: ${url} -> ${abspath}:\n${dl_log}") + endif() + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +function(c4_setup_benchmarking) + c4_log("enabling benchmarks: to build, ${_c4_lprefix}bm-build") + c4_log("enabling benchmarks: to run, ${_c4_lprefix}bm-run") + # umbrella target for building test binaries + add_custom_target(${_c4_lprefix}bm-build) + # umbrella target for running benchmarks + add_custom_target(${_c4_lprefix}bm-run + ${CMAKE_COMMAND} -E echo CWD=${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${_c4_lprefix}bm-build + ) + if(NOT TARGET bm-run) + add_custom_target(bm-run) + add_custom_target(bm-build) + endif() + add_dependencies(bm-run ${_c4_lprefix}bm-run) + add_dependencies(bm-build ${_c4_lprefix}bm-build) + _c4_set_target_folder(${_c4_lprefix}bm-run bm) + _c4_set_target_folder(${_c4_lprefix}bm-build bm) + _c4_set_target_folder(bm-build "/bm") + _c4_set_target_folder(bm-run "/bm") + # download google benchmark + if(NOT TARGET benchmark) + c4_import_remote_proj(googlebenchmark ${CMAKE_CURRENT_BINARY_DIR}/ext/googlebenchmark + REMOTE + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG main GIT_SHALLOW ON + OVERRIDE + BENCHMARK_ENABLE_TESTING OFF + BENCHMARK_ENABLE_EXCEPTIONS OFF + BENCHMARK_ENABLE_LTO OFF + SET_FOLDER_TARGETS ext benchmark benchmark_main + EXCLUDE_FROM_ALL + ) + # + if((CMAKE_CXX_COMPILER_ID STREQUAL GNU) OR (CMAKE_COMPILER_IS_GNUCC)) + target_compile_options(benchmark PRIVATE -Wno-deprecated-declarations) + target_compile_options(benchmark PRIVATE -Wno-restrict) + endif() + # + if(NOT WIN32) + option(${_c4_uprefix}BENCHMARK_CPUPOWER + "set the cpu mode to performance before / powersave after the benchmark" OFF) + if(${_c4_uprefix}BENCHMARK_CPUPOWER) + find_program(C4_SUDO sudo) + find_program(C4_CPUPOWER cpupower) + endif() + endif() + endif() +endfunction() + + +function(c4_add_benchmark_cmd casename) + add_custom_target(${casename} + COMMAND ${ARGN} + VERBATIM + COMMENT "${_c4_prefix}: running benchmark ${casename}: ${ARGN}") + add_dependencies(${_c4_lprefix}bm-build ${casename}) + _c4_set_target_folder(${casename} bm) +endfunction() + + +# assumes this is a googlebenchmark target, and that multiple +# benchmarks are defined from it +function(c4_add_target_benchmark target casename) + set(opt0arg + ) + set(opt1arg + WORKDIR # working directory + FILTER # benchmark patterns to filter + UMBRELLA_TARGET + RESULTS_FILE + ) + set(optnarg + ARGS + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + # + set(name "${target}-${casename}") + set(rdir "${CMAKE_CURRENT_BINARY_DIR}/bm-results") + set(rfile "${rdir}/${name}.json") + if(_RESULTS_FILE) + set(${_RESULTS_FILE} "${rfile}" PARENT_SCOPE) + endif() + if(NOT EXISTS "${rdir}") + file(MAKE_DIRECTORY "${rdir}") + endif() + set(filter) + if(NOT ("${_FILTER}" STREQUAL "")) + set(filter "--benchmark_filter=${_FILTER}") + endif() + set(args_fwd ${filter} --benchmark_out_format=json --benchmark_out=${rfile} ${_ARGS}) + c4_add_benchmark(${target} + "${name}" + "${_WORKDIR}" + "saving results in ${rfile}" + ${args_fwd} + OUTPUT_FILE ${rfile}) + if(_UMBRELLA_TARGET) + add_dependencies(${_UMBRELLA_TARGET} "${name}") + endif() +endfunction() + + +function(c4_add_benchmark target casename work_dir comment) + set(opt0arg + ) + set(opt1arg + OUTPUT_FILE + ) + set(optnarg + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + if(NOT TARGET ${target}) + c4_err("target ${target} does not exist...") + endif() + if(NOT ("${work_dir}" STREQUAL "")) + if(NOT EXISTS "${work_dir}") + file(MAKE_DIRECTORY "${work_dir}") + endif() + endif() + set(exe $<TARGET_FILE:${target}>) + if(${_c4_uprefix}BENCHMARK_CPUPOWER) + if(C4_BM_SUDO AND C4_BM_CPUPOWER) + set(c ${C4_SUDO} ${C4_CPUPOWER} frequency-set --governor performance) + set(cpupow_before + COMMAND echo ${c} + COMMAND ${c}) + set(c ${C4_SUDO} ${C4_CPUPOWER} frequency-set --governor powersave) + set(cpupow_after + COMMAND echo ${c} + COMMAND ${c}) + endif() + endif() + if(_OUTPUT_FILE) + set(_OUTPUT_FILE BYPRODUCTS ${_OUTPUT_FILE}) + set(_OUTPUT_FILE) # otherwise the benchmarks run everytime when building depending targets + endif() + add_custom_target(${casename} + ${cpupow_before} + # this is useful to show the target file (you cannot echo generator variables) + #COMMAND ${CMAKE_COMMAND} -E echo "target file = $<TARGET_FILE:${target}>" + COMMAND ${CMAKE_COMMAND} -E echo "${exe} ${ARGN}" + COMMAND "${exe}" ${ARGN} + ${cpupow_after} + VERBATIM + ${_OUTPUT_FILE} + WORKING_DIRECTORY "${work_dir}" + DEPENDS ${target} + COMMENT "${_c4_lcprefix}: running benchmark ${target}, case ${casename}: ${comment}" + ) + add_dependencies(${_c4_lprefix}bm-build ${target}) + add_dependencies(${_c4_lprefix}bm-run ${casename}) + _c4_set_target_folder(${casename} bm/run) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(_c4cat_get_outname target id ext out) + if("${_c4_lcprefix}" STREQUAL "${target}") + set(p "${target}") + else() + set(p "${_c4_lcprefix}.${target}") + endif() + set(${out} "${CMAKE_CURRENT_BINARY_DIR}/${p}.${id}.${ext}" PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_srcs in out) + _c4cat_filter_extensions("${in}" "${C4_SRC_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_hdrs in out) + _c4cat_filter_extensions("${in}" "${C4_HDR_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_srcs_hdrs in out) + _c4cat_filter_extensions("${in}" "${C4_HDR_EXTS};${C4_SRC_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_additional_exts in out) + _c4cat_filter_extensions("${in}" "${C4_ADD_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_extensions in filter out) + set(l) + foreach(fn ${in}) # don't quote the list here + _c4cat_get_file_ext("${fn}" ext) + _c4cat_one_of("${ext}" "${filter}" yes) + if(${yes}) + list(APPEND l "${fn}") + endif() + endforeach() + set(${out} "${l}" PARENT_SCOPE) +endfunction() + +function(_c4cat_get_file_ext in out) + # https://stackoverflow.com/questions/30049180/strip-filename-shortest-extension-by-cmake-get-filename-removing-the-last-ext + string(REGEX MATCH "^.*\\.([^.]*)$" dummy ${in}) + set(${out} ${CMAKE_MATCH_1} PARENT_SCOPE) +endfunction() + +function(_c4cat_one_of ext candidates out) + foreach(e ${candidates}) + if("${ext}" STREQUAL "${e}") + set(${out} TRUE PARENT_SCOPE) + return() + endif() + endforeach() + set(${out} FALSE PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# given a list of source files, return a list with full paths +function(c4_to_full_path source_list source_list_with_full_paths) + set(l) + foreach(f ${source_list}) + if(IS_ABSOLUTE "${f}") + list(APPEND l "${f}") + else() + list(APPEND l "${CMAKE_CURRENT_SOURCE_DIR}/${f}") + endif() + endforeach() + set(${source_list_with_full_paths} ${l} PARENT_SCOPE) +endfunction() + + +# convert a list to a string separated with spaces +function(c4_separate_list input_list output_string) + set(s) + foreach(e ${input_list}) + set(s "${s} ${e}") + endforeach() + set(${output_string} ${s} PARENT_SCOPE) +endfunction() + + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +endif(NOT _c4_project_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake b/thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake new file mode 100644 index 000000000..a064f91ee --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake @@ -0,0 +1,292 @@ +# (C) 2017 Joao Paulo Magalhaes <[email protected]> +if(NOT _c4_sanitize_target_included) +set(_c4_sanitize_target_included ON) + +include(CMakeDependentOption) +include(PrintVar) + +function(_c4_default_if_not_set var dft) + if("${${var}}" STREQUAL "") + option(${var} "" ${dft}) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_setup_sanitize umbrella_option) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") + return() + endif() + if(NOT ((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))) + return() + endif() + + _c4_default_if_not_set(C4_SANITIZE ON) + _c4_default_if_not_set(C4_SANITIZE_ONLY OFF) + _c4_default_if_not_set(C4_ASAN ON) + _c4_default_if_not_set(C4_TSAN ON) + _c4_default_if_not_set(C4_MSAN ON) + _c4_default_if_not_set(C4_UBSAN ON) + + cmake_dependent_option(${_c4_uprefix}SANITIZE "turn on clang sanitizer targets" ${C4_SANITIZE} ${umbrella_option} OFF) + cmake_dependent_option(${_c4_uprefix}SANITIZE_ONLY "compile only sanitize targets (not the regular unsanitized targets)" ${C4_SANITIZE_ONLY} ${umbrella_option} OFF) + + # options for individual sanitizers - contingent on sanitize on/off + cmake_dependent_option(${_c4_uprefix}ASAN "" ${C4_ASAN} "${_c4_uprefix}SANITIZE" OFF) + cmake_dependent_option(${_c4_uprefix}TSAN "" ${C4_TSAN} "${_c4_uprefix}SANITIZE" OFF) + cmake_dependent_option(${_c4_uprefix}MSAN "" ${C4_MSAN} "${_c4_uprefix}SANITIZE" OFF) + cmake_dependent_option(${_c4_uprefix}UBSAN "" ${C4_UBSAN} "${_c4_uprefix}SANITIZE" OFF) + + if(${_c4_uprefix}SANITIZE) + string(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" LLVM_VERSION "${CMAKE_CXX_COMPILER_VERSION}") + find_program(LLVM_SYMBOLIZER llvm-symbolizer + NAMES llvm-symbolizer-${LLVM_VERSION} llvm-symbolizer + DOC "symbolizer to use in sanitize tools") + if(NOT LLVM_SYMBOLIZER) + string(REGEX REPLACE "([0-9]+)\\.[0-9]+.*" "\\1" LLVM_VERSION "${CMAKE_CXX_COMPILER_VERSION}") + find_program(LLVM_SYMBOLIZER llvm-symbolizer + NAMES llvm-symbolizer-${LLVM_VERSION} llvm-symbolizer + DOC "symbolizer to use in sanitize tools") + if(NOT LLVM_SYMBOLIZER) + message(FATAL_ERROR "could not find symbolizer. LLVM_VERSION=${LLVM_VERSION}") + endif() + endif() + + set(ss) # string to report enabled sanitizers + + if(${_c4_uprefix}ASAN) + set(ss "asan") + set(${_c4_uprefix}ASAN_CFLAGS "-O1 -g -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "compile flags for clang address sanitizer: https://clang.llvm.org/docs/AddressSanitizer.html") + set(${_c4_uprefix}ASAN_LFLAGS "-g -fsanitize=address" CACHE STRING "linker flags for clang address sanitizer: https://clang.llvm.org/docs/AddressSanitizer.html") + set(${_c4_uprefix}ASAN_RENV "env ASAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} ASAN_OPTIONS=symbolize=1" CACHE STRING "run environment for clang address sanitizer: https://clang.llvm.org/docs/AddressSanitizer.html") + # the flags are strings; we need to separate them into a list + # to prevent cmake from quoting them when passing to the targets + separate_arguments(${_c4_uprefix}ASAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}ASAN_CFLAGS}) + separate_arguments(${_c4_uprefix}ASAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}ASAN_LFLAGS}) + endif() + + if(${_c4_uprefix}TSAN) + set(ss "${ss} tsan") + set(${_c4_uprefix}TSAN_CFLAGS "-O1 -g -fsanitize=thread -fno-omit-frame-pointer" CACHE STRING "compile flags for clang thread sanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html") + set(${_c4_uprefix}TSAN_LFLAGS "-g -fsanitize=thread" CACHE STRING "linker flags for clang thread sanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html") + set(${_c4_uprefix}TSAN_RENV "env TSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} TSAN_OPTIONS=symbolize=1" CACHE STRING "run environment for clang thread sanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html") + separate_arguments(${_c4_uprefix}TSAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}TSAN_CFLAGS}) + separate_arguments(${_c4_uprefix}TSAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}TSAN_LFLAGS}) + endif() + + if(${_c4_uprefix}MSAN) + set(ss "${ss} msan") + set(${_c4_uprefix}MSAN_CFLAGS "-O1 -g -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "compile flags for clang memory sanitizer: https://clang.llvm.org/docs/MemorySanitizer.html") + set(${_c4_uprefix}MSAN_LFLAGS "-g -fsanitize=memory" CACHE STRING "linker flags for clang memory sanitizer: https://clang.llvm.org/docs/MemorySanitizer.html") + set(${_c4_uprefix}MSAN_RENV "env MSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} MSAN_OPTIONS=symbolize=1" CACHE STRING "run environment for clang memory sanitizer: https://clang.llvm.org/docs/MemorySanitizer.html") + separate_arguments(${_c4_uprefix}MSAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}MSAN_CFLAGS}) + separate_arguments(${_c4_uprefix}MSAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}MSAN_LFLAGS}) + endif() + + if(${_c4_uprefix}UBSAN) + set(ss "${ss} ubsan") + set(${_c4_uprefix}UBSAN_CFLAGS "-g -fsanitize=undefined" CACHE STRING "compile flags for clang undefined behaviour sanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html") + set(${_c4_uprefix}UBSAN_LFLAGS "-g -fsanitize=undefined" CACHE STRING "linker flags for clang undefined behaviour sanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html") + set(${_c4_uprefix}UBSAN_RENV "env UBSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} UBSAN_OPTIONS='symbolize=1 print_stacktrace=1'" CACHE STRING "run environment for clang undefined behaviour sanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html") + separate_arguments(${_c4_uprefix}UBSAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}UBSAN_CFLAGS}) + separate_arguments(${_c4_uprefix}UBSAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}UBSAN_LFLAGS}) + endif() + + c4_dbg("enabled clang sanitizers: ${ss}") + endif() # ${_c4_uprefix}SANITIZE + +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_sanitize_get_target_command name which_sanitizer_ output) + string(TOUPPER ${which_sanitizer_} which_sanitizer) + if("${which_sanitizer}" STREQUAL ASAN) + elseif("${which_sanitizer}" STREQUAL TSAN) + elseif("${which_sanitizer}" STREQUAL MSAN) + elseif("${which_sanitizer}" STREQUAL UBSAN) + else() + message(FATAL_ERROR "the sanitizer must be one of: ASAN, TSAN, MSAN, UBSAN") + endif() + separate_arguments(cmd UNIX_COMMAND "${${_c4_uprefix}${which_sanitizer}_RENV} ${name}") + set(${output} ${cmd} PARENT_SCOPE) +endfunction() + +function(_sanitize_set_target_folder tgt folder) + if(folder) + set_target_properties(${tgt} PROPERTIES FOLDER "${folder}") + endif() +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_sanitize_target name) + set(opt0arg + LIBRARY + EXECUTABLE + ) + set(opt1arg + OUTPUT_TARGET_NAMES + FOLDER + ) + set(optnarg + SOURCES + INC_DIRS # TODO public, interface, private + LIBS # TODO public, interface, private + LIB_DIRS # TODO public, interface, private + DEFS # TODO public, interface, private + CFLAGS # TODO public, interface, private + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + + if((NOT _LIBRARY) AND (NOT _EXECUTABLE)) + c4_err("either LIBRARY or EXECUTABLE must be specified") + endif() + + if(${_c4_uprefix}SANITIZE AND NOT TARGET ${_c4_lprefix}sanitize) + add_custom_target(${_c4_lprefix}sanitize) + _sanitize_set_target_folder(${_c4_lprefix}sanitize "${_FOLDER}") + endif() + if(${_c4_uprefix}ASAN AND NOT TARGET ${_c4_lprefix}asan-all) + add_custom_target(${_c4_lprefix}asan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}asan-all) + _sanitize_set_target_folder(${_c4_lprefix}asan-all "${_FOLDER}") + endif() + if(${_c4_uprefix}MSAN AND NOT TARGET ${_c4_lprefix}msan-all) + add_custom_target(${_c4_lprefix}msan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}msan-all) + _sanitize_set_target_folder(${_c4_lprefix}msan-all "${_FOLDER}") + endif() + if(${_c4_uprefix}TSAN AND NOT TARGET ${_c4_lprefix}tsan-all) + add_custom_target(${_c4_lprefix}tsan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}tsan-all) + _sanitize_set_target_folder(${_c4_lprefix}tsan-all "${_FOLDER}") + endif() + if(${_c4_uprefix}UBSAN AND NOT TARGET ${_c4_lprefix}ubsan-all) + add_custom_target(${_c4_lprefix}ubsan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}ubsan-all) + _sanitize_set_target_folder(${_c4_lprefix}ubsan-all "${_FOLDER}") + endif() + + if(${_c4_uprefix}ASAN OR ${_c4_uprefix}MSAN OR ${_c4_uprefix}TSAN OR ${_c4_uprefix}UBSAN) + add_custom_target(${name}-sanitize-all) + _sanitize_set_target_folder(${name}-sanitize-all "${_FOLDER}") + endif() + + set(targets) + + # https://clang.llvm.org/docs/AddressSanitizer.html + if(${_c4_uprefix}ASAN) + if(${_LIBRARY}) + add_library(${name}-asan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-asan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-asan "${_FOLDER}") + list(APPEND targets ${name}-asan) + target_include_directories(${name}-asan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-asan) + list(APPEND _real_libs ${_l}-asan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-asan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-asan PUBLIC ${_DEFS}) + target_compile_options(${name}-asan PUBLIC ${_CFLAGS} ${${_c4_uprefix}ASAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-asan PUBLIC ${${_c4_uprefix}ASAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}asan-all ${name}-asan) + add_dependencies(${name}-sanitize-all ${name}-asan) + endif() + + # https://clang.llvm.org/docs/ThreadSanitizer.html + if(${_c4_uprefix}TSAN) + if(${_LIBRARY}) + add_library(${name}-tsan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-tsan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-tsan "${_FOLDER}") + list(APPEND targets ${name}-tsan) + target_include_directories(${name}-tsan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-tsan) + list(APPEND _real_libs ${_l}-tsan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-tsan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-tsan PUBLIC ${_DEFS}) + target_compile_options(${name}-tsan PUBLIC ${_CFLAGS} ${${_c4_uprefix}TSAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-tsan PUBLIC ${${_c4_uprefix}TSAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}tsan-all ${name}-tsan) + add_dependencies(${name}-sanitize-all ${name}-tsan) + endif() + + # https://clang.llvm.org/docs/MemorySanitizer.html + if(${_c4_uprefix}MSAN) + if(${_LIBRARY}) + add_library(${name}-msan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-msan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-msan "${_FOLDER}") + list(APPEND targets ${name}-msan) + target_include_directories(${name}-msan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-msan) + list(APPEND _real_libs ${_l}-msan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-msan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-msan PUBLIC ${_DEFS}) + target_compile_options(${name}-msan PUBLIC ${_CFLAGS} ${${_c4_uprefix}MSAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-msan PUBLIC ${${_c4_uprefix}MSAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}msan-all ${name}-msan) + add_dependencies(${name}-sanitize-all ${name}-msan) + endif() + + # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html + if(${_c4_uprefix}UBSAN) + if(${_LIBRARY}) + add_library(${name}-ubsan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-ubsan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-ubsan "${_FOLDER}") + list(APPEND targets ${name}-ubsan) + target_include_directories(${name}-ubsan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-ubsan) + list(APPEND _real_libs ${_l}-ubsan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-ubsan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-ubsan PUBLIC ${_DEFS}) + target_compile_options(${name}-ubsan PUBLIC ${_CFLAGS} ${${_c4_uprefix}UBSAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-ubsan PUBLIC ${${_c4_uprefix}UBSAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}ubsan-all ${name}-ubsan) + add_dependencies(${name}-sanitize-all ${name}-ubsan) + endif() + + if(_OUTPUT_TARGET_NAMES) + set(${_OUTPUT_TARGET_NAMES} ${targets} PARENT_SCOPE) + endif() +endfunction() + + +endif(NOT _c4_sanitize_target_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake b/thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake new file mode 100644 index 000000000..06de5ca2f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake @@ -0,0 +1,154 @@ +include(PVS-Studio) +include(GetFlags) +include(c4GetTargetPropertyRecursive) + + +function(_c4sta_default_if_not_set var dft) + if("${${var}}" STREQUAL "") + set(${var} "${dft}" PARENT_SCOPE) + endif() +endfunction() + + +function(c4_setup_static_analysis umbrella_option) + if(WIN32) + c4_dbg("no static analyzer available in WIN32") + return() + endif() + if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") + c4_dbg("Coverage build: disabling static analyzers") + return() + endif() + _c4sta_default_if_not_set(C4_LINT ${umbrella_option}) + _c4sta_default_if_not_set(C4_LINT_TESTS ${umbrella_option}) + _c4sta_default_if_not_set(C4_LINT_CLANG_TIDY ${umbrella_option}) + _c4sta_default_if_not_set(C4_LINT_PVS_STUDIO OFF) + # option to turn lints on/off + cmake_dependent_option(${_c4_uprefix}LINT "add static analyzer targets" ${C4_LINT} ${umbrella_option} OFF) + cmake_dependent_option(${_c4_uprefix}LINT_TESTS "add tests to run static analyzer targets" ${C4_LINT_TESTS} ${umbrella_option} OFF) + # options for individual lints - contingent on linting on/off + cmake_dependent_option(${_c4_uprefix}LINT_CLANG_TIDY "use the clang-tidy static analyzer" ${C4_LINT_CLANG_TIDY} "${_c4_uprefix}LINT" ON) + cmake_dependent_option(${_c4_uprefix}LINT_PVS_STUDIO "use the PVS-Studio static analyzer https://www.viva64.com/en/b/0457/" ${C4_LINT_PVS_STUDIO} "${_c4_uprefix}LINT" OFF) + if(${_c4_uprefix}LINT_CLANG_TIDY) + find_program(CLANG_TIDY clang-tidy) + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO) + set(${_c4_uprefix}LINT_PVS_STUDIO_FORMAT "errorfile" CACHE STRING "PVS-Studio output format. Choices: xml,csv,errorfile(like gcc/clang),tasklist(qtcreator)") + endif() + # + set(sa) + if(${_c4_uprefix}LINT_CLANG_TIDY) + set(sa "clang_tidy") + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO) + set(sa "${sa} PVS-Studio") + endif() + if(sa) + c4_dbg("enabled static analyzers: ${sa}") + endif() +endfunction() + + +function(c4_static_analysis_target target_name folder generated_targets) + set(any_linter OFF) + if(${_c4_uprefix}LINT_CLANG_TIDY OR ${_c4_uprefix}LINT_PVS_STUDIO) + set(any_linter ON) + endif() + if(${_c4_uprefix}LINT AND any_linter) + # umbrella target for running all linters for this particular target + if(any_linter AND NOT TARGET ${_c4_lprefix}lint-all) + add_custom_target(${_c4_lprefix}lint-all) + if(folder) + #message(STATUS "${target_name}: folder=${folder}") + set_target_properties(${_c4_lprefix}lint-all PROPERTIES FOLDER "${folder}") + endif() + endif() + if(${_c4_uprefix}LINT_CLANG_TIDY) + c4_static_analysis_clang_tidy(${target_name} + ${target_name}-lint-clang_tidy + ${_c4_lprefix}lint-all-clang_tidy + "${folder}") + list(APPEND ${generated_targets} ${_c4_lprefix}lint-clang_tidy) + add_dependencies(${_c4_lprefix}lint-all ${_c4_lprefix}lint-all-clang_tidy) + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO) + c4_static_analysis_pvs_studio(${target_name} + ${target_name}-lint-pvs_studio + ${_c4_lprefix}lint-all-pvs_studio + "${folder}") + list(APPEND ${generated_targets} ${_c4_lprefix}lint-pvs_studio) + add_dependencies(${_c4_lprefix}lint-all ${_c4_lprefix}lint-all-pvs_studio) + endif() + endif() +endfunction() + + +function(c4_static_analysis_add_tests target_name) + if(${_c4_uprefix}LINT_CLANG_TIDY AND ${_c4_uprefix}LINT_TESTS) + add_test(NAME ${target_name}-lint-clang_tidy-run + COMMAND + ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR} --target ${target_name}-lint-clang_tidy) + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO AND ${_c4_uprefix}LINT_TESTS) + add_test(NAME ${target_name}-lint-pvs_studio-run + COMMAND + ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR} --target ${target_name}-lint-pvs_studio) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_static_analysis_clang_tidy subj_target lint_target umbrella_target folder) + c4_static_analysis_clang_tidy_get_cmd(${subj_target} ${lint_target} cmd) + string(REPLACE ";" " " cmd_str "${cmd}") + add_custom_target(${lint_target} + COMMAND ${CMAKE_COMMAND} -E echo "cd ${CMAKE_CURRENT_SOURCE_DIR} ; ${cmd_str}" + COMMAND ${cmd} + VERBATIM + COMMENT "clang-tidy: analyzing sources of ${subj_target}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(folder) + set_target_properties(${lint_target} PROPERTIES FOLDER "${folder}") + endif() + if(NOT TARGET ${umbrella_target}) + add_custom_target(${umbrella_target}) + endif() + add_dependencies(${umbrella_target} ${lint_target}) +endfunction() + +function(c4_static_analysis_clang_tidy_get_cmd subj_target lint_target cmd) + get_target_property(_clt_all_srcs ${subj_target} SOURCES) + _c4cat_filter_srcs_hdrs("${_clt_all_srcs}" _clt_srcs) + set(result "${CLANG_TIDY}" -p ${CMAKE_BINARY_DIR} --header-filter=.* ${_clt_srcs}) + set(${cmd} ${result} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_static_analysis_pvs_studio subj_target lint_target umbrella_target folder) + c4_get_target_property_recursive(_c4al_pvs_incs ${subj_target} INCLUDE_DIRECTORIES) + c4_get_include_flags(_c4al_pvs_incs ${_c4al_pvs_incs}) + separate_arguments(_c4al_cxx_flags_sep UNIX_COMMAND "${CMAKE_CXX_FLAGS} ${_c4al_pvs_incs}") + separate_arguments(_c4al_c_flags_sep UNIX_COMMAND "${CMAKE_C_FLAGS} ${_c4al_pvs_incs}") + pvs_studio_add_target(TARGET ${lint_target} + ALL # indicates that the analysis starts when you build the project + #PREPROCESSOR ${_c4al_preproc} + FORMAT tasklist + LOG "${CMAKE_CURRENT_BINARY_DIR}/${subj_target}.pvs-analysis.tasks" + ANALYZE ${name} #main_target subtarget:path/to/subtarget + CXX_FLAGS ${_c4al_cxx_flags_sep} + C_FLAGS ${_c4al_c_flags_sep} + #CONFIG "/path/to/PVS-Studio.cfg" + ) + if(folder) + set_target_properties(${lint_target} PROPERTIES FOLDER "${folder}") + endif() + if(NOT TARGET ${umbrella_target}) + add_custom_target(${umbrella_target}) + endif() + add_dependencies(${umbrella_target} ${lint_target}) +endfunction() + +function(c4_static_analysis_pvs_studio_get_cmd subj_target lint_target cmd) + set(${cmd} $<RULE_LAUNCH_CUSTOM:${subj_target}> PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake b/thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake new file mode 100644 index 000000000..07835977b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake @@ -0,0 +1,5 @@ +include(c4project) + +function(c4stl_add_target name) + c4_add_target(c4stl ${name} ${ARGN}) +endfunction() # c4stl_add_target diff --git a/thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp b/thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp new file mode 100644 index 000000000..83cad6256 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp @@ -0,0 +1,69 @@ +#ifndef _C4_COMPAT_GCC_4_8_HPP_ +#define _C4_COMPAT_GCC_4_8_HPP_ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 8 +/* STL polyfills for old GNU compilers */ + +_Pragma("GCC diagnostic ignored \"-Wshadow\"") +_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") + +#if __cplusplus +#include <cstdint> +#include <type_traits> + +namespace std { + +template<typename _Tp> +struct is_trivially_copyable : public integral_constant<bool, + is_destructible<_Tp>::value && __has_trivial_destructor(_Tp) && + (__has_trivial_constructor(_Tp) || __has_trivial_copy(_Tp) || __has_trivial_assign(_Tp))> +{ }; + +template<typename _Tp> +using is_trivially_copy_constructible = has_trivial_copy_constructor<_Tp>; + +template<typename _Tp> +using is_trivially_default_constructible = has_trivial_default_constructor<_Tp>; + +template<typename _Tp> +using is_trivially_copy_assignable = has_trivial_copy_assign<_Tp>; + +/* not supported */ +template<typename _Tp> +struct is_trivially_move_constructible : false_type +{ }; + +/* not supported */ +template<typename _Tp> +struct is_trivially_move_assignable : false_type +{ }; + +inline void *align(size_t __align, size_t __size, void*& __ptr, size_t& __space) noexcept +{ + if (__space < __size) + return nullptr; + const auto __intptr = reinterpret_cast<uintptr_t>(__ptr); + const auto __aligned = (__intptr - 1u + __align) & -__align; + const auto __diff = __aligned - __intptr; + if (__diff > (__space - __size)) + return nullptr; + else + { + __space -= __diff; + return __ptr = reinterpret_cast<void*>(__aligned); + } +} +typedef long double max_align_t ; + +} +#else // __cplusplus + +#include <string.h> +// see https://sourceware.org/bugzilla/show_bug.cgi?id=25399 (ubuntu gcc-4.8) +#define memset(s, c, count) __builtin_memset(s, c, count) + +#endif // __cplusplus + +#endif // __GNUC__ == 4 && __GNUC_MINOR__ >= 8 + +#endif // _C4_COMPAT_GCC_4_8_HPP_ diff --git a/thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch b/thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch new file mode 100644 index 000000000..07f0ca577 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch @@ -0,0 +1,97 @@ +Multi-line macros support is not guaranteed with gcc-4.8. + +This uses temporary objects to work-arround this limitation, main drawback is +that compared code is not displayed in message anymore (only "val" placeholders). + +--- googletest/include/gtest/gtest.h ++++ googletest/include/gtest/gtest.h +@@ -2040,6 +2040,80 @@ class TestWithParam : public Test, publi + // ASSERT_LT(i, array_size); + // ASSERT_GT(records.size(), 0) << "There is no record left."; + ++#if __GNUC__ == 4 && __GNUC_MINOR__ >= 8 ++/* ++ * multi-line macros support is not guaranteed with gcc-4.8. ++ * This uses temporary objects to work-arround this limitation, main drawback is ++ * that compared code is not displayed in message anymore (only "val" placeholders) ++ */ ++ ++enum class CompatExpectSelector { EQ, NE, LE, LT, GE, GT }; ++ ++template <CompatExpectSelector T> ++struct CompatExpect ++{ ++ const char *file; ++ int line; ++ ::testing::AssertionResult gtest_ar = AssertionSuccess(); ++ ::testing::Message msg; ++ ++ CompatExpect(const char *file, int line) : file(file), line(line) {} ++ ~CompatExpect() { ++ if (!gtest_ar) ++ GTEST_MESSAGE_AT_(file, line, gtest_ar.failure_message(), ::testing::TestPartResult::kNonFatalFailure) << msg; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::EQ, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::EqHelper::Compare("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::NE, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperNE("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::LE, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperLE("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::LT, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperLT("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::GE, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperGE("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::GT, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperGT("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1> ++ CompatExpect &operator << (const T1 &t) { ++ msg << t; ++ return *this; ++ } ++}; ++#define EXPECT_EQ ::testing::CompatExpect<::testing::CompatExpectSelector::EQ>{__FILE__,__LINE__} ++#define EXPECT_NE ::testing::CompatExpect<::testing::CompatExpectSelector::NE>{__FILE__,__LINE__} ++#define EXPECT_LE ::testing::CompatExpect<::testing::CompatExpectSelector::LE>{__FILE__,__LINE__} ++#define EXPECT_LT ::testing::CompatExpect<::testing::CompatExpectSelector::LT>{__FILE__,__LINE__} ++#define EXPECT_GE ::testing::CompatExpect<::testing::CompatExpectSelector::GE>{__FILE__,__LINE__} ++#define EXPECT_GT ::testing::CompatExpect<::testing::CompatExpectSelector::GT>{__FILE__,__LINE__} ++ ++#else ++ + #define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) + #define EXPECT_NE(val1, val2) \ +@@ -2053,6 +2127,8 @@ class TestWithParam : public Test, publi + #define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + ++#endif ++ + #define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) + #define GTEST_ASSERT_NE(val1, val2) \ diff --git a/thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt b/thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt new file mode 100644 index 000000000..baeb2ce4f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt @@ -0,0 +1,3 @@ +sphinx +sphinx_rtd_theme +breathe |