diff options
111 files changed, 13172 insertions, 63 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 699fd4d..75a0b52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -538,7 +538,7 @@ macro (CMAKE_BUILD_UTILITIES) #--------------------------------------------------------------------- # Build jsoncpp library. if(CMAKE_USE_SYSTEM_JSONCPP) - find_package(JsonCpp) + find_package(JsonCpp 1.4.1) if(NOT JsonCpp_FOUND) message(FATAL_ERROR "CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!") diff --git a/Help/index.rst b/Help/index.rst index fe1b73c..a948939 100644 --- a/Help/index.rst +++ b/Help/index.rst @@ -30,6 +30,7 @@ Reference Manuals /manual/cmake-compile-features.7 /manual/cmake-developer.7 /manual/cmake-env-variables.7 + /manual/cmake-file-api.7 /manual/cmake-generator-expressions.7 /manual/cmake-generators.7 /manual/cmake-language.7 diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst new file mode 100644 index 0000000..f3e0208 --- /dev/null +++ b/Help/manual/cmake-file-api.7.rst @@ -0,0 +1,1111 @@ +.. cmake-manual-description: CMake File-Based API + +cmake-file-api(7) +***************** + +.. only:: html + + .. contents:: + +Introduction +============ + +CMake provides a file-based API that clients may use to get semantic +information about the buildsystems CMake generates. Clients may use +the API by writing query files to a specific location in a build tree +to request zero or more `Object Kinds`_. When CMake generates the +buildsystem in that build tree it will read the query files and write +reply files for the client to read. + +The file-based API uses a ``<build>/.cmake/api/`` directory at the top +of a build tree. The API is versioned to support changes to the layout +of files within the API directory. API file layout versioning is +orthogonal to the versioning of `Object Kinds`_ used in replies. +This version of CMake supports only one API version, `API v1`_. + +API v1 +====== + +API v1 is housed in the ``<build>/.cmake/api/v1/`` directory. +It has the following subdirectories: + +``query/`` + Holds query files written by clients. + These may be `v1 Shared Stateless Query Files`_, + `v1 Client Stateless Query Files`_, or `v1 Client Stateful Query Files`_. + +``reply/`` + Holds reply files written by CMake whenever it runs to generate a build + system. These are indexed by a `v1 Reply Index File`_ file that may + reference additional `v1 Reply Files`_. CMake owns all reply files. + Clients must never remove them. + + Clients may look for and read a reply index file at any time. + Clients may optionally create the ``reply/`` directory at any time + and monitor it for the appearance of a new reply index file. + +v1 Shared Stateless Query Files +------------------------------- + +Shared stateless query files allow clients to share requests for +major versions of the `Object Kinds`_ and get all requested versions +recognized by the CMake that runs. + +Clients may create shared requests by creating empty files in the +``v1/query/`` directory. The form is:: + + <build>/.cmake/api/v1/query/<kind>-v<major> + +where ``<kind>`` is one of the `Object Kinds`_, ``-v`` is literal, +and ``<major>`` is the major version number. + +Files of this form are stateless shared queries not owned by any specific +client. Once created they should not be removed without external client +coordination or human intervention. + +v1 Client Stateless Query Files +------------------------------- + +Client stateless query files allow clients to create owned requests for +major versions of the `Object Kinds`_ and get all requested versions +recognized by the CMake that runs. + +Clients may create owned requests by creating empty files in +client-specific query subdirectories. The form is:: + + <build>/.cmake/api/v1/query/client-<client>/<kind>-v<major> + +where ``client-`` is literal, ``<client>`` is a string uniquely +identifying the client, ``<kind>`` is one of the `Object Kinds`_, +``-v`` is literal, and ``<major>`` is the major version number. +Each client must choose a unique ``<client>`` identifier via its +own means. + +Files of this form are stateless queries owned by the client ``<client>``. +The owning client may remove them at any time. + +v1 Client Stateful Query Files +------------------------------ + +Stateful query files allow clients to request a list of versions of +each of the `Object Kinds`_ and get only the most recent version +recognized by the CMake that runs. + +Clients may create owned stateful queries by creating ``query.json`` +files in client-specific query subdirectories. The form is:: + + <build>/.cmake/api/v1/query/client-<client>/query.json + +where ``client-`` is literal, ``<client>`` is a string uniquely +identifying the client, and ``query.json`` is literal. Each client +must choose a unique ``<client>`` identifier via its own means. + +``query.json`` files are stateful queries owned by the client ``<client>``. +The owning client may update or remove them at any time. When a +given client installation is updated it may then update the stateful +query it writes to build trees to request newer object versions. +This can be used to avoid asking CMake to generate multiple object +versions unnecessarily. + +A ``query.json`` file must contain a JSON object: + +.. code-block:: json + + { + "requests": [ + { "kind": "<kind>" , "version": 1 }, + { "kind": "<kind>" , "version": { "major": 1, "minor": 2 } }, + { "kind": "<kind>" , "version": [2, 1] }, + { "kind": "<kind>" , "version": [2, { "major": 1, "minor": 2 }] }, + { "kind": "<kind>" , "version": 1, "client": {} }, + { "kind": "..." } + ], + "client": {} + } + +The members are: + +``requests`` + A JSON array containing zero or more requests. Each request is + a JSON object with members: + + ``kind`` + Specifies one of the `Object Kinds`_ to be included in the reply. + + ``version`` + Indicates the version(s) of the object kind that the client + understands. Versions have major and minor components following + semantic version conventions. The value must be + + * a JSON integer specifying a (non-negative) major version number, or + * a JSON object containing ``major`` and (optionally) ``minor`` + members specifying non-negative integer version components, or + * a JSON array whose elements are each one of the above. + + ``client`` + Optional member reserved for use by the client. This value is + preserved in the reply written for the client in the + `v1 Reply Index File`_ but is otherwise ignored. Clients may use + this to pass custom information with a request through to its reply. + + For each requested object kind CMake will choose the *first* version + that it recognizes for that kind among those listed in the request. + The response will use the selected *major* version with the highest + *minor* version known to the running CMake for that major version. + Therefore clients should list all supported major versions in + preferred order along with the minimal minor version required + for each major version. + +``client`` + Optional member reserved for use by the client. This value is + preserved in the reply written for the client in the + `v1 Reply Index File`_ but is otherwise ignored. Clients may use + this to pass custom information with a query through to its reply. + +Other ``query.json`` top-level members are reserved for future use. +If present they are ignored for forward compatibility. + +v1 Reply Index File +------------------- + +CMake writes an ``index-*.json`` file to the ``v1/reply/`` directory +whenever it runs to generate a build system. Clients must read the +reply index file first and may read other `v1 Reply Files`_ only by +following references. The form of the reply index file name is:: + + <build>/.cmake/api/v1/reply/index-<unspecified>.json + +where ``index-`` is literal and ``<unspecified>`` is an unspecified +name selected by CMake. Whenever a new index file is generated it +is given a new name and any old one is deleted. During the short +time between these steps there may be multiple index files present; +the one with the largest name in lexicographic order is the current +index file. + +The reply index file contains a JSON object: + +.. code-block:: json + + { + "cmake": { + "version": { + "major": 3, "minor": 14, "patch": 0, "suffix": "", + "string": "3.14.0", "isDirty": false + }, + "paths": { + "cmake": "/prefix/bin/cmake", + "ctest": "/prefix/bin/ctest", + "cpack": "/prefix/bin/cpack", + "root": "/prefix/share/cmake-3.14" + }, + "generator": { + "name": "Unix Makefiles" + } + }, + "objects": [ + { "kind": "<kind>", + "version": { "major": 1, "minor": 0 }, + "jsonFile": "<file>" }, + { "...": "..." } + ], + "reply": { + "<kind>-v<major>": { "kind": "<kind>", + "version": { "major": 1, "minor": 0 }, + "jsonFile": "<file>" }, + "<unknown>": { "error": "unknown query file" }, + "...": {}, + "client-<client>": { + "<kind>-v<major>": { "kind": "<kind>", + "version": { "major": 1, "minor": 0 }, + "jsonFile": "<file>" }, + "<unknown>": { "error": "unknown query file" }, + "...": {}, + "query.json": { + "requests": [ {}, {}, {} ], + "responses": [ + { "kind": "<kind>", + "version": { "major": 1, "minor": 0 }, + "jsonFile": "<file>" }, + { "error": "unknown query file" }, + { "...": {} } + ], + "client": {} + } + } + } + } + +The members are: + +``cmake`` + A JSON object containing information about the instance of CMake that + generated the reply. It contains members: + + ``version`` + A JSON object specifying the version of CMake with members: + + ``major``, ``minor``, ``patch`` + Integer values specifying the major, minor, and patch version components. + ``suffix`` + A string specifying the version suffix, if any, e.g. ``g0abc3``. + ``string`` + A string specifying the full version in the format + ``<major>.<minor>.<patch>[-<suffix>]``. + ``isDirty`` + A boolean indicating whether the version was built from a version + controlled source tree with local modifications. + + ``paths`` + A JSON object specifying paths to things that come with CMake. + It has members for ``cmake``, ``ctest``, and ``cpack`` whose values + are JSON strings specifying the absolute path to each tool, + represented with forward slashes. It also has a ``root`` member for + the absolute path to the directory containing CMake resources like the + ``Modules/`` directory (see :variable:`CMAKE_ROOT`). + + ``generator`` + A JSON object describing the CMake generator used for the build. + It has members: + + ``name`` + A string specifying the name of the generator. + ``platform`` + If the generator supports :variable:`CMAKE_GENERATOR_PLATFORM`, + this is a string specifying the generator platform name. + +``objects`` + A JSON array listing all versions of all `Object Kinds`_ generated + as part of the reply. Each array entry is a + `v1 Reply File Reference`_. + +``reply`` + A JSON object mirroring the content of the ``query/`` directory + that CMake loaded to produce the reply. The members are of the form + + ``<kind>-v<major>`` + A member of this form appears for each of the + `v1 Shared Stateless Query Files`_ that CMake recognized as a + request for object kind ``<kind>`` with major version ``<major>``. + The value is a `v1 Reply File Reference`_ to the corresponding + reply file for that object kind and version. + + ``<unknown>`` + A member of this form appears for each of the + `v1 Shared Stateless Query Files`_ that CMake did not recognize. + The value is a JSON object with a single ``error`` member + containing a string with an error message indicating that the + query file is unknown. + + ``client-<client>`` + A member of this form appears for each client-owned directory + holding `v1 Client Stateless Query Files`_. + The value is a JSON object mirroring the content of the + ``query/client-<client>/`` directory. The members are of the form: + + ``<kind>-v<major>`` + A member of this form appears for each of the + `v1 Client Stateless Query Files`_ that CMake recognized as a + request for object kind ``<kind>`` with major version ``<major>``. + The value is a `v1 Reply File Reference`_ to the corresponding + reply file for that object kind and version. + + ``<unknown>`` + A member of this form appears for each of the + `v1 Client Stateless Query Files`_ that CMake did not recognize. + The value is a JSON object with a single ``error`` member + containing a string with an error message indicating that the + query file is unknown. + + ``query.json`` + This member appears for clients using + `v1 Client Stateful Query Files`_. + If the ``query.json`` file failed to read or parse as a JSON object, + this member is a JSON object with a single ``error`` member + containing a string with an error message. Otherwise, this member + is a JSON object mirroring the content of the ``query.json`` file. + The members are: + + ``client`` + A copy of the ``query.json`` file ``client`` member, if it exists. + + ``requests`` + A copy of the ``query.json`` file ``requests`` member, if it exists. + + ``responses`` + If the ``query.json`` file ``requests`` member is missing or invalid, + this member is a JSON object with a single ``error`` member + containing a string with an error message. Otherwise, this member + contains a JSON array with a response for each entry of the + ``requests`` array, in the same order. Each response is + + * a JSON object with a single ``error`` member containing a string + with an error message, or + * a `v1 Reply File Reference`_ to the corresponding reply file for + the requested object kind and selected version. + +After reading the reply index file, clients may read the other +`v1 Reply Files`_ it references. + +v1 Reply File Reference +^^^^^^^^^^^^^^^^^^^^^^^ + +The reply index file represents each reference to another reply file +using a JSON object with members: + +``kind`` + A string specifying one of the `Object Kinds`_. +``version`` + A JSON object with members ``major`` and ``minor`` specifying + integer version components of the object kind. +``jsonFile`` + A JSON string specifying a path relative to the reply index file + to another JSON file containing the object. + +v1 Reply Files +-------------- + +Reply files containing specific `Object Kinds`_ are written by CMake. +The names of these files are unspecified and must not be interpreted +by clients. Clients must first read the `v1 Reply Index File`_ and +and follow references to the names of the desired response objects. + +Reply files (including the index file) will never be replaced by +files of the same name but different content. This allows a client +to read the files concurrently with a running CMake that may generate +a new reply. However, after generating a new reply CMake will attempt +to remove reply files from previous runs that it did not just write. +If a client attempts to read a reply file referenced by the index but +finds the file missing, that means a concurrent CMake has generated +a new reply. The client may simply start again by reading the new +reply index file. + +Object Kinds +============ + +The CMake file-based API reports semantic information about the build +system using the following kinds of JSON objects. Each kind of object +is versioned independently using semantic versioning with major and +minor components. Every kind of object has the form: + +.. code-block:: json + + { + "kind": "<kind>", + "version": { "major": 1, "minor": 0 }, + "...": {} + } + +The ``kind`` member is a string specifying the object kind name. +The ``version`` member is a JSON object with ``major`` and ``minor`` +members specifying integer components of the object kind's version. +Additional top-level members are specific to each object kind. + +Object Kind "codemodel" +----------------------- + +The ``codemodel`` object kind describes the build system structure as +modeled by CMake. + +There is only one ``codemodel`` object major version, version 2. +Version 1 does not exist to avoid confusion with that from +:manual:`cmake-server(7)` mode. + +"codemodel" version 2 +^^^^^^^^^^^^^^^^^^^^^ + +``codemodel`` object version 2 is a JSON object: + +.. code-block:: json + + { + "kind": "codemodel", + "version": { "major": 2, "minor": 0 }, + "paths": { + "source": "/path/to/top-level-source-dir", + "build": "/path/to/top-level-build-dir" + }, + "configurations": [ + { + "name": "Debug", + "directories": [ + { + "source": ".", + "build": ".", + "childIndexes": [ 1 ], + "projectIndex": 0, + "targetIndexes": [ 0 ], + "hasInstallRule": true, + "minimumCMakeVersion": { + "string": "3.14" + } + }, + { + "source": "sub", + "build": "sub", + "parentIndex": 0, + "projectIndex": 0, + "targetIndexes": [ 1 ], + "minimumCMakeVersion": { + "string": "3.14" + } + } + ], + "projects": [ + { + "name": "MyProject", + "directoryIndexes": [ 0, 1 ], + "targetIndexes": [ 0, 1 ] + } + ], + "targets": [ + { + "name": "MyExecutable", + "directoryIndex": 0, + "projectIndex": 0, + "jsonFile": "<file>" + }, + { + "name": "MyLibrary", + "directoryIndex": 1, + "projectIndex": 0, + "jsonFile": "<file>" + } + ] + } + ] + } + +The members specific to ``codemodel`` objects are: + +``paths`` + A JSON object containing members: + + ``source`` + A string specifying the absolute path to the top-level source directory, + represented with forward slashes. + + ``build`` + A string specifying the absolute path to the top-level build directory, + represented with forward slashes. + +``configurations`` + A JSON array of entries corresponding to available build configurations. + On single-configuration generators there is one entry for the value + of the :variable:`CMAKE_BUILD_TYPE` variable. For multi-configuration + generators there is an entry for each configuration listed in the + :variable:`CMAKE_CONFIGURATION_TYPES` variable. + Each entry is a JSON object containing members: + + ``name`` + A string specifying the name of the configuration, e.g. ``Debug``. + + ``directories`` + A JSON array of entries each corresponding to a build system directory + whose source directory contains a ``CMakeLists.txt`` file. The first + entry corresponds to the top-level directory. Each entry is a + JSON object containing members: + + ``source`` + A string specifying the path to the source directory, represented + with forward slashes. If the directory is inside the top-level + source directory then the path is specified relative to that + directory (with ``.`` for the top-level source directory itself). + Otherwise the path is absolute. + + ``build`` + A string specifying the path to the build directory, represented + with forward slashes. If the directory is inside the top-level + build directory then the path is specified relative to that + directory (with ``.`` for the top-level build directory itself). + Otherwise the path is absolute. + + ``parentIndex`` + Optional member that is present when the directory is not top-level. + The value is an unsigned integer 0-based index of another entry in + the main ``directories`` array that corresponds to the parent + directory that added this directory as a subdirectory. + + ``childIndexes`` + Optional member that is present when the directory has subdirectories. + The value is a JSON array of entries corresponding to child directories + created by the :command:`add_subdirectory` or :command:`subdirs` + command. Each entry is an unsigned integer 0-based index of another + entry in the main ``directories`` array. + + ``projectIndex`` + An unsigned integer 0-based index into the main ``projects`` array + indicating the build system project to which the this directory belongs. + + ``targetIndexes`` + Optional member that is present when the directory itself has targets, + excluding those belonging to subdirectories. The value is a JSON + array of entries corresponding to the targets. Each entry is an + unsigned integer 0-based index into the main ``targets`` array. + + ``minimumCMakeVersion`` + Optional member present when a minimum required version of CMake is + known for the directory. This is the ``<min>`` version given to the + most local call to the :command:`cmake_minimum_required(VERSION)` + command in the directory itself or one of its ancestors. + The value is a JSON object with one member: + + ``string`` + A string specifying the minimum required version in the format:: + + <major>.<minor>[.<patch>[.<tweak>]][<suffix>] + + Each component is an unsigned integer and the suffix may be an + arbitrary string. + + ``hasInstallRule`` + Optional member that is present with boolean value ``true`` when + the directory or one of its subdirectories contains any + :command:`install` rules, i.e. whether a ``make install`` + or equivalent rule is available. + + ``projects`` + A JSON array of entries corresponding to the top-level project + and sub-projects defined in the build system. Each (sub-)project + corresponds to a source directory whose ``CMakeLists.txt`` file + calls the :command:`project` command with a project name different + from its parent directory. The first entry corresponds to the + top-level project. + + Each entry is a JSON object containing members: + + ``name`` + A string specifying the name given to the :command:`project` command. + + ``parentIndex`` + Optional member that is present when the project is not top-level. + The value is an unsigned integer 0-based index of another entry in + the main ``projects`` array that corresponds to the parent project + that added this project as a sub-project. + + ``childIndexes`` + Optional member that is present when the project has sub-projects. + The value is a JSON array of entries corresponding to the sub-projects. + Each entry is an unsigned integer 0-based index of another + entry in the main ``projects`` array. + + ``directoryIndexes`` + A JSON array of entries corresponding to build system directories + that are part of the project. The first entry corresponds to the + top-level directory of the project. Each entry is an unsigned + integer 0-based index into the main ``directories`` array. + + ``targetIndexes`` + Optional member that is present when the project itself has targets, + excluding those belonging to sub-projects. The value is a JSON + array of entries corresponding to the targets. Each entry is an + unsigned integer 0-based index into the main ``targets`` array. + + ``targets`` + A JSON array of entries corresponding to the build system targets. + Such targets are created by calls to :command:`add_executable`, + :command:`add_library`, and :command:`add_custom_target`, excluding + imported targets and interface libraries (which do not generate any + build rules). Each entry is a JSON object containing members: + + ``name`` + A string specifying the target name. + + ``id`` + A string uniquely identifying the target. This matches the ``id`` + field in the file referenced by ``jsonFile``. + + ``directoryIndex`` + An unsigned integer 0-based index into the main ``directories`` array + indicating the build system directory in which the target is defined. + + ``projectIndex`` + An unsigned integer 0-based index into the main ``projects`` array + indicating the build system project in which the target is defined. + + ``jsonFile`` + A JSON string specifying a path relative to the codemodel file + to another JSON file containing a + `"codemodel" version 2 "target" object`_. + +"codemodel" version 2 "target" object +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A codemodel "target" object is referenced by a `"codemodel" version 2`_ +object's ``targets`` array. Each "target" object is a JSON object +with members: + +``name`` + A string specifying the logical name of the target. + +``id`` + A string uniquely identifying the target. The format is unspecified + and should not be interpreted by clients. + +``type`` + A string specifying the type of the target. The value is one of + ``EXECUTABLE``, ``STATIC_LIBRARY``, ``SHARED_LIBRARY``, + ``MODULE_LIBRARY``, ``OBJECT_LIBRARY``, or ``UTILITY``. + +``backtrace`` + Optional member that is present when a CMake language backtrace to + the command in the source code that created the target is available. + The value is an unsigned integer 0-based index into the + ``backtraceGraph`` member's ``nodes`` array. + +``folder`` + Optional member that is present when the :prop_tgt:`FOLDER` target + property is set. The value is a JSON object with one member: + + ``name`` + A string specifying the name of the target folder. + +``paths`` + A JSON object containing members: + + ``source`` + A string specifying the path to the target's source directory, + represented with forward slashes. If the directory is inside the + top-level source directory then the path is specified relative to + that directory (with ``.`` for the top-level source directory itself). + Otherwise the path is absolute. + + ``build`` + A string specifying the path to the target's build directory, + represented with forward slashes. If the directory is inside the + top-level build directory then the path is specified relative to + that directory (with ``.`` for the top-level build directory itself). + Otherwise the path is absolute. + +``nameOnDisk`` + Optional member that is present for executable and library targets + that are linked or archived into a single primary artifact. + The value is a string specifying the file name of that artifact on disk. + +``artifacts`` + Optional member that is present for executable and library targets + that produce artifacts on disk meant for consumption by dependents. + The value is a JSON array of entries corresponding to the artifacts. + Each entry is a JSON object containing one member: + + ``path`` + A string specifying the path to the file on disk, represented with + forward slashes. If the file is inside the top-level build directory + then the path is specified relative to that directory. + Otherwise the path is absolute. + +``isGeneratorProvided`` + Optional member that is present with boolean value ``true`` if the + target is provided by CMake's build system generator rather than by + a command in the source code. + +``install`` + Optional member that is present when the target has an :command:`install` + rule. The value is a JSON object with members: + + ``prefix`` + A JSON object specifying the installation prefix. It has one member: + + ``path`` + A string specifying the value of :variable:`CMAKE_INSTALL_PREFIX`. + + ``destinations`` + A JSON array of entries specifying an install destination path. + Each entry is a JSON object with members: + + ``path`` + A string specifying the install destination path. The path may + be absolute or relative to the install prefix. + + ``backtrace`` + Optional member that is present when a CMake language backtrace to + the :command:`install` command invocation that specified this + destination is available. The value is an unsigned integer 0-based + index into the ``backtraceGraph`` member's ``nodes`` array. + +``link`` + Optional member that is present for executables and shared library + targets that link into a runtime binary. The value is a JSON object + with members describing the link step: + + ``language`` + A string specifying the language (e.g. ``C``, ``CXX``, ``Fortran``) + of the toolchain is used to invoke the linker. + + ``commandFragments`` + Optional member that is present when fragments of the link command + line invocation are available. The value is a JSON array of entries + specifying ordered fragments. Each entry is a JSON object with members: + + ``fragment`` + A string specifying a fragment of the link command line invocation. + The value is encoded in the build system's native shell format. + + ``role`` + A string specifying the role of the fragment's content: + + * ``flags``: link flags. + * ``libraries``: link library file paths or flags. + * ``libraryPath``: library search path flags. + * ``frameworkPath``: macOS framework search path flags. + + ``lto`` + Optional member that is present with boolean value ``true`` + when link-time optimization (a.k.a. interprocedural optimization + or link-time code generation) is enabled. + + ``sysroot`` + Optional member that is present when the :variable:`CMAKE_SYSROOT_LINK` + or :variable:`CMAKE_SYSROOT` variable is defined. The value is a + JSON object with one member: + + ``path`` + A string specifying the absolute path to the sysroot, represented + with forward slashes. + +``archive`` + Optional member that is present for static library targets. The value + is a JSON object with members describing the archive step: + + ``commandFragments`` + Optional member that is present when fragments of the archiver command + line invocation are available. The value is a JSON array of entries + specifying the fragments. Each entry is a JSON object with members: + + ``fragment`` + A string specifying a fragment of the archiver command line invocation. + The value is encoded in the build system's native shell format. + + ``role`` + A string specifying the role of the fragment's content: + + * ``flags``: archiver flags. + + ``lto`` + Optional member that is present with boolean value ``true`` + when link-time optimization (a.k.a. interprocedural optimization + or link-time code generation) is enabled. + +``dependencies`` + Optional member that is present when the target depends on other targets. + The value is a JSON array of entries corresponding to the dependencies. + Each entry is a JSON object with members: + + ``id`` + A string uniquely identifying the target on which this target depends. + This matches the main ``id`` member of the other target. + + ``backtrace`` + Optional member that is present when a CMake language backtrace to + the :command:`add_dependencies`, :command:`target_link_libraries`, + or other command invocation that created this dependency is + available. The value is an unsigned integer 0-based index into + the ``backtraceGraph`` member's ``nodes`` array. + +``sources`` + A JSON array of entries corresponding to the target's source files. + Each entry is a JSON object with members: + + ``path`` + A string specifying the path to the source file on disk, represented + with forward slashes. If the file is inside the top-level source + directory then the path is specified relative to that directory. + Otherwise the path is absolute. + + ``compileGroupIndex`` + Optional member that is present when the source is compiled. + The value is an unsigned integer 0-based index into the + ``compileGroups`` array. + + ``sourceGroupIndex`` + Optional member that is present when the source is part of a source + group either via the :command:`source_group` command or by default. + The value is an unsigned integer 0-based index into the + ``sourceGroups`` array. + + ``isGenerated`` + Optional member that is present with boolean value ``true`` if + the source is :prop_sf:`GENERATED`. + + ``backtrace`` + Optional member that is present when a CMake language backtrace to + the :command:`target_sources`, :command:`add_executable`, + :command:`add_library`, :command:`add_custom_target`, or other + command invocation that added this source to the target is + available. The value is an unsigned integer 0-based index into + the ``backtraceGraph`` member's ``nodes`` array. + +``sourceGroups`` + Optional member that is present when sources are grouped together by + the :command:`source_group` command or by default. The value is a + JSON array of entries corresponding to the groups. Each entry is + a JSON object with members: + + ``name`` + A string specifying the name of the source group. + + ``sourceIndexes`` + A JSON array listing the sources belonging to the group. + Each entry is an unsigned integer 0-based index into the + main ``sources`` array for the target. + +``compileGroups`` + Optional member that is present when the target has sources that compile. + The value is a JSON array of entries corresponding to groups of sources + that all compile with the same settings. Each entry is a JSON object + with members: + + ``sourceIndexes`` + A JSON array listing the sources belonging to the group. + Each entry is an unsigned integer 0-based index into the + main ``sources`` array for the target. + + ``language`` + A string specifying the language (e.g. ``C``, ``CXX``, ``Fortran``) + of the toolchain is used to compile the source file. + + ``compileCommandFragments`` + Optional member that is present when fragments of the compiler command + line invocation are available. The value is a JSON array of entries + specifying ordered fragments. Each entry is a JSON object with + one member: + + ``fragment`` + A string specifying a fragment of the compile command line invocation. + The value is encoded in the build system's native shell format. + + ``includes`` + Optional member that is present when there are include directories. + The value is a JSON array with an entry for each directory. Each + entry is a JSON object with members: + + ``path`` + A string specifying the path to the include directory, + represented with forward slashes. + + ``isSystem`` + Optional member that is present with boolean value ``true`` if + the include directory is marked as a system include directory. + + ``backtrace`` + Optional member that is present when a CMake language backtrace to + the :command:`target_include_directories` or other command invocation + that added this include directory is available. The value is + an unsigned integer 0-based index into the ``backtraceGraph`` + member's ``nodes`` array. + + ``defines`` + Optional member that is present when there are preprocessor definitions. + The value is a JSON array with an entry for each definition. Each + entry is a JSON object with members: + + ``define`` + A string specifying the preprocessor definition in the format + ``<name>[=<value>]``, e.g. ``DEF`` or ``DEF=1``. + + ``backtrace`` + Optional member that is present when a CMake language backtrace to + the :command:`target_compile_definitions` or other command invocation + that added this preprocessor definition is available. The value is + an unsigned integer 0-based index into the ``backtraceGraph`` + member's ``nodes`` array. + + ``sysroot`` + Optional member that is present when the + :variable:`CMAKE_SYSROOT_COMPILE` or :variable:`CMAKE_SYSROOT` + variable is defined. The value is a JSON object with one member: + + ``path`` + A string specifying the absolute path to the sysroot, represented + with forward slashes. + +``backtraceGraph`` + A JSON object describing the graph of backtraces whose nodes are + referenced from ``backtrace`` members elsewhere. The members are: + + ``nodes`` + A JSON array listing nodes in the backtrace graph. Each entry + is a JSON object with members: + + ``file`` + An unsigned integer 0-based index into the backtrace ``files`` array. + + ``line`` + An optional member present when the node represents a line within + the file. The value is an unsigned integer 1-based line number. + + ``command`` + An optional member present when the node represents a command + invocation within the file. The value is an unsigned integer + 0-based index into the backtrace ``commands`` array. + + ``parent`` + An optional member present when the node is not the bottom of + the call stack. The value is an unsigned integer 0-based index + of another entry in the backtrace ``nodes`` array. + + ``commands`` + A JSON array listing command names referenced by backtrace nodes. + Each entry is a string specifying a command name. + + ``files`` + A JSON array listing CMake language files referenced by backtrace nodes. + Each entry is a string specifying the path to a file, represented + with forward slashes. If the file is inside the top-level source + directory then the path is specified relative to that directory. + Otherwise the path is absolute. + +Object Kind "cache" +------------------- + +The ``cache`` object kind lists cache entries. These are the +:ref:`CMake Language Variables` stored in the persistent cache +(``CMakeCache.txt``) for the build tree. + +There is only one ``cache`` object major version, version 2. +Version 1 does not exist to avoid confusion with that from +:manual:`cmake-server(7)` mode. + +"cache" version 2 +^^^^^^^^^^^^^^^^^ + +``cache`` object version 2 is a JSON object: + +.. code-block:: json + + { + "kind": "cache", + "version": { "major": 2, "minor": 0 }, + "entries": [ + { + "name": "BUILD_SHARED_LIBS", + "value": "ON", + "type": "BOOL", + "properties": [ + { + "name": "HELPSTRING", + "value": "Build shared libraries" + } + ] + }, + { + "name": "CMAKE_GENERATOR", + "value": "Unix Makefiles", + "type": "INTERNAL", + "properties": [ + { + "name": "HELPSTRING", + "value": "Name of generator." + } + ] + } + ] + } + +The members specific to ``cache`` objects are: + +``entries`` + A JSON array whose entries are each a JSON object specifying a + cache entry. The members of each entry are: + + ``name`` + A string specifying the name of the entry. + + ``value`` + A string specifying the value of the entry. + + ``type`` + A string specifying the type of the entry used by + :manual:`cmake-gui(1)` to choose a widget for editing. + + ``properties`` + A JSON array of entries specifying associated + :ref:`cache entry properties <Cache Entry Properties>`. + Each entry is a JSON object containing members: + + ``name`` + A string specifying the name of the cache entry property. + + ``value`` + A string specifying the value of the cache entry property. + +Object Kind "cmakeFiles" +------------------------ + +The ``cmakeFiles`` object kind lists files used by CMake while +configuring and generating the build system. These include the +``CMakeLists.txt`` files as well as included ``.cmake`` files. + +There is only one ``cmakeFiles`` object major version, version 1. + +"cmakeFiles" version 1 +^^^^^^^^^^^^^^^^^^^^^^ + +``cmakeFiles`` object version 1 is a JSON object: + +.. code-block:: json + + { + "kind": "cmakeFiles", + "version": { "major": 1, "minor": 0 }, + "paths": { + "build": "/path/to/top-level-build-dir", + "source": "/path/to/top-level-source-dir" + }, + "inputs": [ + { + "path": "CMakeLists.txt" + }, + { + "isGenerated": true, + "path": "/path/to/top-level-build-dir/.../CMakeSystem.cmake" + }, + { + "isExternal": true, + "path": "/path/to/external/third-party/module.cmake" + }, + { + "isCMake": true, + "isExternal": true, + "path": "/path/to/cmake/Modules/CMakeGenericSystem.cmake" + } + ] + } + +The members specific to ``cmakeFiles`` objects are: + +``paths`` + A JSON object containing members: + + ``source`` + A string specifying the absolute path to the top-level source directory, + represented with forward slashes. + + ``build`` + A string specifying the absolute path to the top-level build directory, + represented with forward slashes. + +``inputs`` + A JSON array whose entries are each a JSON object specifying an input + file used by CMake when configuring and generating the build system. + The members of each entry are: + + ``path`` + A string specifying the path to an input file to CMake, represented + with forward slashes. If the file is inside the top-level source + directory then the path is specified relative to that directory. + Otherwise the path is absolute. + + ``isGenerated`` + Optional member that is present with boolean value ``true`` + if the path specifies a file that is under the top-level + build directory and the build is out-of-source. + This member is not available on in-source builds. + + ``isExternal`` + Optional member that is present with boolean value ``true`` + if the path specifies a file that is not under the top-level + source or build directories. + + ``isCMake`` + Optional member that is present with boolean value ``true`` + if the path specifies a file in the CMake installation. diff --git a/Help/release/dev/fileapi.rst b/Help/release/dev/fileapi.rst new file mode 100644 index 0000000..c3f03ef --- /dev/null +++ b/Help/release/dev/fileapi.rst @@ -0,0 +1,5 @@ +fileapi +------- + +* A file-based api for clients to get semantic buildsystem information + has been added. See the :manual:`cmake-file-api(7)` manual. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 464d6f3..91c45bc 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -207,6 +207,14 @@ set(SRCS cmExtraKateGenerator.h cmExtraSublimeTextGenerator.cxx cmExtraSublimeTextGenerator.h + cmFileAPI.cxx + cmFileAPI.h + cmFileAPICache.cxx + cmFileAPICache.h + cmFileAPICodemodel.cxx + cmFileAPICodemodel.h + cmFileAPICMakeFiles.cxx + cmFileAPICMakeFiles.h cmFileLock.cxx cmFileLock.h cmFileLockPool.cxx @@ -375,6 +383,9 @@ set(SRCS cmXMLWriter.h cmake.cxx cmake.h + cm_string_view.cxx + cm_string_view.hxx + cm_static_string_view.hxx cmCommand.cxx cmCommand.h @@ -566,6 +577,8 @@ set(SRCS cmSiteNameCommand.h cmSourceGroupCommand.cxx cmSourceGroupCommand.h + cmString.cxx + cmString.hxx cmStringReplaceHelper.cxx cmStringCommand.cxx cmStringCommand.h diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 7abab01..b43f9fd 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 13) -set(CMake_VERSION_PATCH 20181212) +set(CMake_VERSION_PATCH 20181213) #set(CMake_VERSION_RC 1) diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx new file mode 100644 index 0000000..89bd258 --- /dev/null +++ b/Source/cmFileAPI.cxx @@ -0,0 +1,800 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPI.h" + +#include "cmAlgorithms.h" +#include "cmCryptoHash.h" +#include "cmFileAPICMakeFiles.h" +#include "cmFileAPICache.h" +#include "cmFileAPICodemodel.h" +#include "cmGlobalGenerator.h" +#include "cmSystemTools.h" +#include "cmTimestamp.h" +#include "cmake.h" +#include "cmsys/Directory.hxx" +#include "cmsys/FStream.hxx" + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <ctime> +#include <iomanip> +#include <sstream> +#include <utility> + +cmFileAPI::cmFileAPI(cmake* cm) + : CMakeInstance(cm) +{ + this->APIv1 = + this->CMakeInstance->GetHomeOutputDirectory() + "/.cmake/api/v1"; + + Json::CharReaderBuilder rbuilder; + rbuilder["collectComments"] = false; + rbuilder["failIfExtra"] = true; + rbuilder["rejectDupKeys"] = false; + rbuilder["strictRoot"] = true; + this->JsonReader = + std::unique_ptr<Json::CharReader>(rbuilder.newCharReader()); + + Json::StreamWriterBuilder wbuilder; + wbuilder["indentation"] = "\t"; + this->JsonWriter = + std::unique_ptr<Json::StreamWriter>(wbuilder.newStreamWriter()); +} + +void cmFileAPI::ReadQueries() +{ + std::string const query_dir = this->APIv1 + "/query"; + this->QueryExists = cmSystemTools::FileIsDirectory(query_dir); + if (!this->QueryExists) { + return; + } + + // Load queries at the top level. + std::vector<std::string> queries = cmFileAPI::LoadDir(query_dir); + + // Read the queries and save for later. + for (std::string& query : queries) { + if (cmHasLiteralPrefix(query, "client-")) { + this->ReadClient(query); + } else if (!cmFileAPI::ReadQuery(query, this->TopQuery.Known)) { + this->TopQuery.Unknown.push_back(std::move(query)); + } + } +} + +void cmFileAPI::WriteReplies() +{ + if (this->QueryExists) { + cmSystemTools::MakeDirectory(this->APIv1 + "/reply"); + this->WriteJsonFile(this->BuildReplyIndex(), "index", ComputeSuffixTime); + } + + this->RemoveOldReplyFiles(); +} + +std::vector<std::string> cmFileAPI::LoadDir(std::string const& dir) +{ + std::vector<std::string> files; + cmsys::Directory d; + d.Load(dir); + for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) { + std::string f = d.GetFile(i); + if (f != "." && f != "..") { + files.push_back(std::move(f)); + } + } + std::sort(files.begin(), files.end()); + return files; +} + +void cmFileAPI::RemoveOldReplyFiles() +{ + std::string const reply_dir = this->APIv1 + "/reply"; + std::vector<std::string> files = this->LoadDir(reply_dir); + for (std::string const& f : files) { + if (this->ReplyFiles.find(f) == this->ReplyFiles.end()) { + std::string file = reply_dir + "/" + f; + cmSystemTools::RemoveFile(file); + } + } +} + +bool cmFileAPI::ReadJsonFile(std::string const& file, Json::Value& value, + std::string& error) +{ + std::vector<char> content; + + cmsys::ifstream fin; + if (!cmSystemTools::FileIsDirectory(file)) { + fin.open(file.c_str(), std::ios::binary); + } + auto finEnd = fin.rdbuf()->pubseekoff(0, std::ios::end); + if (finEnd > 0) { + size_t finSize = finEnd; + try { + // Allocate a buffer to read the whole file. + content.resize(finSize); + + // Now read the file from the beginning. + fin.seekg(0, std::ios::beg); + fin.read(content.data(), finSize); + } catch (...) { + fin.setstate(std::ios::failbit); + } + } + fin.close(); + if (!fin) { + value = Json::Value(); + error = "failed to read from file"; + return false; + } + + // Parse our buffer as json. + if (!this->JsonReader->parse(content.data(), content.data() + content.size(), + &value, &error)) { + value = Json::Value(); + return false; + } + + return true; +} + +std::string cmFileAPI::WriteJsonFile( + Json::Value const& value, std::string const& prefix, + std::string (*computeSuffix)(std::string const&)) +{ + std::string fileName; + + // Write the json file with a temporary name. + std::string const& tmpFile = this->APIv1 + "/tmp.json"; + cmsys::ofstream ftmp(tmpFile.c_str()); + this->JsonWriter->write(value, &ftmp); + ftmp << "\n"; + ftmp.close(); + if (!ftmp) { + cmSystemTools::RemoveFile(tmpFile); + return fileName; + } + + // Compute the final name for the file. + fileName = prefix + "-" + computeSuffix(tmpFile) + ".json"; + + // Create the destination. + std::string file = this->APIv1 + "/reply"; + cmSystemTools::MakeDirectory(file); + file += "/"; + file += fileName; + + // If the final name already exists then assume it has proper content. + // Otherwise, atomically place the reply file at its final name + if (cmSystemTools::FileExists(file, true) || + !cmSystemTools::RenameFile(tmpFile.c_str(), file.c_str())) { + cmSystemTools::RemoveFile(tmpFile); + } + + // Record this among files we have just written. + this->ReplyFiles.insert(fileName); + + return fileName; +} + +Json::Value cmFileAPI::MaybeJsonFile(Json::Value in, std::string const& prefix) +{ + Json::Value out; + if (in.isObject() || in.isArray()) { + out = Json::objectValue; + out["jsonFile"] = this->WriteJsonFile(in, prefix); + } else { + out = std::move(in); + } + return out; +} + +std::string cmFileAPI::ComputeSuffixHash(std::string const& file) +{ + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256); + std::string hash = hasher.HashFile(file); + hash.resize(20, '0'); + return hash; +} + +std::string cmFileAPI::ComputeSuffixTime(std::string const&) +{ + std::chrono::milliseconds ms = + std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now().time_since_epoch()); + std::chrono::seconds s = + std::chrono::duration_cast<std::chrono::seconds>(ms); + + std::time_t ts = s.count(); + std::size_t tms = ms.count() % 1000; + + cmTimestamp cmts; + std::ostringstream ss; + ss << cmts.CreateTimestampFromTimeT(ts, "%Y-%m-%dT%H-%M-%S", true) << '-' + << std::setfill('0') << std::setw(4) << tms; + return ss.str(); +} + +bool cmFileAPI::ReadQuery(std::string const& query, + std::vector<Object>& objects) +{ + // Parse the "<kind>-" syntax. + std::string::size_type sep_pos = query.find('-'); + if (sep_pos == std::string::npos) { + return false; + } + std::string kindName = query.substr(0, sep_pos); + std::string verStr = query.substr(sep_pos + 1); + if (kindName == ObjectKindName(ObjectKind::CodeModel)) { + Object o; + o.Kind = ObjectKind::CodeModel; + if (verStr == "v2") { + o.Version = 2; + } else { + return false; + } + objects.push_back(o); + return true; + } + if (kindName == ObjectKindName(ObjectKind::Cache)) { + Object o; + o.Kind = ObjectKind::Cache; + if (verStr == "v2") { + o.Version = 2; + } else { + return false; + } + objects.push_back(o); + return true; + } + if (kindName == ObjectKindName(ObjectKind::CMakeFiles)) { + Object o; + o.Kind = ObjectKind::CMakeFiles; + if (verStr == "v1") { + o.Version = 1; + } else { + return false; + } + objects.push_back(o); + return true; + } + if (kindName == ObjectKindName(ObjectKind::InternalTest)) { + Object o; + o.Kind = ObjectKind::InternalTest; + if (verStr == "v1") { + o.Version = 1; + } else if (verStr == "v2") { + o.Version = 2; + } else { + return false; + } + objects.push_back(o); + return true; + } + return false; +} + +void cmFileAPI::ReadClient(std::string const& client) +{ + // Load queries for the client. + std::string clientDir = this->APIv1 + "/query/" + client; + std::vector<std::string> queries = this->LoadDir(clientDir); + + // Read the queries and save for later. + ClientQuery& clientQuery = this->ClientQueries[client]; + for (std::string& query : queries) { + if (query == "query.json") { + clientQuery.HaveQueryJson = true; + this->ReadClientQuery(client, clientQuery.QueryJson); + } else if (!this->ReadQuery(query, clientQuery.DirQuery.Known)) { + clientQuery.DirQuery.Unknown.push_back(std::move(query)); + } + } +} + +void cmFileAPI::ReadClientQuery(std::string const& client, ClientQueryJson& q) +{ + // Read the query.json file. + std::string queryFile = this->APIv1 + "/query/" + client + "/query.json"; + Json::Value query; + if (!this->ReadJsonFile(queryFile, query, q.Error)) { + return; + } + if (!query.isObject()) { + q.Error = "query root is not an object"; + return; + } + + Json::Value const& clientValue = query["client"]; + if (!clientValue.isNull()) { + q.ClientValue = clientValue; + } + q.RequestsValue = std::move(query["requests"]); + q.Requests = this->BuildClientRequests(q.RequestsValue); +} + +Json::Value cmFileAPI::BuildReplyIndex() +{ + Json::Value index(Json::objectValue); + + // Report information about this version of CMake. + index["cmake"] = this->BuildCMake(); + + // Reply to all queries that we loaded. + Json::Value& reply = index["reply"] = this->BuildReply(this->TopQuery); + for (auto const& client : this->ClientQueries) { + std::string const& clientName = client.first; + ClientQuery const& clientQuery = client.second; + reply[clientName] = this->BuildClientReply(clientQuery); + } + + // Move our index of generated objects into its field. + Json::Value& objects = index["objects"] = Json::arrayValue; + for (auto& entry : this->ReplyIndexObjects) { + objects.append(std::move(entry.second)); // NOLINT(*) + } + + return index; +} + +Json::Value cmFileAPI::BuildCMake() +{ + Json::Value cmake = Json::objectValue; + cmake["version"] = this->CMakeInstance->ReportVersionJson(); + Json::Value& cmake_paths = cmake["paths"] = Json::objectValue; + cmake_paths["cmake"] = cmSystemTools::GetCMakeCommand(); + cmake_paths["ctest"] = cmSystemTools::GetCTestCommand(); + cmake_paths["cpack"] = cmSystemTools::GetCPackCommand(); + cmake_paths["root"] = cmSystemTools::GetCMakeRoot(); + cmake["generator"] = this->CMakeInstance->GetGlobalGenerator()->GetJson(); + return cmake; +} + +Json::Value cmFileAPI::BuildReply(Query const& q) +{ + Json::Value reply = Json::objectValue; + for (Object const& o : q.Known) { + std::string const& name = ObjectName(o); + reply[name] = this->AddReplyIndexObject(o); + } + + for (std::string const& name : q.Unknown) { + reply[name] = cmFileAPI::BuildReplyError("unknown query file"); + } + return reply; +} + +Json::Value cmFileAPI::BuildReplyError(std::string const& error) +{ + Json::Value e = Json::objectValue; + e["error"] = error; + return e; +} + +Json::Value const& cmFileAPI::AddReplyIndexObject(Object const& o) +{ + Json::Value& indexEntry = this->ReplyIndexObjects[o]; + if (!indexEntry.isNull()) { + // The reply object has already been generated. + return indexEntry; + } + + // Generate this reply object. + Json::Value const& object = this->BuildObject(o); + assert(object.isObject()); + + // Populate this index entry. + indexEntry = Json::objectValue; + indexEntry["kind"] = object["kind"]; + indexEntry["version"] = object["version"]; + indexEntry["jsonFile"] = this->WriteJsonFile(object, ObjectName(o)); + return indexEntry; +} + +const char* cmFileAPI::ObjectKindName(ObjectKind kind) +{ + // Keep in sync with ObjectKind enum. + static const char* objectKindNames[] = { + "codemodel", // + "cache", // + "cmakeFiles", // + "__test" // + }; + return objectKindNames[size_t(kind)]; +} + +std::string cmFileAPI::ObjectName(Object const& o) +{ + std::string name = ObjectKindName(o.Kind); + name += "-v"; + name += std::to_string(o.Version); + return name; +} + +Json::Value cmFileAPI::BuildObject(Object const& object) +{ + Json::Value value; + + switch (object.Kind) { + case ObjectKind::CodeModel: + value = this->BuildCodeModel(object); + break; + case ObjectKind::Cache: + value = this->BuildCache(object); + break; + case ObjectKind::CMakeFiles: + value = this->BuildCMakeFiles(object); + break; + case ObjectKind::InternalTest: + value = this->BuildInternalTest(object); + break; + } + + return value; +} + +cmFileAPI::ClientRequests cmFileAPI::BuildClientRequests( + Json::Value const& requests) +{ + ClientRequests result; + if (requests.isNull()) { + result.Error = "'requests' member missing"; + return result; + } + if (!requests.isArray()) { + result.Error = "'requests' member is not an array"; + return result; + } + + result.reserve(requests.size()); + for (Json::Value const& request : requests) { + result.emplace_back(this->BuildClientRequest(request)); + } + + return result; +} + +cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest( + Json::Value const& request) +{ + ClientRequest r; + + if (!request.isObject()) { + r.Error = "request is not an object"; + return r; + } + + Json::Value const& kind = request["kind"]; + if (kind.isNull()) { + r.Error = "'kind' member missing"; + return r; + } + if (!kind.isString()) { + r.Error = "'kind' member is not a string"; + return r; + } + std::string const& kindName = kind.asString(); + + if (kindName == this->ObjectKindName(ObjectKind::CodeModel)) { + r.Kind = ObjectKind::CodeModel; + } else if (kindName == this->ObjectKindName(ObjectKind::Cache)) { + r.Kind = ObjectKind::Cache; + } else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) { + r.Kind = ObjectKind::CMakeFiles; + } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) { + r.Kind = ObjectKind::InternalTest; + } else { + r.Error = "unknown request kind '" + kindName + "'"; + return r; + } + + Json::Value const& version = request["version"]; + if (version.isNull()) { + r.Error = "'version' member missing"; + return r; + } + std::vector<RequestVersion> versions; + if (!cmFileAPI::ReadRequestVersions(version, versions, r.Error)) { + return r; + } + + switch (r.Kind) { + case ObjectKind::CodeModel: + this->BuildClientRequestCodeModel(r, versions); + break; + case ObjectKind::Cache: + this->BuildClientRequestCache(r, versions); + break; + case ObjectKind::CMakeFiles: + this->BuildClientRequestCMakeFiles(r, versions); + break; + case ObjectKind::InternalTest: + this->BuildClientRequestInternalTest(r, versions); + break; + } + + return r; +} + +Json::Value cmFileAPI::BuildClientReply(ClientQuery const& q) +{ + Json::Value reply = this->BuildReply(q.DirQuery); + + if (!q.HaveQueryJson) { + return reply; + } + + Json::Value& reply_query_json = reply["query.json"]; + ClientQueryJson const& qj = q.QueryJson; + + if (!qj.Error.empty()) { + reply_query_json = this->BuildReplyError(qj.Error); + return reply; + } + + if (!qj.ClientValue.isNull()) { + reply_query_json["client"] = qj.ClientValue; + } + + if (!qj.RequestsValue.isNull()) { + reply_query_json["requests"] = qj.RequestsValue; + } + + reply_query_json["responses"] = this->BuildClientReplyResponses(qj.Requests); + + return reply; +} + +Json::Value cmFileAPI::BuildClientReplyResponses( + ClientRequests const& requests) +{ + Json::Value responses; + + if (!requests.Error.empty()) { + responses = this->BuildReplyError(requests.Error); + return responses; + } + + responses = Json::arrayValue; + for (ClientRequest const& request : requests) { + responses.append(this->BuildClientReplyResponse(request)); + } + + return responses; +} + +Json::Value cmFileAPI::BuildClientReplyResponse(ClientRequest const& request) +{ + Json::Value response; + if (!request.Error.empty()) { + response = this->BuildReplyError(request.Error); + return response; + } + response = this->AddReplyIndexObject(request); + return response; +} + +bool cmFileAPI::ReadRequestVersions(Json::Value const& version, + std::vector<RequestVersion>& versions, + std::string& error) +{ + if (version.isArray()) { + for (Json::Value const& v : version) { + if (!ReadRequestVersion(v, /*inArray=*/true, versions, error)) { + return false; + } + } + } else { + if (!ReadRequestVersion(version, /*inArray=*/false, versions, error)) { + return false; + } + } + return true; +} + +bool cmFileAPI::ReadRequestVersion(Json::Value const& version, bool inArray, + std::vector<RequestVersion>& result, + std::string& error) +{ + if (version.isUInt()) { + RequestVersion v; + v.Major = version.asUInt(); + result.push_back(v); + return true; + } + + if (!version.isObject()) { + if (inArray) { + error = "'version' array entry is not a non-negative integer or object"; + } else { + error = + "'version' member is not a non-negative integer, object, or array"; + } + return false; + } + + Json::Value const& major = version["major"]; + if (major.isNull()) { + error = "'version' object 'major' member missing"; + return false; + } + if (!major.isUInt()) { + error = "'version' object 'major' member is not a non-negative integer"; + return false; + } + + RequestVersion v; + v.Major = major.asUInt(); + + Json::Value const& minor = version["minor"]; + if (minor.isUInt()) { + v.Minor = minor.asUInt(); + } else if (!minor.isNull()) { + error = "'version' object 'minor' member is not a non-negative integer"; + return false; + } + + result.push_back(v); + + return true; +} + +std::string cmFileAPI::NoSupportedVersion( + std::vector<RequestVersion> const& versions) +{ + std::ostringstream msg; + msg << "no supported version specified"; + if (!versions.empty()) { + msg << " among:"; + for (RequestVersion const& v : versions) { + msg << " " << v.Major << "." << v.Minor; + } + } + return msg.str(); +} + +// The "codemodel" object kind. + +static unsigned int const CodeModelV2Minor = 0; + +void cmFileAPI::BuildClientRequestCodeModel( + ClientRequest& r, std::vector<RequestVersion> const& versions) +{ + // Select a known version from those requested. + for (RequestVersion const& v : versions) { + if ((v.Major == 2 && v.Minor <= CodeModelV2Minor)) { + r.Version = v.Major; + break; + } + } + if (!r.Version) { + r.Error = NoSupportedVersion(versions); + } +} + +Json::Value cmFileAPI::BuildCodeModel(Object const& object) +{ + using namespace std::placeholders; + Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version); + codemodel["kind"] = this->ObjectKindName(object.Kind); + + Json::Value& version = codemodel["version"] = Json::objectValue; + if (object.Version == 2) { + version["major"] = 2; + version["minor"] = CodeModelV2Minor; + } else { + return codemodel; // should be unreachable + } + + return codemodel; +} + +// The "cache" object kind. + +static unsigned int const CacheV2Minor = 0; + +void cmFileAPI::BuildClientRequestCache( + ClientRequest& r, std::vector<RequestVersion> const& versions) +{ + // Select a known version from those requested. + for (RequestVersion const& v : versions) { + if ((v.Major == 2 && v.Minor <= CacheV2Minor)) { + r.Version = v.Major; + break; + } + } + if (!r.Version) { + r.Error = NoSupportedVersion(versions); + } +} + +Json::Value cmFileAPI::BuildCache(Object const& object) +{ + using namespace std::placeholders; + Json::Value cache = cmFileAPICacheDump(*this, object.Version); + cache["kind"] = this->ObjectKindName(object.Kind); + + Json::Value& version = cache["version"] = Json::objectValue; + if (object.Version == 2) { + version["major"] = 2; + version["minor"] = CacheV2Minor; + } else { + return cache; // should be unreachable + } + + return cache; +} + +// The "cmakeFiles" object kind. + +static unsigned int const CMakeFilesV1Minor = 0; + +void cmFileAPI::BuildClientRequestCMakeFiles( + ClientRequest& r, std::vector<RequestVersion> const& versions) +{ + // Select a known version from those requested. + for (RequestVersion const& v : versions) { + if ((v.Major == 1 && v.Minor <= CMakeFilesV1Minor)) { + r.Version = v.Major; + break; + } + } + if (!r.Version) { + r.Error = NoSupportedVersion(versions); + } +} + +Json::Value cmFileAPI::BuildCMakeFiles(Object const& object) +{ + using namespace std::placeholders; + Json::Value cmakeFiles = cmFileAPICMakeFilesDump(*this, object.Version); + cmakeFiles["kind"] = this->ObjectKindName(object.Kind); + + Json::Value& version = cmakeFiles["version"] = Json::objectValue; + if (object.Version == 1) { + version["major"] = 1; + version["minor"] = CMakeFilesV1Minor; + } else { + return cmakeFiles; // should be unreachable + } + + return cmakeFiles; +} + +// The "__test" object kind is for internal testing of CMake. + +static unsigned int const InternalTestV1Minor = 3; +static unsigned int const InternalTestV2Minor = 0; + +void cmFileAPI::BuildClientRequestInternalTest( + ClientRequest& r, std::vector<RequestVersion> const& versions) +{ + // Select a known version from those requested. + for (RequestVersion const& v : versions) { + if ((v.Major == 1 && v.Minor <= InternalTestV1Minor) || // + (v.Major == 2 && v.Minor <= InternalTestV2Minor)) { + r.Version = v.Major; + break; + } + } + if (!r.Version) { + r.Error = NoSupportedVersion(versions); + } +} + +Json::Value cmFileAPI::BuildInternalTest(Object const& object) +{ + Json::Value test = Json::objectValue; + test["kind"] = this->ObjectKindName(object.Kind); + Json::Value& version = test["version"] = Json::objectValue; + if (object.Version == 2) { + version["major"] = 2; + version["minor"] = InternalTestV2Minor; + } else { + version["major"] = 1; + version["minor"] = InternalTestV1Minor; + } + return test; +} diff --git a/Source/cmFileAPI.h b/Source/cmFileAPI.h new file mode 100644 index 0000000..341b072 --- /dev/null +++ b/Source/cmFileAPI.h @@ -0,0 +1,204 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileAPI_h +#define cmFileAPI_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_jsoncpp_reader.h" +#include "cm_jsoncpp_value.h" +#include "cm_jsoncpp_writer.h" + +#include <map> +#include <memory> // IWYU pragma: keep +#include <string> +#include <unordered_set> +#include <vector> + +class cmake; + +class cmFileAPI +{ +public: + cmFileAPI(cmake* cm); + + /** Read fileapi queries from disk. */ + void ReadQueries(); + + /** Write fileapi replies to disk. */ + void WriteReplies(); + + /** Get the "cmake" instance with which this was constructed. */ + cmake* GetCMakeInstance() const { return this->CMakeInstance; } + + /** Convert a JSON object or array into an object with a single + "jsonFile" member specifying a file named with the given prefix + and holding the original object. Other JSON types are unchanged. */ + Json::Value MaybeJsonFile(Json::Value in, std::string const& prefix); + +private: + cmake* CMakeInstance; + + /** The api/v1 directory location. */ + std::string APIv1; + + /** The set of files we have just written to the reply directory. */ + std::unordered_set<std::string> ReplyFiles; + + static std::vector<std::string> LoadDir(std::string const& dir); + void RemoveOldReplyFiles(); + + // Keep in sync with ObjectKindName. + enum class ObjectKind + { + CodeModel, + Cache, + CMakeFiles, + InternalTest + }; + + /** Identify one object kind and major version. */ + struct Object + { + ObjectKind Kind; + unsigned long Version = 0; + friend bool operator<(Object const& l, Object const& r) + { + if (l.Kind != r.Kind) { + return l.Kind < r.Kind; + } + return l.Version < r.Version; + } + }; + + /** Represent content of a query directory. */ + struct Query + { + /** Known object kind-version pairs. */ + std::vector<Object> Known; + /** Unknown object kind names. */ + std::vector<std::string> Unknown; + }; + + /** Represent one request in a client 'query.json'. */ + struct ClientRequest : public Object + { + /** Empty if request is valid, else the error string. */ + std::string Error; + }; + + /** Represent the "requests" in a client 'query.json'. */ + struct ClientRequests : public std::vector<ClientRequest> + { + /** Empty if requests field is valid, else the error string. */ + std::string Error; + }; + + /** Represent the content of a client query.json file. */ + struct ClientQueryJson + { + /** The error string if parsing failed, else empty. */ + std::string Error; + + /** The 'query.json' object "client" member if it exists, else null. */ + Json::Value ClientValue; + + /** The 'query.json' object "requests" member if it exists, else null. */ + Json::Value RequestsValue; + + /** Requests extracted from 'query.json'. */ + ClientRequests Requests; + }; + + /** Represent content of a client query directory. */ + struct ClientQuery + { + /** The content of the client query directory except 'query.json'. */ + Query DirQuery; + + /** True if 'query.json' exists. */ + bool HaveQueryJson = false; + + /** The 'query.json' content. */ + ClientQueryJson QueryJson; + }; + + /** Whether the top-level query directory exists at all. */ + bool QueryExists = false; + + /** The content of the top-level query directory. */ + Query TopQuery; + + /** The content of each "client-$client" query directory. */ + std::map<std::string, ClientQuery> ClientQueries; + + /** Reply index object generated for object kind/version. + This populates the "objects" field of the reply index. */ + std::map<Object, Json::Value> ReplyIndexObjects; + + std::unique_ptr<Json::CharReader> JsonReader; + std::unique_ptr<Json::StreamWriter> JsonWriter; + + bool ReadJsonFile(std::string const& file, Json::Value& value, + std::string& error); + + std::string WriteJsonFile( + Json::Value const& value, std::string const& prefix, + std::string (*computeSuffix)(std::string const&) = ComputeSuffixHash); + static std::string ComputeSuffixHash(std::string const&); + static std::string ComputeSuffixTime(std::string const&); + + static bool ReadQuery(std::string const& query, + std::vector<Object>& objects); + void ReadClient(std::string const& client); + void ReadClientQuery(std::string const& client, ClientQueryJson& q); + + Json::Value BuildReplyIndex(); + Json::Value BuildCMake(); + Json::Value BuildReply(Query const& q); + static Json::Value BuildReplyError(std::string const& error); + Json::Value const& AddReplyIndexObject(Object const& o); + + static const char* ObjectKindName(ObjectKind kind); + static std::string ObjectName(Object const& o); + + Json::Value BuildObject(Object const& object); + + ClientRequests BuildClientRequests(Json::Value const& requests); + ClientRequest BuildClientRequest(Json::Value const& request); + Json::Value BuildClientReply(ClientQuery const& q); + Json::Value BuildClientReplyResponses(ClientRequests const& requests); + Json::Value BuildClientReplyResponse(ClientRequest const& request); + + struct RequestVersion + { + unsigned int Major = 0; + unsigned int Minor = 0; + }; + static bool ReadRequestVersions(Json::Value const& version, + std::vector<RequestVersion>& versions, + std::string& error); + static bool ReadRequestVersion(Json::Value const& version, bool inArray, + std::vector<RequestVersion>& result, + std::string& error); + static std::string NoSupportedVersion( + std::vector<RequestVersion> const& versions); + + void BuildClientRequestCodeModel( + ClientRequest& r, std::vector<RequestVersion> const& versions); + Json::Value BuildCodeModel(Object const& object); + + void BuildClientRequestCache(ClientRequest& r, + std::vector<RequestVersion> const& versions); + Json::Value BuildCache(Object const& object); + + void BuildClientRequestCMakeFiles( + ClientRequest& r, std::vector<RequestVersion> const& versions); + Json::Value BuildCMakeFiles(Object const& object); + + void BuildClientRequestInternalTest( + ClientRequest& r, std::vector<RequestVersion> const& versions); + Json::Value BuildInternalTest(Object const& object); +}; + +#endif diff --git a/Source/cmFileAPICMakeFiles.cxx b/Source/cmFileAPICMakeFiles.cxx new file mode 100644 index 0000000..799a047 --- /dev/null +++ b/Source/cmFileAPICMakeFiles.cxx @@ -0,0 +1,113 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPICMakeFiles.h" + +#include "cmFileAPI.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#include "cm_jsoncpp_value.h" + +#include <string> + +namespace { + +class CMakeFiles +{ + cmFileAPI& FileAPI; + unsigned long Version; + std::string CMakeModules; + std::string const& TopSource; + std::string const& TopBuild; + bool OutOfSource; + + Json::Value DumpPaths(); + Json::Value DumpInputs(); + Json::Value DumpInput(std::string const& file); + +public: + CMakeFiles(cmFileAPI& fileAPI, unsigned long version); + Json::Value Dump(); +}; + +CMakeFiles::CMakeFiles(cmFileAPI& fileAPI, unsigned long version) + : FileAPI(fileAPI) + , Version(version) + , CMakeModules(cmSystemTools::GetCMakeRoot() + "/Modules") + , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory()) + , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory()) + , OutOfSource(TopBuild != TopSource) +{ + static_cast<void>(this->Version); +} + +Json::Value CMakeFiles::Dump() +{ + Json::Value cmakeFiles = Json::objectValue; + cmakeFiles["paths"] = this->DumpPaths(); + cmakeFiles["inputs"] = DumpInputs(); + return cmakeFiles; +} + +Json::Value CMakeFiles::DumpPaths() +{ + Json::Value paths = Json::objectValue; + paths["source"] = this->TopSource; + paths["build"] = this->TopBuild; + return paths; +} + +Json::Value CMakeFiles::DumpInputs() +{ + Json::Value inputs = Json::arrayValue; + + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + for (cmLocalGenerator const* lg : gg->GetLocalGenerators()) { + cmMakefile const* mf = lg->GetMakefile(); + for (std::string const& file : mf->GetListFiles()) { + inputs.append(this->DumpInput(file)); + } + } + + return inputs; +} + +Json::Value CMakeFiles::DumpInput(std::string const& file) +{ + Json::Value input = Json::objectValue; + + bool const isCMake = cmSystemTools::IsSubDirectory(file, this->CMakeModules); + if (isCMake) { + input["isCMake"] = true; + } + + if (!cmSystemTools::IsSubDirectory(file, this->TopSource) && + !cmSystemTools::IsSubDirectory(file, this->TopBuild)) { + input["isExternal"] = true; + } + + if (this->OutOfSource && + cmSystemTools::IsSubDirectory(file, this->TopBuild)) { + input["isGenerated"] = true; + } + + std::string path = file; + if (!isCMake && cmSystemTools::IsSubDirectory(path, this->TopSource)) { + // Use a relative path within the source directory. + path = cmSystemTools::RelativePath(this->TopSource, path); + } + input["path"] = path; + + return input; +} +} + +Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, unsigned long version) +{ + CMakeFiles cmakeFiles(fileAPI, version); + return cmakeFiles.Dump(); +} diff --git a/Source/cmFileAPICMakeFiles.h b/Source/cmFileAPICMakeFiles.h new file mode 100644 index 0000000..a851c32 --- /dev/null +++ b/Source/cmFileAPICMakeFiles.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileAPICMakeFiles_h +#define cmFileAPICMakeFiles_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_jsoncpp_value.h" + +class cmFileAPI; + +extern Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, + unsigned long version); + +#endif diff --git a/Source/cmFileAPICache.cxx b/Source/cmFileAPICache.cxx new file mode 100644 index 0000000..074994a --- /dev/null +++ b/Source/cmFileAPICache.cxx @@ -0,0 +1,105 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPICache.h" + +#include "cmFileAPI.h" +#include "cmState.h" +#include "cmake.h" + +#include "cm_jsoncpp_value.h" + +#include <algorithm> +#include <string> +#include <vector> + +namespace { + +class Cache +{ + cmFileAPI& FileAPI; + unsigned long Version; + cmState* State; + + Json::Value DumpEntries(); + Json::Value DumpEntry(std::string const& name); + Json::Value DumpEntryProperties(std::string const& name); + Json::Value DumpEntryProperty(std::string const& name, + std::string const& prop); + +public: + Cache(cmFileAPI& fileAPI, unsigned long version); + Json::Value Dump(); +}; + +Cache::Cache(cmFileAPI& fileAPI, unsigned long version) + : FileAPI(fileAPI) + , Version(version) + , State(this->FileAPI.GetCMakeInstance()->GetState()) +{ + static_cast<void>(this->Version); +} + +Json::Value Cache::Dump() +{ + Json::Value cache = Json::objectValue; + cache["entries"] = DumpEntries(); + return cache; +} + +Json::Value Cache::DumpEntries() +{ + Json::Value entries = Json::arrayValue; + + std::vector<std::string> names = this->State->GetCacheEntryKeys(); + std::sort(names.begin(), names.end()); + + for (std::string const& name : names) { + entries.append(this->DumpEntry(name)); + } + + return entries; +} + +Json::Value Cache::DumpEntry(std::string const& name) +{ + Json::Value entry = Json::objectValue; + entry["name"] = name; + entry["type"] = + cmState::CacheEntryTypeToString(this->State->GetCacheEntryType(name)); + entry["value"] = this->State->GetCacheEntryValue(name); + + Json::Value properties = this->DumpEntryProperties(name); + if (!properties.empty()) { + entry["properties"] = std::move(properties); + } + + return entry; +} + +Json::Value Cache::DumpEntryProperties(std::string const& name) +{ + Json::Value properties = Json::arrayValue; + std::vector<std::string> props = + this->State->GetCacheEntryPropertyList(name); + std::sort(props.begin(), props.end()); + for (std::string const& prop : props) { + properties.append(this->DumpEntryProperty(name, prop)); + } + return properties; +} + +Json::Value Cache::DumpEntryProperty(std::string const& name, + std::string const& prop) +{ + Json::Value property = Json::objectValue; + property["name"] = prop; + property["value"] = this->State->GetCacheEntryProperty(name, prop); + return property; +} +} + +Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI, unsigned long version) +{ + Cache cache(fileAPI, version); + return cache.Dump(); +} diff --git a/Source/cmFileAPICache.h b/Source/cmFileAPICache.h new file mode 100644 index 0000000..09d9e1c --- /dev/null +++ b/Source/cmFileAPICache.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileAPICache_h +#define cmFileAPICache_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_jsoncpp_value.h" + +class cmFileAPI; + +extern Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI, + unsigned long version); + +#endif diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx new file mode 100644 index 0000000..078d1d5 --- /dev/null +++ b/Source/cmFileAPICodemodel.cxx @@ -0,0 +1,1247 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmFileAPICodemodel.h" + +#include "cmCryptoHash.h" +#include "cmFileAPI.h" +#include "cmGeneratorExpression.h" +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmInstallGenerator.h" +#include "cmInstallSubdirectoryGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmLinkLineComputer.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmSourceFile.h" +#include "cmSourceGroup.h" +#include "cmState.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmStateTypes.h" +#include "cmSystemTools.h" +#include "cmTarget.h" +#include "cmTargetDepend.h" +#include "cmake.h" + +#include "cm_jsoncpp_value.h" + +#include <algorithm> +#include <cassert> +#include <map> +#include <set> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +namespace { + +class Codemodel +{ + cmFileAPI& FileAPI; + unsigned long Version; + + Json::Value DumpPaths(); + Json::Value DumpConfigurations(); + Json::Value DumpConfiguration(std::string const& config); + +public: + Codemodel(cmFileAPI& fileAPI, unsigned long version); + Json::Value Dump(); +}; + +class CodemodelConfig +{ + cmFileAPI& FileAPI; + unsigned long Version; + std::string const& Config; + std::string TopSource; + std::string TopBuild; + + struct Directory + { + cmStateSnapshot Snapshot; + cmLocalGenerator const* LocalGenerator = nullptr; + Json::Value TargetIndexes = Json::arrayValue; + Json::ArrayIndex ProjectIndex; + bool HasInstallRule = false; + }; + std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder> + DirectoryMap; + std::vector<Directory> Directories; + + struct Project + { + cmStateSnapshot Snapshot; + static const Json::ArrayIndex NoParentIndex = + static_cast<Json::ArrayIndex>(-1); + Json::ArrayIndex ParentIndex = NoParentIndex; + Json::Value ChildIndexes = Json::arrayValue; + Json::Value DirectoryIndexes = Json::arrayValue; + Json::Value TargetIndexes = Json::arrayValue; + }; + std::map<cmStateSnapshot, Json::ArrayIndex, cmStateSnapshot::StrictWeakOrder> + ProjectMap; + std::vector<Project> Projects; + + void ProcessDirectories(); + + Json::ArrayIndex GetDirectoryIndex(cmLocalGenerator const* lg); + Json::ArrayIndex GetDirectoryIndex(cmStateSnapshot s); + + Json::ArrayIndex AddProject(cmStateSnapshot s); + + Json::Value DumpTargets(); + Json::Value DumpTarget(cmGeneratorTarget* gt, Json::ArrayIndex ti); + + Json::Value DumpDirectories(); + Json::Value DumpDirectory(Directory& d); + + Json::Value DumpProjects(); + Json::Value DumpProject(Project& p); + + Json::Value DumpMinimumCMakeVersion(cmStateSnapshot s); + +public: + CodemodelConfig(cmFileAPI& fileAPI, unsigned long version, + std::string const& config); + Json::Value Dump(); +}; + +std::string RelativeIfUnder(std::string const& top, std::string const& in) +{ + std::string out; + if (in == top) { + out = "."; + } else if (cmSystemTools::IsSubDirectory(in, top)) { + out = in.substr(top.size() + 1); + } else { + out = in; + } + return out; +} + +std::string TargetId(cmGeneratorTarget const* gt, std::string const& topBuild) +{ + cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256); + std::string path = RelativeIfUnder( + topBuild, gt->GetLocalGenerator()->GetCurrentBinaryDirectory()); + std::string hash = hasher.HashString(path); + hash.resize(20, '0'); + return gt->GetName() + CMAKE_DIRECTORY_ID_SEP + hash; +} + +class BacktraceData +{ + std::string TopSource; + std::unordered_map<std::string, Json::ArrayIndex> CommandMap; + std::unordered_map<std::string, Json::ArrayIndex> FileMap; + std::unordered_map<cmListFileContext const*, Json::ArrayIndex> NodeMap; + Json::Value Commands = Json::arrayValue; + Json::Value Files = Json::arrayValue; + Json::Value Nodes = Json::arrayValue; + + Json::ArrayIndex AddCommand(std::string const& command) + { + auto i = this->CommandMap.find(command); + if (i == this->CommandMap.end()) { + auto cmdIndex = static_cast<Json::ArrayIndex>(this->Commands.size()); + i = this->CommandMap.emplace(command, cmdIndex).first; + this->Commands.append(command); + } + return i->second; + } + + Json::ArrayIndex AddFile(std::string const& file) + { + auto i = this->FileMap.find(file); + if (i == this->FileMap.end()) { + auto fileIndex = static_cast<Json::ArrayIndex>(this->Files.size()); + i = this->FileMap.emplace(file, fileIndex).first; + this->Files.append(RelativeIfUnder(this->TopSource, file)); + } + return i->second; + } + +public: + BacktraceData(std::string const& topSource); + bool Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index); + Json::Value Dump(); +}; + +BacktraceData::BacktraceData(std::string const& topSource) + : TopSource(topSource) +{ +} + +bool BacktraceData::Add(cmListFileBacktrace const& bt, Json::ArrayIndex& index) +{ + if (bt.Empty()) { + return false; + } + cmListFileContext const* top = &bt.Top(); + auto found = this->NodeMap.find(top); + if (found != this->NodeMap.end()) { + index = found->second; + return true; + } + Json::Value entry = Json::objectValue; + entry["file"] = this->AddFile(top->FilePath); + if (top->Line) { + entry["line"] = static_cast<int>(top->Line); + } + if (!top->Name.empty()) { + entry["command"] = this->AddCommand(top->Name); + } + Json::ArrayIndex parent; + if (this->Add(bt.Pop(), parent)) { + entry["parent"] = parent; + } + index = this->NodeMap[top] = this->Nodes.size(); + this->Nodes.append(std::move(entry)); // NOLINT(*) + return true; +} + +Json::Value BacktraceData::Dump() +{ + Json::Value backtraceGraph; + this->CommandMap.clear(); + this->FileMap.clear(); + this->NodeMap.clear(); + backtraceGraph["commands"] = std::move(this->Commands); + backtraceGraph["files"] = std::move(this->Files); + backtraceGraph["nodes"] = std::move(this->Nodes); + return backtraceGraph; +} + +struct CompileData +{ + struct IncludeEntry + { + BT<std::string> Path; + bool IsSystem = false; + IncludeEntry(BT<std::string> path, bool isSystem) + : Path(std::move(path)) + , IsSystem(isSystem) + { + } + }; + + void SetDefines(std::set<BT<std::string>> const& defines); + + std::string Language; + std::string Sysroot; + std::vector<BT<std::string>> Flags; + std::vector<BT<std::string>> Defines; + std::vector<IncludeEntry> Includes; +}; + +void CompileData::SetDefines(std::set<BT<std::string>> const& defines) +{ + this->Defines.reserve(defines.size()); + for (BT<std::string> const& d : defines) { + this->Defines.push_back(d); + } +} + +class Target +{ + cmGeneratorTarget* GT; + std::string const& Config; + std::string TopSource; + std::string TopBuild; + std::vector<cmSourceGroup> SourceGroupsLocal; + BacktraceData Backtraces; + + std::map<std::string, CompileData> CompileDataMap; + + std::unordered_map<cmSourceFile const*, Json::ArrayIndex> SourceMap; + Json::Value Sources = Json::arrayValue; + + struct SourceGroup + { + std::string Name; + Json::Value SourceIndexes = Json::arrayValue; + }; + std::unordered_map<cmSourceGroup const*, Json::ArrayIndex> SourceGroupsMap; + std::vector<SourceGroup> SourceGroups; + + struct CompileGroup + { + std::map<Json::Value, Json::ArrayIndex>::iterator Entry; + Json::Value SourceIndexes = Json::arrayValue; + }; + std::map<Json::Value, Json::ArrayIndex> CompileGroupMap; + std::vector<CompileGroup> CompileGroups; + + void ProcessLanguages(); + void ProcessLanguage(std::string const& lang); + + Json::ArrayIndex AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si); + CompileData BuildCompileData(cmSourceFile* sf); + Json::ArrayIndex AddSourceCompileGroup(cmSourceFile* sf, + Json::ArrayIndex si); + void AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt); + Json::Value DumpPaths(); + Json::Value DumpCompileData(CompileData cd); + Json::Value DumpInclude(CompileData::IncludeEntry const& inc); + Json::Value DumpDefine(BT<std::string> const& def); + Json::Value DumpSources(); + Json::Value DumpSource(cmGeneratorTarget::SourceAndKind const& sk, + Json::ArrayIndex si); + Json::Value DumpSourceGroups(); + Json::Value DumpSourceGroup(SourceGroup& sg); + Json::Value DumpCompileGroups(); + Json::Value DumpCompileGroup(CompileGroup& cg); + Json::Value DumpSysroot(std::string const& path); + Json::Value DumpInstall(); + Json::Value DumpInstallPrefix(); + Json::Value DumpInstallDestinations(); + Json::Value DumpInstallDestination(cmInstallTargetGenerator* itGen); + Json::Value DumpArtifacts(); + Json::Value DumpLink(); + Json::Value DumpArchive(); + Json::Value DumpLinkCommandFragments(); + Json::Value DumpCommandFragments(std::vector<BT<std::string>> const& frags); + Json::Value DumpCommandFragment(BT<std::string> const& frag, + std::string const& role = std::string()); + Json::Value DumpDependencies(); + Json::Value DumpDependency(cmTargetDepend const& td); + Json::Value DumpFolder(); + +public: + Target(cmGeneratorTarget* gt, std::string const& config); + Json::Value Dump(); +}; + +Codemodel::Codemodel(cmFileAPI& fileAPI, unsigned long version) + : FileAPI(fileAPI) + , Version(version) +{ +} + +Json::Value Codemodel::Dump() +{ + Json::Value codemodel = Json::objectValue; + + codemodel["paths"] = this->DumpPaths(); + codemodel["configurations"] = this->DumpConfigurations(); + + return codemodel; +} + +Json::Value Codemodel::DumpPaths() +{ + Json::Value paths = Json::objectValue; + paths["source"] = this->FileAPI.GetCMakeInstance()->GetHomeDirectory(); + paths["build"] = this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory(); + return paths; +} + +Json::Value Codemodel::DumpConfigurations() +{ + std::vector<std::string> configs; + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + auto makefiles = gg->GetMakefiles(); + if (!makefiles.empty()) { + makefiles[0]->GetConfigurations(configs); + if (configs.empty()) { + configs.emplace_back(); + } + } + Json::Value configurations = Json::arrayValue; + for (std::string const& config : configs) { + configurations.append(this->DumpConfiguration(config)); + } + return configurations; +} + +Json::Value Codemodel::DumpConfiguration(std::string const& config) +{ + CodemodelConfig configuration(this->FileAPI, this->Version, config); + return configuration.Dump(); +} + +CodemodelConfig::CodemodelConfig(cmFileAPI& fileAPI, unsigned long version, + std::string const& config) + : FileAPI(fileAPI) + , Version(version) + , Config(config) + , TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory()) + , TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory()) +{ + static_cast<void>(this->Version); +} + +Json::Value CodemodelConfig::Dump() +{ + Json::Value configuration = Json::objectValue; + configuration["name"] = this->Config; + this->ProcessDirectories(); + configuration["targets"] = this->DumpTargets(); + configuration["directories"] = this->DumpDirectories(); + configuration["projects"] = this->DumpProjects(); + return configuration; +} + +void CodemodelConfig::ProcessDirectories() +{ + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + std::vector<cmLocalGenerator*> const& localGens = gg->GetLocalGenerators(); + + // Add directories in forward order to process parents before children. + this->Directories.reserve(localGens.size()); + for (cmLocalGenerator* lg : localGens) { + auto directoryIndex = + static_cast<Json::ArrayIndex>(this->Directories.size()); + this->Directories.emplace_back(); + Directory& d = this->Directories[directoryIndex]; + d.Snapshot = lg->GetStateSnapshot().GetBuildsystemDirectory(); + d.LocalGenerator = lg; + this->DirectoryMap[d.Snapshot] = directoryIndex; + + d.ProjectIndex = this->AddProject(d.Snapshot); + this->Projects[d.ProjectIndex].DirectoryIndexes.append(directoryIndex); + } + + // Update directories in reverse order to process children before parents. + for (auto di = this->Directories.rbegin(); di != this->Directories.rend(); + ++di) { + Directory& d = *di; + + // Accumulate the presence of install rules on the way up. + for (auto gen : d.LocalGenerator->GetMakefile()->GetInstallGenerators()) { + if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(gen)) { + d.HasInstallRule = true; + break; + } + } + if (!d.HasInstallRule) { + for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) { + cmStateSnapshot childDir = child.GetBuildsystemDirectory(); + Json::ArrayIndex const childIndex = this->GetDirectoryIndex(childDir); + if (this->Directories[childIndex].HasInstallRule) { + d.HasInstallRule = true; + break; + } + } + } + } +} + +Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmLocalGenerator const* lg) +{ + return this->GetDirectoryIndex( + lg->GetStateSnapshot().GetBuildsystemDirectory()); +} + +Json::ArrayIndex CodemodelConfig::GetDirectoryIndex(cmStateSnapshot s) +{ + auto i = this->DirectoryMap.find(s); + assert(i != this->DirectoryMap.end()); + return i->second; +} + +Json::ArrayIndex CodemodelConfig::AddProject(cmStateSnapshot s) +{ + cmStateSnapshot ps = s.GetBuildsystemDirectoryParent(); + if (ps.IsValid() && ps.GetProjectName() == s.GetProjectName()) { + // This directory is part of its parent directory project. + Json::ArrayIndex const parentDirIndex = this->GetDirectoryIndex(ps); + return this->Directories[parentDirIndex].ProjectIndex; + } + + // This directory starts a new project. + auto projectIndex = static_cast<Json::ArrayIndex>(this->Projects.size()); + this->Projects.emplace_back(); + Project& p = this->Projects[projectIndex]; + p.Snapshot = s; + this->ProjectMap[s] = projectIndex; + if (ps.IsValid()) { + Json::ArrayIndex const parentDirIndex = this->GetDirectoryIndex(ps); + p.ParentIndex = this->Directories[parentDirIndex].ProjectIndex; + this->Projects[p.ParentIndex].ChildIndexes.append(projectIndex); + } + return projectIndex; +} + +Json::Value CodemodelConfig::DumpTargets() +{ + Json::Value targets = Json::arrayValue; + + std::vector<cmGeneratorTarget*> targetList; + cmGlobalGenerator* gg = + this->FileAPI.GetCMakeInstance()->GetGlobalGenerator(); + for (cmLocalGenerator const* lg : gg->GetLocalGenerators()) { + std::vector<cmGeneratorTarget*> const& list = lg->GetGeneratorTargets(); + targetList.insert(targetList.end(), list.begin(), list.end()); + } + std::sort(targetList.begin(), targetList.end(), + [](cmGeneratorTarget* l, cmGeneratorTarget* r) { + return l->GetName() < r->GetName(); + }); + + for (cmGeneratorTarget* gt : targetList) { + if (gt->GetType() == cmStateEnums::GLOBAL_TARGET || + gt->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + + targets.append(this->DumpTarget(gt, targets.size())); + } + + return targets; +} + +Json::Value CodemodelConfig::DumpTarget(cmGeneratorTarget* gt, + Json::ArrayIndex ti) +{ + Target t(gt, this->Config); + std::string prefix = "target-" + gt->GetName(); + if (!this->Config.empty()) { + prefix += "-" + this->Config; + } + Json::Value target = this->FileAPI.MaybeJsonFile(t.Dump(), prefix); + target["name"] = gt->GetName(); + target["id"] = TargetId(gt, this->TopBuild); + + // Cross-reference directory containing target. + Json::ArrayIndex di = this->GetDirectoryIndex(gt->GetLocalGenerator()); + target["directoryIndex"] = di; + this->Directories[di].TargetIndexes.append(ti); + + // Cross-reference project containing target. + Json::ArrayIndex pi = this->Directories[di].ProjectIndex; + target["projectIndex"] = pi; + this->Projects[pi].TargetIndexes.append(ti); + + return target; +} + +Json::Value CodemodelConfig::DumpDirectories() +{ + Json::Value directories = Json::arrayValue; + for (Directory& d : this->Directories) { + directories.append(this->DumpDirectory(d)); + } + return directories; +} + +Json::Value CodemodelConfig::DumpDirectory(Directory& d) +{ + Json::Value directory = Json::objectValue; + + std::string sourceDir = d.Snapshot.GetDirectory().GetCurrentSource(); + directory["source"] = RelativeIfUnder(this->TopSource, sourceDir); + + std::string buildDir = d.Snapshot.GetDirectory().GetCurrentBinary(); + directory["build"] = RelativeIfUnder(this->TopBuild, buildDir); + + cmStateSnapshot parentDir = d.Snapshot.GetBuildsystemDirectoryParent(); + if (parentDir.IsValid()) { + directory["parentIndex"] = this->GetDirectoryIndex(parentDir); + } + + Json::Value childIndexes = Json::arrayValue; + for (cmStateSnapshot const& child : d.Snapshot.GetChildren()) { + childIndexes.append( + this->GetDirectoryIndex(child.GetBuildsystemDirectory())); + } + if (!childIndexes.empty()) { + directory["childIndexes"] = std::move(childIndexes); + } + + directory["projectIndex"] = d.ProjectIndex; + + if (!d.TargetIndexes.empty()) { + directory["targetIndexes"] = std::move(d.TargetIndexes); + } + + Json::Value minimumCMakeVersion = this->DumpMinimumCMakeVersion(d.Snapshot); + if (!minimumCMakeVersion.isNull()) { + directory["minimumCMakeVersion"] = std::move(minimumCMakeVersion); + } + + if (d.HasInstallRule) { + directory["hasInstallRule"] = true; + } + + return directory; +} + +Json::Value CodemodelConfig::DumpProjects() +{ + Json::Value projects = Json::arrayValue; + for (Project& p : this->Projects) { + projects.append(this->DumpProject(p)); + } + return projects; +} + +Json::Value CodemodelConfig::DumpProject(Project& p) +{ + Json::Value project = Json::objectValue; + + project["name"] = p.Snapshot.GetProjectName(); + + if (p.ParentIndex != Project::NoParentIndex) { + project["parentIndex"] = p.ParentIndex; + } + + if (!p.ChildIndexes.empty()) { + project["childIndexes"] = std::move(p.ChildIndexes); + } + + project["directoryIndexes"] = std::move(p.DirectoryIndexes); + + if (!p.TargetIndexes.empty()) { + project["targetIndexes"] = std::move(p.TargetIndexes); + } + + return project; +} + +Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s) +{ + Json::Value minimumCMakeVersion; + if (std::string const* def = + s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) { + minimumCMakeVersion = Json::objectValue; + minimumCMakeVersion["string"] = *def; + } + return minimumCMakeVersion; +} + +Target::Target(cmGeneratorTarget* gt, std::string const& config) + : GT(gt) + , Config(config) + , TopSource(gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeDirectory()) + , TopBuild( + gt->GetGlobalGenerator()->GetCMakeInstance()->GetHomeOutputDirectory()) + , SourceGroupsLocal(this->GT->Makefile->GetSourceGroups()) + , Backtraces(this->TopSource) +{ +} + +Json::Value Target::Dump() +{ + Json::Value target = Json::objectValue; + + cmStateEnums::TargetType const type = this->GT->GetType(); + + target["name"] = this->GT->GetName(); + target["type"] = cmState::GetTargetTypeName(type); + target["id"] = TargetId(this->GT, this->TopBuild); + target["paths"] = this->DumpPaths(); + if (this->GT->Target->GetIsGeneratorProvided()) { + target["isGeneratorProvided"] = true; + } + + this->AddBacktrace(target, this->GT->GetBacktrace()); + + if (this->GT->Target->GetHaveInstallRule()) { + target["install"] = this->DumpInstall(); + } + + if (this->GT->HaveWellDefinedOutputFiles()) { + Json::Value artifacts = this->DumpArtifacts(); + if (!artifacts.empty()) { + target["artifacts"] = std::move(artifacts); + } + } + + if (type == cmStateEnums::EXECUTABLE || + type == cmStateEnums::SHARED_LIBRARY || + type == cmStateEnums::MODULE_LIBRARY) { + target["nameOnDisk"] = this->GT->GetFullName(this->Config); + target["link"] = this->DumpLink(); + } else if (type == cmStateEnums::STATIC_LIBRARY) { + target["nameOnDisk"] = this->GT->GetFullName(this->Config); + target["archive"] = this->DumpArchive(); + } + + Json::Value dependencies = this->DumpDependencies(); + if (!dependencies.empty()) { + target["dependencies"] = dependencies; + } + + { + this->ProcessLanguages(); + + target["sources"] = this->DumpSources(); + + Json::Value folder = this->DumpFolder(); + if (!folder.isNull()) { + target["folder"] = std::move(folder); + } + + Json::Value sourceGroups = this->DumpSourceGroups(); + if (!sourceGroups.empty()) { + target["sourceGroups"] = std::move(sourceGroups); + } + + Json::Value compileGroups = this->DumpCompileGroups(); + if (!compileGroups.empty()) { + target["compileGroups"] = std::move(compileGroups); + } + } + + target["backtraceGraph"] = this->Backtraces.Dump(); + + return target; +} + +void Target::ProcessLanguages() +{ + std::set<std::string> languages; + this->GT->GetLanguages(languages, this->Config); + for (std::string const& lang : languages) { + this->ProcessLanguage(lang); + } +} + +void Target::ProcessLanguage(std::string const& lang) +{ + CompileData& cd = this->CompileDataMap[lang]; + cd.Language = lang; + if (const char* sysrootCompile = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { + cd.Sysroot = sysrootCompile; + } else if (const char* sysroot = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) { + cd.Sysroot = sysroot; + } + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + { + // FIXME: Add flags from end section of ExpandRuleVariable, + // which may need to be factored out. + std::string flags; + lg->GetTargetCompileFlags(this->GT, this->Config, lang, flags); + cd.Flags.emplace_back(std::move(flags), cmListFileBacktrace()); + } + std::set<BT<std::string>> defines = + lg->GetTargetDefines(this->GT, this->Config, lang); + cd.SetDefines(defines); + std::vector<BT<std::string>> includePathList = + lg->GetIncludeDirectories(this->GT, lang, this->Config, true); + for (BT<std::string> const& i : includePathList) { + cd.Includes.emplace_back( + i, this->GT->IsSystemIncludeDirectory(i.Value, this->Config, lang)); + } +} + +Json::ArrayIndex Target::AddSourceGroup(cmSourceGroup* sg, Json::ArrayIndex si) +{ + std::unordered_map<cmSourceGroup const*, Json::ArrayIndex>::iterator i = + this->SourceGroupsMap.find(sg); + if (i == this->SourceGroupsMap.end()) { + auto sgIndex = static_cast<Json::ArrayIndex>(this->SourceGroups.size()); + i = this->SourceGroupsMap.emplace(sg, sgIndex).first; + SourceGroup g; + g.Name = sg->GetFullName(); + this->SourceGroups.push_back(std::move(g)); + } + this->SourceGroups[i->second].SourceIndexes.append(si); + return i->second; +} + +CompileData Target::BuildCompileData(cmSourceFile* sf) +{ + CompileData fd; + + fd.Language = sf->GetLanguage(); + if (fd.Language.empty()) { + return fd; + } + CompileData const& cd = this->CompileDataMap.at(fd.Language); + + fd.Sysroot = cd.Sysroot; + + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + cmGeneratorExpressionInterpreter genexInterpreter(lg, this->Config, this->GT, + fd.Language); + + fd.Flags = cd.Flags; + const std::string COMPILE_FLAGS("COMPILE_FLAGS"); + if (const char* cflags = sf->GetProperty(COMPILE_FLAGS)) { + std::string flags = genexInterpreter.Evaluate(cflags, COMPILE_FLAGS); + fd.Flags.emplace_back(std::move(flags), cmListFileBacktrace()); + } + const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); + if (const char* coptions = sf->GetProperty(COMPILE_OPTIONS)) { + std::string flags; + lg->AppendCompileOptions( + flags, genexInterpreter.Evaluate(coptions, COMPILE_OPTIONS)); + fd.Flags.emplace_back(std::move(flags), cmListFileBacktrace()); + } + + // Add include directories from source file properties. + { + std::vector<std::string> includes; + const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); + if (const char* cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) { + const std::string& evaluatedIncludes = + genexInterpreter.Evaluate(cincludes, INCLUDE_DIRECTORIES); + lg->AppendIncludeDirectories(includes, evaluatedIncludes, *sf); + + for (std::string const& include : includes) { + bool const isSystemInclude = this->GT->IsSystemIncludeDirectory( + include, this->Config, fd.Language); + fd.Includes.emplace_back(include, isSystemInclude); + } + } + } + fd.Includes.insert(fd.Includes.end(), cd.Includes.begin(), + cd.Includes.end()); + + const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); + std::set<std::string> fileDefines; + if (const char* defs = sf->GetProperty(COMPILE_DEFINITIONS)) { + lg->AppendDefines(fileDefines, + genexInterpreter.Evaluate(defs, COMPILE_DEFINITIONS)); + } + + const std::string defPropName = + "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config); + if (const char* config_defs = sf->GetProperty(defPropName)) { + lg->AppendDefines( + fileDefines, + genexInterpreter.Evaluate(config_defs, COMPILE_DEFINITIONS)); + } + + std::set<BT<std::string>> defines; + defines.insert(fileDefines.begin(), fileDefines.end()); + defines.insert(cd.Defines.begin(), cd.Defines.end()); + + fd.SetDefines(defines); + + return fd; +} + +Json::ArrayIndex Target::AddSourceCompileGroup(cmSourceFile* sf, + Json::ArrayIndex si) +{ + Json::Value compileDataJson = + this->DumpCompileData(this->BuildCompileData(sf)); + std::map<Json::Value, Json::ArrayIndex>::iterator i = + this->CompileGroupMap.find(compileDataJson); + if (i == this->CompileGroupMap.end()) { + Json::ArrayIndex cgIndex = + static_cast<Json::ArrayIndex>(this->CompileGroups.size()); + i = + this->CompileGroupMap.emplace(std::move(compileDataJson), cgIndex).first; + CompileGroup g; + g.Entry = i; + this->CompileGroups.push_back(std::move(g)); + } + this->CompileGroups[i->second].SourceIndexes.append(si); + return i->second; +} + +void Target::AddBacktrace(Json::Value& object, cmListFileBacktrace const& bt) +{ + Json::ArrayIndex backtrace; + if (this->Backtraces.Add(bt, backtrace)) { + object["backtrace"] = backtrace; + } +} + +Json::Value Target::DumpPaths() +{ + Json::Value paths = Json::objectValue; + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + + std::string const& sourceDir = lg->GetCurrentSourceDirectory(); + paths["source"] = RelativeIfUnder(this->TopSource, sourceDir); + + std::string const& buildDir = lg->GetCurrentBinaryDirectory(); + paths["build"] = RelativeIfUnder(this->TopBuild, buildDir); + + return paths; +} + +Json::Value Target::DumpSources() +{ + Json::Value sources = Json::arrayValue; + cmGeneratorTarget::KindedSources const& kinded = + this->GT->GetKindedSources(this->Config); + for (cmGeneratorTarget::SourceAndKind const& sk : kinded.Sources) { + sources.append(this->DumpSource(sk, sources.size())); + } + return sources; +} + +Json::Value Target::DumpSource(cmGeneratorTarget::SourceAndKind const& sk, + Json::ArrayIndex si) +{ + Json::Value source = Json::objectValue; + + std::string const path = sk.Source.Value->GetFullPath(); + source["path"] = RelativeIfUnder(this->TopSource, path); + if (sk.Source.Value->GetPropertyAsBool("GENERATED")) { + source["isGenerated"] = true; + } + this->AddBacktrace(source, sk.Source.Backtrace); + + if (cmSourceGroup* sg = + this->GT->Makefile->FindSourceGroup(path, this->SourceGroupsLocal)) { + source["sourceGroupIndex"] = this->AddSourceGroup(sg, si); + } + + switch (sk.Kind) { + case cmGeneratorTarget::SourceKindObjectSource: { + source["compileGroupIndex"] = + this->AddSourceCompileGroup(sk.Source.Value, si); + } break; + case cmGeneratorTarget::SourceKindAppManifest: + case cmGeneratorTarget::SourceKindCertificate: + case cmGeneratorTarget::SourceKindCustomCommand: + case cmGeneratorTarget::SourceKindExternalObject: + case cmGeneratorTarget::SourceKindExtra: + case cmGeneratorTarget::SourceKindHeader: + case cmGeneratorTarget::SourceKindIDL: + case cmGeneratorTarget::SourceKindManifest: + case cmGeneratorTarget::SourceKindModuleDefinition: + case cmGeneratorTarget::SourceKindResx: + case cmGeneratorTarget::SourceKindXaml: + break; + } + + return source; +} + +Json::Value Target::DumpCompileData(CompileData cd) +{ + Json::Value result = Json::objectValue; + + if (!cd.Language.empty()) { + result["language"] = cd.Language; + } + if (!cd.Sysroot.empty()) { + result["sysroot"] = this->DumpSysroot(cd.Sysroot); + } + if (!cd.Flags.empty()) { + result["compileCommandFragments"] = this->DumpCommandFragments(cd.Flags); + } + if (!cd.Includes.empty()) { + Json::Value includes = Json::arrayValue; + for (auto const& i : cd.Includes) { + includes.append(this->DumpInclude(i)); + } + result["includes"] = includes; + } + if (!cd.Defines.empty()) { + Json::Value defines = Json::arrayValue; + for (BT<std::string> const& d : cd.Defines) { + defines.append(this->DumpDefine(d)); + } + result["defines"] = std::move(defines); + } + + return result; +} + +Json::Value Target::DumpInclude(CompileData::IncludeEntry const& inc) +{ + Json::Value include = Json::objectValue; + include["path"] = inc.Path.Value; + if (inc.IsSystem) { + include["isSystem"] = true; + } + this->AddBacktrace(include, inc.Path.Backtrace); + return include; +} + +Json::Value Target::DumpDefine(BT<std::string> const& def) +{ + Json::Value define = Json::objectValue; + define["define"] = def.Value; + this->AddBacktrace(define, def.Backtrace); + return define; +} + +Json::Value Target::DumpSourceGroups() +{ + Json::Value sourceGroups = Json::arrayValue; + for (auto& sg : this->SourceGroups) { + sourceGroups.append(this->DumpSourceGroup(sg)); + } + return sourceGroups; +} + +Json::Value Target::DumpSourceGroup(SourceGroup& sg) +{ + Json::Value group = Json::objectValue; + group["name"] = sg.Name; + group["sourceIndexes"] = std::move(sg.SourceIndexes); + return group; +} + +Json::Value Target::DumpCompileGroups() +{ + Json::Value compileGroups = Json::arrayValue; + for (auto& cg : this->CompileGroups) { + compileGroups.append(this->DumpCompileGroup(cg)); + } + return compileGroups; +} + +Json::Value Target::DumpCompileGroup(CompileGroup& cg) +{ + Json::Value group = cg.Entry->first; + group["sourceIndexes"] = std::move(cg.SourceIndexes); + return group; +} + +Json::Value Target::DumpSysroot(std::string const& path) +{ + Json::Value sysroot = Json::objectValue; + sysroot["path"] = path; + return sysroot; +} + +Json::Value Target::DumpInstall() +{ + Json::Value install = Json::objectValue; + install["prefix"] = this->DumpInstallPrefix(); + install["destinations"] = this->DumpInstallDestinations(); + return install; +} + +Json::Value Target::DumpInstallPrefix() +{ + Json::Value prefix = Json::objectValue; + std::string p = + this->GT->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"); + cmSystemTools::ConvertToUnixSlashes(p); + prefix["path"] = p; + return prefix; +} + +Json::Value Target::DumpInstallDestinations() +{ + Json::Value destinations = Json::arrayValue; + auto installGens = this->GT->Makefile->GetInstallGenerators(); + for (auto iGen : installGens) { + auto itGen = dynamic_cast<cmInstallTargetGenerator*>(iGen); + if (itGen != nullptr && itGen->GetTarget() == this->GT) { + destinations.append(this->DumpInstallDestination(itGen)); + } + } + return destinations; +} + +Json::Value Target::DumpInstallDestination(cmInstallTargetGenerator* itGen) +{ + Json::Value destination = Json::objectValue; + destination["path"] = itGen->GetDestination(this->Config); + this->AddBacktrace(destination, itGen->GetBacktrace()); + return destination; +} + +Json::Value Target::DumpArtifacts() +{ + Json::Value artifacts = Json::arrayValue; + + // Object libraries have only object files as artifacts. + if (this->GT->GetType() == cmStateEnums::OBJECT_LIBRARY) { + if (!this->GT->GetGlobalGenerator()->HasKnownObjectFileLocation(nullptr)) { + return artifacts; + } + std::vector<cmSourceFile const*> objectSources; + this->GT->GetObjectSources(objectSources, this->Config); + std::string const obj_dir = this->GT->GetObjectDirectory(this->Config); + for (cmSourceFile const* sf : objectSources) { + const std::string& obj = this->GT->GetObjectName(sf); + Json::Value artifact = Json::objectValue; + artifact["path"] = RelativeIfUnder(this->TopBuild, obj_dir + obj); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + return artifacts; + } + + // Other target types always have a "main" artifact. + { + Json::Value artifact = Json::objectValue; + artifact["path"] = + RelativeIfUnder(this->TopBuild, + this->GT->GetFullPath( + this->Config, cmStateEnums::RuntimeBinaryArtifact)); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + + // Add Windows-specific artifacts produced by the linker. + if (this->GT->IsDLLPlatform() && + this->GT->GetType() != cmStateEnums::STATIC_LIBRARY) { + if (this->GT->GetType() == cmStateEnums::SHARED_LIBRARY || + this->GT->IsExecutableWithExports()) { + Json::Value artifact = Json::objectValue; + artifact["path"] = + RelativeIfUnder(this->TopBuild, + this->GT->GetFullPath( + this->Config, cmStateEnums::ImportLibraryArtifact)); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + cmGeneratorTarget::OutputInfo const* output = + this->GT->GetOutputInfo(this->Config); + if (output && !output->PdbDir.empty()) { + Json::Value artifact = Json::objectValue; + artifact["path"] = RelativeIfUnder(this->TopBuild, + output->PdbDir + '/' + + this->GT->GetPDBName(this->Config)); + artifacts.append(std::move(artifact)); // NOLINT(*) + } + } + return artifacts; +} + +Json::Value Target::DumpLink() +{ + Json::Value link = Json::objectValue; + std::string lang = this->GT->GetLinkerLanguage(this->Config); + link["language"] = lang; + { + Json::Value commandFragments = this->DumpLinkCommandFragments(); + if (!commandFragments.empty()) { + link["commandFragments"] = std::move(commandFragments); + } + } + if (const char* sysrootLink = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { + link["sysroot"] = this->DumpSysroot(sysrootLink); + } else if (const char* sysroot = + this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) { + link["sysroot"] = this->DumpSysroot(sysroot); + } + if (this->GT->IsIPOEnabled(lang, this->Config)) { + link["lto"] = true; + } + return link; +} + +Json::Value Target::DumpArchive() +{ + Json::Value archive = Json::objectValue; + { + // The "link" fragments not relevant to static libraries are empty. + Json::Value commandFragments = this->DumpLinkCommandFragments(); + if (!commandFragments.empty()) { + archive["commandFragments"] = std::move(commandFragments); + } + } + std::string lang = this->GT->GetLinkerLanguage(this->Config); + if (this->GT->IsIPOEnabled(lang, this->Config)) { + archive["lto"] = true; + } + return archive; +} + +Json::Value Target::DumpLinkCommandFragments() +{ + Json::Value linkFragments = Json::arrayValue; + + std::string linkLanguageFlags; + std::string linkFlags; + std::string frameworkPath; + std::string linkPath; + std::string linkLibs; + cmLocalGenerator* lg = this->GT->GetLocalGenerator(); + cmLinkLineComputer linkLineComputer(lg, + lg->GetStateSnapshot().GetDirectory()); + lg->GetTargetFlags(&linkLineComputer, this->Config, linkLibs, + linkLanguageFlags, linkFlags, frameworkPath, linkPath, + this->GT); + linkLanguageFlags = cmSystemTools::TrimWhitespace(linkLanguageFlags); + linkFlags = cmSystemTools::TrimWhitespace(linkFlags); + frameworkPath = cmSystemTools::TrimWhitespace(frameworkPath); + linkPath = cmSystemTools::TrimWhitespace(linkPath); + linkLibs = cmSystemTools::TrimWhitespace(linkLibs); + + if (!linkLanguageFlags.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(linkLanguageFlags), "flags")); + } + + if (!linkFlags.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(linkFlags), "flags")); + } + + if (!frameworkPath.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(frameworkPath), "frameworkPath")); + } + + if (!linkPath.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(linkPath), "libraryPath")); + } + + if (!linkLibs.empty()) { + linkFragments.append( + this->DumpCommandFragment(std::move(linkLibs), "libraries")); + } + + return linkFragments; +} + +Json::Value Target::DumpCommandFragments( + std::vector<BT<std::string>> const& frags) +{ + Json::Value commandFragments = Json::arrayValue; + for (BT<std::string> const& f : frags) { + commandFragments.append(this->DumpCommandFragment(f)); + } + return commandFragments; +} + +Json::Value Target::DumpCommandFragment(BT<std::string> const& frag, + std::string const& role) +{ + Json::Value fragment = Json::objectValue; + fragment["fragment"] = frag.Value; + if (!role.empty()) { + fragment["role"] = role; + } + this->AddBacktrace(fragment, frag.Backtrace); + return fragment; +} + +Json::Value Target::DumpDependencies() +{ + Json::Value dependencies = Json::arrayValue; + cmGlobalGenerator* gg = this->GT->GetGlobalGenerator(); + for (cmTargetDepend const& td : gg->GetTargetDirectDepends(this->GT)) { + dependencies.append(this->DumpDependency(td)); + } + return dependencies; +} + +Json::Value Target::DumpDependency(cmTargetDepend const& td) +{ + Json::Value dependency = Json::objectValue; + dependency["id"] = TargetId(td, this->TopBuild); + this->AddBacktrace(dependency, td.GetBacktrace()); + return dependency; +} + +Json::Value Target::DumpFolder() +{ + Json::Value folder; + if (const char* f = this->GT->GetProperty("FOLDER")) { + folder = Json::objectValue; + folder["name"] = f; + } + return folder; +} +} + +Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, unsigned long version) +{ + Codemodel codemodel(fileAPI, version); + return codemodel.Dump(); +} diff --git a/Source/cmFileAPICodemodel.h b/Source/cmFileAPICodemodel.h new file mode 100644 index 0000000..ffbd928 --- /dev/null +++ b/Source/cmFileAPICodemodel.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmFileAPICodemodel_h +#define cmFileAPICodemodel_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_jsoncpp_value.h" + +class cmFileAPI; + +extern Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI, + unsigned long version); + +#endif diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 8b20db6..35d716c 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -112,6 +112,15 @@ cmGlobalGenerator::~cmGlobalGenerator() delete this->ExtraGenerator; } +#if defined(CMAKE_BUILD_WITH_CMAKE) +Json::Value cmGlobalGenerator::GetJson() const +{ + Json::Value generator = Json::objectValue; + generator["name"] = this->GetName(); + return generator; +} +#endif + bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i, cmMakefile* mf) { diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 1ea2d24..4e6b6de 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -24,6 +24,7 @@ #if defined(CMAKE_BUILD_WITH_CMAKE) # include "cmFileLockPool.h" +# include "cm_jsoncpp_value.h" #endif #define CMAKE_DIRECTORY_ID_SEP "::@" @@ -70,6 +71,11 @@ public: return codecvt::None; } +#if defined(CMAKE_BUILD_WITH_CMAKE) + /** Get a JSON object describing the generator. */ + virtual Json::Value GetJson() const; +#endif + /** Tell the generator about the target system. */ virtual bool SetSystemName(std::string const&, cmMakefile*) { return true; } diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 0b6c149..3648086 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -254,6 +254,15 @@ cmLocalGenerator* cmGlobalVisualStudio7Generator::CreateLocalGenerator( return lg; } +#if defined(CMAKE_BUILD_WITH_CMAKE) +Json::Value cmGlobalVisualStudio7Generator::GetJson() const +{ + Json::Value generator = this->cmGlobalVisualStudioGenerator::GetJson(); + generator["platform"] = this->GetPlatformName(); + return generator; +} +#endif + std::string const& cmGlobalVisualStudio7Generator::GetPlatformName() const { if (!this->GeneratorPlatform.empty()) { diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h index 251478d..59e7a98 100644 --- a/Source/cmGlobalVisualStudio7Generator.h +++ b/Source/cmGlobalVisualStudio7Generator.h @@ -28,6 +28,10 @@ public: ///! Create a local generator appropriate to this Global Generator cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override; +#if defined(CMAKE_BUILD_WITH_CMAKE) + Json::Value GetJson() const override; +#endif + bool SetSystemName(std::string const& s, cmMakefile* mf) override; bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override; diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx index c2510f3..a649f5e 100644 --- a/Source/cmStateSnapshot.cxx +++ b/Source/cmStateSnapshot.cxx @@ -66,6 +66,12 @@ bool cmStateSnapshot::IsValid() const : false; } +cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectory() const +{ + return cmStateSnapshot(this->State, + this->Position->BuildSystemDirectory->DirectoryEnd); +} + cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const { cmStateSnapshot snapshot; diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h index 014c62e..c315f48 100644 --- a/Source/cmStateSnapshot.h +++ b/Source/cmStateSnapshot.h @@ -37,6 +37,7 @@ public: std::vector<cmStateSnapshot> GetChildren(); bool IsValid() const; + cmStateSnapshot GetBuildsystemDirectory() const; cmStateSnapshot GetBuildsystemDirectoryParent() const; cmStateSnapshot GetCallStackParent() const; cmStateSnapshot GetCallStackBottom() const; diff --git a/Source/cmString.cxx b/Source/cmString.cxx new file mode 100644 index 0000000..2a0c125 --- /dev/null +++ b/Source/cmString.cxx @@ -0,0 +1,152 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#define _SCL_SECURE_NO_WARNINGS + +#include "cmString.hxx" + +#include <memory> +#include <ostream> +#include <stdexcept> +#include <string> +#include <type_traits> + +namespace cm { + +static std::string const empty_string_; + +void String::internally_mutate_to_stable_string() +{ + // We assume that only one thread mutates this instance at + // a time even if we point to a shared string buffer refernced + // by other threads. + *this = String(data(), size()); +} + +bool String::is_stable() const +{ + return str_if_stable() != nullptr; +} + +void String::stabilize() +{ + if (is_stable()) { + return; + } + this->internally_mutate_to_stable_string(); +} + +std::string const* String::str_if_stable() const +{ + if (!data()) { + // We view no string. + // This is stable for the lifetime of our current value. + return &empty_string_; + } + + if (string_ && data() == string_->data() && size() == string_->size()) { + // We view an entire string. + // This is stable for the lifetime of our current value. + return string_.get(); + } + + return nullptr; +} + +std::string const& String::str() +{ + if (std::string const* s = str_if_stable()) { + return *s; + } + // Mutate to hold a std::string that is stable for the lifetime + // of our current value. + this->internally_mutate_to_stable_string(); + return *string_; +} + +const char* String::c_str() +{ + const char* c = data(); + if (c == nullptr) { + return c; + } + + // We always point into a null-terminated string so it is safe to + // access one past the end. If it is a null byte then we can use + // the pointer directly. + if (c[size()] == '\0') { + return c; + } + + // Mutate to hold a std::string so we can get a null terminator. + this->internally_mutate_to_stable_string(); + c = string_->c_str(); + return c; +} + +String& String::insert(size_type index, size_type count, char ch) +{ + std::string s; + s.reserve(size() + count); + s.assign(data(), size()); + s.insert(index, count, ch); + return *this = std::move(s); +} + +String& String::erase(size_type index, size_type count) +{ + if (index > size()) { + throw std::out_of_range("Index out of range in String::erase"); + } + size_type const rcount = std::min(count, size() - index); + size_type const rindex = index + rcount; + std::string s; + s.reserve(size() - rcount); + s.assign(data(), index); + s.append(data() + rindex, size() - rindex); + return *this = std::move(s); +} + +String String::substr(size_type pos, size_type count) const +{ + if (pos > size()) { + throw std::out_of_range("Index out of range in String::substr"); + } + return String(*this, pos, count); +} + +String::String(std::string&& s, Private) + : string_(std::make_shared<std::string>(std::move(s))) + , view_(string_->data(), string_->size()) +{ +} + +String::size_type String::copy(char* dest, size_type count, + size_type pos) const +{ + return view_.copy(dest, count, pos); +} + +std::ostream& operator<<(std::ostream& os, String const& s) +{ + return os.write(s.data(), s.size()); +} + +std::string& operator+=(std::string& self, String const& s) +{ + return self += s.view(); +} + +String IntoString<char*>::into_string(const char* s) +{ + if (!s) { + return String(); + } + return std::string(s); +} + +string_view AsStringView<String>::view(String const& s) +{ + return s.view(); +} + +} // namespace cm diff --git a/Source/cmString.hxx b/Source/cmString.hxx new file mode 100644 index 0000000..1623a43 --- /dev/null +++ b/Source/cmString.hxx @@ -0,0 +1,815 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmString_hxx +#define cmString_hxx + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_static_string_view.hxx" +#include "cm_string_view.hxx" + +#include <algorithm> +#include <functional> +#include <initializer_list> +#include <memory> +#include <ostream> +#include <string> +#include <type_traits> + +namespace cm { + +class String; + +/** + * Trait to convert type T into a String. + * Implementations must derive from 'std::true_type' + * and define an 'into_string' member that accepts + * type T (by value or reference) and returns one of: + * + * - 'std::string' to construct an owned instance. + * - 'cm::string_view' to construct a borrowed or null instances. + * The buffer from which the view is borrowed must outlive + * all copies of the resulting String, e.g. static storage. + * - 'cm::String' for already-constructed instances. + */ +template <typename T> +struct IntoString : std::false_type +{ +}; + +template <typename T> +struct IntoString<T&> : IntoString<T> +{ +}; + +template <typename T> +struct IntoString<T const> : IntoString<T> +{ +}; + +template <typename T> +struct IntoString<T const*> : IntoString<T*> +{ +}; + +template <typename T, std::string::size_type N> +struct IntoString<T const[N]> : IntoString<T[N]> +{ +}; + +template <> +struct IntoString<char*> : std::true_type +{ + static String into_string(const char* s); +}; + +template <> +struct IntoString<std::nullptr_t> : std::true_type +{ + static string_view into_string(std::nullptr_t) { return string_view(); } +}; + +template <std::string::size_type N> +struct IntoString<char[N]> : std::true_type +{ + static std::string into_string(char const (&s)[N]) + { + return std::string(s, N - 1); + } +}; + +template <> +struct IntoString<std::string> : std::true_type +{ + static std::string into_string(std::string s) { return s; } +}; + +template <> +struct IntoString<string_view> : std::true_type +{ + static std::string into_string(string_view s) { return std::string(s); } +}; + +template <> +struct IntoString<static_string_view> : std::true_type +{ + static string_view into_string(static_string_view s) { return s; } +}; + +template <> +struct IntoString<char> : std::true_type +{ + static std::string into_string(char const& c) { return std::string(1, c); } +}; + +/** + * Trait to convert type T into a 'cm::string_view'. + * Implementations must derive from 'std::true_type' and + * define a 'view' member that accepts type T (by reference) + * and returns a 'cm::string_view'. + */ +template <typename T> +struct AsStringView : std::false_type +{ +}; + +template <typename T> +struct AsStringView<T&> : AsStringView<T> +{ +}; + +template <typename T> +struct AsStringView<T const> : AsStringView<T> +{ +}; + +template <typename T> +struct AsStringView<T const*> : AsStringView<T*> +{ +}; + +template <typename T, std::string::size_type N> +struct AsStringView<T const[N]> : AsStringView<T[N]> +{ +}; + +template <> +struct AsStringView<char*> : std::true_type +{ + static string_view view(const char* s) { return s; } +}; + +template <std::string::size_type N> +struct AsStringView<char[N]> : std::true_type +{ + static string_view view(char const (&s)[N]) { return string_view(s, N - 1); } +}; + +template <> +struct AsStringView<std::string> : std::true_type +{ + static string_view view(std::string const& s) { return s; } +}; + +template <> +struct AsStringView<char> : std::true_type +{ + static string_view view(const char& s) { return string_view(&s, 1); } +}; + +template <> +struct AsStringView<string_view> : std::true_type +{ + static string_view view(string_view const& s) { return s; } +}; + +template <> +struct AsStringView<static_string_view> : std::true_type +{ + static string_view view(static_string_view const& s) { return s; } +}; + +template <> +struct AsStringView<String> : std::true_type +{ + static string_view view(String const& s); +}; + +/** + * \class String + * + * A custom string type that holds a view of a string buffer + * and optionally shares ownership of the buffer. Instances + * may have one of the following states: + * + * - null: views and owns nothing. + * Conversion to 'bool' is 'false'. + * 'data()' and 'c_str()' return nullptr. + * 'size()' returns 0. + * 'str()' returns an empty string. + * + * - borrowed: views a string but does not own it. This is used + * to bind to static storage (e.g. string literals) or for + * temporary instances that do not outlive the borrowed buffer. + * Copies and substrings still borrow the original buffer. + * Mutation allocates a new internal string and converts to + * the 'owned' state. + * Conversion to 'bool' is 'true'. + * 'c_str()' may internally mutate to the 'owned' state. + * 'str()' internally mutates to the 'owned' state. + * + * - owned: views an immutable 'std::string' instance owned internally. + * Copies and substrings share ownership of the internal string. + * Mutation allocates a new internal string. + * Conversion to 'bool' is 'true'. + */ +class String +{ + enum class Private + { + }; + +public: + using traits_type = std::string::traits_type; + using value_type = string_view::value_type; + using pointer = string_view::pointer; + using const_pointer = string_view::const_pointer; + using reference = string_view::reference; + using const_reference = string_view::const_reference; + using const_iterator = string_view::const_iterator; + using iterator = string_view::const_iterator; + using const_reverse_iterator = string_view::const_reverse_iterator; + using reverse_iterator = string_view::const_reverse_iterator; + using difference_type = string_view::difference_type; + using size_type = string_view::size_type; + + static size_type const npos = string_view::npos; + + /** Construct a null string. */ + String() = default; + + /** Construct from any type implementing the IntoString trait. */ + template <typename T, + typename = typename std::enable_if<IntoString<T>::value>::type> + String(T&& s) + : String(IntoString<T>::into_string(std::forward<T>(s)), Private()) + { + } + + /** Construct via std::string initializer list constructor. */ + String(std::initializer_list<char> il) + : String(std::string(il)) + { + } + + /** Construct by copying the specified buffer. */ + String(const char* d, size_type s) + : String(std::string(d, s)) + { + } + + /** Construct by copying from input iterator range. */ + template <typename InputIterator> + String(InputIterator first, InputIterator last) + : String(std::string(first, last)) + { + } + + /** Construct a string with 'n' copies of character 'c'. */ + String(size_type n, char c) + : String(std::string(n, c)) + { + } + + /** Construct from a substring of another String instance. + This shares ownership of the other string's buffer + but views only a substring. */ + String(String const& s, size_type pos, size_type count = npos) + : string_(s.string_) + , view_(s.data() + pos, std::min(count, s.size() - pos)) + { + } + + /** Construct by moving from another String instance. + The other instance is left as a null string. */ + String(String&& s) noexcept + : string_(std::move(s.string_)) + , view_(s.view_) + { + s.view_ = string_view(); + } + + /** Construct by copying from another String instance. + This shares ownership of the other string's buffer. */ + String(String const&) noexcept = default; + + ~String() = default; + + /** Construct by borrowing an externally-owned buffer. The buffer + must outlive the returned instance and all copies of it. */ + static String borrow(string_view v) { return String(v, Private()); } + + /** Assign by moving from another String instance. + The other instance is left as a null string. */ + String& operator=(String&& s) noexcept + { + string_ = std::move(s.string_); + view_ = s.view_; + s.view_ = string_view(); + return *this; + } + + /** Assign by copying from another String instance. + This shares ownership of the other string's buffer. */ + String& operator=(String const&) noexcept = default; + + /** Assign from any type implementing the IntoString trait. */ + template <typename T> + typename // NOLINT(*) + std::enable_if<IntoString<T>::value, String&>::type + operator=(T&& s) + { + *this = String(std::forward<T>(s)); + return *this; + } + + /** Assign via std::string initializer list constructor. */ + String& operator=(std::initializer_list<char> il) + { + *this = String(il); + return *this; + } + + /** Return true if the instance is not a null string. */ + explicit operator bool() const noexcept { return data() != nullptr; } + + /** Return a view of the string. */ + string_view view() const noexcept { return view_; } + + /** Return true if the instance is an empty stringn or null string. */ + bool empty() const noexcept { return view_.empty(); } + + /** Return a pointer to the start of the string. */ + const char* data() const noexcept { return view_.data(); } + + /** Return the length of the string in bytes. */ + size_type size() const noexcept { return view_.size(); } + size_type length() const noexcept { return view_.length(); } + + /** Return the character at the given position. + No bounds checking is performed. */ + char operator[](size_type pos) const noexcept { return view_[pos]; } + + /** Return the character at the given position. + If the position is out of bounds, throws std::out_of_range. */ + char at(size_type pos) const { return view_.at(pos); } + + char front() const noexcept { return view_.front(); } + + char back() const noexcept { return view_.back(); } + + /** Return true if this instance is stable and otherwise false. + An instance is stable if it is in the 'null' state or if it is + an 'owned' state not produced by substring operations, or + after a call to 'stabilize()' or 'str()'. */ + bool is_stable() const; + + /** If 'is_stable()' does not return true, mutate so it does. */ + void stabilize(); + + /** Get a pointer to a normal std::string if 'is_stable()' returns + true and otherwise nullptr. The pointer is valid until this + instance is mutated or destroyed. */ + std::string const* str_if_stable() const; + + /** Get a refernce to a normal std::string. The reference + is valid until this instance is mutated or destroyed. */ + std::string const& str(); + + /** Get a pointer to a C-style null-terminated string + containing the same value as this instance. The pointer + is valid until this instance is mutated, destroyed, + or str() is called. */ + const char* c_str(); + + const_iterator begin() const noexcept { return view_.begin(); } + const_iterator end() const noexcept { return view_.end(); } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_reverse_iterator rbegin() const noexcept { return view_.rbegin(); } + const_reverse_iterator rend() const noexcept { return view_.rend(); } + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + const_reverse_iterator crend() const noexcept { return rend(); } + + /** Append to the string using any type that implements the + AsStringView trait. */ + template <typename T> + typename std::enable_if<AsStringView<T>::value, String&>::type operator+=( + T&& s) + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + std::string r; + r.reserve(size() + v.size()); + r.assign(data(), size()); + r.append(v.data(), v.size()); + return *this = std::move(r); + } + + /** Assign to an empty string. */ + void clear() { *this = ""_s; } + + /** Insert 'count' copies of 'ch' at position 'index'. */ + String& insert(size_type index, size_type count, char ch); + + /** Erase 'count' characters starting at position 'index'. */ + String& erase(size_type index = 0, size_type count = npos); + + void push_back(char ch) + { + std::string s; + s.reserve(size() + 1); + s.assign(data(), size()); + s.push_back(ch); + *this = std::move(s); + } + + void pop_back() { *this = String(*this, 0, size() - 1); } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, String&>::type replace( + size_type pos, size_type count, T&& s) + { + const_iterator first = begin() + pos; + const_iterator last = first + count; + return replace(first, last, std::forward<T>(s)); + } + + template <typename InputIterator> + String& replace(const_iterator first, const_iterator last, + InputIterator first2, InputIterator last2) + { + std::string out; + out.append(view_.begin(), first); + out.append(first2, last2); + out.append(last, view_.end()); + return *this = std::move(out); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, String&>::type replace( + const_iterator first, const_iterator last, T&& s) + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + std::string out; + out.reserve((first - view_.begin()) + v.size() + (view_.end() - last)); + out.append(view_.begin(), first); + out.append(v.data(), v.size()); + out.append(last, view_.end()); + return *this = std::move(out); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, String&>::type replace( + size_type pos, size_type count, T&& s, size_type pos2, + size_type count2 = npos) + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + v = v.substr(pos2, count2); + return replace(pos, count, v); + } + + String& replace(size_type pos, size_type count, size_type count2, char ch) + { + const_iterator first = begin() + pos; + const_iterator last = first + count; + return replace(first, last, count2, ch); + } + + String& replace(const_iterator first, const_iterator last, size_type count2, + char ch) + { + std::string out; + out.reserve((first - view_.begin()) + count2 + (view_.end() - last)); + out.append(view_.begin(), first); + out.append(count2, ch); + out.append(last, view_.end()); + return *this = std::move(out); + } + + size_type copy(char* dest, size_type count, size_type pos = 0) const; + + void resize(size_type count) { resize(count, char()); } + + void resize(size_type count, char ch) + { + std::string s; + s.reserve(count); + if (count <= size()) { + s.assign(data(), count); + } else { + s.assign(data(), size()); + s.resize(count, ch); + } + *this = std::move(s); + } + + void swap(String& other) + { + std::swap(string_, other.string_); + std::swap(view_, other.view_); + } + + /** Return a substring starting at position 'pos' and + consisting of at most 'count' characters. */ + String substr(size_type pos = 0, size_type count = npos) const; + + template <typename T> + typename std::enable_if<AsStringView<T>::value, int>::type compare( + T&& s) const + { + return view_.compare(AsStringView<T>::view(std::forward<T>(s))); + } + + int compare(size_type pos1, size_type count1, string_view v) const + { + return view_.compare(pos1, count1, v); + } + + int compare(size_type pos1, size_type count1, string_view v, size_type pos2, + size_type count2) const + { + return view_.compare(pos1, count1, v, pos2, count2); + } + + int compare(size_type pos1, size_type count1, const char* s) const + { + return view_.compare(pos1, count1, s); + } + + int compare(size_type pos1, size_type count1, const char* s, + size_type count2) const + { + return view_.compare(pos1, count1, s, count2); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, size_type>::type find( + T&& s, size_type pos = 0) const + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + return view_.find(v, pos); + } + + size_type find(const char* s, size_type pos, size_type count) const + { + return view_.find(s, pos, count); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, size_type>::type rfind( + T&& s, size_type pos = npos) const + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + return view_.rfind(v, pos); + } + + size_type rfind(const char* s, size_type pos, size_type count) const + { + return view_.rfind(s, pos, count); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, size_type>::type + find_first_of(T&& s, size_type pos = 0) const + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + return view_.find_first_of(v, pos); + } + + size_type find_first_of(const char* s, size_type pos, size_type count) const + { + return view_.find_first_of(s, pos, count); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, size_type>::type + find_first_not_of(T&& s, size_type pos = 0) const + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + return view_.find_first_not_of(v, pos); + } + + size_type find_first_not_of(const char* s, size_type pos, + size_type count) const + { + return view_.find_first_not_of(s, pos, count); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, size_type>::type + find_last_of(T&& s, size_type pos = npos) const + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + return view_.find_last_of(v, pos); + } + + size_type find_last_of(const char* s, size_type pos, size_type count) const + { + return view_.find_last_of(s, pos, count); + } + + template <typename T> + typename std::enable_if<AsStringView<T>::value, size_type>::type + find_last_not_of(T&& s, size_type pos = npos) const + { + string_view v = AsStringView<T>::view(std::forward<T>(s)); + return view_.find_last_not_of(v, pos); + } + + size_type find_last_not_of(const char* s, size_type pos, + size_type count) const + { + return view_.find_last_not_of(s, pos, count); + } + +private: + // Internal constructor to move from existing String. + String(String&& s, Private) noexcept + : String(std::move(s)) + { + } + + // Internal constructor for dynamically allocated string. + String(std::string&& s, Private); + + // Internal constructor for view of statically allocated string. + String(string_view v, Private) + : string_() + , view_(v) + { + } + + void internally_mutate_to_stable_string(); + + std::shared_ptr<std::string const> string_; + string_view view_; +}; + +template <typename L, typename R> +typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value, + bool>::type +operator==(L&& l, R&& r) +{ + return (AsStringView<L>::view(std::forward<L>(l)) == + AsStringView<R>::view(std::forward<R>(r))); +} + +template <typename L, typename R> +typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value, + bool>::type +operator!=(L&& l, R&& r) +{ + return (AsStringView<L>::view(std::forward<L>(l)) != + AsStringView<R>::view(std::forward<R>(r))); +} + +template <typename L, typename R> +typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value, + bool>::type +operator<(L&& l, R&& r) +{ + return (AsStringView<L>::view(std::forward<L>(l)) < + AsStringView<R>::view(std::forward<R>(r))); +} + +template <typename L, typename R> +typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value, + bool>::type +operator<=(L&& l, R&& r) +{ + return (AsStringView<L>::view(std::forward<L>(l)) <= + AsStringView<R>::view(std::forward<R>(r))); +} + +template <typename L, typename R> +typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value, + bool>::type +operator>(L&& l, R&& r) +{ + return (AsStringView<L>::view(std::forward<L>(l)) > + AsStringView<R>::view(std::forward<R>(r))); +} + +template <typename L, typename R> +typename std::enable_if<AsStringView<L>::value && AsStringView<R>::value, + bool>::type +operator>=(L&& l, R&& r) +{ + return (AsStringView<L>::view(std::forward<L>(l)) >= + AsStringView<R>::view(std::forward<R>(r))); +} + +std::ostream& operator<<(std::ostream& os, String const& s); +std::string& operator+=(std::string& self, String const& s); + +template <typename L, typename R> +struct StringOpPlus +{ + L l; + R r; +#if defined(__SUNPRO_CC) + StringOpPlus(L in_l, R in_r) + : l(in_l) + , r(in_r) + { + } +#endif + operator std::string() const; + std::string::size_type size() const { return l.size() + r.size(); } +}; + +template <typename T> +struct StringAdd +{ + static const bool value = AsStringView<T>::value; + typedef string_view temp_type; + template <typename S> + static temp_type temp(S&& s) + { + return AsStringView<T>::view(std::forward<S>(s)); + } +}; + +template <typename L, typename R> +struct StringAdd<StringOpPlus<L, R>> : std::true_type +{ + typedef StringOpPlus<L, R> const& temp_type; + static temp_type temp(temp_type s) { return s; } +}; + +template <typename L, typename R> +StringOpPlus<L, R>::operator std::string() const +{ + std::string s; + s.reserve(size()); + s += *this; + return s; +} + +template <typename L, typename R> +std::string& operator+=(std::string& s, StringOpPlus<L, R> const& a) +{ + s.reserve(s.size() + a.size()); + s += a.l; + s += a.r; + return s; +} + +template <typename L, typename R> +String& operator+=(String& s, StringOpPlus<L, R> const& a) +{ + std::string r; + r.reserve(s.size() + a.size()); + r.assign(s.data(), s.size()); + r += a.l; + r += a.r; + s = std::move(r); + return s; +} + +template <typename L, typename R> +std::ostream& operator<<(std::ostream& os, StringOpPlus<L, R> const& a) +{ + return os << a.l << a.r; +} + +template <typename L, typename R> +struct IntoString<StringOpPlus<L, R>> : std::true_type +{ + static std::string into_string(StringOpPlus<L, R> const& a) { return a; } +}; + +template <typename L, typename R> +typename std::enable_if<StringAdd<L>::value && StringAdd<R>::value, + StringOpPlus<typename StringAdd<L>::temp_type, + typename StringAdd<R>::temp_type>>::type +operator+(L&& l, R&& r) +{ + return { StringAdd<L>::temp(std::forward<L>(l)), + StringAdd<R>::temp(std::forward<R>(r)) }; +} + +template <typename LL, typename LR, typename R> +typename std::enable_if<AsStringView<R>::value, bool>::type operator==( + StringOpPlus<LL, LR> const& l, R&& r) +{ + return std::string(l) == AsStringView<R>::view(std::forward<R>(r)); +} + +template <typename L, typename RL, typename RR> +typename std::enable_if<AsStringView<L>::value, bool>::type operator==( + L&& l, StringOpPlus<RL, RR> const& r) +{ + return AsStringView<L>::view(std::forward<L>(l)) == std::string(r); +} + +} // namespace cm + +namespace std { + +template <> +struct hash<cm::String> +{ + typedef cm::String argument_type; + typedef size_t result_type; + + result_type operator()(argument_type const& s) const noexcept + { + result_type const h(std::hash<cm::string_view>{}(s.view())); + return h; + } +}; +} + +#endif diff --git a/Source/cmTimestamp.h b/Source/cmTimestamp.h index 8dd499a..2f75acb 100644 --- a/Source/cmTimestamp.h +++ b/Source/cmTimestamp.h @@ -23,12 +23,12 @@ public: const std::string& formatString, bool utcFlag); -private: - time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const; - std::string CreateTimestampFromTimeT(time_t timeT, std::string formatString, bool utcFlag) const; +private: + time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const; + std::string AddTimestampComponent(char flag, struct tm& timeStruct, time_t timeT) const; }; diff --git a/Source/cm_static_string_view.hxx b/Source/cm_static_string_view.hxx new file mode 100644 index 0000000..1bef0c6 --- /dev/null +++ b/Source/cm_static_string_view.hxx @@ -0,0 +1,41 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cm_static_string_view_hxx +#define cm_static_string_view_hxx + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cm_string_view.hxx" + +#include <cstddef> + +namespace cm { + +/** A string_view that only binds to static storage. + * + * This is used together with the `""_s` user-defined literal operator + * to construct a type-safe abstraction of a string_view that only views + * statically allocated strings. These strings are const and available + * for the entire lifetime of the program. + */ +class static_string_view : public string_view +{ + static_string_view(string_view v) + : string_view(v) + { + } + + friend static_string_view operator"" _s(const char* data, size_t size); +}; + +/** Create a static_string_view using `""_s` literal syntax. */ +inline static_string_view operator"" _s(const char* data, size_t size) +{ + return string_view(data, size); +} + +} // namespace cm + +using cm::operator"" _s; + +#endif diff --git a/Source/cm_string_view.cxx b/Source/cm_string_view.cxx new file mode 100644 index 0000000..61fa80e --- /dev/null +++ b/Source/cm_string_view.cxx @@ -0,0 +1,301 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cm_string_view.hxx" + +#ifndef CMake_HAVE_CXX_STRING_VIEW + +# include "cm_kwiml.h" + +# include <algorithm> +# include <ostream> +# include <stdexcept> + +namespace cm { + +string_view::const_reference string_view::at(size_type pos) const +{ + if (pos >= size_) { + throw std::out_of_range("Index out of range in string_view::at"); + } + return data_[pos]; +} + +string_view::size_type string_view::copy(char* dest, size_type count, + size_type pos) const +{ + if (pos > size_) { + throw std::out_of_range("Index out of range in string_view::copy"); + } + size_type const rcount = std::min(count, size_ - pos); + traits_type::copy(dest, data_ + pos, rcount); + return rcount; +} + +string_view string_view::substr(size_type pos, size_type count) const +{ + if (pos > size_) { + throw std::out_of_range("Index out of range in string_view::substr"); + } + size_type const rcount = std::min(count, size_ - pos); + return string_view(data_ + pos, rcount); +} + +int string_view::compare(string_view v) const noexcept +{ + size_type const rlen = std::min(size_, v.size_); + int c = traits_type::compare(data_, v.data_, rlen); + if (c == 0) { + if (size_ < v.size_) { + c = -1; + } else if (size_ > v.size_) { + c = 1; + } + } + return c; +} + +int string_view::compare(size_type pos1, size_type count1, string_view v) const +{ + return substr(pos1, count1).compare(v); +} + +int string_view::compare(size_type pos1, size_type count1, string_view v, + size_type pos2, size_type count2) const +{ + return substr(pos1, count1).compare(v.substr(pos2, count2)); +} + +int string_view::compare(const char* s) const +{ + return compare(string_view(s)); +} + +int string_view::compare(size_type pos1, size_type count1, const char* s) const +{ + return substr(pos1, count1).compare(string_view(s)); +} + +int string_view::compare(size_type pos1, size_type count1, const char* s, + size_type count2) const +{ + return substr(pos1, count1).compare(string_view(s, count2)); +} + +string_view::size_type string_view::find(string_view v, size_type pos) const + noexcept +{ + for (; pos + v.size_ <= size_; ++pos) { + if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) == 0) { + return pos; + } + } + return npos; +} + +string_view::size_type string_view::find(char c, size_type pos) const noexcept +{ + return find(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find(const char* s, size_type pos, + size_type count) const +{ + return find(string_view(s, count), pos); +} + +string_view::size_type string_view::find(const char* s, size_type pos) const +{ + return find(string_view(s), pos); +} + +string_view::size_type string_view::rfind(string_view v, size_type pos) const + noexcept +{ + if (size_ >= v.size_) { + for (pos = std::min(pos, size_ - v.size_) + 1; pos > 0;) { + --pos; + if (std::char_traits<char>::compare(data_ + pos, v.data_, v.size_) == + 0) { + return pos; + } + } + } + return npos; +} + +string_view::size_type string_view::rfind(char c, size_type pos) const noexcept +{ + return rfind(string_view(&c, 1), pos); +} + +string_view::size_type string_view::rfind(const char* s, size_type pos, + size_type count) const +{ + return rfind(string_view(s, count), pos); +} + +string_view::size_type string_view::rfind(const char* s, size_type pos) const +{ + return rfind(string_view(s), pos); +} + +string_view::size_type string_view::find_first_of(string_view v, + size_type pos) const noexcept +{ + for (; pos < size_; ++pos) { + if (traits_type::find(v.data_, v.size_, data_[pos])) { + return pos; + } + } + return npos; +} + +string_view::size_type string_view::find_first_of(char c, size_type pos) const + noexcept +{ + return find_first_of(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find_first_of(const char* s, size_type pos, + size_type count) const +{ + return find_first_of(string_view(s, count), pos); +} + +string_view::size_type string_view::find_first_of(const char* s, + size_type pos) const +{ + return find_first_of(string_view(s), pos); +} + +string_view::size_type string_view::find_last_of(string_view v, + size_type pos) const noexcept +{ + if (size_ > 0) { + for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) { + --pos; + if (traits_type::find(v.data_, v.size_, data_[pos])) { + return pos; + } + } + } + return npos; +} + +string_view::size_type string_view::find_last_of(char c, size_type pos) const + noexcept +{ + return find_last_of(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find_last_of(const char* s, size_type pos, + size_type count) const +{ + return find_last_of(string_view(s, count), pos); +} + +string_view::size_type string_view::find_last_of(const char* s, + size_type pos) const +{ + return find_last_of(string_view(s), pos); +} + +string_view::size_type string_view::find_first_not_of(string_view v, + size_type pos) const + noexcept +{ + for (; pos < size_; ++pos) { + if (!traits_type::find(v.data_, v.size_, data_[pos])) { + return pos; + } + } + return npos; +} + +string_view::size_type string_view::find_first_not_of(char c, + size_type pos) const + noexcept +{ + return find_first_not_of(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find_first_not_of(const char* s, + size_type pos, + size_type count) const +{ + return find_first_not_of(string_view(s, count), pos); +} + +string_view::size_type string_view::find_first_not_of(const char* s, + size_type pos) const +{ + return find_first_not_of(string_view(s), pos); +} + +string_view::size_type string_view::find_last_not_of(string_view v, + size_type pos) const + noexcept +{ + if (size_ > 0) { + for (pos = std::min(pos, size_ - 1) + 1; pos > 0;) { + --pos; + if (!traits_type::find(v.data_, v.size_, data_[pos])) { + return pos; + } + } + } + return npos; +} + +string_view::size_type string_view::find_last_not_of(char c, + size_type pos) const + noexcept +{ + return find_last_not_of(string_view(&c, 1), pos); +} + +string_view::size_type string_view::find_last_not_of(const char* s, + size_type pos, + size_type count) const +{ + return find_last_not_of(string_view(s, count), pos); +} + +string_view::size_type string_view::find_last_not_of(const char* s, + size_type pos) const +{ + return find_last_not_of(string_view(s), pos); +} + +std::ostream& operator<<(std::ostream& o, string_view v) +{ + return o.write(v.data(), v.size()); +} + +std::string& operator+=(std::string& s, string_view v) +{ + s.append(v.data(), v.size()); + return s; +} +} + +std::hash<cm::string_view>::result_type std::hash<cm::string_view>::operator()( + argument_type const& s) const noexcept +{ + // FNV-1a hash. + static KWIML_INT_uint64_t const fnv_offset_basis = 0xcbf29ce484222325; + static KWIML_INT_uint64_t const fnv_prime = 0x100000001b3; + KWIML_INT_uint64_t h = fnv_offset_basis; + for (char const& c : s) { + h = h ^ KWIML_INT_uint64_t(KWIML_INT_uint8_t(c)); + h = h * fnv_prime; + } + return result_type(h); +} +#else +// Avoid empty translation unit. +void cm_string_view_cxx() +{ +} +#endif diff --git a/Source/cm_string_view.hxx b/Source/cm_string_view.hxx new file mode 100644 index 0000000..d368ed8 --- /dev/null +++ b/Source/cm_string_view.hxx @@ -0,0 +1,217 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cm_string_view_hxx +#define cm_string_view_hxx + +#include "cmConfigure.h" // IWYU pragma: keep + +#if __cplusplus >= 201703L || defined(_MSVC_LANG) && _MSVC_LANG >= 201703L +# define CMake_HAVE_CXX_STRING_VIEW +#endif + +#ifdef CMake_HAVE_CXX_STRING_VIEW +# include <string_view> +namespace cm { +using std::string_view; +} +#else +# include <cstddef> +# include <functional> +# include <iosfwd> +# include <iterator> +# include <string> + +namespace cm { + +class string_view +{ +public: + using traits_type = std::string::traits_type; + using value_type = char; + using pointer = char*; + using const_pointer = const char*; + using reference = char&; + using const_reference = char const&; + using const_iterator = const char*; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using reverse_iterator = const_reverse_iterator; + using size_type = std::string::size_type; + using difference_type = std::string::difference_type; + + static size_type const npos = static_cast<size_type>(-1); + + string_view() noexcept = default; + string_view(string_view const&) noexcept = default; + + string_view(const char* s, size_t count) noexcept + : data_(s) + , size_(count) + { + } + + string_view(const char* s) noexcept + : data_(s) + , size_(traits_type::length(s)) + { + } + + // C++17 does not define this constructor. Instead it defines + // a conversion operator on std::string to create a string_view. + // Since this implementation is used in C++11, std::string does + // not have that conversion. + string_view(std::string const& s) noexcept + : data_(s.data()) + , size_(s.size()) + { + } + + // C++17 does not define this conversion. Instead it defines + // a constructor on std::string that can take a string_view. + // Since this implementation is used in C++11, std::string does + // not have that constructor. + explicit operator std::string() const { return std::string(data_, size_); } + + string_view& operator=(string_view const&) = default; + + const_iterator begin() const noexcept { return data_; } + const_iterator end() const noexcept { return data_ + size_; } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(end()); + } + const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + const_reverse_iterator crend() const noexcept { return rend(); } + + const_reference operator[](size_type pos) const noexcept + { + return data_[pos]; + } + const_reference at(size_type pos) const; + const_reference front() const noexcept { return data_[0]; } + const_reference back() const noexcept { return data_[size_ - 1]; } + const_pointer data() const noexcept { return data_; } + + size_type size() const noexcept { return size_; } + size_type length() const noexcept { return size_; } + size_type max_size() const noexcept { return npos - 1; } + bool empty() const noexcept { return size_ == 0; } + + void remove_prefix(size_type n) noexcept + { + data_ += n; + size_ -= n; + } + void remove_suffix(size_type n) noexcept { size_ -= n; } + void swap(string_view& v) noexcept + { + string_view tmp = v; + v = *this; + *this = tmp; + } + + size_type copy(char* dest, size_type count, size_type pos = 0) const; + string_view substr(size_type pos = 0, size_type count = npos) const; + + int compare(string_view v) const noexcept; + int compare(size_type pos1, size_type count1, string_view v) const; + int compare(size_type pos1, size_type count1, string_view v, size_type pos2, + size_type count2) const; + int compare(const char* s) const; + int compare(size_type pos1, size_type count1, const char* s) const; + int compare(size_type pos1, size_type count1, const char* s, + size_type count2) const; + + size_type find(string_view v, size_type pos = 0) const noexcept; + size_type find(char c, size_type pos = 0) const noexcept; + size_type find(const char* s, size_type pos, size_type count) const; + size_type find(const char* s, size_type pos = 0) const; + + size_type rfind(string_view v, size_type pos = npos) const noexcept; + size_type rfind(char c, size_type pos = npos) const noexcept; + size_type rfind(const char* s, size_type pos, size_type count) const; + size_type rfind(const char* s, size_type pos = npos) const; + + size_type find_first_of(string_view v, size_type pos = 0) const noexcept; + size_type find_first_of(char c, size_type pos = 0) const noexcept; + size_type find_first_of(const char* s, size_type pos, size_type count) const; + size_type find_first_of(const char* s, size_type pos = 0) const; + + size_type find_last_of(string_view v, size_type pos = npos) const noexcept; + size_type find_last_of(char c, size_type pos = npos) const noexcept; + size_type find_last_of(const char* s, size_type pos, size_type count) const; + size_type find_last_of(const char* s, size_type pos = npos) const; + + size_type find_first_not_of(string_view v, size_type pos = 0) const noexcept; + size_type find_first_not_of(char c, size_type pos = 0) const noexcept; + size_type find_first_not_of(const char* s, size_type pos, + size_type count) const; + size_type find_first_not_of(const char* s, size_type pos = 0) const; + + size_type find_last_not_of(string_view v, size_type pos = npos) const + noexcept; + size_type find_last_not_of(char c, size_type pos = npos) const noexcept; + size_type find_last_not_of(const char* s, size_type pos, + size_type count) const; + size_type find_last_not_of(const char* s, size_type pos = npos) const; + +private: + const char* data_ = nullptr; + size_type size_ = 0; +}; + +std::ostream& operator<<(std::ostream& o, string_view v); + +std::string& operator+=(std::string& s, string_view v); + +inline bool operator==(string_view l, string_view r) noexcept +{ + return l.compare(r) == 0; +} + +inline bool operator!=(string_view l, string_view r) noexcept +{ + return l.compare(r) != 0; +} + +inline bool operator<(string_view l, string_view r) noexcept +{ + return l.compare(r) < 0; +} + +inline bool operator<=(string_view l, string_view r) noexcept +{ + return l.compare(r) <= 0; +} + +inline bool operator>(string_view l, string_view r) noexcept +{ + return l.compare(r) > 0; +} + +inline bool operator>=(string_view l, string_view r) noexcept +{ + return l.compare(r) >= 0; +} +} + +namespace std { + +template <> +struct hash<cm::string_view> +{ + typedef cm::string_view argument_type; + typedef size_t result_type; + result_type operator()(argument_type const& s) const noexcept; +}; +} + +#endif +#endif diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 2ac7f4d..e81d14b 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -30,6 +30,7 @@ #if defined(CMAKE_BUILD_WITH_CMAKE) # include "cm_jsoncpp_writer.h" +# include "cmFileAPI.h" # include "cmGraphVizWriter.h" # include "cmVariableWatch.h" # include <unordered_map> @@ -1443,6 +1444,11 @@ int cmake::ActualConfigure() this->TruncateOutputLog("CMakeError.log"); } +#if defined(CMAKE_BUILD_WITH_CMAKE) + this->FileAPI = cm::make_unique<cmFileAPI>(this); + this->FileAPI->ReadQueries(); +#endif + // actually do the configure this->GlobalGenerator->Configure(); // Before saving the cache @@ -1682,6 +1688,10 @@ int cmake::Generate() // for the Visual Studio and Xcode generators.) this->SaveCache(this->GetHomeOutputDirectory()); +#if defined(CMAKE_BUILD_WITH_CMAKE) + this->FileAPI->WriteReplies(); +#endif + return 0; } diff --git a/Source/cmake.h b/Source/cmake.h index d3d0e80..d00acc7 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <map> +#include <memory> // IWYU pragma: keep #include <set> #include <string> #include <unordered_set> @@ -21,6 +22,7 @@ #endif class cmExternalMakefileProjectGeneratorFactory; +class cmFileAPI; class cmFileTimeComparison; class cmGlobalGenerator; class cmGlobalGeneratorFactory; @@ -528,6 +530,7 @@ private: #if defined(CMAKE_BUILD_WITH_CMAKE) cmVariableWatch* VariableWatch; + std::unique_ptr<cmFileAPI> FileAPI; #endif cmState* State; diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx index 331f16e..c5160a9 100644 --- a/Source/kwsys/SystemTools.cxx +++ b/Source/kwsys/SystemTools.cxx @@ -82,6 +82,10 @@ # include <signal.h> /* sigprocmask */ #endif +#ifdef __linux +# include <linux/fs.h> +#endif + // Windows API. #if defined(_WIN32) # include <windows.h> @@ -2158,6 +2162,120 @@ bool SystemTools::FilesDiffer(const std::string& source, } /** + * Blockwise copy source to destination file + */ +static bool CopyFileContentBlockwise(const std::string& source, + const std::string& destination) +{ +// Open files +#if defined(_WIN32) + kwsys::ifstream fin( + Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)).c_str(), + std::ios::in | std::ios::binary); +#else + kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary); +#endif + if (!fin) { + return false; + } + + // try and remove the destination file so that read only destination files + // can be written to. + // If the remove fails continue so that files in read only directories + // that do not allow file removal can be modified. + SystemTools::RemoveFile(destination); + +#if defined(_WIN32) + kwsys::ofstream fout( + Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(destination)).c_str(), + std::ios::out | std::ios::trunc | std::ios::binary); +#else + kwsys::ofstream fout(destination.c_str(), + std::ios::out | std::ios::trunc | std::ios::binary); +#endif + if (!fout) { + return false; + } + + // This copy loop is very sensitive on certain platforms with + // slightly broken stream libraries (like HPUX). Normally, it is + // incorrect to not check the error condition on the fin.read() + // before using the data, but the fin.gcount() will be zero if an + // error occurred. Therefore, the loop should be safe everywhere. + while (fin) { + const int bufferSize = 4096; + char buffer[bufferSize]; + + fin.read(buffer, bufferSize); + if (fin.gcount()) { + fout.write(buffer, fin.gcount()); + } else { + break; + } + } + + // Make sure the operating system has finished writing the file + // before closing it. This will ensure the file is finished before + // the check below. + fout.flush(); + + fin.close(); + fout.close(); + + if (!fout) { + return false; + } + + return true; +} + +/** + * Clone the source file to the destination file + * + * If available, the Linux FICLONE ioctl is used to create a check + * copy-on-write clone of the source file. + * + * The method returns false for the following cases: + * - The code has not been compiled on Linux or the ioctl was unknown + * - The source and destination is on different file systems + * - The underlying filesystem does not support file cloning + * - An unspecified error occurred + */ +static bool CloneFileContent(const std::string& source, + const std::string& destination) +{ +#if defined(__linux) && defined(FICLONE) + int in = open(source.c_str(), O_RDONLY); + if (in < 0) { + return false; + } + + SystemTools::RemoveFile(destination); + + int out = + open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (out < 0) { + close(in); + return false; + } + + int result = ioctl(out, FICLONE, in); + close(in); + close(out); + + if (result < 0) { + return false; + } + + return true; +#else + (void)source; + (void)destination; + return false; +#endif +} + +/** * Copy a file named by "source" to the file named by "destination". */ bool SystemTools::CopyFileAlways(const std::string& source, @@ -2174,9 +2292,6 @@ bool SystemTools::CopyFileAlways(const std::string& source, if (SystemTools::FileIsDirectory(source)) { SystemTools::MakeDirectory(destination); } else { - const int bufferSize = 4096; - char buffer[bufferSize]; - // If destination is a directory, try to create a file with the same // name as the source in that directory. @@ -2195,62 +2310,12 @@ bool SystemTools::CopyFileAlways(const std::string& source, SystemTools::MakeDirectory(destination_dir); -// Open files -#if defined(_WIN32) - kwsys::ifstream fin( - Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(source)).c_str(), - std::ios::in | std::ios::binary); -#else - kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary); -#endif - if (!fin) { - return false; - } - - // try and remove the destination file so that read only destination files - // can be written to. - // If the remove fails continue so that files in read only directories - // that do not allow file removal can be modified. - SystemTools::RemoveFile(real_destination); - -#if defined(_WIN32) - kwsys::ofstream fout( - Encoding::ToNarrow(Encoding::ToWindowsExtendedPath(real_destination)) - .c_str(), - std::ios::out | std::ios::trunc | std::ios::binary); -#else - kwsys::ofstream fout(real_destination.c_str(), - std::ios::out | std::ios::trunc | std::ios::binary); -#endif - if (!fout) { - return false; - } - - // This copy loop is very sensitive on certain platforms with - // slightly broken stream libraries (like HPUX). Normally, it is - // incorrect to not check the error condition on the fin.read() - // before using the data, but the fin.gcount() will be zero if an - // error occurred. Therefore, the loop should be safe everywhere. - while (fin) { - fin.read(buffer, bufferSize); - if (fin.gcount()) { - fout.write(buffer, fin.gcount()); - } else { - break; + if (!CloneFileContent(source, real_destination)) { + // if cloning did not succeed, fall back to blockwise copy + if (!CopyFileContentBlockwise(source, real_destination)) { + return false; } } - - // Make sure the operating system has finished writing the file - // before closing it. This will ensure the file is finished before - // the check below. - fout.flush(); - - fin.close(); - fout.close(); - - if (!fout) { - return false; - } } if (perms) { if (!SystemTools::SetPermissions(real_destination, perm)) { diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 126076d..f6a9153 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories( set(CMakeLib_TESTS testGeneratedFileStream.cxx testRST.cxx + testString.cxx testSystemTools.cxx testUTF8.cxx testXMLParser.cxx diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx new file mode 100644 index 0000000..2aa1459 --- /dev/null +++ b/Tests/CMakeLib/testString.cxx @@ -0,0 +1,1347 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include "cmString.hxx" + +#include "cm_static_string_view.hxx" +#include "cm_string_view.hxx" + +#include <cstring> +#include <iostream> +#include <iterator> +#include <sstream> +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> + +#define ASSERT_TRUE(x) \ + if (!(x)) { \ + std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ + return false; \ + } + +static bool testConstructDefault() +{ + std::cout << "testConstructDefault()\n"; + cm::String str; + cm::String const& str_const = str; + ASSERT_TRUE(bool(str_const) == false); + ASSERT_TRUE(str_const.data() == nullptr); + ASSERT_TRUE(str_const.size() == 0); + ASSERT_TRUE(str_const.empty()); + ASSERT_TRUE(str_const.is_stable()); + ASSERT_TRUE(str.c_str() == nullptr); + ASSERT_TRUE(str.str().empty()); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testFromNullPtr(cm::String str) +{ + cm::String const& str_const = str; + ASSERT_TRUE(bool(str_const) == false); + ASSERT_TRUE(str_const.data() == nullptr); + ASSERT_TRUE(str_const.size() == 0); + ASSERT_TRUE(str_const.empty()); + ASSERT_TRUE(str_const.is_stable()); + ASSERT_TRUE(str.c_str() == nullptr); + ASSERT_TRUE(str.str().empty()); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromNullPtr() +{ + std::cout << "testConstructFromNullPtr()\n"; + return testFromNullPtr(nullptr); +} + +static bool testAssignFromNullPtr() +{ + std::cout << "testAssignFromNullPtr()\n"; + cm::String str; + str = nullptr; + return testFromNullPtr(str); +} + +static bool testFromCStrNull(cm::String str) +{ + cm::String const& str_const = str; + ASSERT_TRUE(bool(str_const) == false); + ASSERT_TRUE(str_const.data() == nullptr); + ASSERT_TRUE(str_const.size() == 0); + ASSERT_TRUE(str_const.empty()); + ASSERT_TRUE(str_const.is_stable()); + ASSERT_TRUE(str.c_str() == nullptr); + ASSERT_TRUE(str.str().empty()); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromCStrNull() +{ + std::cout << "testConstructFromCStrNull()\n"; + const char* null = nullptr; + return testFromCStrNull(null); +} + +static bool testAssignFromCStrNull() +{ + std::cout << "testAssignFromCStrNull()\n"; + const char* null = nullptr; + cm::String str; + str = null; + return testFromCStrNull(str); +} + +static char const charArray[] = "abc"; + +static bool testFromCharArray(cm::String str) +{ + cm::String const& str_const = str; + ASSERT_TRUE(str_const.data() != charArray); + ASSERT_TRUE(str_const.size() == sizeof(charArray) - 1); + ASSERT_TRUE(str_const.is_stable()); + ASSERT_TRUE(str.c_str() != charArray); + ASSERT_TRUE(str.is_stable()); + cm::String substr = str.substr(1); + cm::String const& substr_const = substr; + ASSERT_TRUE(substr_const.data() != &charArray[1]); + ASSERT_TRUE(substr_const.size() == 2); + ASSERT_TRUE(!substr_const.is_stable()); + ASSERT_TRUE(substr.c_str() != &charArray[1]); + ASSERT_TRUE(!substr.is_stable()); + return true; +} + +static bool testConstructFromCharArray() +{ + std::cout << "testConstructFromCharArray()\n"; + return testFromCharArray(charArray); +} + +static bool testAssignFromCharArray() +{ + std::cout << "testAssignFromCharArray()\n"; + cm::String str; + str = charArray; + return testFromCharArray(str); +} + +static const char* cstr = "abc"; + +static bool testFromCStr(cm::String const& str) +{ + ASSERT_TRUE(str.data() != cstr); + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), cstr, 3) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromCStr() +{ + std::cout << "testConstructFromCStr()\n"; + return testFromCStr(cstr); + ; +} + +static bool testAssignFromCStr() +{ + std::cout << "testAssignFromCStr()\n"; + cm::String str; + str = cstr; + return testFromCStr(str); + ; +} + +static const std::string stdstr = "abc"; + +static bool testFromStdString(cm::String const& str) +{ +#if defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI + // It would be nice to check this everywhere, but several platforms + // still use a CoW implementation even in C++11. + ASSERT_TRUE(str.data() != stdstr.data()); +#endif + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), stdstr.data(), 3) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromStdString() +{ + std::cout << "testConstructFromStdString()\n"; + return testFromStdString(stdstr); +} + +static bool testAssignFromStdString() +{ + std::cout << "testAssignFromStdString()\n"; + cm::String str; + str = stdstr; + return testFromStdString(str); +} + +static bool testConstructFromView() +{ + std::cout << "testConstructFromView()\n"; + cm::string_view view = cstr; + return testFromCStr(view); +} + +static bool testAssignFromView() +{ + std::cout << "testAssignFromView()\n"; + cm::string_view view = cstr; + cm::String str; + str = view; + return testFromCStr(str); +} + +static bool testFromChar(cm::String const& str) +{ + ASSERT_TRUE(str.size() == 1); + ASSERT_TRUE(std::strncmp(str.data(), "a", 1) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromChar() +{ + std::cout << "testConstructFromChar()\n"; + return testFromChar('a'); +} + +static bool testAssignFromChar() +{ + std::cout << "testAssignFromChar()\n"; + cm::String str; + str = 'a'; + return testFromChar(str); +} + +static bool testConstructFromInitList() +{ + std::cout << "testConstructFromInitList()\n"; + cm::String const str{ 'a', 'b', 'c' }; + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), "abc", 3) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testAssignFromInitList() +{ + std::cout << "testAssignFromInitList()\n"; + cm::String str; + str = { 'a', 'b', 'c' }; + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), "abc", 3) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromBuffer() +{ + std::cout << "testConstructFromBuffer()\n"; + cm::String const str(cstr, 3); + return testFromCStr(str); +} + +static bool testConstructFromInputIterator() +{ + std::cout << "testConstructFromInputIterator()\n"; + cm::String const str(cstr, cstr + 3); + ASSERT_TRUE(str.data() != cstr); + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), cstr, 3) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testConstructFromN() +{ + std::cout << "testConstructFromN()\n"; + cm::String const str(3, 'a'); + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), "aaa", 3) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static const auto staticStringView = "abc"_s; + +static bool testFromStaticStringView(cm::String str) +{ + cm::String const& str_const = str; + ASSERT_TRUE(str_const.data() == staticStringView.data()); + ASSERT_TRUE(str_const.size() == staticStringView.size()); + ASSERT_TRUE(!str_const.is_stable()); + ASSERT_TRUE(str.c_str() == staticStringView); + ASSERT_TRUE(!str.is_stable()); + cm::String substr = str.substr(1); + cm::String const& substr_const = substr; + ASSERT_TRUE(substr_const.data() == &staticStringView[1]); + ASSERT_TRUE(substr_const.size() == 2); + ASSERT_TRUE(!substr_const.is_stable()); + ASSERT_TRUE(substr.c_str() == &staticStringView[1]); + ASSERT_TRUE(!substr.is_stable()); + return true; +} + +static bool testConstructFromStaticStringView() +{ + std::cout << "testConstructFromStaticStringView()\n"; + return testFromStaticStringView(staticStringView); +} + +static bool testAssignFromStaticStringView() +{ + std::cout << "testAssignFromStaticStringView()\n"; + cm::String str; + str = staticStringView; + return testFromStaticStringView(str); +} + +static bool testConstructCopy() +{ + std::cout << "testConstructCopy()\n"; + cm::String s1 = std::string("abc"); + cm::String s2 = s1; + ASSERT_TRUE(s1.data() == s2.data()); + ASSERT_TRUE(s1.size() == 3); + ASSERT_TRUE(s2.size() == 3); + ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0); + ASSERT_TRUE(s1.is_stable()); + ASSERT_TRUE(s2.is_stable()); + return true; +} + +static bool testConstructMove() +{ + std::cout << "testConstructMove()\n"; + cm::String s1 = std::string("abc"); + cm::String s2 = std::move(s1); + ASSERT_TRUE(s1.data() == nullptr); + ASSERT_TRUE(s1.size() == 0); + ASSERT_TRUE(s2.size() == 3); + ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0); + ASSERT_TRUE(s1.is_stable()); + ASSERT_TRUE(s2.is_stable()); + return true; +} + +static bool testAssignCopy() +{ + std::cout << "testAssignCopy()\n"; + cm::String s1 = std::string("abc"); + cm::String s2; + s2 = s1; + ASSERT_TRUE(s1.data() == s2.data()); + ASSERT_TRUE(s1.size() == 3); + ASSERT_TRUE(s2.size() == 3); + ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0); + ASSERT_TRUE(s1.is_stable()); + ASSERT_TRUE(s2.is_stable()); + return true; +} + +static bool testAssignMove() +{ + std::cout << "testAssignMove()\n"; + cm::String s1 = std::string("abc"); + cm::String s2; + s2 = std::move(s1); + ASSERT_TRUE(s1.data() == nullptr); + ASSERT_TRUE(s1.size() == 0); + ASSERT_TRUE(s2.size() == 3); + ASSERT_TRUE(std::strncmp(s2.data(), "abc", 3) == 0); + ASSERT_TRUE(s1.is_stable()); + ASSERT_TRUE(s2.is_stable()); + return true; +} + +static bool testOperatorBool() +{ + std::cout << "testOperatorBool()\n"; + cm::String str; + ASSERT_TRUE(!str); + str = ""; + ASSERT_TRUE(str); + str = static_cast<const char*>(nullptr); + ASSERT_TRUE(!str); + str = std::string(); + ASSERT_TRUE(str); + str = nullptr; + ASSERT_TRUE(!str); + return true; +} + +static bool testOperatorIndex() +{ + std::cout << "testOperatorIndex()\n"; + cm::String str = "abc"; + ASSERT_TRUE(str[0] == 'a'); + ASSERT_TRUE(str[1] == 'b'); + ASSERT_TRUE(str[2] == 'c'); + return true; +} + +static bool testOperatorPlusEqual() +{ + std::cout << "testOperatorPlusEqual()\n"; + cm::String str = "a"; + str += "b"; + { + const char* c = "c"; + str += c; + } + str += 'd'; + str += std::string("e"); + str += cm::string_view("f", 1); + str += cm::String("g"); + ASSERT_TRUE(str.size() == 7); + ASSERT_TRUE(std::strncmp(str.data(), "abcdefg", 7) == 0); + ASSERT_TRUE(str.is_stable()); + return true; +} + +static bool testOperatorCompare() +{ + std::cout << "testOperatorCompare()\n"; + cm::String str = "b"; + { + ASSERT_TRUE(str == "b"); + ASSERT_TRUE("b" == str); + ASSERT_TRUE(str != "a"); + ASSERT_TRUE("a" != str); + ASSERT_TRUE(str < "c"); + ASSERT_TRUE("a" < str); + ASSERT_TRUE(str > "a"); + ASSERT_TRUE("c" > str); + ASSERT_TRUE(str <= "b"); + ASSERT_TRUE("b" <= str); + ASSERT_TRUE(str >= "b"); + ASSERT_TRUE("b" >= str); + } + { + const char* a = "a"; + const char* b = "b"; + const char* c = "c"; + ASSERT_TRUE(str == b); + ASSERT_TRUE(b == str); + ASSERT_TRUE(str != a); + ASSERT_TRUE(a != str); + ASSERT_TRUE(str < c); + ASSERT_TRUE(a < str); + ASSERT_TRUE(str > a); + ASSERT_TRUE(c > str); + ASSERT_TRUE(str <= b); + ASSERT_TRUE(b <= str); + ASSERT_TRUE(str >= b); + ASSERT_TRUE(b >= str); + } + { + ASSERT_TRUE(str == 'b'); + ASSERT_TRUE('b' == str); + ASSERT_TRUE(str != 'a'); + ASSERT_TRUE('a' != str); + ASSERT_TRUE(str < 'c'); + ASSERT_TRUE('a' < str); + ASSERT_TRUE(str > 'a'); + ASSERT_TRUE('c' > str); + ASSERT_TRUE(str <= 'b'); + ASSERT_TRUE('b' <= str); + ASSERT_TRUE(str >= 'b'); + ASSERT_TRUE('b' >= str); + } + { + std::string const a = "a"; + std::string const b = "b"; + std::string const c = "c"; + ASSERT_TRUE(str == b); + ASSERT_TRUE(b == str); + ASSERT_TRUE(str != a); + ASSERT_TRUE(a != str); + ASSERT_TRUE(str < c); + ASSERT_TRUE(a < str); + ASSERT_TRUE(str > a); + ASSERT_TRUE(c > str); + ASSERT_TRUE(str <= b); + ASSERT_TRUE(b <= str); + ASSERT_TRUE(str >= b); + ASSERT_TRUE(b >= str); + } + { + cm::string_view const a("a", 1); + cm::string_view const b("b", 1); + cm::string_view const c("c", 1); + ASSERT_TRUE(str == b); + ASSERT_TRUE(b == str); + ASSERT_TRUE(str != a); + ASSERT_TRUE(a != str); + ASSERT_TRUE(str < c); + ASSERT_TRUE(a < str); + ASSERT_TRUE(str > a); + ASSERT_TRUE(c > str); + ASSERT_TRUE(str <= b); + ASSERT_TRUE(b <= str); + ASSERT_TRUE(str >= b); + ASSERT_TRUE(b >= str); + } + { + cm::String const a("a"); + cm::String const b("b"); + cm::String const c("c"); + ASSERT_TRUE(str == b); + ASSERT_TRUE(b == str); + ASSERT_TRUE(str != a); + ASSERT_TRUE(a != str); + ASSERT_TRUE(str < c); + ASSERT_TRUE(a < str); + ASSERT_TRUE(str > a); + ASSERT_TRUE(c > str); + ASSERT_TRUE(str <= b); + ASSERT_TRUE(b <= str); + ASSERT_TRUE(str >= b); + ASSERT_TRUE(b >= str); + } + return true; +} + +static bool testOperatorStream() +{ + std::cout << "testOperatorStream()\n"; + std::ostringstream ss; + ss << "a" << cm::String("b") << 'c'; + ASSERT_TRUE(ss.str() == "abc"); + return true; +} + +static bool testOperatorStdStringPlusEqual() +{ + std::cout << "testOperatorStdStringPlusEqual()\n"; + std::string s = "a"; + s += cm::String("b"); + ASSERT_TRUE(s == "ab"); + return true; +} + +static bool testMethod_borrow() +{ + std::cout << "testMethod_borrow()\n"; + std::string s = "abc"; + cm::String str = cm::String::borrow(s); + ASSERT_TRUE(str.data() == s.data()); + ASSERT_TRUE(str.size() == s.size()); + ASSERT_TRUE(str == s); + return true; +} + +static bool testMethod_view() +{ + std::cout << "testMethod_view()\n"; + cm::String str; + ASSERT_TRUE(str.view().data() == nullptr); + ASSERT_TRUE(str.view().size() == 0); + str = charArray; + ASSERT_TRUE(str.view().data() != charArray); + ASSERT_TRUE(str.view().size() == sizeof(charArray) - 1); + str = std::string("abc"); + ASSERT_TRUE(str.view().size() == 3); + ASSERT_TRUE(strncmp(str.view().data(), "abc", 3) == 0); + return true; +} + +static bool testMethod_empty() +{ + std::cout << "testMethod_empty()\n"; + cm::String str; + ASSERT_TRUE(str.empty()); + str = ""; + ASSERT_TRUE(str.empty()); + str = "abc"; + ASSERT_TRUE(!str.empty()); + str = std::string(); + ASSERT_TRUE(str.empty()); + str = std::string("abc"); + ASSERT_TRUE(!str.empty()); + return true; +} + +static bool testMethod_length() +{ + std::cout << "testMethod_length()\n"; + cm::String str; + ASSERT_TRUE(str.length() == 0); + str = ""; + ASSERT_TRUE(str.length() == 0); + str = "abc"; + ASSERT_TRUE(str.length() == 3); + str = std::string(); + ASSERT_TRUE(str.length() == 0); + str = std::string("abc"); + ASSERT_TRUE(str.length() == 3); + return true; +} + +static bool testMethod_at() +{ + std::cout << "testMethod_at()\n"; + cm::String str = "abc"; + ASSERT_TRUE(str.at(0) == 'a'); + ASSERT_TRUE(str.at(1) == 'b'); + ASSERT_TRUE(str.at(2) == 'c'); + bool except_out_of_range = false; + try { + str.at(3); + } catch (std::out_of_range&) { + except_out_of_range = true; + } + ASSERT_TRUE(except_out_of_range); + return true; +} + +static bool testMethod_front_back() +{ + std::cout << "testMethod_front_back()\n"; + cm::String str = "abc"; + ASSERT_TRUE(str.front() == 'a'); + ASSERT_TRUE(str.back() == 'c'); + return true; +} + +static bool testMethodIterators() +{ + std::cout << "testMethodIterators()\n"; + cm::String str = "abc"; + ASSERT_TRUE(*str.begin() == 'a'); + ASSERT_TRUE(*(str.end() - 1) == 'c'); + ASSERT_TRUE(str.end() - str.begin() == 3); + ASSERT_TRUE(*str.cbegin() == 'a'); + ASSERT_TRUE(*(str.cend() - 1) == 'c'); + ASSERT_TRUE(str.cend() - str.cbegin() == 3); + ASSERT_TRUE(*str.rbegin() == 'c'); + ASSERT_TRUE(*(str.rend() - 1) == 'a'); + ASSERT_TRUE(str.rend() - str.rbegin() == 3); + ASSERT_TRUE(*str.crbegin() == 'c'); + ASSERT_TRUE(*(str.crend() - 1) == 'a'); + ASSERT_TRUE(str.crend() - str.crbegin() == 3); + return true; +} + +static bool testMethod_clear() +{ + std::cout << "testMethod_clear()\n"; + cm::String str = "abc"; + ASSERT_TRUE(!str.empty()); + str.clear(); + ASSERT_TRUE(str.empty()); + return true; +} + +static bool testMethod_insert() +{ + std::cout << "testMethod_insert()\n"; + cm::String str = "abc"; + str.insert(1, 2, 'd').insert(0, 1, '_'); + ASSERT_TRUE(str.size() == 6); + ASSERT_TRUE(std::strncmp(str.data(), "_addbc", 6) == 0); + return true; +} + +static bool testMethod_erase() +{ + std::cout << "testMethod_erase()\n"; + cm::String str = "abcdefg"; + str.erase(5).erase(1, 2); + ASSERT_TRUE(str.size() == 3); + ASSERT_TRUE(std::strncmp(str.data(), "ade", 3) == 0); + return true; +} + +static bool testMethod_push_back() +{ + std::cout << "testMethod_push_back()\n"; + cm::String str = "abc"; + str.push_back('d'); + ASSERT_TRUE(str == "abcd"); + return true; +} + +static bool testMethod_pop_back() +{ + std::cout << "testMethod_pop_back()\n"; + cm::String str = "abc"; + str.pop_back(); + ASSERT_TRUE(str == "ab"); + return true; +} + +static bool testMethod_replace() +{ + std::cout << "testMethod_replace()\n"; + { + cm::String str = "abcd"; + const char* bc = "bc"; + ASSERT_TRUE(str.replace(1, 2, "BC") == "aBCd"); + ASSERT_TRUE(str.replace(1, 2, bc) == "abcd"); + ASSERT_TRUE(str.replace(1, 2, 'x') == "axd"); + ASSERT_TRUE(str.replace(1, 1, std::string("bc")) == "abcd"); + ASSERT_TRUE(str.replace(1, 2, cm::string_view("BC", 2)) == "aBCd"); + ASSERT_TRUE(str.replace(1, 2, cm::String("bc")) == "abcd"); + } + { + cm::String str = "abcd"; + const char* bc = "bc"; + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, "BC") == "aBCd"); + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, bc) == "abcd"); + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, 'x') == "axd"); + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 2, + std::string("bc")) == "abcd"); + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, + cm::string_view("BC", 2)) == "aBCd"); + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, + cm::String("bc")) == "abcd"); + } + { + cm::String str = "abcd"; + const char* bc = "_bc"; + ASSERT_TRUE(str.replace(1, 2, "_BC_", 1, 2) == "aBCd"); + ASSERT_TRUE(str.replace(1, 2, bc, 1) == "abcd"); + ASSERT_TRUE(str.replace(1, 2, 'x', 0) == "axd"); + ASSERT_TRUE(str.replace(1, 1, std::string("_bc_"), 1, 2) == "abcd"); + ASSERT_TRUE(str.replace(1, 2, cm::string_view("_BC", 3), 1) == "aBCd"); + ASSERT_TRUE(str.replace(1, 2, cm::String("_bc_"), 1, 2) == "abcd"); + } + { + cm::String str = "abcd"; + const char* bc = "_bc_"; + ASSERT_TRUE(str.replace(1, 2, 2, 'x') == "axxd"); + ASSERT_TRUE(str.replace(str.begin() + 1, str.begin() + 3, 2, 'y') == + "ayyd"); + ASSERT_TRUE( + str.replace(str.begin() + 1, str.begin() + 3, bc + 1, bc + 3) == "abcd"); + } + return true; +} + +static bool testMethod_copy() +{ + std::cout << "testMethod_copy()\n"; + cm::String str = "abc"; + char dest[2]; + cm::String::size_type n = str.copy(dest, 2, 1); + ASSERT_TRUE(n == 2); + ASSERT_TRUE(std::strncmp(dest, "bc", 2) == 0); + n = str.copy(dest, 2); + ASSERT_TRUE(n == 2); + ASSERT_TRUE(std::strncmp(dest, "ab", 2) == 0); + return true; +} + +static bool testMethod_resize() +{ + std::cout << "testMethod_resize()\n"; + cm::String str = "abc"; + str.resize(3); + ASSERT_TRUE(str == "abc"); + str.resize(2); + ASSERT_TRUE(str == "ab"); + str.resize(3, 'c'); + ASSERT_TRUE(str == "abc"); + return true; +} + +static bool testMethod_swap() +{ + std::cout << "testMethod_swap()\n"; + cm::String str1 = std::string("1"); + cm::String str2 = std::string("2"); + str1.swap(str2); + ASSERT_TRUE(str1 == "2"); + ASSERT_TRUE(str2 == "1"); + return true; +} + +static bool testMethod_substr_AtEnd(cm::String str) +{ + cm::String substr = str.substr(1); + ASSERT_TRUE(substr.data() == str.data() + 1); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(!substr.is_stable()); + + // c_str() at the end of the buffer does not internally mutate. + ASSERT_TRUE(std::strcmp(substr.c_str(), "bc") == 0); + ASSERT_TRUE(substr.c_str() == str.data() + 1); + ASSERT_TRUE(substr.data() == str.data() + 1); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(!substr.is_stable()); + + // str() internally mutates. + ASSERT_TRUE(substr.str() == "bc"); + ASSERT_TRUE(substr.is_stable()); + ASSERT_TRUE(substr.data() != str.data() + 1); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(substr.c_str() != str.data() + 1); + ASSERT_TRUE(std::strcmp(substr.c_str(), "bc") == 0); + return true; +} + +static bool testMethod_substr_AtEndBorrowed() +{ + std::cout << "testMethod_substr_AtEndBorrowed()\n"; + return testMethod_substr_AtEnd("abc"_s); +} + +static bool testMethod_substr_AtEndOwned() +{ + std::cout << "testMethod_substr_AtEndOwned()\n"; + return testMethod_substr_AtEnd(std::string("abc")); +} + +static bool testMethod_substr_AtStart(cm::String str) +{ + { + cm::String substr = str.substr(0, 2); + ASSERT_TRUE(substr.data() == str.data()); + ASSERT_TRUE(substr.size() == 2); + + // c_str() not at the end of the buffer internally mutates. + const char* substr_c = substr.c_str(); + ASSERT_TRUE(std::strcmp(substr_c, "ab") == 0); + ASSERT_TRUE(substr_c != str.data()); + ASSERT_TRUE(substr.data() != str.data()); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(substr.is_stable()); + + // str() does not need to internally mutate after c_str() did so + ASSERT_TRUE(substr.str() == "ab"); + ASSERT_TRUE(substr.is_stable()); + ASSERT_TRUE(substr.data() == substr_c); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(substr.c_str() == substr_c); + } + + { + cm::String substr = str.substr(0, 2); + ASSERT_TRUE(substr.data() == str.data()); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(!substr.is_stable()); + + // str() internally mutates. + ASSERT_TRUE(substr.str() == "ab"); + ASSERT_TRUE(substr.is_stable()); + ASSERT_TRUE(substr.data() != str.data()); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(substr.c_str() != str.data()); + + // c_str() does not internally after str() did so + const char* substr_c = substr.c_str(); + ASSERT_TRUE(std::strcmp(substr_c, "ab") == 0); + ASSERT_TRUE(substr_c == substr.data()); + ASSERT_TRUE(substr.size() == 2); + ASSERT_TRUE(substr.is_stable()); + } + + return true; +} + +static bool testMethod_substr_AtStartBorrowed() +{ + std::cout << "testMethod_substr_AtStartBorrowed()\n"; + return testMethod_substr_AtStart("abc"_s); +} + +static bool testMethod_substr_AtStartOwned() +{ + std::cout << "testMethod_substr_AtStartOwned()\n"; + return testMethod_substr_AtStart(std::string("abc")); +} + +static bool testMethod_compare() +{ + std::cout << "testMethod_compare()\n"; + cm::String str = "b"; + ASSERT_TRUE(str.compare("a") > 0); + ASSERT_TRUE(str.compare("b") == 0); + ASSERT_TRUE(str.compare("c") < 0); + { + const char* a = "a"; + const char* b = "b"; + const char* c = "c"; + ASSERT_TRUE(str.compare(a) > 0); + ASSERT_TRUE(str.compare(b) == 0); + ASSERT_TRUE(str.compare(c) < 0); + } + ASSERT_TRUE(str.compare('a') > 0); + ASSERT_TRUE(str.compare('b') == 0); + ASSERT_TRUE(str.compare('c') < 0); + ASSERT_TRUE(str.compare(std::string("a")) > 0); + ASSERT_TRUE(str.compare(std::string("b")) == 0); + ASSERT_TRUE(str.compare(std::string("c")) < 0); + ASSERT_TRUE(str.compare(cm::string_view("a_", 1)) > 0); + ASSERT_TRUE(str.compare(cm::string_view("b_", 1)) == 0); + ASSERT_TRUE(str.compare(cm::string_view("c_", 1)) < 0); + ASSERT_TRUE(str.compare(cm::String("a")) > 0); + ASSERT_TRUE(str.compare(cm::String("b")) == 0); + ASSERT_TRUE(str.compare(cm::String("c")) < 0); + ASSERT_TRUE(str.compare(0, 1, cm::string_view("a", 1)) > 0); + ASSERT_TRUE(str.compare(1, 0, cm::string_view("", 0)) == 0); + ASSERT_TRUE(str.compare(0, 1, cm::string_view("ac", 2), 1, 1) < 0); + ASSERT_TRUE(str.compare(1, 0, "") == 0); + ASSERT_TRUE(str.compare(1, 0, "_", 0) == 0); + return true; +} + +static bool testMethod_find() +{ + std::cout << "testMethod_find()\n"; + cm::String str = "abcabc"; + ASSERT_TRUE(str.find("a") == 0); + ASSERT_TRUE(str.find("a", 1) == 3); + { + const char* a = "a"; + ASSERT_TRUE(str.find(a) == 0); + ASSERT_TRUE(str.find(a, 1) == 3); + } + ASSERT_TRUE(str.find('a') == 0); + ASSERT_TRUE(str.find('a', 1) == 3); + ASSERT_TRUE(str.find(std::string("a")) == 0); + ASSERT_TRUE(str.find(std::string("a"), 1) == 3); + ASSERT_TRUE(str.find(cm::string_view("a_", 1)) == 0); + ASSERT_TRUE(str.find(cm::string_view("a_", 1), 1) == 3); + ASSERT_TRUE(str.find(cm::String("a")) == 0); + ASSERT_TRUE(str.find(cm::String("a"), 1) == 3); + ASSERT_TRUE(str.find("ab_", 1, 2) == 3); + return true; +} + +static bool testMethod_rfind() +{ + std::cout << "testMethod_rfind()\n"; + cm::String str = "abcabc"; + ASSERT_TRUE(str.rfind("a") == 3); + ASSERT_TRUE(str.rfind("a", 1) == 0); + { + const char* a = "a"; + ASSERT_TRUE(str.rfind(a) == 3); + ASSERT_TRUE(str.rfind(a, 1) == 0); + } + ASSERT_TRUE(str.rfind('a') == 3); + ASSERT_TRUE(str.rfind('a', 1) == 0); + ASSERT_TRUE(str.rfind(std::string("a")) == 3); + ASSERT_TRUE(str.rfind(std::string("a"), 1) == 0); + ASSERT_TRUE(str.rfind(cm::string_view("a_", 1)) == 3); + ASSERT_TRUE(str.rfind(cm::string_view("a_", 1), 1) == 0); + ASSERT_TRUE(str.rfind(cm::String("a")) == 3); + ASSERT_TRUE(str.rfind(cm::String("a"), 1) == 0); + ASSERT_TRUE(str.rfind("ab_", 1, 2) == 0); + return true; +} + +static bool testMethod_find_first_of() +{ + std::cout << "testMethod_find_first_of()\n"; + cm::String str = "abcabc"; + ASSERT_TRUE(str.find_first_of("_a") == 0); + ASSERT_TRUE(str.find_first_of("_a", 1) == 3); + { + const char* a = "_a"; + ASSERT_TRUE(str.find_first_of(a) == 0); + ASSERT_TRUE(str.find_first_of(a, 1) == 3); + } + ASSERT_TRUE(str.find_first_of('a') == 0); + ASSERT_TRUE(str.find_first_of('a', 1) == 3); + ASSERT_TRUE(str.find_first_of(std::string("_a")) == 0); + ASSERT_TRUE(str.find_first_of(std::string("_a"), 1) == 3); + ASSERT_TRUE(str.find_first_of(cm::string_view("ba_", 1)) == 1); + ASSERT_TRUE(str.find_first_of(cm::string_view("ba_", 1), 2) == 4); + ASSERT_TRUE(str.find_first_of(cm::String("ab")) == 0); + ASSERT_TRUE(str.find_first_of(cm::String("ab"), 2) == 3); + ASSERT_TRUE(str.find_first_of("_ab", 1, 2) == 3); + return true; +} + +static bool testMethod_find_first_not_of() +{ + std::cout << "testMethod_find_first_not_of()\n"; + cm::String str = "abcabc"; + ASSERT_TRUE(str.find_first_not_of("_a") == 1); + ASSERT_TRUE(str.find_first_not_of("_a", 2) == 2); + { + const char* a = "_a"; + ASSERT_TRUE(str.find_first_not_of(a) == 1); + ASSERT_TRUE(str.find_first_not_of(a, 2) == 2); + } + ASSERT_TRUE(str.find_first_not_of('a') == 1); + ASSERT_TRUE(str.find_first_not_of('a', 2) == 2); + ASSERT_TRUE(str.find_first_not_of(std::string("_a")) == 1); + ASSERT_TRUE(str.find_first_not_of(std::string("_a"), 2) == 2); + ASSERT_TRUE(str.find_first_not_of(cm::string_view("ba_", 1)) == 0); + ASSERT_TRUE(str.find_first_not_of(cm::string_view("ba_", 1), 1) == 2); + ASSERT_TRUE(str.find_first_not_of(cm::String("_a")) == 1); + ASSERT_TRUE(str.find_first_not_of(cm::String("_a"), 2) == 2); + ASSERT_TRUE(str.find_first_not_of("_bca", 1, 3) == 3); + return true; +} + +static bool testMethod_find_last_of() +{ + std::cout << "testMethod_find_last_of()\n"; + cm::String str = "abcabc"; + ASSERT_TRUE(str.find_last_of("_a") == 3); + ASSERT_TRUE(str.find_last_of("_a", 1) == 0); + { + const char* a = "_a"; + ASSERT_TRUE(str.find_last_of(a) == 3); + ASSERT_TRUE(str.find_last_of(a, 1) == 0); + } + ASSERT_TRUE(str.find_last_of('a') == 3); + ASSERT_TRUE(str.find_last_of('a', 1) == 0); + ASSERT_TRUE(str.find_last_of(std::string("_a")) == 3); + ASSERT_TRUE(str.find_last_of(std::string("_a"), 1) == 0); + ASSERT_TRUE(str.find_last_of(cm::string_view("ba_", 1)) == 4); + ASSERT_TRUE(str.find_last_of(cm::string_view("ba_", 1), 2) == 1); + ASSERT_TRUE(str.find_last_of(cm::String("ab")) == 4); + ASSERT_TRUE(str.find_last_of(cm::String("ab"), 2) == 1); + ASSERT_TRUE(str.find_last_of("_ab", 1, 2) == 0); + return true; +} + +static bool testMethod_find_last_not_of() +{ + std::cout << "testMethod_find_last_not_of()\n"; + cm::String str = "abcabc"; + ASSERT_TRUE(str.find_last_not_of("_a") == 5); + ASSERT_TRUE(str.find_last_not_of("_a", 1) == 1); + { + const char* a = "_a"; + ASSERT_TRUE(str.find_last_not_of(a) == 5); + ASSERT_TRUE(str.find_last_not_of(a, 1) == 1); + } + ASSERT_TRUE(str.find_last_not_of('a') == 5); + ASSERT_TRUE(str.find_last_not_of('a', 1) == 1); + ASSERT_TRUE(str.find_last_not_of(std::string("_a")) == 5); + ASSERT_TRUE(str.find_last_not_of(std::string("_a"), 1) == 1); + ASSERT_TRUE(str.find_last_not_of(cm::string_view("cb_", 1)) == 4); + ASSERT_TRUE(str.find_last_not_of(cm::string_view("cb_", 1), 2) == 1); + ASSERT_TRUE(str.find_last_not_of(cm::String("_a")) == 5); + ASSERT_TRUE(str.find_last_not_of(cm::String("_a"), 1) == 1); + ASSERT_TRUE(str.find_last_not_of("cb_", 2, 2) == 0); + return true; +} + +static bool testAddition() +{ + std::cout << "testAddition()\n"; + { + ASSERT_TRUE(cm::String("a") + "b" == "ab"); + ASSERT_TRUE("ab" == "a" + cm::String("b")); + ASSERT_TRUE("a" + cm::String("b") + "c" == "abc"); + ASSERT_TRUE("abc" == "a" + cm::String("b") + "c"); + ASSERT_TRUE("a" + (cm::String("b") + "c") + "d" == "abcd"); + ASSERT_TRUE("abcd" == "a" + (cm::String("b") + "c") + "d"); + } + { + ASSERT_TRUE(cm::String("a"_s) + "b"_s == "ab"_s); + ASSERT_TRUE("ab"_s == "a"_s + cm::String("b"_s)); + ASSERT_TRUE("a"_s + cm::String("b"_s) + "c"_s == "abc"_s); + ASSERT_TRUE("abc"_s == "a"_s + cm::String("b"_s) + "c"_s); + ASSERT_TRUE("a"_s + (cm::String("b"_s) + "c"_s) + "d"_s == "abcd"_s); + ASSERT_TRUE("abcd"_s == "a"_s + (cm::String("b"_s) + "c"_s) + "d"_s); + } + { + const char* a = "a"; + const char* b = "b"; + const char* ab = "ab"; + ASSERT_TRUE(cm::String(a) + b == ab); + ASSERT_TRUE(ab == a + cm::String(b)); + const char* c = "c"; + const char* abc = "abc"; + ASSERT_TRUE(a + cm::String(b) + c == abc); + ASSERT_TRUE(abc == a + cm::String(b) + c); + const char* d = "d"; + const char* abcd = "abcd"; + ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd); + ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d); + } + { + ASSERT_TRUE(cm::String('a') + 'b' == "ab"); + ASSERT_TRUE("ab" == 'a' + cm::String('b')); + ASSERT_TRUE('a' + cm::String('b') + 'c' == "abc"); + ASSERT_TRUE("abc" == 'a' + cm::String('b') + 'c'); + ASSERT_TRUE('a' + (cm::String('b') + 'c') + 'd' == "abcd"); + ASSERT_TRUE("abcd" == 'a' + (cm::String('b') + 'c') + 'd'); + } + { + std::string a = "a"; + std::string b = "b"; + std::string ab = "ab"; + ASSERT_TRUE(cm::String(a) + b == ab); + ASSERT_TRUE(ab == a + cm::String(b)); + std::string c = "c"; + std::string abc = "abc"; + ASSERT_TRUE(a + cm::String(b) + c == abc); + ASSERT_TRUE(abc == a + cm::String(b) + c); + std::string d = "d"; + std::string abcd = "abcd"; + ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd); + ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d); + } + { + cm::string_view a("a", 1); + cm::string_view b("b", 1); + cm::string_view ab("ab", 2); + ASSERT_TRUE(cm::String(a) + b == ab); + ASSERT_TRUE(ab == a + cm::String(b)); + cm::string_view c("c", 1); + cm::string_view abc("abc", 3); + ASSERT_TRUE(a + cm::String(b) + c == abc); + ASSERT_TRUE(abc == a + cm::String(b) + c); + cm::string_view d("d", 1); + cm::string_view abcd("abcd", 4); + ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd); + ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d); + } + { + cm::String a = "a"; + cm::String b = "b"; + cm::String ab = "ab"; + ASSERT_TRUE(a + b == ab); + ASSERT_TRUE(ab == a + b); + cm::String c = "c"; + cm::String abc = "abc"; + ASSERT_TRUE(a + cm::String(b) + c == abc); + ASSERT_TRUE(abc == a + cm::String(b) + c); + cm::String d = "d"; + cm::String abcd = "abcd"; + ASSERT_TRUE(a + (cm::String(b) + c) + d == abcd); + ASSERT_TRUE(abcd == a + (cm::String(b) + c) + d); + } + { + cm::String str; + str += "a" + cm::String("b") + 'c'; + ASSERT_TRUE(str == "abc"); + ASSERT_TRUE(str.is_stable()); + } + { + std::string s; + s += "a" + cm::String("b") + 'c'; + ASSERT_TRUE(s == "abc"); + } + { + std::ostringstream ss; + ss << ("a" + cm::String("b") + 'c'); + ASSERT_TRUE(ss.str() == "abc"); + } + return true; +} + +static bool testStability() +{ + std::cout << "testStability()\n"; + cm::String str = "abc"_s; + ASSERT_TRUE(!str.is_stable()); + ASSERT_TRUE(str.str_if_stable() == nullptr); + str.stabilize(); + ASSERT_TRUE(str.is_stable()); + std::string const* str_if_stable = str.str_if_stable(); + ASSERT_TRUE(str_if_stable != nullptr); + ASSERT_TRUE(*str_if_stable == "abc"); + str.stabilize(); + ASSERT_TRUE(str.is_stable()); + ASSERT_TRUE(str.str_if_stable() == str_if_stable); + return true; +} + +int testString(int /*unused*/, char* /*unused*/ []) +{ + if (!testConstructDefault()) { + return 1; + } + if (!testConstructFromNullPtr()) { + return 1; + } + if (!testConstructFromCStrNull()) { + return 1; + } + if (!testConstructFromCharArray()) { + return 1; + } + if (!testConstructFromCStr()) { + return 1; + } + if (!testConstructFromStdString()) { + return 1; + } + if (!testConstructFromView()) { + return 1; + } + if (!testConstructFromChar()) { + return 1; + } + if (!testConstructFromInitList()) { + return 1; + } + if (!testConstructFromBuffer()) { + return 1; + } + if (!testConstructFromInputIterator()) { + return 1; + } + if (!testConstructFromN()) { + return 1; + } + if (!testConstructFromStaticStringView()) { + return 1; + } + if (!testConstructCopy()) { + return 1; + } + if (!testConstructMove()) { + return 1; + } + if (!testAssignCopy()) { + return 1; + } + if (!testAssignMove()) { + return 1; + } + if (!testAssignFromChar()) { + return 1; + } + if (!testAssignFromView()) { + return 1; + } + if (!testAssignFromStdString()) { + return 1; + } + if (!testAssignFromCStr()) { + return 1; + } + if (!testAssignFromCharArray()) { + return 1; + } + if (!testAssignFromCStrNull()) { + return 1; + } + if (!testAssignFromNullPtr()) { + return 1; + } + if (!testAssignFromInitList()) { + return 1; + } + if (!testAssignFromStaticStringView()) { + return 1; + } + if (!testOperatorBool()) { + return 1; + } + if (!testOperatorIndex()) { + return 1; + } + if (!testOperatorPlusEqual()) { + return 1; + } + if (!testOperatorCompare()) { + return 1; + } + if (!testOperatorStream()) { + return 1; + } + if (!testOperatorStdStringPlusEqual()) { + return 1; + } + if (!testMethod_borrow()) { + return 1; + } + if (!testMethod_view()) { + return 1; + } + if (!testMethod_empty()) { + return 1; + } + if (!testMethod_length()) { + return 1; + } + if (!testMethod_at()) { + return 1; + } + if (!testMethod_front_back()) { + return 1; + } + if (!testMethod_clear()) { + return 1; + } + if (!testMethod_insert()) { + return 1; + } + if (!testMethod_erase()) { + return 1; + } + if (!testMethod_push_back()) { + return 1; + } + if (!testMethod_pop_back()) { + return 1; + } + if (!testMethod_replace()) { + return 1; + } + if (!testMethod_copy()) { + return 1; + } + if (!testMethod_resize()) { + return 1; + } + if (!testMethod_swap()) { + return 1; + } + if (!testMethodIterators()) { + return 1; + } + if (!testMethod_substr_AtEndBorrowed()) { + return 1; + } + if (!testMethod_substr_AtEndOwned()) { + return 1; + } + if (!testMethod_substr_AtStartBorrowed()) { + return 1; + } + if (!testMethod_substr_AtStartOwned()) { + return 1; + } + if (!testMethod_compare()) { + return 1; + } + if (!testMethod_find()) { + return 1; + } + if (!testMethod_rfind()) { + return 1; + } + if (!testMethod_find_first_of()) { + return 1; + } + if (!testMethod_find_first_not_of()) { + return 1; + } + if (!testMethod_find_last_of()) { + return 1; + } + if (!testMethod_find_last_not_of()) { + return 1; + } + if (!testAddition()) { + return 1; + } + if (!testStability()) { + return 1; + } + return 0; +} diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 5ff2538..67fd65a 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -66,6 +66,9 @@ function(add_RunCMake_test_group test types) endforeach() endfunction() +# Some tests use python for extra checks. +find_package(PythonInterp QUIET) + if(XCODE_VERSION AND "${XCODE_VERSION}" VERSION_LESS 6.1) set(Swift_ARGS -DXCODE_BELOW_6_1=1) endif() @@ -155,6 +158,7 @@ add_RunCMake_test(DisallowedCommands) add_RunCMake_test(ExternalData) add_RunCMake_test(FeatureSummary) add_RunCMake_test(FPHSA) +add_RunCMake_test(FileAPI -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}) add_RunCMake_test(FindBoost) add_RunCMake_test(FindLua) add_RunCMake_test(FindOpenGL) diff --git a/Tests/RunCMake/FileAPI/CMakeLists.txt b/Tests/RunCMake/FileAPI/CMakeLists.txt new file mode 100644 index 0000000..44025d3 --- /dev/null +++ b/Tests/RunCMake/FileAPI/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.12) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/FileAPI/ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/ClientStateful-check.cmake new file mode 100644 index 0000000..1e9aab6 --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateful-check.cmake @@ -0,0 +1,68 @@ +set(expect + query + query/client-client-member + query/client-client-member/query.json + query/client-empty-array + query/client-empty-array/query.json + query/client-empty-object + query/client-empty-object/query.json + query/client-json-bad-root + query/client-json-bad-root/query.json + query/client-json-empty + query/client-json-empty/query.json + query/client-json-extra + query/client-json-extra/query.json + query/client-not-file + query/client-not-file/query.json + query/client-request-array-negative-major-version + query/client-request-array-negative-major-version/query.json + query/client-request-array-negative-minor-version + query/client-request-array-negative-minor-version/query.json + query/client-request-array-negative-version + query/client-request-array-negative-version/query.json + query/client-request-array-no-major-version + query/client-request-array-no-major-version/query.json + query/client-request-array-no-supported-version + query/client-request-array-no-supported-version-among + query/client-request-array-no-supported-version-among/query.json + query/client-request-array-no-supported-version/query.json + query/client-request-array-version-1 + query/client-request-array-version-1-1 + query/client-request-array-version-1-1/query.json + query/client-request-array-version-1/query.json + query/client-request-array-version-2 + query/client-request-array-version-2/query.json + query/client-request-negative-major-version + query/client-request-negative-major-version/query.json + query/client-request-negative-minor-version + query/client-request-negative-minor-version/query.json + query/client-request-negative-version + query/client-request-negative-version/query.json + query/client-request-no-major-version + query/client-request-no-major-version/query.json + query/client-request-no-version + query/client-request-no-version/query.json + query/client-request-version-1 + query/client-request-version-1-1 + query/client-request-version-1-1/query.json + query/client-request-version-1/query.json + query/client-request-version-2 + query/client-request-version-2/query.json + query/client-requests-bad + query/client-requests-bad/query.json + query/client-requests-empty + query/client-requests-empty/query.json + query/client-requests-not-kinded + query/client-requests-not-kinded/query.json + query/client-requests-not-objects + query/client-requests-not-objects/query.json + query/client-requests-unknown + query/client-requests-unknown/query.json + reply + reply/__test-v1-[0-9a-f]+.json + reply/__test-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(ClientStateful) diff --git a/Tests/RunCMake/FileAPI/ClientStateful-check.py b/Tests/RunCMake/FileAPI/ClientStateful-check.py new file mode 100644 index 0000000..f3d20d1 --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateful-check.py @@ -0,0 +1,258 @@ +from check_index import * + +def check_reply(q): + assert is_dict(q) + assert sorted(q.keys()) == [ + "client-client-member", + "client-empty-array", + "client-empty-object", + "client-json-bad-root", + "client-json-empty", + "client-json-extra", + "client-not-file", + "client-request-array-negative-major-version", + "client-request-array-negative-minor-version", + "client-request-array-negative-version", + "client-request-array-no-major-version", + "client-request-array-no-supported-version", + "client-request-array-no-supported-version-among", + "client-request-array-version-1", + "client-request-array-version-1-1", + "client-request-array-version-2", + "client-request-negative-major-version", + "client-request-negative-minor-version", + "client-request-negative-version", + "client-request-no-major-version", + "client-request-no-version", + "client-request-version-1", + "client-request-version-1-1", + "client-request-version-2", + "client-requests-bad", + "client-requests-empty", + "client-requests-not-kinded", + "client-requests-not-objects", + "client-requests-unknown", + ] + expected = [ + (check_query_client_member, "client-client-member"), + (check_query_empty_array, "client-empty-array"), + (check_query_empty_object, "client-empty-object"), + (check_query_json_bad_root, "client-json-bad-root"), + (check_query_json_empty, "client-json-empty"), + (check_query_json_extra, "client-json-extra"), + (check_query_not_file, "client-not-file"), + (check_query_requests_bad, "client-requests-bad"), + (check_query_requests_empty, "client-requests-empty"), + (check_query_requests_not_kinded, "client-requests-not-kinded"), + (check_query_requests_not_objects, "client-requests-not-objects"), + (check_query_requests_unknown, "client-requests-unknown"), + ] + for (f, k) in expected: + assert is_dict(q[k]) + assert sorted(q[k].keys()) == ["query.json"] + f(q[k]["query.json"]) + expected = [ + (check_query_response_array_negative_major_version, "client-request-array-negative-major-version"), + (check_query_response_array_negative_minor_version, "client-request-array-negative-minor-version"), + (check_query_response_array_negative_version, "client-request-array-negative-version"), + (check_query_response_array_no_major_version, "client-request-array-no-major-version"), + (check_query_response_array_no_supported_version, "client-request-array-no-supported-version"), + (check_query_response_array_no_supported_version_among, "client-request-array-no-supported-version-among"), + (check_query_response_array_version_1, "client-request-array-version-1"), + (check_query_response_array_version_1_1, "client-request-array-version-1-1"), + (check_query_response_array_version_2, "client-request-array-version-2"), + (check_query_response_negative_major_version, "client-request-negative-major-version"), + (check_query_response_negative_minor_version, "client-request-negative-minor-version"), + (check_query_response_negative_version, "client-request-negative-version"), + (check_query_response_no_major_version, "client-request-no-major-version"), + (check_query_response_no_version, "client-request-no-version"), + (check_query_response_version_1, "client-request-version-1"), + (check_query_response_version_1_1, "client-request-version-1-1"), + (check_query_response_version_2, "client-request-version-2"), + ] + for (f, k) in expected: + assert is_dict(q[k]) + assert sorted(q[k].keys()) == ["query.json"] + assert is_dict(q[k]["query.json"]) + assert sorted(q[k]["query.json"].keys()) == ["requests", "responses"] + r = q[k]["query.json"]["requests"] + assert is_list(r) + assert len(r) == 1 + assert is_dict(r[0]) + assert r[0]["kind"] == "__test" + r = q[k]["query.json"]["responses"] + assert is_list(r) + assert len(r) == 1 + assert is_dict(r[0]) + f(r[0]) + +def check_query_client_member(q): + assert is_dict(q) + assert sorted(q.keys()) == ["client", "responses"] + assert is_dict(q["client"]) + assert sorted(q["client"].keys()) == [] + check_error(q["responses"], "'requests' member missing") + +def check_query_empty_array(q): + check_error(q, "query root is not an object") + +def check_query_empty_object(q): + assert is_dict(q) + assert sorted(q.keys()) == ["responses"] + check_error(q["responses"], "'requests' member missing") + +def check_query_json_bad_root(q): + check_error_re(q, "A valid JSON document must be either an array or an object value") + +def check_query_json_empty(q): + check_error_re(q, "value, object or array expected") + +def check_query_json_extra(q): + check_error_re(q, "Extra non-whitespace after JSON value") + +def check_query_not_file(q): + check_error_re(q, "failed to read from file") + +def check_query_requests_bad(q): + assert is_dict(q) + assert sorted(q.keys()) == ["requests", "responses"] + r = q["requests"] + assert is_dict(r) + assert sorted(r.keys()) == [] + check_error(q["responses"], "'requests' member is not an array") + +def check_query_requests_empty(q): + assert is_dict(q) + assert sorted(q.keys()) == ["requests", "responses"] + r = q["requests"] + assert is_list(r) + assert len(r) == 0 + r = q["responses"] + assert is_list(r) + assert len(r) == 0 + +def check_query_requests_not_kinded(q): + assert is_dict(q) + assert sorted(q.keys()) == ["requests", "responses"] + r = q["requests"] + assert is_list(r) + assert len(r) == 4 + assert is_dict(r[0]) + assert sorted(r[0].keys()) == [] + assert is_dict(r[1]) + assert sorted(r[1].keys()) == ["kind"] + assert is_dict(r[1]["kind"]) + assert is_dict(r[2]) + assert sorted(r[2].keys()) == ["kind"] + assert is_list(r[2]["kind"]) + assert is_dict(r[3]) + assert sorted(r[3].keys()) == ["kind"] + assert is_int(r[3]["kind"]) + r = q["responses"] + assert is_list(r) + assert len(r) == 4 + check_error(r[0], "'kind' member missing") + check_error(r[1], "'kind' member is not a string") + check_error(r[2], "'kind' member is not a string") + check_error(r[3], "'kind' member is not a string") + +def check_query_requests_not_objects(q): + assert is_dict(q) + assert sorted(q.keys()) == ["requests", "responses"] + r = q["requests"] + assert is_list(r) + assert len(r) == 3 + assert is_int(r[0]) + assert is_string(r[1]) + assert is_list(r[2]) + r = q["responses"] + assert is_list(r) + assert len(r) == 3 + check_error(r[0], "request is not an object") + check_error(r[1], "request is not an object") + check_error(r[2], "request is not an object") + +def check_query_requests_unknown(q): + assert is_dict(q) + assert sorted(q.keys()) == ["requests", "responses"] + r = q["requests"] + assert is_list(r) + assert len(r) == 3 + assert is_dict(r[0]) + assert sorted(r[0].keys()) == ["kind"] + assert r[0]["kind"] == "unknownC" + assert is_dict(r[1]) + assert sorted(r[1].keys()) == ["kind"] + assert r[1]["kind"] == "unknownB" + assert is_dict(r[2]) + assert sorted(r[2].keys()) == ["kind"] + assert r[2]["kind"] == "unknownA" + r = q["responses"] + assert is_list(r) + assert len(r) == 3 + check_error(r[0], "unknown request kind 'unknownC'") + check_error(r[1], "unknown request kind 'unknownB'") + check_error(r[2], "unknown request kind 'unknownA'") + +def check_query_response_array_negative_major_version(r): + check_error(r, "'version' object 'major' member is not a non-negative integer") + +def check_query_response_array_negative_minor_version(r): + check_error(r, "'version' object 'minor' member is not a non-negative integer") + +def check_query_response_array_negative_version(r): + check_error(r, "'version' array entry is not a non-negative integer or object") + +def check_query_response_array_no_major_version(r): + check_error(r, "'version' object 'major' member missing") + +def check_query_response_array_no_supported_version(r): + check_error(r, "no supported version specified") + +def check_query_response_array_no_supported_version_among(r): + check_error(r, "no supported version specified among: 4.0 3.0") + +def check_query_response_array_version_1(r): + check_index__test(r, 1, 3) + +def check_query_response_array_version_1_1(r): + check_index__test(r, 1, 3) # always uses latest minor version + +def check_query_response_array_version_2(r): + check_index__test(r, 2, 0) + +def check_query_response_negative_major_version(r): + check_error(r, "'version' object 'major' member is not a non-negative integer") + +def check_query_response_negative_minor_version(r): + check_error(r, "'version' object 'minor' member is not a non-negative integer") + +def check_query_response_negative_version(r): + check_error(r, "'version' member is not a non-negative integer, object, or array") + +def check_query_response_no_major_version(r): + check_error(r, "'version' object 'major' member missing") + +def check_query_response_no_version(r): + check_error(r, "'version' member missing") + +def check_query_response_version_1(r): + check_index__test(r, 1, 3) + +def check_query_response_version_1_1(r): + check_index__test(r, 1, 3) # always uses latest minor version + +def check_query_response_version_2(r): + check_index__test(r, 2, 0) + +def check_objects(o): + assert is_list(o) + assert len(o) == 2 + check_index__test(o[0], 1, 3) + check_index__test(o[1], 2, 0) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/ClientStateful-prep.cmake new file mode 100644 index 0000000..5b41d7a --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateful-prep.cmake @@ -0,0 +1,73 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-client-member/query.json" [[{ "client": {} }]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-empty-array/query.json" "[]") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-empty-object/query.json" "{}") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-bad-root/query.json" [["invalid root"]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-empty/query.json" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-extra/query.json" "{}x") +file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-not-file/query.json") + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-bad/query.json" [[{ "requests": {} }]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-empty/query.json" [[{ "requests": [] }]]) + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-not-objects/query.json" [[ +{ "requests": [ 0, "", [] ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-not-kinded/query.json" [[ +{ "requests": [ {}, { "kind": {} }, { "kind": [] }, { "kind": 0 } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-unknown/query.json" [[ +{ "requests": [ { "kind": "unknownC" }, { "kind": "unknownB" }, { "kind": "unknownA" } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-no-version/query.json" [[ +{ "requests": [ { "kind": "__test" } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : -1 } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-no-major-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : {} } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-major-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : { "major": -1 } } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-minor-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : { "major": 0, "minor": -1 } } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [ 1, -1 ] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-major-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [ 1, {} ] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-major-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [ 1, { "major": -1 } ] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-minor-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [ 1, { "major": 0, "minor": -1 } ] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-supported-version/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-supported-version-among/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [4, 3] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-1/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : 1 } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-1-1/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : { "major": 1, "minor": 1 } } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-2/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : { "major": 2 } } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-1/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [3, 1] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-1-1/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [3, { "major": 1, "minor": 1 }, 2 ] } ] } +]]) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-2/query.json" [[ +{ "requests": [ { "kind": "__test", "version" : [3, { "major": 2 } ] } ] } +]]) + +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/ClientStateful.cmake b/Tests/RunCMake/FileAPI/ClientStateful.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateful.cmake diff --git a/Tests/RunCMake/FileAPI/ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/ClientStateless-check.cmake new file mode 100644 index 0000000..955d9be --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateless-check.cmake @@ -0,0 +1,15 @@ +set(expect + query + query/client-foo + query/client-foo/__test-v1 + query/client-foo/__test-v2 + query/client-foo/__test-v3 + query/client-foo/unknown + reply + reply/__test-v1-[0-9a-f]+.json + reply/__test-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(ClientStateless) diff --git a/Tests/RunCMake/FileAPI/ClientStateless-check.py b/Tests/RunCMake/FileAPI/ClientStateless-check.py new file mode 100644 index 0000000..b7da314 --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateless-check.py @@ -0,0 +1,26 @@ +from check_index import * + +def check_reply(r): + assert is_dict(r) + assert sorted(r.keys()) == ["client-foo"] + check_reply_client_foo(r["client-foo"]) + +def check_reply_client_foo(r): + assert is_dict(r) + assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "unknown"] + check_index__test(r["__test-v1"], 1, 3) + check_index__test(r["__test-v2"], 2, 0) + check_error(r["__test-v3"], "unknown query file") + check_error(r["unknown"], "unknown query file") + +def check_objects(o): + assert is_list(o) + assert len(o) == 2 + check_index__test(o[0], 1, 3) + check_index__test(o[1], 2, 0) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/ClientStateless-prep.cmake new file mode 100644 index 0000000..1b8d772 --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateless-prep.cmake @@ -0,0 +1,5 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v1" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v3" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/ClientStateless.cmake b/Tests/RunCMake/FileAPI/ClientStateless.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/ClientStateless.cmake diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake b/Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake new file mode 100644 index 0000000..4959c1e --- /dev/null +++ b/Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake @@ -0,0 +1,20 @@ +set(expect + query + query/__test-v1 + query/__test-v2 + query/__test-v3 + query/client-foo + query/client-foo/__test-v1 + query/client-foo/__test-v2 + query/client-foo/__test-v3 + query/client-foo/unknown + query/query.json + query/unknown + reply + reply/__test-v1-[0-9a-f]+.json + reply/__test-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(DuplicateStateless) diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless-check.py b/Tests/RunCMake/FileAPI/DuplicateStateless-check.py new file mode 100644 index 0000000..3335479 --- /dev/null +++ b/Tests/RunCMake/FileAPI/DuplicateStateless-check.py @@ -0,0 +1,31 @@ +from check_index import * + +def check_reply(r): + assert is_dict(r) + assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "client-foo", "query.json", "unknown"] + check_index__test(r["__test-v1"], 1, 3) + check_index__test(r["__test-v2"], 2, 0) + check_error(r["__test-v3"], "unknown query file") + check_reply_client_foo(r["client-foo"]) + check_error(r["query.json"], "unknown query file") + check_error(r["unknown"], "unknown query file") + +def check_reply_client_foo(r): + assert is_dict(r) + assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "unknown"] + check_index__test(r["__test-v1"], 1, 3) + check_index__test(r["__test-v2"], 2, 0) + check_error(r["__test-v3"], "unknown query file") + check_error(r["unknown"], "unknown query file") + +def check_objects(o): + assert is_list(o) + assert len(o) == 2 + check_index__test(o[0], 1, 3) + check_index__test(o[1], 2, 0) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake b/Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake new file mode 100644 index 0000000..51b9852 --- /dev/null +++ b/Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake @@ -0,0 +1,10 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v2" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/unknown" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v1" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v3" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/DuplicateStateless.cmake b/Tests/RunCMake/FileAPI/DuplicateStateless.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/DuplicateStateless.cmake diff --git a/Tests/RunCMake/FileAPI/Empty-check.cmake b/Tests/RunCMake/FileAPI/Empty-check.cmake new file mode 100644 index 0000000..2764b42 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Empty-check.cmake @@ -0,0 +1,8 @@ +set(expect + query + reply + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(Empty) diff --git a/Tests/RunCMake/FileAPI/Empty-check.py b/Tests/RunCMake/FileAPI/Empty-check.py new file mode 100644 index 0000000..75bf096 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Empty-check.py @@ -0,0 +1,15 @@ +from check_index import * + +def check_reply(r): + assert is_dict(r) + assert sorted(r.keys()) == [] + +def check_objects(o): + assert is_list(o) + assert len(o) == 0 + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/Empty-prep.cmake b/Tests/RunCMake/FileAPI/Empty-prep.cmake new file mode 100644 index 0000000..1d1f69e --- /dev/null +++ b/Tests/RunCMake/FileAPI/Empty-prep.cmake @@ -0,0 +1 @@ +file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query") diff --git a/Tests/RunCMake/FileAPI/Empty.cmake b/Tests/RunCMake/FileAPI/Empty.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Empty.cmake diff --git a/Tests/RunCMake/FileAPI/EmptyClient-check.cmake b/Tests/RunCMake/FileAPI/EmptyClient-check.cmake new file mode 100644 index 0000000..4e5745c --- /dev/null +++ b/Tests/RunCMake/FileAPI/EmptyClient-check.cmake @@ -0,0 +1,9 @@ +set(expect + query + query/client-foo + reply + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(EmptyClient) diff --git a/Tests/RunCMake/FileAPI/EmptyClient-check.py b/Tests/RunCMake/FileAPI/EmptyClient-check.py new file mode 100644 index 0000000..f887908 --- /dev/null +++ b/Tests/RunCMake/FileAPI/EmptyClient-check.py @@ -0,0 +1,20 @@ +from check_index import * + +def check_reply(r): + assert is_dict(r) + assert sorted(r.keys()) == ["client-foo"] + check_reply_client_foo(r["client-foo"]) + +def check_reply_client_foo(r): + assert is_dict(r) + assert sorted(r.keys()) == [] + +def check_objects(o): + assert is_list(o) + assert len(o) == 0 + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/EmptyClient-prep.cmake b/Tests/RunCMake/FileAPI/EmptyClient-prep.cmake new file mode 100644 index 0000000..31512fd --- /dev/null +++ b/Tests/RunCMake/FileAPI/EmptyClient-prep.cmake @@ -0,0 +1,2 @@ +file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/EmptyClient.cmake b/Tests/RunCMake/FileAPI/EmptyClient.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/EmptyClient.cmake diff --git a/Tests/RunCMake/FileAPI/MixedStateless-check.cmake b/Tests/RunCMake/FileAPI/MixedStateless-check.cmake new file mode 100644 index 0000000..5795614 --- /dev/null +++ b/Tests/RunCMake/FileAPI/MixedStateless-check.cmake @@ -0,0 +1,16 @@ +set(expect + query + query/__test-v1 + query/__test-v3 + query/client-foo + query/client-foo/__test-v2 + query/client-foo/unknown + query/query.json + reply + reply/__test-v1-[0-9a-f]+.json + reply/__test-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(MixedStateless) diff --git a/Tests/RunCMake/FileAPI/MixedStateless-check.py b/Tests/RunCMake/FileAPI/MixedStateless-check.py new file mode 100644 index 0000000..be019ab --- /dev/null +++ b/Tests/RunCMake/FileAPI/MixedStateless-check.py @@ -0,0 +1,27 @@ +from check_index import * + +def check_reply(r): + assert is_dict(r) + assert sorted(r.keys()) == ["__test-v1", "__test-v3", "client-foo", "query.json"] + check_index__test(r["__test-v1"], 1, 3) + check_error(r["__test-v3"], "unknown query file") + check_reply_client_foo(r["client-foo"]) + check_error(r["query.json"], "unknown query file") + +def check_reply_client_foo(r): + assert is_dict(r) + assert sorted(r.keys()) == ["__test-v2", "unknown"] + check_index__test(r["__test-v2"], 2, 0) + check_error(r["unknown"], "unknown query file") + +def check_objects(o): + assert is_list(o) + assert len(o) == 2 + check_index__test(o[0], 1, 3) + check_index__test(o[1], 2, 0) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/MixedStateless-prep.cmake b/Tests/RunCMake/FileAPI/MixedStateless-prep.cmake new file mode 100644 index 0000000..030baac --- /dev/null +++ b/Tests/RunCMake/FileAPI/MixedStateless-prep.cmake @@ -0,0 +1,6 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/MixedStateless.cmake b/Tests/RunCMake/FileAPI/MixedStateless.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/MixedStateless.cmake diff --git a/Tests/RunCMake/FileAPI/Nothing-check.cmake b/Tests/RunCMake/FileAPI/Nothing-check.cmake new file mode 100644 index 0000000..cd4f42e --- /dev/null +++ b/Tests/RunCMake/FileAPI/Nothing-check.cmake @@ -0,0 +1 @@ +check_api("^$") diff --git a/Tests/RunCMake/FileAPI/Nothing-prep.cmake b/Tests/RunCMake/FileAPI/Nothing-prep.cmake new file mode 100644 index 0000000..b850d47 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Nothing-prep.cmake @@ -0,0 +1 @@ +file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1") diff --git a/Tests/RunCMake/FileAPI/Nothing.cmake b/Tests/RunCMake/FileAPI/Nothing.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Nothing.cmake diff --git a/Tests/RunCMake/FileAPI/RunCMakeTest.cmake b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake new file mode 100644 index 0000000..f8adb64 --- /dev/null +++ b/Tests/RunCMake/FileAPI/RunCMakeTest.cmake @@ -0,0 +1,58 @@ +include(RunCMake) + +# Function called in *-check.cmake scripts to check api files. +function(check_api expect) + file(GLOB_RECURSE actual + LIST_DIRECTORIES TRUE + RELATIVE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1 + ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/* + ) + if(NOT "${actual}" MATCHES "${expect}") + set(RunCMake_TEST_FAILED "API files: + ${actual} +do not match what we expected: + ${expect} +in directory: + ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1" PARENT_SCOPE) + endif() +endfunction() + +function(check_python case) + if(RunCMake_TEST_FAILED OR NOT PYTHON_EXECUTABLE) + return() + endif() + file(GLOB index ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/index-*.json) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} "${RunCMake_SOURCE_DIR}/${case}-check.py" "${index}" + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_VARIABLE output + ) + if(NOT result EQUAL 0) + string(REPLACE "\n" "\n " output " ${output}") + set(RunCMake_TEST_FAILED "Unexpected index:\n${output}" PARENT_SCOPE) + endif() +endfunction() + +run_cmake(Nothing) +run_cmake(Empty) +run_cmake(EmptyClient) +run_cmake(Stale) +run_cmake(SharedStateless) +run_cmake(ClientStateless) +run_cmake(MixedStateless) +run_cmake(DuplicateStateless) +run_cmake(ClientStateful) + +function(run_object object) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${object}-build) + run_cmake(${object}) + set(RunCMake_TEST_NO_CLEAN 1) + run_cmake_command(${object}-SharedStateless ${CMAKE_COMMAND} .) + run_cmake_command(${object}-ClientStateless ${CMAKE_COMMAND} .) + run_cmake_command(${object}-ClientStateful ${CMAKE_COMMAND} .) +endfunction() + +run_object(codemodel-v2) +run_object(cache-v2) +run_object(cmakeFiles-v1) diff --git a/Tests/RunCMake/FileAPI/SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/SharedStateless-check.cmake new file mode 100644 index 0000000..7f3bb23 --- /dev/null +++ b/Tests/RunCMake/FileAPI/SharedStateless-check.cmake @@ -0,0 +1,15 @@ +set(expect + query + query/__test-v1 + query/__test-v2 + query/__test-v3 + query/query.json + query/unknown + reply + reply/__test-v1-[0-9a-f]+.json + reply/__test-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(SharedStateless) diff --git a/Tests/RunCMake/FileAPI/SharedStateless-check.py b/Tests/RunCMake/FileAPI/SharedStateless-check.py new file mode 100644 index 0000000..79f52d7 --- /dev/null +++ b/Tests/RunCMake/FileAPI/SharedStateless-check.py @@ -0,0 +1,22 @@ +from check_index import * + +def check_reply(r): + assert is_dict(r) + assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "query.json", "unknown"] + check_index__test(r["__test-v1"], 1, 3) + check_index__test(r["__test-v2"], 2, 0) + check_error(r["__test-v3"], "unknown query file") + check_error(r["query.json"], "unknown query file") + check_error(r["unknown"], "unknown query file") + +def check_objects(o): + assert is_list(o) + assert len(o) == 2 + check_index__test(o[0], 1, 3) + check_index__test(o[1], 2, 0) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_cmake(index["cmake"]) +check_reply(index["reply"]) +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/SharedStateless-prep.cmake new file mode 100644 index 0000000..b280414 --- /dev/null +++ b/Tests/RunCMake/FileAPI/SharedStateless-prep.cmake @@ -0,0 +1,6 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v2" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/unknown" "") +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/SharedStateless.cmake b/Tests/RunCMake/FileAPI/SharedStateless.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/SharedStateless.cmake diff --git a/Tests/RunCMake/FileAPI/Stale-check.cmake b/Tests/RunCMake/FileAPI/Stale-check.cmake new file mode 100644 index 0000000..7ee2c9e --- /dev/null +++ b/Tests/RunCMake/FileAPI/Stale-check.cmake @@ -0,0 +1,4 @@ +set(expect + reply + ) +check_api("^${expect}$") diff --git a/Tests/RunCMake/FileAPI/Stale-prep.cmake b/Tests/RunCMake/FileAPI/Stale-prep.cmake new file mode 100644 index 0000000..e920925 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Stale-prep.cmake @@ -0,0 +1 @@ +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "") diff --git a/Tests/RunCMake/FileAPI/Stale.cmake b/Tests/RunCMake/FileAPI/Stale.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/Stale.cmake diff --git a/Tests/RunCMake/FileAPI/alias/CMakeLists.txt b/Tests/RunCMake/FileAPI/alias/CMakeLists.txt new file mode 100644 index 0000000..549307d --- /dev/null +++ b/Tests/RunCMake/FileAPI/alias/CMakeLists.txt @@ -0,0 +1,10 @@ +project(Alias) +enable_language(CXX) + +add_library(c_alias_lib ALIAS c_lib) +add_executable(c_alias_exe ../empty.c) +target_link_libraries(c_alias_exe PRIVATE c_alias_lib) + +add_library(cxx_alias_lib ALIAS cxx_lib) +add_executable(cxx_alias_exe ../empty.cxx) +target_link_libraries(cxx_alias_exe PRIVATE cxx_alias_lib) diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake new file mode 100644 index 0000000..0f5ef28 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/query.json + reply + reply/cache-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(cache-v2) diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake new file mode 100644 index 0000000..9329280 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateful-prep.cmake @@ -0,0 +1,4 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[ +{ "requests": [ { "kind": "cache", "version" : 2 } ] } +]]) diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake new file mode 100644 index 0000000..c406ec8 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/cache-v2 + reply + reply/cache-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(cache-v2) diff --git a/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake new file mode 100644 index 0000000..dccafa5 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-ClientStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cache-v2" "") diff --git a/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake new file mode 100644 index 0000000..f8337eb --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake @@ -0,0 +1,10 @@ +set(expect + query + query/cache-v2 + reply + reply/cache-v2-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(cache-v2) diff --git a/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake new file mode 100644 index 0000000..ee5ac57 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-SharedStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cache-v2" "") diff --git a/Tests/RunCMake/FileAPI/cache-v2-check.py b/Tests/RunCMake/FileAPI/cache-v2-check.py new file mode 100644 index 0000000..756ef80 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2-check.py @@ -0,0 +1,134 @@ +from check_index import * + +def check_objects(o): + assert is_list(o) + assert len(o) == 1 + check_index_object(o[0], "cache", 2, 0, check_object_cache) + +def check_cache_entry(actual, expected): + assert is_dict(actual) + assert sorted(actual.keys()) == ["name", "properties", "type", "value"] + + assert is_string(actual["type"], expected["type"]) + assert is_string(actual["value"], expected["value"]) + + def check_property(actual, expected): + assert is_dict(actual) + assert sorted(actual.keys()) == ["name", "value"] + assert is_string(actual["value"], expected["value"]) + + check_list_match(lambda a, e: is_string(a["name"], e["name"]), actual["properties"], expected["properties"], check=check_property) + +def check_object_cache(o): + assert sorted(o.keys()) == ["entries", "kind", "version"] + # The "kind" and "version" members are handled by check_index_object. + check_list_match(lambda a, e: is_string(a["name"], e["name"]), o["entries"], [ + { + "name": "CM_OPTION_BOOL", + "type": "BOOL", + "value": "OFF", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing option()", + }, + ], + }, + { + "name": "CM_SET_BOOL", + "type": "BOOL", + "value": "ON", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE BOOL)", + }, + { + "name": "ADVANCED", + "value": "1", + }, + ], + }, + { + "name": "CM_SET_FILEPATH", + "type": "FILEPATH", + "value": "dir1/dir2/empty.txt", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE FILEPATH)", + }, + ], + }, + { + "name": "CM_SET_PATH", + "type": "PATH", + "value": "dir1/dir2", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE PATH)", + }, + { + "name": "ADVANCED", + "value": "ON", + }, + ], + }, + { + "name": "CM_SET_STRING", + "type": "STRING", + "value": "test", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE STRING)", + }, + ], + }, + { + "name": "CM_SET_STRINGS", + "type": "STRING", + "value": "1", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE STRING) with STRINGS", + }, + { + "name": "STRINGS", + "value": "1;2;3;4", + }, + ], + }, + { + "name": "CM_SET_INTERNAL", + "type": "INTERNAL", + "value": "int2", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE INTERNAL)", + }, + ], + }, + { + "name": "CM_SET_TYPE", + "type": "STRING", + "value": "1", + "properties": [ + { + "name": "HELPSTRING", + "value": "Testing set(CACHE INTERNAL) with set_property(TYPE)", + }, + { + "name": "ADVANCED", + "value": "0", + }, + ], + }, + ], check=check_cache_entry, allow_extra=True) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/cache-v2.cmake b/Tests/RunCMake/FileAPI/cache-v2.cmake new file mode 100644 index 0000000..45b402d --- /dev/null +++ b/Tests/RunCMake/FileAPI/cache-v2.cmake @@ -0,0 +1,14 @@ +option(CM_OPTION_BOOL "Testing option()" "OFF") +set(CM_SET_BOOL "ON" CACHE BOOL "Testing set(CACHE BOOL)") +mark_as_advanced(CM_SET_BOOL) +set(CM_SET_FILEPATH "dir1/dir2/empty.txt" CACHE FILEPATH "Testing set(CACHE FILEPATH)") +set(CM_SET_PATH "dir1/dir2" CACHE PATH "Testing set(CACHE PATH)") +set_property(CACHE CM_SET_PATH PROPERTY ADVANCED ON) +set(CM_SET_STRING "test" CACHE STRING "Testing set(CACHE STRING)") +set(CM_SET_STRINGS "1" CACHE STRING "Testing set(CACHE STRING) with STRINGS") +set_property(CACHE CM_SET_STRINGS PROPERTY STRINGS "1;2;3;4") +set(CM_SET_INTERNAL "int" CACHE INTERNAL "Testing set(CACHE INTERNAL)") +set_property(CACHE CM_SET_INTERNAL PROPERTY VALUE "int2") +set(CM_SET_TYPE "1" CACHE INTERNAL "Testing set(CACHE INTERNAL) with set_property(TYPE)") +set_property(CACHE CM_SET_TYPE PROPERTY TYPE "STRING") +set_property(CACHE CM_SET_TYPE PROPERTY ADVANCED "0") diff --git a/Tests/RunCMake/FileAPI/check_index.py b/Tests/RunCMake/FileAPI/check_index.py new file mode 100644 index 0000000..cda7234 --- /dev/null +++ b/Tests/RunCMake/FileAPI/check_index.py @@ -0,0 +1,163 @@ +import sys +import os +import json +import re + +if sys.version_info[0] >= 3: + unicode = str + +def is_bool(x, val=None): + return isinstance(x, bool) and (val is None or x == val) + +def is_dict(x): + return isinstance(x, dict) + +def is_list(x): + return isinstance(x, list) + +def is_int(x, val=None): + return (isinstance(x, int) or isinstance(x, long)) and (val is None or x == val) + +def is_string(x, val=None): + return (isinstance(x, str) or isinstance(x, unicode)) and (val is None or x == val) + +def matches(s, pattern): + return is_string(s) and bool(re.search(pattern, s)) + +def check_list_match(match, actual, expected, check=None, check_exception=None, missing_exception=None, extra_exception=None, allow_extra=False): + """ + Handle the common pattern of making sure every actual item "matches" some + item in the expected list, and that neither list has extra items after + matching is completed. + + @param match: Callback to check if an actual item matches an expected + item. Return True if the item matches, return False if the item doesn't + match. + @param actual: List of actual items to search. + @param expected: List of expected items to match. + @param check: Optional function to check that the actual item is valid by + comparing it to the expected item. + @param check_exception: Optional function that returns an argument to + append to any exception thrown by the check function. + @param missing_exception: Optional function that returns an argument to + append to the exception thrown when an item is not found. + @param extra_exception: Optional function that returns an argument to + append to the exception thrown when an extra item is found. + @param allow_extra: Optional parameter allowing there to be extra actual + items after all the expected items have been found. + """ + assert is_list(actual) + _actual = actual[:] + for expected_item in expected: + found = False + for i, actual_item in enumerate(_actual): + if match(actual_item, expected_item): + if check: + try: + check(actual_item, expected_item) + except BaseException as e: + if check_exception: + e.args += (check_exception(actual_item, expected_item),) + raise + found = True + del _actual[i] + break + if missing_exception: + assert found, missing_exception(expected_item) + else: + assert found + if not allow_extra: + if extra_exception: + assert len(_actual) == 0, [extra_exception(a) for a in _actual] + else: + assert len(_actual) == 0 + +def filter_list(f, l): + if l is not None: + l = list(filter(f, l)) + if l == []: + l = None + return l + +def check_cmake(cmake): + assert is_dict(cmake) + assert sorted(cmake.keys()) == ["generator", "paths", "version"] + check_cmake_version(cmake["version"]) + check_cmake_paths(cmake["paths"]) + check_cmake_generator(cmake["generator"]) + +def check_cmake_version(v): + assert is_dict(v) + assert sorted(v.keys()) == ["isDirty", "major", "minor", "patch", "string", "suffix"] + assert is_string(v["string"]) + assert is_int(v["major"]) + assert is_int(v["minor"]) + assert is_int(v["patch"]) + assert is_string(v["suffix"]) + assert is_bool(v["isDirty"]) + +def check_cmake_paths(v): + assert is_dict(v) + assert sorted(v.keys()) == ["cmake", "cpack", "ctest", "root"] + assert is_string(v["cmake"]) + assert is_string(v["cpack"]) + assert is_string(v["ctest"]) + assert is_string(v["root"]) + +def check_cmake_generator(g): + assert is_dict(g) + name = g.get("name", None) + assert is_string(name) + if name.startswith("Visual Studio"): + assert sorted(g.keys()) == ["name", "platform"] + assert is_string(g["platform"]) + else: + assert sorted(g.keys()) == ["name"] + +def check_index_object(indexEntry, kind, major, minor, check): + assert is_dict(indexEntry) + assert sorted(indexEntry.keys()) == ["jsonFile", "kind", "version"] + assert is_string(indexEntry["kind"]) + assert indexEntry["kind"] == kind + assert is_dict(indexEntry["version"]) + assert sorted(indexEntry["version"].keys()) == ["major", "minor"] + assert indexEntry["version"]["major"] == major + assert indexEntry["version"]["minor"] == minor + assert is_string(indexEntry["jsonFile"]) + filepath = os.path.join(reply_dir, indexEntry["jsonFile"]) + with open(filepath) as f: + object = json.load(f) + assert is_dict(object) + assert "kind" in object + assert is_string(object["kind"]) + assert object["kind"] == kind + assert "version" in object + assert is_dict(object["version"]) + assert sorted(object["version"].keys()) == ["major", "minor"] + assert object["version"]["major"] == major + assert object["version"]["minor"] == minor + if check: + check(object) + +def check_index__test(indexEntry, major, minor): + def check(object): + assert sorted(object.keys()) == ["kind", "version"] + check_index_object(indexEntry, "__test", major, minor, check) + +def check_error(value, error): + assert is_dict(value) + assert sorted(value.keys()) == ["error"] + assert is_string(value["error"]) + assert value["error"] == error + +def check_error_re(value, error): + assert is_dict(value) + assert sorted(value.keys()) == ["error"] + assert is_string(value["error"]) + assert re.search(error, value["error"]) + +reply_index = sys.argv[1] +reply_dir = os.path.dirname(reply_index) + +with open(reply_index) as f: + index = json.load(f) diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake new file mode 100644 index 0000000..21e931e --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/query.json + reply + reply/cmakeFiles-v1-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(cmakeFiles-v1) diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake new file mode 100644 index 0000000..7a72696 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateful-prep.cmake @@ -0,0 +1,4 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[ +{ "requests": [ { "kind": "cmakeFiles", "version" : 1 } ] } +]]) diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake new file mode 100644 index 0000000..2ce2e79 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/client-foo + query/client-foo/cmakeFiles-v1 + reply + reply/cmakeFiles-v1-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(cmakeFiles-v1) diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake new file mode 100644 index 0000000..eb96491 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-ClientStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cmakeFiles-v1" "") diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake new file mode 100644 index 0000000..6e3b49a --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-check.cmake @@ -0,0 +1,10 @@ +set(expect + query + query/cmakeFiles-v1 + reply + reply/cmakeFiles-v1-[0-9a-f]+.json + reply/index-[0-9.T-]+.json + ) +check_api("^${expect}$") + +check_python(cmakeFiles-v1) diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake new file mode 100644 index 0000000..8c8bdef --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-SharedStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cmakeFiles-v1" "") diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py new file mode 100644 index 0000000..49dfe87 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py @@ -0,0 +1,94 @@ +from check_index import * + +def check_objects(o): + assert is_list(o) + assert len(o) == 1 + check_index_object(o[0], "cmakeFiles", 1, 0, check_object_cmakeFiles) + +def check_input(actual, expected): + assert is_dict(actual) + expected_keys = ["path"] + + if expected["isGenerated"] is not None: + expected_keys.append("isGenerated") + assert is_bool(actual["isGenerated"], expected["isGenerated"]) + + if expected["isExternal"] is not None: + expected_keys.append("isExternal") + assert is_bool(actual["isExternal"], expected["isExternal"]) + + if expected["isCMake"] is not None: + expected_keys.append("isCMake") + assert is_bool(actual["isCMake"], expected["isCMake"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + +def check_object_cmakeFiles(o): + assert sorted(o.keys()) == ["inputs", "kind", "paths", "version"] + # The "kind" and "version" members are handled by check_index_object. + assert is_dict(o["paths"]) + assert sorted(o["paths"].keys()) == ["build", "source"] + assert matches(o["paths"]["build"], "^.*/Tests/RunCMake/FileAPI/cmakeFiles-v1-build$") + assert matches(o["paths"]["source"], "^.*/Tests/RunCMake/FileAPI$") + + expected = [ + { + "path": "^CMakeLists\\.txt$", + "isGenerated": None, + "isExternal": None, + "isCMake": None, + }, + { + "path": "^cmakeFiles-v1\\.cmake$", + "isGenerated": None, + "isExternal": None, + "isCMake": None, + }, + { + "path": "^dir/CMakeLists\\.txt$", + "isGenerated": None, + "isExternal": None, + "isCMake": None, + }, + { + "path": "^dir/dir/CMakeLists\\.txt$", + "isGenerated": None, + "isExternal": None, + "isCMake": None, + }, + { + "path": "^dir/dirtest\\.cmake$", + "isGenerated": None, + "isExternal": None, + "isCMake": None, + }, + { + "path": "^.*/Tests/RunCMake/FileAPIDummyFile\\.cmake$", + "isGenerated": None, + "isExternal": True, + "isCMake": None, + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/cmakeFiles-v1-build/generated\\.cmake", + "isGenerated": True, + "isExternal": None, + "isCMake": None, + }, + { + "path": "^.*/Modules/CMakeParseArguments\\.cmake$", + "isGenerated": None, + "isExternal": True, + "isCMake": True, + }, + ] + + inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"] + if inSource: + for e in expected: + e["path"] = e["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) + + check_list_match(lambda a, e: matches(a["path"], e["path"]), o["inputs"], expected, check=check_input, allow_extra=True) + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_objects(index["objects"]) diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake new file mode 100644 index 0000000..4d4d757 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake @@ -0,0 +1,8 @@ +include("${CMAKE_CURRENT_LIST_DIR}/dir/dirtest.cmake") +include(CMakeParseArguments) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cmake" "") +include("${CMAKE_CURRENT_BINARY_DIR}/generated.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/../FileAPIDummyFile.cmake") + +add_subdirectory(dir) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake new file mode 100644 index 0000000..fb78e87 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-check.cmake @@ -0,0 +1,12 @@ +set(expect + query + query/client-foo + query/client-foo/query.json + reply + reply/codemodel-v2-[0-9a-f]+\\.json + reply/index-[0-9.T-]+\\.json + .* + ) +check_api("^${expect}$") + +check_python(codemodel-v2) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake new file mode 100644 index 0000000..e2b38ff --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateful-prep.cmake @@ -0,0 +1,4 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[ +{ "requests": [ { "kind": "codemodel", "version" : 2 } ] } +]]) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake new file mode 100644 index 0000000..7c6a35a --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-check.cmake @@ -0,0 +1,12 @@ +set(expect + query + query/client-foo + query/client-foo/codemodel-v2 + reply + reply/codemodel-v2-[0-9a-f]+\\.json + reply/index-[0-9.T-]+\\.json + .* + ) +check_api("^${expect}$") + +check_python(codemodel-v2) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake new file mode 100644 index 0000000..d1ce292 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-ClientStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/codemodel-v2" "") diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake new file mode 100644 index 0000000..cc2f31b --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-check.cmake @@ -0,0 +1,11 @@ +set(expect + query + query/codemodel-v2 + reply + reply/codemodel-v2-[0-9a-f]+\\.json + reply/index-[0-9.T-]+\\.json + .* + ) +check_api("^${expect}$") + +check_python(codemodel-v2) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake new file mode 100644 index 0000000..8a519d5 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-SharedStateless-prep.cmake @@ -0,0 +1,2 @@ +file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query) +file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/codemodel-v2" "") diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py new file mode 100644 index 0000000..18b9347 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -0,0 +1,5085 @@ +from check_index import * + +import sys +import os + +def check_objects(o, g): + assert is_list(o) + assert len(o) == 1 + check_index_object(o[0], "codemodel", 2, 0, check_object_codemodel(g)) + +def check_backtrace(t, b, backtrace): + btg = t["backtraceGraph"] + for expected in backtrace: + assert is_int(b) + node = btg["nodes"][b] + expected_keys = ["file"] + assert matches(btg["files"][node["file"]], expected["file"]) + + if expected["line"] is not None: + expected_keys.append("line") + assert is_int(node["line"], expected["line"]) + + if expected["command"] is not None: + expected_keys.append("command") + assert is_int(node["command"]) + assert is_string(btg["commands"][node["command"]], expected["command"]) + + if expected["hasParent"]: + expected_keys.append("parent") + assert is_int(node["parent"]) + b = node["parent"] + else: + b = None + + assert sorted(node.keys()) == sorted(expected_keys) + + assert b is None + +def check_directory(c): + def _check(actual, expected): + assert is_dict(actual) + expected_keys = ["build", "source", "projectIndex"] + assert matches(actual["build"], expected["build"]) + + assert is_int(actual["projectIndex"]) + assert is_string(c["projects"][actual["projectIndex"]]["name"], expected["projectName"]) + + if expected["parentSource"] is not None: + expected_keys.append("parentIndex") + assert is_int(actual["parentIndex"]) + assert matches(c["directories"][actual["parentIndex"]]["source"], expected["parentSource"]) + + if expected["childSources"] is not None: + expected_keys.append("childIndexes") + check_list_match(lambda a, e: matches(c["directories"][a]["source"], e), + actual["childIndexes"], expected["childSources"], + missing_exception=lambda e: "Child source: %s" % e, + extra_exception=lambda a: "Child source: %s" % a["source"]) + + if expected["targetIds"] is not None: + expected_keys.append("targetIndexes") + check_list_match(lambda a, e: matches(c["targets"][a]["id"], e), + actual["targetIndexes"], expected["targetIds"], + missing_exception=lambda e: "Target ID: %s" % e, + extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"]) + + if expected["minimumCMakeVersion"] is not None: + expected_keys.append("minimumCMakeVersion") + assert is_dict(actual["minimumCMakeVersion"]) + assert sorted(actual["minimumCMakeVersion"].keys()) == ["string"] + assert is_string(actual["minimumCMakeVersion"]["string"], expected["minimumCMakeVersion"]) + + if expected["hasInstallRule"] is not None: + expected_keys.append("hasInstallRule") + assert is_bool(actual["hasInstallRule"], expected["hasInstallRule"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + return _check + +def check_target_backtrace_graph(t): + btg = t["backtraceGraph"] + assert is_dict(btg) + assert sorted(btg.keys()) == ["commands", "files", "nodes"] + assert is_list(btg["commands"]) + + for c in btg["commands"]: + assert is_string(c) + + for f in btg["files"]: + assert is_string(f) + + for n in btg["nodes"]: + expected_keys = ["file"] + assert is_dict(n) + assert is_int(n["file"]) + assert 0 <= n["file"] < len(btg["files"]) + + if "line" in n: + expected_keys.append("line") + assert is_int(n["line"]) + + if "command" in n: + expected_keys.append("command") + assert is_int(n["command"]) + assert 0 <= n["command"] < len(btg["commands"]) + + if "parent" in n: + expected_keys.append("parent") + assert is_int(n["parent"]) + assert 0 <= n["parent"] < len(btg["nodes"]) + + assert sorted(n.keys()) == sorted(expected_keys) + +def check_target(c): + def _check(actual, expected): + assert is_dict(actual) + assert sorted(actual.keys()) == ["directoryIndex", "id", "jsonFile", "name", "projectIndex"] + assert is_int(actual["directoryIndex"]) + assert matches(c["directories"][actual["directoryIndex"]]["source"], expected["directorySource"]) + assert is_string(actual["name"], expected["name"]) + assert is_string(actual["jsonFile"]) + assert is_int(actual["projectIndex"]) + assert is_string(c["projects"][actual["projectIndex"]]["name"], expected["projectName"]) + + filepath = os.path.join(reply_dir, actual["jsonFile"]) + with open(filepath) as f: + obj = json.load(f) + + expected_keys = ["name", "id", "type", "backtraceGraph", "paths", "sources"] + assert is_dict(obj) + assert is_string(obj["name"], expected["name"]) + assert matches(obj["id"], expected["id"]) + assert is_string(obj["type"], expected["type"]) + check_target_backtrace_graph(obj) + + assert is_dict(obj["paths"]) + assert sorted(obj["paths"].keys()) == ["build", "source"] + assert matches(obj["paths"]["build"], expected["build"]) + assert matches(obj["paths"]["source"], expected["source"]) + + def check_source(actual, expected): + assert is_dict(actual) + expected_keys = ["path"] + + if expected["compileGroupLanguage"] is not None: + expected_keys.append("compileGroupIndex") + assert is_string(obj["compileGroups"][actual["compileGroupIndex"]]["language"], expected["compileGroupLanguage"]) + + if expected["sourceGroupName"] is not None: + expected_keys.append("sourceGroupIndex") + assert is_string(obj["sourceGroups"][actual["sourceGroupIndex"]]["name"], expected["sourceGroupName"]) + + if expected["isGenerated"] is not None: + expected_keys.append("isGenerated") + assert is_bool(actual["isGenerated"], expected["isGenerated"]) + + if expected["backtrace"] is not None: + expected_keys.append("backtrace") + check_backtrace(obj, actual["backtrace"], expected["backtrace"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + check_list_match(lambda a, e: matches(a["path"], e["path"]), obj["sources"], + expected["sources"], check=check_source, + check_exception=lambda a, e: "Source file: %s" % a["path"], + missing_exception=lambda e: "Source file: %s" % e["path"], + extra_exception=lambda a: "Source file: %s" % a["path"]) + + if expected["backtrace"] is not None: + expected_keys.append("backtrace") + check_backtrace(obj, obj["backtrace"], expected["backtrace"]) + + if expected["folder"] is not None: + expected_keys.append("folder") + assert is_dict(obj["folder"]) + assert sorted(obj["folder"].keys()) == ["name"] + assert is_string(obj["folder"]["name"], expected["folder"]) + + if expected["nameOnDisk"] is not None: + expected_keys.append("nameOnDisk") + assert matches(obj["nameOnDisk"], expected["nameOnDisk"]) + + if expected["artifacts"] is not None: + expected_keys.append("artifacts") + + def check_artifact(actual, expected): + assert is_dict(actual) + assert sorted(actual.keys()) == ["path"] + + check_list_match(lambda a, e: matches(a["path"], e["path"]), + obj["artifacts"], expected["artifacts"], + check=check_artifact, + check_exception=lambda a, e: "Artifact: %s" % a["path"], + missing_exception=lambda e: "Artifact: %s" % e["path"], + extra_exception=lambda a: "Artifact: %s" % a["path"]) + + if expected["isGeneratorProvided"] is not None: + expected_keys.append("isGeneratorProvided") + assert is_bool(obj["isGeneratorProvided"], expected["isGeneratorProvided"]) + + if expected["install"] is not None: + expected_keys.append("install") + assert is_dict(obj["install"]) + assert sorted(obj["install"].keys()) == ["destinations", "prefix"] + + assert is_dict(obj["install"]["prefix"]) + assert sorted(obj["install"]["prefix"].keys()) == ["path"] + assert matches(obj["install"]["prefix"]["path"], expected["install"]["prefix"]) + + def check_install_destination(actual, expected): + assert is_dict(actual) + expected_keys = ["path"] + + if expected["backtrace"] is not None: + expected_keys.append("backtrace") + check_backtrace(obj, actual["backtrace"], expected["backtrace"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + check_list_match(lambda a, e: matches(a["path"], e["path"]), + obj["install"]["destinations"], expected["install"]["destinations"], + check=check_install_destination, + check_exception=lambda a, e: "Install path: %s" % a["path"], + missing_exception=lambda e: "Install path: %s" % e["path"], + extra_exception=lambda a: "Install path: %s" % a["path"]) + + if expected["link"] is not None: + expected_keys.append("link") + assert is_dict(obj["link"]) + link_keys = ["language"] + + assert is_string(obj["link"]["language"], expected["link"]["language"]) + + # FIXME: Properly test commandFragments + if "commandFragments" in obj["link"]: + link_keys.append("commandFragments") + assert is_list(obj["link"]["commandFragments"]) + for f in obj["link"]["commandFragments"]: + assert is_dict(f) + assert sorted(f.keys()) == ["fragment", "role"] + assert is_string(f["fragment"]) + assert is_string(f["role"]) + assert f["role"] in ("flags", "libraries", "libraryPath", "frameworkPath") + + if expected["link"]["lto"] is not None: + link_keys.append("lto") + assert is_bool(obj["link"]["lto"], expected["link"]["lto"]) + + # FIXME: Properly test sysroot + if "sysroot" in obj["link"]: + link_keys.append("sysroot") + assert is_string(obj["link"]["sysroot"]) + + assert sorted(obj["link"].keys()) == sorted(link_keys) + + if expected["archive"] is not None: + expected_keys.append("archive") + assert is_dict(obj["archive"]) + archive_keys = [] + + # FIXME: Properly test commandFragments + if "commandFragments" in obj["archive"]: + archive_keys.append("commandFragments") + assert is_list(obj["archive"]["commandFragments"]) + for f in obj["archive"]["commandFragments"]: + assert is_dict(f) + assert sorted(f.keys()) == ["fragment", "role"] + assert is_string(f["fragment"]) + assert is_string(f["role"]) + assert f["role"] in ("flags") + + if expected["archive"]["lto"] is not None: + archive_keys.append("lto") + assert is_bool(obj["archive"]["lto"], expected["archive"]["lto"]) + + assert sorted(obj["archive"].keys()) == sorted(archive_keys) + + if expected["dependencies"] is not None: + expected_keys.append("dependencies") + + def check_dependency(actual, expected): + assert is_dict(actual) + expected_keys = ["id"] + + if expected["backtrace"] is not None: + expected_keys.append("backtrace") + check_backtrace(obj, actual["backtrace"], expected["backtrace"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + check_list_match(lambda a, e: matches(a["id"], e["id"]), + obj["dependencies"], expected["dependencies"], + check=check_dependency, + check_exception=lambda a, e: "Dependency ID: %s" % a["id"], + missing_exception=lambda e: "Dependency ID: %s" % e["id"], + extra_exception=lambda a: "Dependency ID: %s" % a["id"]) + + if expected["sourceGroups"] is not None: + expected_keys.append("sourceGroups") + + def check_source_group(actual, expected): + assert is_dict(actual) + assert sorted(actual.keys()) == ["name", "sourceIndexes"] + + check_list_match(lambda a, e: matches(obj["sources"][a]["path"], e), + actual["sourceIndexes"], expected["sourcePaths"], + missing_exception=lambda e: "Source path: %s" % e, + extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"]) + + check_list_match(lambda a, e: is_string(a["name"], e["name"]), + obj["sourceGroups"], expected["sourceGroups"], + check=check_source_group, + check_exception=lambda a, e: "Source group: %s" % a["name"], + missing_exception=lambda e: "Source group: %s" % e["name"], + extra_exception=lambda a: "Source group: %s" % a["name"]) + + if expected["compileGroups"] is not None: + expected_keys.append("compileGroups") + + def check_compile_group(actual, expected): + assert is_dict(actual) + expected_keys = ["sourceIndexes", "language"] + + check_list_match(lambda a, e: matches(obj["sources"][a]["path"], e), + actual["sourceIndexes"], expected["sourcePaths"], + missing_exception=lambda e: "Source path: %s" % e, + extra_exception=lambda a: "Source path: %s" % obj["sources"][a]["path"]) + + # FIXME: Properly test compileCommandFragments + if "compileCommandFragments" in actual: + expected_keys.append("compileCommandFragments") + assert is_list(actual["compileCommandFragments"]) + for f in actual["compileCommandFragments"]: + assert is_dict(f) + assert sorted(f.keys()) == ["fragment"] + assert is_string(f["fragment"]) + + if expected["includes"] is not None: + expected_keys.append("includes") + + def check_include(actual, expected): + assert is_dict(actual) + expected_keys = ["path"] + + if expected["isSystem"] is not None: + expected_keys.append("isSystem") + assert is_bool(actual["isSystem"], expected["isSystem"]) + + if expected["backtrace"] is not None: + expected_keys.append("backtrace") + check_backtrace(obj, actual["backtrace"], expected["backtrace"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + check_list_match(lambda a, e: matches(a["path"], e["path"]), + actual["includes"], expected["includes"], + check=check_include, + check_exception=lambda a, e: "Include path: %s" % a["path"], + missing_exception=lambda e: "Include path: %s" % e["path"], + extra_exception=lambda a: "Include path: %s" % a["path"]) + + if expected["defines"] is not None: + expected_keys.append("defines") + + def check_define(actual, expected): + assert is_dict(actual) + expected_keys = ["define"] + + if expected["backtrace"] is not None: + expected_keys.append("backtrace") + check_backtrace(obj, actual["backtrace"], expected["backtrace"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + check_list_match(lambda a, e: is_string(a["define"], e["define"]), + actual["defines"], expected["defines"], + check=check_define, + check_exception=lambda a, e: "Define: %s" % a["define"], + missing_exception=lambda e: "Define: %s" % e["define"], + extra_exception=lambda a: "Define: %s" % a["define"]) + + # FIXME: Properly test sysroot + if "sysroot" in actual: + expected_keys.append("sysroot") + assert is_string(actual["sysroot"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + check_list_match(lambda a, e: is_string(a["language"], e["language"]), + obj["compileGroups"], expected["compileGroups"], + check=check_compile_group, + check_exception=lambda a, e: "Compile group: %s" % a["language"], + missing_exception=lambda e: "Compile group: %s" % e["language"], + extra_exception=lambda a: "Compile group: %s" % a["language"]) + + assert sorted(obj.keys()) == sorted(expected_keys) + + return _check + +def check_project(c): + def _check(actual, expected): + assert is_dict(actual) + expected_keys = ["name", "directoryIndexes"] + + check_list_match(lambda a, e: matches(c["directories"][a]["source"], e), + actual["directoryIndexes"], expected["directorySources"], + missing_exception=lambda e: "Directory source: %s" % e, + extra_exception=lambda a: "Directory source: %s" % c["directories"][a]["source"]) + + if expected["parentName"] is not None: + expected_keys.append("parentIndex") + assert is_int(actual["parentIndex"]) + assert is_string(c["projects"][actual["parentIndex"]]["name"], expected["parentName"]) + + if expected["childNames"] is not None: + expected_keys.append("childIndexes") + check_list_match(lambda a, e: is_string(c["projects"][a]["name"], e), + actual["childIndexes"], expected["childNames"], + missing_exception=lambda e: "Child name: %s" % e, + extra_exception=lambda a: "Child name: %s" % c["projects"][a]["name"]) + + if expected["targetIds"] is not None: + expected_keys.append("targetIndexes") + check_list_match(lambda a, e: matches(c["targets"][a]["id"], e), + actual["targetIndexes"], expected["targetIds"], + missing_exception=lambda e: "Target ID: %s" % e, + extra_exception=lambda a: "Target ID: %s" % c["targets"][a]["id"]) + + assert sorted(actual.keys()) == sorted(expected_keys) + + return _check + +def gen_check_directories(c, g): + expected = [ + { + "source": "^\\.$", + "build": "^\\.$", + "parentSource": None, + "childSources": [ + "^alias$", + "^custom$", + "^cxx$", + "^imported$", + "^object$", + "^.*/Tests/RunCMake/FileAPIExternalSource$", + "^dir$", + ], + "targetIds": [ + "^ALL_BUILD::@6890427a1f51a3e7e1df$", + "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "^c_exe::@6890427a1f51a3e7e1df$", + "^c_lib::@6890427a1f51a3e7e1df$", + "^c_shared_exe::@6890427a1f51a3e7e1df$", + "^c_shared_lib::@6890427a1f51a3e7e1df$", + "^c_static_exe::@6890427a1f51a3e7e1df$", + "^c_static_lib::@6890427a1f51a3e7e1df$", + "^interface_exe::@6890427a1f51a3e7e1df$", + ], + "projectName": "codemodel-v2", + "minimumCMakeVersion": "3.12", + "hasInstallRule": True, + }, + { + "source": "^alias$", + "build": "^alias$", + "parentSource": "^\\.$", + "childSources": None, + "targetIds": [ + "^ALL_BUILD::@53632cba2752272bb008$", + "^ZERO_CHECK::@53632cba2752272bb008$", + "^c_alias_exe::@53632cba2752272bb008$", + "^cxx_alias_exe::@53632cba2752272bb008$", + ], + "projectName": "Alias", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + { + "source": "^custom$", + "build": "^custom$", + "parentSource": "^\\.$", + "childSources": None, + "targetIds": [ + "^ALL_BUILD::@c11385ffed57b860da63$", + "^ZERO_CHECK::@c11385ffed57b860da63$", + "^custom_exe::@c11385ffed57b860da63$", + "^custom_tgt::@c11385ffed57b860da63$", + ], + "projectName": "Custom", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + { + "source": "^cxx$", + "build": "^cxx$", + "parentSource": "^\\.$", + "childSources": None, + "targetIds": [ + "^ALL_BUILD::@a56b12a3f5c0529fb296$", + "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "^cxx_exe::@a56b12a3f5c0529fb296$", + "^cxx_lib::@a56b12a3f5c0529fb296$", + "^cxx_shared_exe::@a56b12a3f5c0529fb296$", + "^cxx_shared_lib::@a56b12a3f5c0529fb296$", + "^cxx_static_exe::@a56b12a3f5c0529fb296$", + "^cxx_static_lib::@a56b12a3f5c0529fb296$", + ], + "projectName": "Cxx", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + { + "source": "^imported$", + "build": "^imported$", + "parentSource": "^\\.$", + "childSources": None, + "targetIds": [ + "^ALL_BUILD::@ba7eb709d0b48779c6c8$", + "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "^link_imported_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_object_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_static_exe::@ba7eb709d0b48779c6c8$", + ], + "projectName": "Imported", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + { + "source": "^object$", + "build": "^object$", + "parentSource": "^\\.$", + "childSources": None, + "targetIds": [ + "^ALL_BUILD::@5ed5358f70faf8d8af7a$", + "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "^c_object_exe::@5ed5358f70faf8d8af7a$", + "^c_object_lib::@5ed5358f70faf8d8af7a$", + "^cxx_object_exe::@5ed5358f70faf8d8af7a$", + "^cxx_object_lib::@5ed5358f70faf8d8af7a$", + ], + "projectName": "Object", + "minimumCMakeVersion": "3.13", + "hasInstallRule": True, + }, + { + "source": "^dir$", + "build": "^dir$", + "parentSource": "^\\.$", + "childSources": [ + "^dir/dir$", + ], + "targetIds": None, + "projectName": "codemodel-v2", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + { + "source": "^dir/dir$", + "build": "^dir/dir$", + "parentSource": "^dir$", + "childSources": None, + "targetIds": None, + "projectName": "codemodel-v2", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + { + "source": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$", + "parentSource": "^\\.$", + "childSources": None, + "targetIds": [ + "^ALL_BUILD::@[0-9a-f]+$", + "^ZERO_CHECK::@[0-9a-f]+$", + "^generated_exe::@[0-9a-f]+$", + ], + "projectName": "External", + "minimumCMakeVersion": "3.12", + "hasInstallRule": None, + }, + ] + + if matches(g, "^Visual Studio "): + for e in expected: + if e["parentSource"] is not None: + e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^ZERO_CHECK"), e["targetIds"]) + + elif g == "Xcode": + if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""): + for e in expected: + e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(link_imported_object_exe)"), e["targetIds"]) + + else: + for e in expected: + e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"]) + + return expected + +def check_directories(c, g): + check_list_match(lambda a, e: matches(a["source"], e["source"]), c["directories"], gen_check_directories(c, g), + check=check_directory(c), + check_exception=lambda a, e: "Directory source: %s" % a["source"], + missing_exception=lambda e: "Directory source: %s" % e["source"], + extra_exception=lambda a: "Directory source: %s" % a["source"]) + +def gen_check_targets(c, g, inSource): + expected = [ + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^interface_exe::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_lib::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_exe::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_shared_lib::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_shared_exe::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_static_lib::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_static_exe::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + { + "id": "^c_alias_exe::@53632cba2752272bb008$", + "backtrace": None, + }, + { + "id": "^cxx_alias_exe::@53632cba2752272bb008$", + "backtrace": None, + }, + { + "id": "^cxx_lib::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_exe::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^c_object_lib::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^c_object_exe::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^custom_exe::@c11385ffed57b860da63$", + "backtrace": None, + }, + { + "id": "^generated_exe::@[0-9a-f]+$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "interface_exe", + "id": "^interface_exe::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^include_test\\.cmake$", + "line": 3, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^include_test\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": [ + { + "define": "interface_exe_EXPORTS", + "backtrace": None, + }, + ], + }, + ], + "backtrace": [ + { + "file": "^include_test\\.cmake$", + "line": 3, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^include_test\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^my_interface_exe\\.myexe$", + "artifacts": [ + { + "path": "^bin/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?my_interface_exe\\.myexe$", + "_dllExtra": False, + }, + { + "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib)$", + "_dllExtra": True, + }, + { + "path": "^bin/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?my_interface_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "c_lib", + "id": "^c_lib::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "STATIC_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 5, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 5, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^(lib)?c_lib\\.(a|lib)$", + "artifacts": [ + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_lib\\.(a|lib)$", + "_dllExtra": False, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": None, + "archive": { + "lto": None, + }, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "c_exe", + "id": "^c_exe::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 6, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 6, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^c_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^c_lib::@6890427a1f51a3e7e1df$", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 7, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "c_shared_lib", + "id": "^c_shared_lib::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "SHARED_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 9, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": [ + { + "define": "c_shared_lib_EXPORTS", + "backtrace": None, + }, + ], + }, + ], + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 9, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^(lib|cyg)?c_shared_lib\\.(so|dylib|dll)$", + "artifacts": [ + { + "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib\\.(so|dylib|dll)$", + "_dllExtra": False, + }, + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_shared_lib\\.(dll\\.a|lib)$", + "_dllExtra": True, + }, + { + "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?c_shared_lib\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": { + "language": "C", + "lto": True, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "c_shared_exe", + "id": "^c_shared_exe::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 10, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 10, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^c_shared_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_shared_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_shared_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": { + "language": "C", + "lto": True, + }, + "archive": None, + "dependencies": [ + { + "id": "^c_shared_lib::@6890427a1f51a3e7e1df$", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 11, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "c_static_lib", + "id": "^c_static_lib::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "STATIC_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 13, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 13, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^(lib)?c_static_lib\\.(a|lib)$", + "artifacts": [ + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_static_lib\\.(a|lib)$", + "_dllExtra": False, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": None, + "archive": { + "lto": True, + }, + "dependencies": [ + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "c_static_exe", + "id": "^c_static_exe::@6890427a1f51a3e7e1df$", + "directorySource": "^\\.$", + "projectName": "codemodel-v2", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 14, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 14, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^c_static_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_static_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_static_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^\\.$", + "source": "^\\.$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^c_static_lib::@6890427a1f51a3e7e1df$", + "backtrace": [ + { + "file": "^codemodel-v2\\.cmake$", + "line": 15, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^codemodel-v2\\.cmake$", + "line": None, + "command": None, + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": 3, + "command": "include", + "hasParent": True, + }, + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "backtrace": None, + }, + ], + }, + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_lib::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_exe::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + { + "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/cxx/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "cxx_lib", + "id": "^cxx_lib::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "STATIC_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 4, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 4, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^(lib)?cxx_lib\\.(a|lib)$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_lib\\.(a|lib)$", + "_dllExtra": False, + }, + ], + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": None, + "archive": { + "lto": None, + }, + "dependencies": [ + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_exe", + "id": "^cxx_exe::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": "bin", + "nameOnDisk": "^cxx_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^cxx_lib::@a56b12a3f5c0529fb296$", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 6, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_shared_lib", + "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "SHARED_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 9, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": [ + { + "define": "cxx_shared_lib_EXPORTS", + "backtrace": None, + }, + ], + }, + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 9, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^(lib|cyg)?cxx_shared_lib\\.(so|dylib|dll)$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?cxx_shared_lib\\.(so|dylib|dll)$", + "_dllExtra": False, + }, + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib)$", + "_dllExtra": True, + }, + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg)?cxx_shared_lib\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_shared_exe", + "id": "^cxx_shared_exe::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 10, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 10, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^cxx_shared_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_shared_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_shared_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^cxx_shared_lib::@a56b12a3f5c0529fb296$", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 11, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_static_lib", + "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "STATIC_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 13, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 13, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^(lib)?cxx_static_lib\\.(a|lib)$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_static_lib\\.(a|lib)$", + "_dllExtra": False, + }, + ], + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": None, + "archive": { + "lto": None, + }, + "dependencies": [ + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_static_exe", + "id": "^cxx_static_exe::@a56b12a3f5c0529fb296$", + "directorySource": "^cxx$", + "projectName": "Cxx", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 14, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 14, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^cxx_static_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_static_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_static_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^cxx$", + "source": "^cxx$", + "install": None, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^cxx_static_lib::@a56b12a3f5c0529fb296$", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 15, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "backtrace": None, + }, + ], + }, + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@53632cba2752272bb008$", + "directorySource": "^alias$", + "projectName": "Alias", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^alias$", + "source": "^alias$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@53632cba2752272bb008$", + "backtrace": None, + }, + { + "id": "^c_alias_exe::@53632cba2752272bb008$", + "backtrace": None, + }, + { + "id": "^cxx_alias_exe::@53632cba2752272bb008$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@53632cba2752272bb008$", + "directorySource": "^alias$", + "projectName": "Alias", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/alias/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^alias$", + "source": "^alias$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "c_alias_exe", + "id": "^c_alias_exe::@53632cba2752272bb008$", + "directorySource": "^alias$", + "projectName": "Alias", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^c_alias_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_alias_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_alias_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^alias$", + "source": "^alias$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^c_lib::@6890427a1f51a3e7e1df$", + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 6, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@53632cba2752272bb008$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_alias_exe", + "id": "^cxx_alias_exe::@53632cba2752272bb008$", + "directorySource": "^alias$", + "projectName": "Alias", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^cxx_alias_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_alias_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^alias/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_alias_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^alias$", + "source": "^alias$", + "install": None, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^cxx_lib::@a56b12a3f5c0529fb296$", + "backtrace": [ + { + "file": "^alias/CMakeLists\\.txt$", + "line": 10, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^alias/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@53632cba2752272bb008$", + "backtrace": None, + }, + ], + }, + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@5ed5358f70faf8d8af7a$", + "directorySource": "^object$", + "projectName": "Object", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^object$", + "source": "^object$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^c_object_lib::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^c_object_exe::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + { + "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "directorySource": "^object$", + "projectName": "Object", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^object$", + "source": "^object$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "c_object_lib", + "id": "^c_object_lib::@5ed5358f70faf8d8af7a$", + "directorySource": "^object$", + "projectName": "Object", + "type": "OBJECT_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 5, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 5, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": [ + { + "path": "^object/.*/empty(\\.c)?\\.o(bj)?$", + "_dllExtra": False, + }, + ], + "build": "^object$", + "source": "^object$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + ], + }, + { + "name": "c_object_exe", + "id": "^c_object_exe::@5ed5358f70faf8d8af7a$", + "directorySource": "^object$", + "projectName": "Object", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 6, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.c)?\\.o(bj)?$", + "isGenerated": True, + "sourceGroupName": "Object Libraries", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 7, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + { + "name": "Object Libraries", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.c)?\\.o(bj)?$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 6, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^c_object_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_object_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?c_object_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^object$", + "source": "^object$", + "install": { + "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$", + "destinations": [ + { + "path": "bin", + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 13, + "command": "install", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + }, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^c_object_lib::@5ed5358f70faf8d8af7a$", + # FIXME: Add a backtrace here when it becomes available. + # You'll know when it's available, because this test will + # fail. + "backtrace": None, + }, + { + "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_object_lib", + "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$", + "directorySource": "^object$", + "projectName": "Object", + "type": "OBJECT_LIBRARY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 9, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 9, + "command": "add_library", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": [ + { + "path": "^object/.*/empty(\\.cxx)?\\.o(bj)?$", + "_dllExtra": False, + }, + ], + "build": "^object$", + "source": "^object$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + ], + }, + { + "name": "cxx_object_exe", + "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$", + "directorySource": "^object$", + "projectName": "Object", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.cxx$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 10, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.cxx)?\\.o(bj)?$", + "isGenerated": True, + "sourceGroupName": "Object Libraries", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 11, + "command": "target_link_libraries", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.cxx$", + ], + }, + { + "name": "Object Libraries", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.cxx)?\\.o(bj)?$", + ], + }, + ], + "compileGroups": [ + { + "language": "CXX", + "sourcePaths": [ + "^empty\\.cxx$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 10, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^cxx_object_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_object_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^object/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?cxx_object_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^object$", + "source": "^object$", + "install": { + "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$", + "destinations": [ + { + "path": "bin", + "backtrace": [ + { + "file": "^object/CMakeLists\\.txt$", + "line": 13, + "command": "install", + "hasParent": True, + }, + { + "file": "^object/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + }, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^cxx_object_lib::@5ed5358f70faf8d8af7a$", + # FIXME: Add a backtrace here when it becomes available. + # You'll know when it's available, because this test will + # fail. + "backtrace": None, + }, + { + "id": "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "backtrace": None, + }, + ], + }, + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + { + "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/imported/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "link_imported_exe", + "id": "^link_imported_exe::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^link_imported_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + ], + }, + { + "name": "link_imported_shared_exe", + "id": "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 9, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^link_imported_shared_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_shared_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_shared_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + ], + }, + { + "name": "link_imported_static_exe", + "id": "^link_imported_static_exe::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 13, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 13, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^link_imported_static_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_static_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_static_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + ], + }, + { + "name": "link_imported_object_exe", + "id": "^link_imported_object_exe::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 18, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 18, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^link_imported_object_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_object_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_object_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + ], + }, + { + "name": "link_imported_interface_exe", + "id": "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 23, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 23, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^link_imported_interface_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_interface_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^imported/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?link_imported_interface_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^imported$", + "source": "^imported$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "backtrace": None, + }, + ], + }, + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@c11385ffed57b860da63$", + "directorySource": "^custom$", + "projectName": "Custom", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^custom$", + "source": "^custom$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@c11385ffed57b860da63$", + "backtrace": None, + }, + { + "id": "^custom_exe::@c11385ffed57b860da63$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@c11385ffed57b860da63$", + "directorySource": "^custom$", + "projectName": "Custom", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^custom$", + "source": "^custom$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "custom_tgt", + "id": "^custom_tgt::@c11385ffed57b860da63$", + "directorySource": "^custom$", + "projectName": "Custom", + "type": "UTILITY", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/custom_tgt$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 3, + "command": "add_custom_target", + "hasParent": True, + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(custom/)?CMakeFiles/([0-9a-f]+/)?custom_tgt\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/custom/CMakeFiles/custom_tgt$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(custom/)?CMakeFiles/([0-9a-f]+/)?custom_tgt\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 3, + "command": "add_custom_target", + "hasParent": True, + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^custom$", + "source": "^custom$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@c11385ffed57b860da63$", + "backtrace": None, + }, + ], + }, + { + "name": "custom_exe", + "id": "^custom_exe::@c11385ffed57b860da63$", + "directorySource": "^custom$", + "projectName": "Custom", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 4, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^empty\\.c$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^empty\\.c$", + ], + "includes": None, + "defines": None, + }, + ], + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 4, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^custom_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^custom/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?custom_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^custom/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?custom_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^custom$", + "source": "^custom$", + "install": None, + "link": { + "language": "C", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^custom_tgt::@c11385ffed57b860da63$", + "backtrace": [ + { + "file": "^custom/CMakeLists\\.txt$", + "line": 5, + "command": "add_dependencies", + "hasParent": True, + }, + { + "file": "^custom/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "id": "^ZERO_CHECK::@c11385ffed57b860da63$", + "backtrace": None, + }, + ], + }, + { + "name": "ALL_BUILD", + "id": "^ALL_BUILD::@[0-9a-f]+$", + "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "projectName": "External", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ALL_BUILD\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$", + "source": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "install": None, + "link": None, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@[0-9a-f]+$", + "backtrace": None, + }, + { + "id": "^generated_exe::@[0-9a-f]+$", + "backtrace": None, + }, + ], + }, + { + "name": "ZERO_CHECK", + "id": "^ZERO_CHECK::@[0-9a-f]+$", + "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "projectName": "External", + "type": "UTILITY", + "isGeneratorProvided": True, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK$", + "isGenerated": True, + "sourceGroupName": "", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK$", + ], + }, + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/CMakeFiles/ZERO_CHECK\\.rule$", + ], + }, + ], + "compileGroups": None, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": None, + "artifacts": None, + "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$", + "source": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "install": None, + "link": None, + "archive": None, + "dependencies": None, + }, + { + "name": "generated_exe", + "id": "^generated_exe::@[0-9a-f]+$", + "directorySource": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "projectName": "External", + "type": "EXECUTABLE", + "isGeneratorProvided": None, + "sources": [ + { + "path": "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$", + "isGenerated": None, + "sourceGroupName": "Source Files", + "compileGroupLanguage": "C", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$", + "isGenerated": True, + "sourceGroupName": "Generated Source Files", + "compileGroupLanguage": "CXX", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 6, + "command": "target_sources", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "sourceGroups": [ + { + "name": "Source Files", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$", + ], + }, + { + "name": "Generated Source Files", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$", + ], + }, + ], + "compileGroups": [ + { + "language": "C", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPIExternalSource/empty\\.c$", + ], + "includes": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$", + "isSystem": None, + "backtrace": None, + }, + { + "path": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "isSystem": True, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 11, + "command": "target_include_directories", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "defines": [ + { + "define": "EMPTY_C=1", + "backtrace": None, + }, + { + "define": "SRC_DUMMY", + "backtrace": None, + }, + { + "define": "GENERATED_EXE=1", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 12, + "command": "target_compile_definitions", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "define": "TGT_DUMMY", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 12, + "command": "target_compile_definitions", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + }, + { + "language": "CXX", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/generated\\.cxx$", + ], + "includes": [ + { + "path": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "isSystem": True, + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 11, + "command": "target_include_directories", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + "defines": [ + { + "define": "GENERATED_EXE=1", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 12, + "command": "target_compile_definitions", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + { + "define": "TGT_DUMMY", + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 12, + "command": "target_compile_definitions", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ], + }, + ], + "backtrace": [ + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": 5, + "command": "add_executable", + "hasParent": True, + }, + { + "file": "^.*/Tests/RunCMake/FileAPIExternalSource/CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + "folder": None, + "nameOnDisk": "^generated_exe(\\.exe)?$", + "artifacts": [ + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?generated_exe(\\.exe)?$", + "_dllExtra": False, + }, + { + "path": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?generated_exe\\.pdb$", + "_dllExtra": True, + }, + ], + "build": "^.*/Tests/RunCMake/FileAPI/FileAPIExternalBuild$", + "source": "^.*/Tests/RunCMake/FileAPIExternalSource$", + "install": None, + "link": { + "language": "CXX", + "lto": None, + }, + "archive": None, + "dependencies": [ + { + "id": "^ZERO_CHECK::@[0-9a-f]+$", + "backtrace": None, + }, + ], + }, + ] + + if not os.path.exists(os.path.join(reply_dir, "..", "..", "..", "..", "ipo_enabled.txt")): + for e in expected: + try: + e["link"]["lto"] = None + except TypeError: # "link" is not a dict, no problem. + pass + try: + e["archive"]["lto"] = None + except TypeError: # "archive" is not a dict, no problem. + pass + + if inSource: + for e in expected: + if e["sources"] is not None: + for s in e["sources"]: + s["path"] = s["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) + if e["sourceGroups"] is not None: + for g in e["sourceGroups"]: + g["sourcePaths"] = [p.replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) for p in g["sourcePaths"]] + if e["compileGroups"] is not None: + for g in e["compileGroups"]: + g["sourcePaths"] = [p.replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) for p in g["sourcePaths"]] + + if matches(g, "^Visual Studio "): + expected = filter_list(lambda e: e["name"] not in ("ZERO_CHECK") or e["id"] == "^ZERO_CHECK::@6890427a1f51a3e7e1df$", expected) + for e in expected: + if e["type"] == "UTILITY": + if e["id"] == "^ZERO_CHECK::@6890427a1f51a3e7e1df$": + e["sources"] = [ + { + "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$", + "isGenerated": True, + "sourceGroupName": "CMake Rules", + "compileGroupLanguage": None, + "backtrace": [ + { + "file": "^CMakeLists\\.txt$", + "line": None, + "command": None, + "hasParent": False, + }, + ], + }, + ] + e["sourceGroups"] = [ + { + "name": "CMake Rules", + "sourcePaths": [ + "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/CMakeFiles/([0-9a-f]+/)?generate\\.stamp\\.rule$", + ], + }, + ] + elif e["name"] in ("ALL_BUILD"): + e["sources"] = [] + e["sourceGroups"] = None + if e["dependencies"] is not None: + for d in e["dependencies"]: + if matches(d["id"], "^\\^ZERO_CHECK::@"): + d["id"] = "^ZERO_CHECK::@6890427a1f51a3e7e1df$" + + elif g == "Xcode": + if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""): + expected = filter_list(lambda e: e["name"] not in ("link_imported_object_exe"), expected) + for e in expected: + e["dependencies"] = filter_list(lambda d: not matches(d["id"], "^\\^link_imported_object_exe::@"), e["dependencies"]) + if e["name"] in ("c_object_lib", "cxx_object_lib"): + e["artifacts"] = None + + else: + for e in expected: + e["dependencies"] = filter_list(lambda d: not matches(d["id"], "^\\^ZERO_CHECK::@"), e["dependencies"]) + + expected = filter_list(lambda t: t["name"] not in ("ALL_BUILD", "ZERO_CHECK"), expected) + + if sys.platform not in ("win32", "cygwin", "msys"): + for e in expected: + e["artifacts"] = filter_list(lambda a: not a["_dllExtra"], e["artifacts"]) + + return expected + +def check_targets(c, g, inSource): + check_list_match(lambda a, e: matches(a["id"], e["id"]), + c["targets"], gen_check_targets(c, g, inSource), + check=check_target(c), + check_exception=lambda a, e: "Target ID: %s" % a["id"], + missing_exception=lambda e: "Target ID: %s" % e["id"], + extra_exception=lambda a: "Target ID: %s" % a["id"]) + +def gen_check_projects(c, g): + expected = [ + { + "name": "codemodel-v2", + "parentName": None, + "childNames": [ + "Alias", + "Custom", + "Cxx", + "Imported", + "Object", + "External", + ], + "directorySources": [ + "^\\.$", + "^dir$", + "^dir/dir$", + ], + "targetIds": [ + "^ALL_BUILD::@6890427a1f51a3e7e1df$", + "^ZERO_CHECK::@6890427a1f51a3e7e1df$", + "^interface_exe::@6890427a1f51a3e7e1df$", + "^c_lib::@6890427a1f51a3e7e1df$", + "^c_exe::@6890427a1f51a3e7e1df$", + "^c_shared_lib::@6890427a1f51a3e7e1df$", + "^c_shared_exe::@6890427a1f51a3e7e1df$", + "^c_static_lib::@6890427a1f51a3e7e1df$", + "^c_static_exe::@6890427a1f51a3e7e1df$", + ], + }, + { + "name": "Cxx", + "parentName": "codemodel-v2", + "childNames": None, + "directorySources": [ + "^cxx$", + ], + "targetIds": [ + "^ALL_BUILD::@a56b12a3f5c0529fb296$", + "^ZERO_CHECK::@a56b12a3f5c0529fb296$", + "^cxx_lib::@a56b12a3f5c0529fb296$", + "^cxx_exe::@a56b12a3f5c0529fb296$", + "^cxx_shared_lib::@a56b12a3f5c0529fb296$", + "^cxx_shared_exe::@a56b12a3f5c0529fb296$", + "^cxx_static_lib::@a56b12a3f5c0529fb296$", + "^cxx_static_exe::@a56b12a3f5c0529fb296$", + ], + }, + { + "name": "Alias", + "parentName": "codemodel-v2", + "childNames": None, + "directorySources": [ + "^alias$", + ], + "targetIds": [ + "^ALL_BUILD::@53632cba2752272bb008$", + "^ZERO_CHECK::@53632cba2752272bb008$", + "^c_alias_exe::@53632cba2752272bb008$", + "^cxx_alias_exe::@53632cba2752272bb008$", + ], + }, + { + "name": "Object", + "parentName": "codemodel-v2", + "childNames": None, + "directorySources": [ + "^object$", + ], + "targetIds": [ + "^ALL_BUILD::@5ed5358f70faf8d8af7a$", + "^ZERO_CHECK::@5ed5358f70faf8d8af7a$", + "^c_object_lib::@5ed5358f70faf8d8af7a$", + "^c_object_exe::@5ed5358f70faf8d8af7a$", + "^cxx_object_lib::@5ed5358f70faf8d8af7a$", + "^cxx_object_exe::@5ed5358f70faf8d8af7a$", + ], + }, + { + "name": "Imported", + "parentName": "codemodel-v2", + "childNames": None, + "directorySources": [ + "^imported$", + ], + "targetIds": [ + "^ALL_BUILD::@ba7eb709d0b48779c6c8$", + "^ZERO_CHECK::@ba7eb709d0b48779c6c8$", + "^link_imported_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_shared_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_static_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_object_exe::@ba7eb709d0b48779c6c8$", + "^link_imported_interface_exe::@ba7eb709d0b48779c6c8$", + ], + }, + { + "name": "Custom", + "parentName": "codemodel-v2", + "childNames": None, + "directorySources": [ + "^custom$", + ], + "targetIds": [ + "^ALL_BUILD::@c11385ffed57b860da63$", + "^ZERO_CHECK::@c11385ffed57b860da63$", + "^custom_tgt::@c11385ffed57b860da63$", + "^custom_exe::@c11385ffed57b860da63$", + ], + }, + { + "name": "External", + "parentName": "codemodel-v2", + "childNames": None, + "directorySources": [ + "^.*/Tests/RunCMake/FileAPIExternalSource$", + ], + "targetIds": [ + "^ALL_BUILD::@[0-9a-f]+$", + "^ZERO_CHECK::@[0-9a-f]+$", + "^generated_exe::@[0-9a-f]+$", + ], + }, + ] + + if matches(g, "^Visual Studio "): + for e in expected: + if e["parentName"] is not None: + e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^ZERO_CHECK"), e["targetIds"]) + + elif g == "Xcode": + if ';' in os.environ.get("CMAKE_OSX_ARCHITECTURES", ""): + for e in expected: + e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(link_imported_object_exe)"), e["targetIds"]) + + else: + for e in expected: + e["targetIds"] = filter_list(lambda t: not matches(t, "^\\^(ALL_BUILD|ZERO_CHECK)"), e["targetIds"]) + + return expected + +def check_projects(c, g): + check_list_match(lambda a, e: is_string(a["name"], e["name"]), c["projects"], gen_check_projects(c, g), + check=check_project(c), + check_exception=lambda a, e: "Project name: %s" % a["name"], + missing_exception=lambda e: "Project name: %s" % e["name"], + extra_exception=lambda a: "Project name: %s" % a["name"]) + +def check_object_codemodel_configuration(c, g, inSource): + assert sorted(c.keys()) == ["directories", "name", "projects", "targets"] + assert is_string(c["name"]) + check_directories(c, g) + check_targets(c, g, inSource) + check_projects(c, g) + +def check_object_codemodel(g): + def _check(o): + assert sorted(o.keys()) == ["configurations", "kind", "paths", "version"] + # The "kind" and "version" members are handled by check_index_object. + assert is_dict(o["paths"]) + assert sorted(o["paths"].keys()) == ["build", "source"] + assert matches(o["paths"]["build"], "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build$") + assert matches(o["paths"]["source"], "^.*/Tests/RunCMake/FileAPI$") + + inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"] + + if matches(g, "^(Visual Studio |Xcode$)"): + assert sorted([c["name"] for c in o["configurations"]]) == ["Debug", "MinSizeRel", "RelWithDebInfo", "Release"] + else: + assert len(o["configurations"]) == 1 + assert o["configurations"][0]["name"] in ("", "Debug", "Release", "RelWithDebInfo", "MinSizeRel") + + for c in o["configurations"]: + check_object_codemodel_configuration(c, g, inSource) + return _check + +assert is_dict(index) +assert sorted(index.keys()) == ["cmake", "objects", "reply"] +check_objects(index["objects"], index["cmake"]["generator"]["name"]) diff --git a/Tests/RunCMake/FileAPI/codemodel-v2.cmake b/Tests/RunCMake/FileAPI/codemodel-v2.cmake new file mode 100644 index 0000000..72073d5 --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2.cmake @@ -0,0 +1,35 @@ +enable_language(C) + +include("${CMAKE_CURRENT_LIST_DIR}/include_test.cmake") + +add_library(c_lib empty.c) +add_executable(c_exe empty.c) +target_link_libraries(c_exe PRIVATE c_lib) + +add_library(c_shared_lib SHARED empty.c) +add_executable(c_shared_exe empty.c) +target_link_libraries(c_shared_exe PRIVATE c_shared_lib) + +add_library(c_static_lib STATIC empty.c) +add_executable(c_static_exe empty.c) +target_link_libraries(c_static_exe PRIVATE c_static_lib) + +add_subdirectory(cxx) +add_subdirectory(alias) +add_subdirectory(object) +add_subdirectory(imported) +add_subdirectory(custom) +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../FileAPIExternalSource" "${CMAKE_CURRENT_BINARY_DIR}/../FileAPIExternalBuild") +add_subdirectory(dir) + +set_property(TARGET c_shared_lib PROPERTY LIBRARY_OUTPUT_DIRECTORY lib) +set_property(TARGET c_shared_lib PROPERTY RUNTIME_OUTPUT_DIRECTORY lib) + +include(CheckIPOSupported) +check_ipo_supported(RESULT _ipo LANGUAGES C) +if(_ipo) + set_property(TARGET c_shared_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON) + set_property(TARGET c_shared_exe PROPERTY INTERPROCEDURAL_OPTIMIZATION ON) + set_property(TARGET c_static_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON) + file(WRITE "${CMAKE_BINARY_DIR}/ipo_enabled.txt" "") +endif() diff --git a/Tests/RunCMake/FileAPI/custom/CMakeLists.txt b/Tests/RunCMake/FileAPI/custom/CMakeLists.txt new file mode 100644 index 0000000..1cdf5c2 --- /dev/null +++ b/Tests/RunCMake/FileAPI/custom/CMakeLists.txt @@ -0,0 +1,5 @@ +project(Custom) + +add_custom_target(custom_tgt COMMAND ${CMAKE_COMMAND} -E echo "Building custom_tgt") +add_executable(custom_exe ../empty.c) +add_dependencies(custom_exe custom_tgt) diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt new file mode 100644 index 0000000..29b61b8 --- /dev/null +++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt @@ -0,0 +1,15 @@ +project(Cxx) +enable_language(CXX) + +add_library(cxx_lib ../empty.cxx) +add_executable(cxx_exe ../empty.cxx) +target_link_libraries(cxx_exe PRIVATE cxx_lib) +set_property(TARGET cxx_exe PROPERTY FOLDER bin) + +add_library(cxx_shared_lib SHARED ../empty.cxx) +add_executable(cxx_shared_exe ../empty.cxx) +target_link_libraries(cxx_shared_exe PRIVATE cxx_shared_lib) + +add_library(cxx_static_lib STATIC ../empty.cxx) +add_executable(cxx_static_exe ../empty.cxx) +target_link_libraries(cxx_static_exe PRIVATE cxx_static_lib) diff --git a/Tests/RunCMake/FileAPI/dir/CMakeLists.txt b/Tests/RunCMake/FileAPI/dir/CMakeLists.txt new file mode 100644 index 0000000..780445d --- /dev/null +++ b/Tests/RunCMake/FileAPI/dir/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(dir) diff --git a/Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt b/Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt diff --git a/Tests/RunCMake/FileAPI/dir/dirtest.cmake b/Tests/RunCMake/FileAPI/dir/dirtest.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/dir/dirtest.cmake diff --git a/Tests/RunCMake/FileAPI/empty.c b/Tests/RunCMake/FileAPI/empty.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/empty.c diff --git a/Tests/RunCMake/FileAPI/empty.cxx b/Tests/RunCMake/FileAPI/empty.cxx new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPI/empty.cxx diff --git a/Tests/RunCMake/FileAPI/imported/CMakeLists.txt b/Tests/RunCMake/FileAPI/imported/CMakeLists.txt new file mode 100644 index 0000000..d36d88b --- /dev/null +++ b/Tests/RunCMake/FileAPI/imported/CMakeLists.txt @@ -0,0 +1,24 @@ +project(Imported) + +add_library(imported_lib UNKNOWN IMPORTED) +add_executable(imported_exe IMPORTED) +add_executable(link_imported_exe ../empty.c) +target_link_libraries(link_imported_exe PRIVATE imported_lib) + +add_library(imported_shared_lib SHARED IMPORTED) +add_executable(link_imported_shared_exe ../empty.c) +target_link_libraries(link_imported_shared_exe PRIVATE imported_shared_lib) + +add_library(imported_static_lib STATIC IMPORTED) +add_executable(link_imported_static_exe ../empty.c) +target_link_libraries(link_imported_static_exe PRIVATE imported_static_lib) + +if(NOT CMAKE_GENERATOR STREQUAL "Xcode" OR NOT CMAKE_OSX_ARCHITECTURES MATCHES "[;$]") + add_library(imported_object_lib OBJECT IMPORTED) + add_executable(link_imported_object_exe ../empty.c) + target_link_libraries(link_imported_object_exe PRIVATE imported_object_lib) +endif() + +add_library(imported_interface_lib INTERFACE IMPORTED) +add_executable(link_imported_interface_exe ../empty.c) +target_link_libraries(link_imported_interface_exe PRIVATE imported_interface_lib) diff --git a/Tests/RunCMake/FileAPI/include_test.cmake b/Tests/RunCMake/FileAPI/include_test.cmake new file mode 100644 index 0000000..c74d264 --- /dev/null +++ b/Tests/RunCMake/FileAPI/include_test.cmake @@ -0,0 +1,9 @@ +add_library(interface_lib INTERFACE) +target_compile_definitions(interface_lib INTERFACE COMPILED_WITH_INTERFACE_LIB) +add_executable(interface_exe empty.c) +target_link_libraries(interface_exe PRIVATE inteface_lib) +set_property(TARGET interface_exe PROPERTY ENABLE_EXPORTS ON) +set_property(TARGET interface_exe PROPERTY RUNTIME_OUTPUT_DIRECTORY bin) +set_property(TARGET interface_exe PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib) +set_property(TARGET interface_exe PROPERTY OUTPUT_NAME my_interface_exe) +set_property(TARGET interface_exe PROPERTY SUFFIX .myexe) diff --git a/Tests/RunCMake/FileAPI/object/CMakeLists.txt b/Tests/RunCMake/FileAPI/object/CMakeLists.txt new file mode 100644 index 0000000..9773b81 --- /dev/null +++ b/Tests/RunCMake/FileAPI/object/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.13) +project(Object) +enable_language(CXX) + +add_library(c_object_lib OBJECT ../empty.c) +add_executable(c_object_exe ../empty.c) +target_link_libraries(c_object_exe PRIVATE c_object_lib) + +add_library(cxx_object_lib OBJECT ../empty.cxx) +add_executable(cxx_object_exe ../empty.cxx) +target_link_libraries(cxx_object_exe PRIVATE cxx_object_lib) + +install(TARGETS c_object_exe cxx_object_exe DESTINATION bin) diff --git a/Tests/RunCMake/FileAPIDummyFile.cmake b/Tests/RunCMake/FileAPIDummyFile.cmake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPIDummyFile.cmake diff --git a/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt new file mode 100644 index 0000000..f5670a7 --- /dev/null +++ b/Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt @@ -0,0 +1,12 @@ +project(External) +enable_language(CXX) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx" "") +add_executable(generated_exe empty.c) +target_sources(generated_exe PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx") +source_group("Generated Source Files" FILES "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx") +set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx" PROPERTY GENERATED ON) +set_property(SOURCE empty.c PROPERTY COMPILE_DEFINITIONS EMPTY_C=1 SRC_DUMMY) +set_property(SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}") +target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY) diff --git a/Tests/RunCMake/FileAPIExternalSource/empty.c b/Tests/RunCMake/FileAPIExternalSource/empty.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FileAPIExternalSource/empty.c diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake index c076ad9..4bacd96 100644 --- a/Tests/RunCMake/RunCMake.cmake +++ b/Tests/RunCMake/RunCMake.cmake @@ -45,6 +45,11 @@ function(run_cmake test) file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") endif() file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") + if(RunCMake-prep-file AND EXISTS ${top_src}/${RunCMake-prep-file}) + include(${top_src}/${RunCMake-prep-file}) + else() + include(${top_src}/${test}-prep.cmake OPTIONAL) + endif() if(NOT DEFINED RunCMake_TEST_OPTIONS) set(RunCMake_TEST_OPTIONS "") endif() diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp index a68d767..c5231bb 100644 --- a/Utilities/IWYU/mapping.imp +++ b/Utilities/IWYU/mapping.imp @@ -123,6 +123,8 @@ { symbol: [ "SIGINT", private, "\"cm_uv.h\"", public ] }, { symbol: [ "ssize_t", private, "\"cm_uv.h\"", public ] }, + { symbol: [ "Json::ArrayIndex", private, "\"cm_jsoncpp_value.h\"", public ] }, + { symbol: [ "std::ifstream", private, "\"cmsys/FStream.hxx\"", public ] }, { symbol: [ "std::ofstream", private, "\"cmsys/FStream.hxx\"", public ] }, { symbol: [ "cmsys::ifstream", private, "\"cmsys/FStream.hxx\"", public ] }, diff --git a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp index 0249cc9..6eeba0e 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp +++ b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp @@ -1581,7 +1581,7 @@ bool OurReader::decodeNumber(Token& token, Value& decoded) { ++current; // TODO: Help the compiler do the div and mod at compile time or get rid of them. Value::LargestUInt maxIntegerValue = - isNegative ? Value::LargestUInt(-Value::minLargestInt) + isNegative ? Value::LargestUInt(Value::minLargestInt) : Value::maxLargestUInt; Value::LargestUInt threshold = maxIntegerValue / 10; Value::LargestUInt value = 0; @@ -430,6 +430,7 @@ CMAKE_CXX_SOURCES="\ cmake \ cmakemain \ cmcmd \ + cm_string_view \ " if ${cmake_system_mingw}; then |