:twisted_rightwards_arrows: merge branch 'release/2.1.1'
diff --git a/.doozer.json b/.doozer.json
deleted file mode 100644
index 7dbaa5d..0000000
--- a/.doozer.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "targets": {
- "xenial-i386": {
- "buildenv": "xenial-i386",
- "builddeps": ["build-essential", "cmake"],
- "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"]
- },
- "xenial-amd64": {
- "buildenv": "xenial-amd64",
- "builddeps": ["build-essential", "cmake"],
- "buildcmd": ["mkdir cm", "cd cm", "cmake ..", "cmake --build .", "ctest --output-on-failure"]
- },
- "fedora24-x86_64": {
- "buildenv": "fedora24-x86_64",
- "builddeps": ["cmake", "make", "clang"],
- "buildcmd": ["mkdir cm", "cd cm", "CXX=clang++ cmake ..", "cmake --build .", "ctest --output-on-failure"]
- },
- "osx": {
- "buildenv": "osx",
- "builddeps": ["build-essential"],
- "buildcmd": ["make check"]
- }
- }
-}
diff --git a/.gitignore b/.gitignore
index 6e15abc..69a81cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
*.gcno
*.gcda
+build
working
doc/xml
diff --git a/.travis.yml b/.travis.yml
index ef5c614..c3cba69 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -228,6 +228,14 @@
# make sure CXX is correctly set
- if [[ "${COMPILER}" != "" ]]; then export CXX=${COMPILER}; fi
+ # get CMake (only for systems with brew - macOS)
+ - |
+ if [[ !(-x $(which cmake)) && (-x $(which brew)) ]]; then
+ brew update
+ brew install cmake
+ cmake --version
+ fi
+
# install LLVM/clang when LLVM_VERSION is set
- |
if [[ "${LLVM_VERSION}" != "" ]]; then
@@ -266,7 +274,10 @@
- $CXX --version
# compile and execute unit tests
- - make check
+ - mkdir -p build && cd build
+ - cmake .. && cmake --build . --config Release -- -j4
+ - ctest -C Release -V
+ - cd ..
# check if homebrew works (only checks develop branch)
- if [ `which brew` ]; then
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f1ccf47..30e3966 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.0)
# define the project
-project(nlohmann_json VERSION 2.1.0 LANGUAGES CXX)
+project(nlohmann_json VERSION 2.1.1 LANGUAGES CXX)
enable_testing()
@@ -16,6 +16,8 @@
set(JSON_CONFIG_DESTINATION "cmake")
set(JSON_INCLUDE_DESTINATION "include/nlohmann")
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
+
# create and configure the library target
add_library(${JSON_TARGET_NAME} INTERFACE)
target_include_directories(${JSON_TARGET_NAME} INTERFACE
@@ -23,7 +25,7 @@
$<INSTALL_INTERFACE:${JSON_INCLUDE_DESTINATION}>)
# create and configure the unit test target
-if (BuildTests)
+if(BuildTests)
add_subdirectory(test)
endif()
@@ -39,17 +41,17 @@
# export the library target and store build directory in package registry
export(TARGETS ${JSON_TARGET_NAME}
- FILE "${CMAKE_CURRENT_BINARY_DIR}/${JSON_TARGETS_FILENAME}")
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/${JSON_TARGETS_FILENAME}")
export(PACKAGE ${JSON_PACKAGE_NAME})
# install library target and config files
install(TARGETS ${JSON_TARGET_NAME}
- EXPORT ${JSON_PACKAGE_NAME})
+ EXPORT ${JSON_PACKAGE_NAME})
install(FILES "src/json.hpp"
- DESTINATION ${JSON_INCLUDE_DESTINATION})
+ DESTINATION ${JSON_INCLUDE_DESTINATION})
install(EXPORT ${JSON_PACKAGE_NAME}
- FILE ${JSON_TARGETS_FILENAME}
- DESTINATION ${JSON_CONFIG_DESTINATION})
+ FILE ${JSON_TARGETS_FILENAME}
+ DESTINATION ${JSON_CONFIG_DESTINATION})
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIG_FILENAME}"
- "${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIGVERSION_FILENAME}"
- DESTINATION ${JSON_CONFIG_DESTINATION})
+ "${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIGVERSION_FILENAME}"
+ DESTINATION ${JSON_CONFIG_DESTINATION})
diff --git a/ChangeLog.md b/ChangeLog.md
index 0e2f8da..76244c5 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,40 @@
# Change Log
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
+## [v2.1.1](https://github.com/nlohmann/json/releases/tag/v2.1.1) (2017-02-25)
+[Full Changelog](https://github.com/nlohmann/json/compare/v2.1.0...v2.1.1)
+
+- warning in the library [\#472](https://github.com/nlohmann/json/issues/472)
+- How to create an array of Objects? [\#470](https://github.com/nlohmann/json/issues/470)
+- \[Bug?\] Cannot get int pointer, but int64\_t works [\#468](https://github.com/nlohmann/json/issues/468)
+- Illegal indirection [\#467](https://github.com/nlohmann/json/issues/467)
+- in vs can't find linkageId [\#466](https://github.com/nlohmann/json/issues/466)
+- Roundtrip error while parsing "1000000000000000010E5" [\#465](https://github.com/nlohmann/json/issues/465)
+- C4996 error and warning with Visual Studio [\#463](https://github.com/nlohmann/json/issues/463)
+- Support startIndex for from\_cbor/from\_msgpack [\#462](https://github.com/nlohmann/json/issues/462)
+- question: monospace font used in feature slideshow? [\#460](https://github.com/nlohmann/json/issues/460)
+- Object.keys\(\) [\#459](https://github.com/nlohmann/json/issues/459)
+- Use “, “ as delimiter for json-objects. [\#457](https://github.com/nlohmann/json/issues/457)
+- Enum -\> string during serialization and vice versa [\#455](https://github.com/nlohmann/json/issues/455)
+- doubles are printed as integers [\#454](https://github.com/nlohmann/json/issues/454)
+- Warnings with Visual Studio c++ \(VS2015 Update 3\) [\#453](https://github.com/nlohmann/json/issues/453)
+- Heap-buffer-overflow \(OSS-Fuzz issue 585\) [\#452](https://github.com/nlohmann/json/issues/452)
+- use of undeclared identifier 'UINT8\_MAX' [\#451](https://github.com/nlohmann/json/issues/451)
+- Question on the lifetime managment of objects at the lower levels [\#449](https://github.com/nlohmann/json/issues/449)
+- Json should not be constructible with 'json\*' [\#448](https://github.com/nlohmann/json/issues/448)
+- Move value\_t to namespace scope [\#447](https://github.com/nlohmann/json/issues/447)
+- Typo in README.md [\#446](https://github.com/nlohmann/json/issues/446)
+- make check compilation is unneccesarily slow [\#445](https://github.com/nlohmann/json/issues/445)
+- Problem in dump\(\) in json.h caused by ss.imbue [\#444](https://github.com/nlohmann/json/issues/444)
+- I want to create Windows Application in Visual Studio 2015 c++, and i have a problem [\#443](https://github.com/nlohmann/json/issues/443)
+- Implicit conversion issues [\#442](https://github.com/nlohmann/json/issues/442)
+- Parsing of floats locale dependent [\#302](https://github.com/nlohmann/json/issues/302)
+
+- Speedup CI builds using cotire [\#461](https://github.com/nlohmann/json/pull/461) ([tusharpm](https://github.com/tusharpm))
+- TurpentineDistillery feature/locale independent str to num [\#450](https://github.com/nlohmann/json/pull/450) ([nlohmann](https://github.com/nlohmann))
+- README: adjust boost::optional example [\#439](https://github.com/nlohmann/json/pull/439) ([jaredgrubb](https://github.com/jaredgrubb))
+- fix \#414 - comparing to 0 literal [\#415](https://github.com/nlohmann/json/pull/415) ([stanmihai4](https://github.com/stanmihai4))
+
## [v2.1.0](https://github.com/nlohmann/json/releases/tag/v2.1.0) (2017-01-28)
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.10...v2.1.0)
@@ -20,6 +54,7 @@
- size parameter for parse\(\) [\#419](https://github.com/nlohmann/json/issues/419)
- json.hpp forcibly defines GCC\_VERSION [\#417](https://github.com/nlohmann/json/issues/417)
- Use-of-uninitialized-value \(OSS-Fuzz issue 377\) [\#416](https://github.com/nlohmann/json/issues/416)
+- comparing to 0 literal [\#414](https://github.com/nlohmann/json/issues/414)
- Single char converted to ASCII code instead of string [\#413](https://github.com/nlohmann/json/issues/413)
- How to know if a string was parsed as utf-8? [\#406](https://github.com/nlohmann/json/issues/406)
- Overloaded += to add objects to an array makes no sense? [\#404](https://github.com/nlohmann/json/issues/404)
@@ -37,6 +72,7 @@
- conversion from/to user-defined types [\#435](https://github.com/nlohmann/json/pull/435) ([nlohmann](https://github.com/nlohmann))
- Fix documentation error [\#430](https://github.com/nlohmann/json/pull/430) ([vjon](https://github.com/vjon))
+- locale-independent num-to-str [\#378](https://github.com/nlohmann/json/pull/378) ([TurpentineDistillery](https://github.com/TurpentineDistillery))
## [v2.0.10](https://github.com/nlohmann/json/releases/tag/v2.0.10) (2017-01-02)
[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.9...v2.0.10)
diff --git a/Makefile b/Makefile
index 56cbdb0..ac75bca 100644
--- a/Makefile
+++ b/Makefile
@@ -42,6 +42,28 @@
##########################################################################
+# warning detector
+##########################################################################
+
+# calling Clang with all warnings, except:
+# -Wno-documentation-unknown-command: code uses user-defined commands like @complexity
+# -Wno-exit-time-destructors: warning in Catch code
+# -Wno-keyword-macro: unit-tests use "#define private public"
+# -Wno-deprecated-declarations: some functions are deprecated until 3.0.0
+# -Wno-range-loop-analysis: iterator_wrapper tests tests "for(const auto i...)"
+pedantic:
+ $(MAKE) json_unit CXXFLAGS="\
+ -std=c++11 \
+ -Werror \
+ -Weverything \
+ -Wno-documentation-unknown-command \
+ -Wno-exit-time-destructors \
+ -Wno-keyword-macro \
+ -Wno-deprecated-declarations \
+ -Wno-range-loop-analysis"
+
+
+##########################################################################
# fuzzing
##########################################################################
@@ -94,7 +116,7 @@
# run clang sanitize (we are overrding the CXXFLAGS provided by travis in order to use gcc's libstdc++)
clang_sanitize: clean
- CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE)
+ CXX=clang++ CXXFLAGS="-g -O2 -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" $(MAKE) check
##########################################################################
diff --git a/README.md b/README.md
index 1ee358e..1334180 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
[![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
-[![Build status](https://doozer.io/badge/nlohmann/json/buildstatus/develop)](https://doozer.io/user/nlohmann/json)
[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
@@ -26,6 +26,8 @@
- [Supported compilers](#supported-compilers)
- [License](#license)
- [Thanks](#thanks)
+- [Used third-party tools](#used-third-party-tools)
+- [Projects using JSON for Modern C++](#projects-using-json-for-modern-c)
- [Notes](#notes)
- [Execute unit tests](#execute-unit-tests)
@@ -167,8 +169,14 @@
"pi": 3.141
}
)"_json;
+```
-// or explicitly
+Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object.
+
+The above example can also be expressed explicitly using `json::parse()`:
+
+```cpp
+// parse explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
```
@@ -563,7 +571,9 @@
}
static void from_json(const json& j, boost::optional<T>& opt) {
- if (!j.is_null()) {
+ if (j.is_null()) {
+ opt = boost::none;
+ } else {
opt = j.get<T>(); // same as above, but with
// adl_serializer<T>::from_json
}
@@ -800,7 +810,7 @@
- [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable.
- [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
- [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
-- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser and improved the benchmarking code.
+- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
- [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan.
- [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning.
- [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
@@ -811,10 +821,49 @@
- [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio.
- [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types.
- [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
+- [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
+- [ukhegg](https://github.com/ukhegg) found proposed an improvement for the examples section.
+- [rswanson-ihi](https://github.com/rswanson-ihi) noted a type in the README.
+- [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
+- [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
+## Used third-party tools
+
+The library itself contains of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of thirs-party tools and services. Thanks a lot!
+
+- [**American fuzzy lop**](http://lcamtuf.coredump.cx/afl/) for fuzz testing
+- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
+- [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code identation
+- [**benchpress**](https://github.com/sbs-ableton/benchpress) to benchmark the code
+- [**Catch**](https://github.com/philsquared/Catch) for the unit tests
+- [**Clang**](http://clang.llvm.org) for compilation with code sanitizers
+- [**Cmake**](https://cmake.org) for build automation
+- [**Codacity**](https://www.codacy.com) for further [code analysis](https://www.codacy.com/app/nlohmann/json)
+- [**cotire**](https://github.com/sakra/cotire) to speed of compilation
+- [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
+- [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
+- [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
+- [**cxxopts**](https://github.com/jarro2783/cxxopts) to let benchpress parse command-line parameters
+- [**Doxygen**](http://www.stack.nl/~dimitri/doxygen/) to generate [documentation](https://nlohmann.github.io/json/)
+- [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages
+- [**Github Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
+- [**libFuzzer**](http://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
+- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library
+- [**re2c**](http://re2c.org) to generate an automaton for the lexical analysis
+- [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox)
+- [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
+- [**Valgrind**](http://valgrind.org) to check for correct memory management
+- [**Wandbox**](http://melpon.org/wandbox) for [online examples](http://melpon.org/wandbox/permlink/4NEU6ZZMoM9lpIex)
+
+
+## Projects using JSON for Modern C++
+
+The library is currently used in Apple macOS Sierra and iOS 10. I am not sure what they are using the library for, but I am happy that it runs on so many devices.
+
+
## Notes
- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726).
@@ -838,7 +887,7 @@
$ ./test/json_unit "*""
===============================================================================
-All tests passed (11202052 assertions in 47 test cases)
+All tests passed (11202597 assertions in 47 test cases)
```
Alternatively, you can use [CMake](https://cmake.org) and run
diff --git a/appveyor.yml b/appveyor.yml
index a527ef7..3960f4d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,10 +1,10 @@
-version: '{build}'
-os: Visual Studio 2015
-init: []
-install: []
-build_script:
-- set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
-- cmake . -G "Visual Studio 14 2015"
-- cmake --build . --config Release
-test_script:
-- ctest -C Release -V
+version: '{build}'
+os: Visual Studio 2015
+init: []
+install: []
+build_script:
+- set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
+- cmake . -G "Visual Studio 14 2015"
+- cmake --build . --config Release
+test_script:
+- ctest -C Release -V
diff --git a/cmake/cotire.cmake b/cmake/cotire.cmake
new file mode 100644
index 0000000..ab61100
--- /dev/null
+++ b/cmake/cotire.cmake
@@ -0,0 +1,4008 @@
+# - cotire (compile time reducer)
+#
+# See the cotire manual for usage hints.
+#
+#=============================================================================
+# Copyright 2012-2016 Sascha Kratky
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#=============================================================================
+
+if(__COTIRE_INCLUDED)
+ return()
+endif()
+set(__COTIRE_INCLUDED TRUE)
+
+# call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode
+# cmake_minimum_required also sets the policy version as a side effect, which we have to avoid
+if (NOT CMAKE_SCRIPT_MODE_FILE)
+ cmake_policy(PUSH)
+endif()
+cmake_minimum_required(VERSION 2.8.12)
+if (NOT CMAKE_SCRIPT_MODE_FILE)
+ cmake_policy(POP)
+endif()
+
+set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}")
+set (COTIRE_CMAKE_MODULE_VERSION "1.7.9")
+
+# activate select policies
+if (POLICY CMP0025)
+ # Compiler id for Apple Clang is now AppleClang
+ cmake_policy(SET CMP0025 NEW)
+endif()
+
+if (POLICY CMP0026)
+ # disallow use of the LOCATION target property
+ cmake_policy(SET CMP0026 NEW)
+endif()
+
+if (POLICY CMP0038)
+ # targets may not link directly to themselves
+ cmake_policy(SET CMP0038 NEW)
+endif()
+
+if (POLICY CMP0039)
+ # utility targets may not have link dependencies
+ cmake_policy(SET CMP0039 NEW)
+endif()
+
+if (POLICY CMP0040)
+ # target in the TARGET signature of add_custom_command() must exist
+ cmake_policy(SET CMP0040 NEW)
+endif()
+
+if (POLICY CMP0045)
+ # error on non-existent target in get_target_property
+ cmake_policy(SET CMP0045 NEW)
+endif()
+
+if (POLICY CMP0046)
+ # error on non-existent dependency in add_dependencies
+ cmake_policy(SET CMP0046 NEW)
+endif()
+
+if (POLICY CMP0049)
+ # do not expand variables in target source entries
+ cmake_policy(SET CMP0049 NEW)
+endif()
+
+if (POLICY CMP0050)
+ # disallow add_custom_command SOURCE signatures
+ cmake_policy(SET CMP0050 NEW)
+endif()
+
+if (POLICY CMP0051)
+ # include TARGET_OBJECTS expressions in a target's SOURCES property
+ cmake_policy(SET CMP0051 NEW)
+endif()
+
+if (POLICY CMP0053)
+ # simplify variable reference and escape sequence evaluation
+ cmake_policy(SET CMP0053 NEW)
+endif()
+
+if (POLICY CMP0054)
+ # only interpret if() arguments as variables or keywords when unquoted
+ cmake_policy(SET CMP0054 NEW)
+endif()
+
+include(CMakeParseArguments)
+include(ProcessorCount)
+
+function (cotire_get_configuration_types _configsVar)
+ set (_configs "")
+ if (CMAKE_CONFIGURATION_TYPES)
+ list (APPEND _configs ${CMAKE_CONFIGURATION_TYPES})
+ endif()
+ if (CMAKE_BUILD_TYPE)
+ list (APPEND _configs "${CMAKE_BUILD_TYPE}")
+ endif()
+ if (_configs)
+ list (REMOVE_DUPLICATES _configs)
+ set (${_configsVar} ${_configs} PARENT_SCOPE)
+ else()
+ set (${_configsVar} "None" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function (cotire_get_source_file_extension _sourceFile _extVar)
+ # get_filename_component returns extension from first occurrence of . in file name
+ # this function computes the extension from last occurrence of . in file name
+ string (FIND "${_sourceFile}" "." _index REVERSE)
+ if (_index GREATER -1)
+ math (EXPR _index "${_index} + 1")
+ string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt)
+ else()
+ set (_sourceExt "")
+ endif()
+ set (${_extVar} "${_sourceExt}" PARENT_SCOPE)
+endfunction()
+
+macro (cotire_check_is_path_relative_to _path _isRelativeVar)
+ set (${_isRelativeVar} FALSE)
+ if (IS_ABSOLUTE "${_path}")
+ foreach (_dir ${ARGN})
+ file (RELATIVE_PATH _relPath "${_dir}" "${_path}")
+ if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\."))
+ set (${_isRelativeVar} TRUE)
+ break()
+ endif()
+ endforeach()
+ endif()
+endmacro()
+
+function (cotire_filter_language_source_files _language _target _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar)
+ if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS)
+ set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}")
+ else()
+ set (_languageExtensions "")
+ endif()
+ if (CMAKE_${_language}_IGNORE_EXTENSIONS)
+ set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}")
+ else()
+ set (_ignoreExtensions "")
+ endif()
+ if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS)
+ set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}")
+ else()
+ set (_excludeExtensions "")
+ endif()
+ if (COTIRE_DEBUG AND _languageExtensions)
+ message (STATUS "${_language} source file extensions: ${_languageExtensions}")
+ endif()
+ if (COTIRE_DEBUG AND _ignoreExtensions)
+ message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}")
+ endif()
+ if (COTIRE_DEBUG AND _excludeExtensions)
+ message (STATUS "${_language} exclude extensions: ${_excludeExtensions}")
+ endif()
+ if (CMAKE_VERSION VERSION_LESS "3.1.0")
+ set (_allSourceFiles ${ARGN})
+ else()
+ # as of CMake 3.1 target sources may contain generator expressions
+ # since we cannot obtain required property information about source files added
+ # through generator expressions at configure time, we filter them out
+ string (GENEX_STRIP "${ARGN}" _allSourceFiles)
+ endif()
+ set (_filteredSourceFiles "")
+ set (_excludedSourceFiles "")
+ foreach (_sourceFile ${_allSourceFiles})
+ get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY)
+ get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT)
+ get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC)
+ if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic)
+ cotire_get_source_file_extension("${_sourceFile}" _sourceExt)
+ if (_sourceExt)
+ list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex)
+ if (_ignoreIndex LESS 0)
+ list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex)
+ if (_excludeIndex GREATER -1)
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ else()
+ list (FIND _languageExtensions "${_sourceExt}" _sourceIndex)
+ if (_sourceIndex GREATER -1)
+ # consider source file unless it is excluded explicitly
+ get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED)
+ if (_sourceIsExcluded)
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ else()
+ list (APPEND _filteredSourceFiles "${_sourceFile}")
+ endif()
+ else()
+ get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE)
+ if ("${_sourceLanguage}" STREQUAL "${_language}")
+ # add to excluded sources, if file is not ignored and has correct language without having the correct extension
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ endif()
+ endif()
+ endif()
+ endif()
+ endif()
+ endif()
+ endforeach()
+ # separate filtered source files from already cotired ones
+ # the COTIRE_TARGET property of a source file may be set while a target is being processed by cotire
+ set (_sourceFiles "")
+ set (_cotiredSourceFiles "")
+ foreach (_sourceFile ${_filteredSourceFiles})
+ get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET)
+ if (_sourceIsCotired)
+ list (APPEND _cotiredSourceFiles "${_sourceFile}")
+ else()
+ get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS)
+ if (_sourceCompileFlags)
+ # add to excluded sources, if file has custom compile flags
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ else()
+ list (APPEND _sourceFiles "${_sourceFile}")
+ endif()
+ endif()
+ endforeach()
+ if (COTIRE_DEBUG)
+ if (_sourceFiles)
+ message (STATUS "Filtered ${_target} ${_language} sources: ${_sourceFiles}")
+ endif()
+ if (_excludedSourceFiles)
+ message (STATUS "Excluded ${_target} ${_language} sources: ${_excludedSourceFiles}")
+ endif()
+ if (_cotiredSourceFiles)
+ message (STATUS "Cotired ${_target} ${_language} sources: ${_cotiredSourceFiles}")
+ endif()
+ endif()
+ set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE)
+ set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE)
+ set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type)
+ set (_filteredObjects "")
+ foreach (_object ${ARGN})
+ get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET)
+ if (_isSet)
+ get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
+ if (_propertyValue)
+ list (APPEND _filteredObjects "${_object}")
+ endif()
+ endif()
+ endforeach()
+ set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type)
+ set (_filteredObjects "")
+ foreach (_object ${ARGN})
+ get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET)
+ if (_isSet)
+ get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
+ if (NOT _propertyValue)
+ list (APPEND _filteredObjects "${_object}")
+ endif()
+ endif()
+ endforeach()
+ set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_file_property_values _valuesVar _property)
+ set (_values "")
+ foreach (_sourceFile ${ARGN})
+ get_source_file_property(_propertyValue "${_sourceFile}" ${_property})
+ if (_propertyValue)
+ list (APPEND _values "${_propertyValue}")
+ endif()
+ endforeach()
+ set (${_valuesVar} ${_values} PARENT_SCOPE)
+endfunction()
+
+function (cotire_resolve_config_properites _configurations _propertiesVar)
+ set (_properties "")
+ foreach (_property ${ARGN})
+ if ("${_property}" MATCHES "<CONFIG>")
+ foreach (_config ${_configurations})
+ string (TOUPPER "${_config}" _upperConfig)
+ string (REPLACE "<CONFIG>" "${_upperConfig}" _configProperty "${_property}")
+ list (APPEND _properties ${_configProperty})
+ endforeach()
+ else()
+ list (APPEND _properties ${_property})
+ endif()
+ endforeach()
+ set (${_propertiesVar} ${_properties} PARENT_SCOPE)
+endfunction()
+
+function (cotire_copy_set_properites _configurations _type _source _target)
+ cotire_resolve_config_properites("${_configurations}" _properties ${ARGN})
+ foreach (_property ${_properties})
+ get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET)
+ if (_isSet)
+ get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property})
+ set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}")
+ endif()
+ endforeach()
+endfunction()
+
+function (cotire_get_target_usage_requirements _target _targetRequirementsVar)
+ set (_targetRequirements "")
+ get_target_property(_librariesToProcess ${_target} LINK_LIBRARIES)
+ while (_librariesToProcess)
+ # remove from head
+ list (GET _librariesToProcess 0 _library)
+ list (REMOVE_AT _librariesToProcess 0)
+ if (TARGET ${_library})
+ list (FIND _targetRequirements ${_library} _index)
+ if (_index LESS 0)
+ list (APPEND _targetRequirements ${_library})
+ # BFS traversal of transitive libraries
+ get_target_property(_libraries ${_library} INTERFACE_LINK_LIBRARIES)
+ if (_libraries)
+ list (APPEND _librariesToProcess ${_libraries})
+ list (REMOVE_DUPLICATES _librariesToProcess)
+ endif()
+ endif()
+ endif()
+ endwhile()
+ set (${_targetRequirementsVar} ${_targetRequirements} PARENT_SCOPE)
+endfunction()
+
+function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar)
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ set (_flagPrefix "[/-]")
+ else()
+ set (_flagPrefix "--?")
+ endif()
+ set (_optionFlag "")
+ set (_matchedOptions "")
+ set (_unmatchedOptions "")
+ foreach (_compileFlag ${ARGN})
+ if (_compileFlag)
+ if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}")
+ # option with separate argument
+ list (APPEND _matchedOptions "${_compileFlag}")
+ set (_optionFlag "")
+ elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$")
+ # remember option
+ set (_optionFlag "${CMAKE_MATCH_2}")
+ elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$")
+ # option with joined argument
+ list (APPEND _matchedOptions "${CMAKE_MATCH_3}")
+ set (_optionFlag "")
+ else()
+ # flush remembered option
+ if (_optionFlag)
+ list (APPEND _matchedOptions "${_optionFlag}")
+ set (_optionFlag "")
+ endif()
+ # add to unfiltered options
+ list (APPEND _unmatchedOptions "${_compileFlag}")
+ endif()
+ endif()
+ endforeach()
+ if (_optionFlag)
+ list (APPEND _matchedOptions "${_optionFlag}")
+ endif()
+ if (COTIRE_DEBUG AND _matchedOptions)
+ message (STATUS "Filter ${_flagFilter} matched: ${_matchedOptions}")
+ endif()
+ if (COTIRE_DEBUG AND _unmatchedOptions)
+ message (STATUS "Filter ${_flagFilter} unmatched: ${_unmatchedOptions}")
+ endif()
+ set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE)
+ set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_is_target_supported _target _isSupportedVar)
+ if (NOT TARGET "${_target}")
+ set (${_isSupportedVar} FALSE PARENT_SCOPE)
+ return()
+ endif()
+ get_target_property(_imported ${_target} IMPORTED)
+ if (_imported)
+ set (${_isSupportedVar} FALSE PARENT_SCOPE)
+ return()
+ endif()
+ get_target_property(_targetType ${_target} TYPE)
+ if (NOT _targetType MATCHES "EXECUTABLE|(STATIC|SHARED|MODULE|OBJECT)_LIBRARY")
+ set (${_isSupportedVar} FALSE PARENT_SCOPE)
+ return()
+ endif()
+ set (${_isSupportedVar} TRUE PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_compile_flags _config _language _target _flagsVar)
+ string (TOUPPER "${_config}" _upperConfig)
+ # collect options from CMake language variables
+ set (_compileFlags "")
+ if (CMAKE_${_language}_FLAGS)
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}")
+ endif()
+ if (CMAKE_${_language}_FLAGS_${_upperConfig})
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}")
+ endif()
+ if (_target)
+ # add target compile flags
+ get_target_property(_targetflags ${_target} COMPILE_FLAGS)
+ if (_targetflags)
+ set (_compileFlags "${_compileFlags} ${_targetflags}")
+ endif()
+ endif()
+ if (UNIX)
+ separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}")
+ elseif(WIN32)
+ separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}")
+ else()
+ separate_arguments(_compileFlags)
+ endif()
+ # target compile options
+ if (_target)
+ get_target_property(_targetOptions ${_target} COMPILE_OPTIONS)
+ if (_targetOptions)
+ list (APPEND _compileFlags ${_targetOptions})
+ endif()
+ endif()
+ # interface compile options from linked library targets
+ if (_target)
+ set (_linkedTargets "")
+ cotire_get_target_usage_requirements(${_target} _linkedTargets)
+ foreach (_linkedTarget ${_linkedTargets})
+ get_target_property(_targetOptions ${_linkedTarget} INTERFACE_COMPILE_OPTIONS)
+ if (_targetOptions)
+ list (APPEND _compileFlags ${_targetOptions})
+ endif()
+ endforeach()
+ endif()
+ # handle language standard properties
+ if (CMAKE_${_language}_STANDARD_DEFAULT)
+ # used compiler supports language standard levels
+ if (_target)
+ get_target_property(_targetLanguageStandard ${_target} ${_language}_STANDARD)
+ if (_targetLanguageStandard)
+ set (_type "EXTENSION")
+ get_property(_isSet TARGET ${_target} PROPERTY ${_language}_EXTENSIONS SET)
+ if (_isSet)
+ get_target_property(_targetUseLanguageExtensions ${_target} ${_language}_EXTENSIONS)
+ if (NOT _targetUseLanguageExtensions)
+ set (_type "STANDARD")
+ endif()
+ endif()
+ if (CMAKE_${_language}${_targetLanguageStandard}_${_type}_COMPILE_OPTION)
+ list (APPEND _compileFlags "${CMAKE_${_language}${_targetLanguageStandard}_${_type}_COMPILE_OPTION}")
+ endif()
+ endif()
+ endif()
+ endif()
+ # handle the POSITION_INDEPENDENT_CODE target property
+ if (_target)
+ get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE)
+ if (_targetPIC)
+ get_target_property(_targetType ${_target} TYPE)
+ if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE)
+ list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_PIE}")
+ elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC)
+ list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_PIC}")
+ endif()
+ endif()
+ endif()
+ # handle visibility target properties
+ if (_target)
+ get_target_property(_targetVisibility ${_target} ${_language}_VISIBILITY_PRESET)
+ if (_targetVisibility AND CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY)
+ list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY}${_targetVisibility}")
+ endif()
+ get_target_property(_targetVisibilityInlines ${_target} VISIBILITY_INLINES_HIDDEN)
+ if (_targetVisibilityInlines AND CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN)
+ list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN}")
+ endif()
+ endif()
+ # platform specific flags
+ if (APPLE)
+ get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig})
+ if (NOT _architectures)
+ get_target_property(_architectures ${_target} OSX_ARCHITECTURES)
+ endif()
+ if (_architectures)
+ foreach (_arch ${_architectures})
+ list (APPEND _compileFlags "-arch" "${_arch}")
+ endforeach()
+ endif()
+ if (CMAKE_OSX_SYSROOT)
+ if (CMAKE_${_language}_SYSROOT_FLAG)
+ list (APPEND _compileFlags "${CMAKE_${_language}_SYSROOT_FLAG}" "${CMAKE_OSX_SYSROOT}")
+ else()
+ list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}")
+ endif()
+ endif()
+ if (CMAKE_OSX_DEPLOYMENT_TARGET)
+ if (CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG)
+ list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ else()
+ list (APPEND _compileFlags "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ endif()
+ endif()
+ endif()
+ if (COTIRE_DEBUG AND _compileFlags)
+ message (STATUS "Target ${_target} compile flags: ${_compileFlags}")
+ endif()
+ set (${_flagsVar} ${_compileFlags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_include_directories _config _language _target _includeDirsVar _systemIncludeDirsVar)
+ set (_includeDirs "")
+ set (_systemIncludeDirs "")
+ # default include dirs
+ if (CMAKE_INCLUDE_CURRENT_DIR)
+ list (APPEND _includeDirs "${CMAKE_CURRENT_BINARY_DIR}")
+ list (APPEND _includeDirs "${CMAKE_CURRENT_SOURCE_DIR}")
+ endif()
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags)
+ # parse additional include directories from target compile flags
+ if (CMAKE_INCLUDE_FLAG_${_language})
+ string (STRIP "${CMAKE_INCLUDE_FLAG_${_language}}" _includeFlag)
+ string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}")
+ if (_includeFlag)
+ set (_dirs "")
+ cotire_filter_compile_flags("${_language}" "${_includeFlag}" _dirs _ignore ${_targetFlags})
+ if (_dirs)
+ list (APPEND _includeDirs ${_dirs})
+ endif()
+ endif()
+ endif()
+ # parse additional system include directories from target compile flags
+ if (CMAKE_INCLUDE_SYSTEM_FLAG_${_language})
+ string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" _includeFlag)
+ string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}")
+ if (_includeFlag)
+ set (_dirs "")
+ cotire_filter_compile_flags("${_language}" "${_includeFlag}" _dirs _ignore ${_targetFlags})
+ if (_dirs)
+ list (APPEND _systemIncludeDirs ${_dirs})
+ endif()
+ endif()
+ endif()
+ # target include directories
+ get_directory_property(_dirs DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" INCLUDE_DIRECTORIES)
+ if (_target)
+ get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES)
+ if (_targetDirs)
+ list (APPEND _dirs ${_targetDirs})
+ endif()
+ get_target_property(_targetDirs ${_target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
+ if (_targetDirs)
+ list (APPEND _systemIncludeDirs ${_targetDirs})
+ endif()
+ endif()
+ # interface include directories from linked library targets
+ if (_target)
+ set (_linkedTargets "")
+ cotire_get_target_usage_requirements(${_target} _linkedTargets)
+ foreach (_linkedTarget ${_linkedTargets})
+ get_target_property(_linkedTargetType ${_linkedTarget} TYPE)
+ if (CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE AND NOT CMAKE_VERSION VERSION_LESS "3.4.0" AND
+ _linkedTargetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY")
+ # CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE refers to CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR
+ # at the time, when the target was created. These correspond to the target properties BINARY_DIR and SOURCE_DIR
+ # which are only available with CMake 3.4 or later.
+ get_target_property(_targetDirs ${_linkedTarget} BINARY_DIR)
+ if (_targetDirs)
+ list (APPEND _dirs ${_targetDirs})
+ endif()
+ get_target_property(_targetDirs ${_linkedTarget} SOURCE_DIR)
+ if (_targetDirs)
+ list (APPEND _dirs ${_targetDirs})
+ endif()
+ endif()
+ get_target_property(_targetDirs ${_linkedTarget} INTERFACE_INCLUDE_DIRECTORIES)
+ if (_targetDirs)
+ list (APPEND _dirs ${_targetDirs})
+ endif()
+ get_target_property(_targetDirs ${_linkedTarget} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
+ if (_targetDirs)
+ list (APPEND _systemIncludeDirs ${_targetDirs})
+ endif()
+ endforeach()
+ endif()
+ if (dirs)
+ list (REMOVE_DUPLICATES _dirs)
+ endif()
+ list (LENGTH _includeDirs _projectInsertIndex)
+ foreach (_dir ${_dirs})
+ if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE)
+ cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}")
+ if (_isRelative)
+ list (LENGTH _includeDirs _len)
+ if (_len EQUAL _projectInsertIndex)
+ list (APPEND _includeDirs "${_dir}")
+ else()
+ list (INSERT _includeDirs _projectInsertIndex "${_dir}")
+ endif()
+ math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1")
+ else()
+ list (APPEND _includeDirs "${_dir}")
+ endif()
+ else()
+ list (APPEND _includeDirs "${_dir}")
+ endif()
+ endforeach()
+ list (REMOVE_DUPLICATES _includeDirs)
+ list (REMOVE_DUPLICATES _systemIncludeDirs)
+ if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES)
+ list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES})
+ endif()
+ if (WIN32)
+ # convert Windows paths in include directories to CMake paths
+ if (_includeDirs)
+ set (_paths "")
+ foreach (_dir ${_includeDirs})
+ file (TO_CMAKE_PATH "${_dir}" _path)
+ list (APPEND _paths "${_path}")
+ endforeach()
+ set (_includeDirs ${_paths})
+ endif()
+ if (_systemIncludeDirs)
+ set (_paths "")
+ foreach (_dir ${_systemIncludeDirs})
+ file (TO_CMAKE_PATH "${_dir}" _path)
+ list (APPEND _paths "${_path}")
+ endforeach()
+ set (_systemIncludeDirs ${_paths})
+ endif()
+ endif()
+ if (COTIRE_DEBUG AND _includeDirs)
+ message (STATUS "Target ${_target} include dirs: ${_includeDirs}")
+ endif()
+ set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE)
+ if (COTIRE_DEBUG AND _systemIncludeDirs)
+ message (STATUS "Target ${_target} system include dirs: ${_systemIncludeDirs}")
+ endif()
+ set (${_systemIncludeDirsVar} ${_systemIncludeDirs} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_export_symbol _target _exportSymbolVar)
+ set (_exportSymbol "")
+ get_target_property(_targetType ${_target} TYPE)
+ get_target_property(_enableExports ${_target} ENABLE_EXPORTS)
+ if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR
+ (_targetType STREQUAL "EXECUTABLE" AND _enableExports))
+ get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL)
+ if (NOT _exportSymbol)
+ set (_exportSymbol "${_target}_EXPORTS")
+ endif()
+ string (MAKE_C_IDENTIFIER "${_exportSymbol}" _exportSymbol)
+ endif()
+ set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_compile_definitions _config _language _target _definitionsVar)
+ string (TOUPPER "${_config}" _upperConfig)
+ set (_configDefinitions "")
+ # CMAKE_INTDIR for multi-configuration build systems
+ if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".")
+ list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"")
+ endif()
+ # target export define symbol
+ cotire_get_target_export_symbol("${_target}" _defineSymbol)
+ if (_defineSymbol)
+ list (APPEND _configDefinitions "${_defineSymbol}")
+ endif()
+ # directory compile definitions
+ get_directory_property(_definitions DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ get_directory_property(_definitions DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMPILE_DEFINITIONS_${_upperConfig})
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ # target compile definitions
+ get_target_property(_definitions ${_target} COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig})
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ # interface compile definitions from linked library targets
+ set (_linkedTargets "")
+ cotire_get_target_usage_requirements(${_target} _linkedTargets)
+ foreach (_linkedTarget ${_linkedTargets})
+ get_target_property(_definitions ${_linkedTarget} INTERFACE_COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ endforeach()
+ # parse additional compile definitions from target compile flags
+ # and don't look at directory compile definitions, which we already handled
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags)
+ cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags})
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ list (REMOVE_DUPLICATES _configDefinitions)
+ if (COTIRE_DEBUG AND _configDefinitions)
+ message (STATUS "Target ${_target} compile definitions: ${_configDefinitions}")
+ endif()
+ set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_compiler_flags _config _language _target _compilerFlagsVar)
+ # parse target compile flags omitting compile definitions and include directives
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags)
+ set (_flagFilter "D")
+ if (CMAKE_INCLUDE_FLAG_${_language})
+ string (STRIP "${CMAKE_INCLUDE_FLAG_${_language}}" _includeFlag)
+ string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}")
+ if (_includeFlag)
+ set (_flagFilter "${_flagFilter}|${_includeFlag}")
+ endif()
+ endif()
+ if (CMAKE_INCLUDE_SYSTEM_FLAG_${_language})
+ string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" _includeFlag)
+ string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}")
+ if (_includeFlag)
+ set (_flagFilter "${_flagFilter}|${_includeFlag}")
+ endif()
+ endif()
+ set (_compilerFlags "")
+ cotire_filter_compile_flags("${_language}" "${_flagFilter}" _ignore _compilerFlags ${_targetFlags})
+ if (COTIRE_DEBUG AND _compilerFlags)
+ message (STATUS "Target ${_target} compiler flags: ${_compilerFlags}")
+ endif()
+ set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_sys_root_paths _pathsVar)
+ if (APPLE)
+ if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT)
+ foreach (_path IN LISTS ${_pathsVar})
+ if (IS_ABSOLUTE "${_path}")
+ get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE)
+ if (EXISTS "${_path}")
+ list (APPEND ${_pathsVar} "${_path}")
+ endif()
+ endif()
+ endforeach()
+ endif()
+ endif()
+ set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar)
+ set (_extraProperties ${ARGN})
+ set (_result "")
+ if (_extraProperties)
+ list (FIND _extraProperties "${_sourceFile}" _index)
+ if (_index GREATER -1)
+ math (EXPR _index "${_index} + 1")
+ list (LENGTH _extraProperties _len)
+ math (EXPR _len "${_len} - 1")
+ foreach (_index RANGE ${_index} ${_len})
+ list (GET _extraProperties ${_index} _value)
+ if (_value MATCHES "${_pattern}")
+ list (APPEND _result "${_value}")
+ else()
+ break()
+ endif()
+ endforeach()
+ endif()
+ endif()
+ set (${_resultVar} ${_result} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar)
+ set (_compileDefinitions "")
+ if (NOT CMAKE_SCRIPT_MODE_FILE)
+ string (TOUPPER "${_config}" _upperConfig)
+ get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _compileDefinitions ${_definitions})
+ endif()
+ get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig})
+ if (_definitions)
+ list (APPEND _compileDefinitions ${_definitions})
+ endif()
+ endif()
+ cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN})
+ if (_definitions)
+ list (APPEND _compileDefinitions ${_definitions})
+ endif()
+ if (COTIRE_DEBUG AND _compileDefinitions)
+ message (STATUS "Source ${_sourceFile} compile definitions: ${_compileDefinitions}")
+ endif()
+ set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_files_compile_definitions _config _language _definitionsVar)
+ set (_configDefinitions "")
+ foreach (_sourceFile ${ARGN})
+ cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions)
+ if (_sourceDefinitions)
+ list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-")
+ endif()
+ endforeach()
+ set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar)
+ set (_sourceUndefs "")
+ if (NOT CMAKE_SCRIPT_MODE_FILE)
+ get_source_file_property(_undefs "${_sourceFile}" ${_property})
+ if (_undefs)
+ list (APPEND _sourceUndefs ${_undefs})
+ endif()
+ endif()
+ cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN})
+ if (_undefs)
+ list (APPEND _sourceUndefs ${_undefs})
+ endif()
+ if (COTIRE_DEBUG AND _sourceUndefs)
+ message (STATUS "Source ${_sourceFile} ${_property} undefs: ${_sourceUndefs}")
+ endif()
+ set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_files_undefs _property _sourceUndefsVar)
+ set (_sourceUndefs "")
+ foreach (_sourceFile ${ARGN})
+ cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs)
+ if (_undefs)
+ list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-")
+ endif()
+ endforeach()
+ set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE)
+endfunction()
+
+macro (cotire_set_cmd_to_prologue _cmdVar)
+ set (${_cmdVar} "${CMAKE_COMMAND}")
+ if (COTIRE_DEBUG)
+ list (APPEND ${_cmdVar} "--warn-uninitialized")
+ endif()
+ list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$<CONFIGURATION>")
+ if (COTIRE_VERBOSE)
+ list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON")
+ elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles")
+ list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)")
+ endif()
+endmacro()
+
+function (cotire_init_compile_cmd _cmdVar _language _compilerLauncher _compilerExe _compilerArg1)
+ if (NOT _compilerLauncher)
+ set (_compilerLauncher ${CMAKE_${_language}_COMPILER_LAUNCHER})
+ endif()
+ if (NOT _compilerExe)
+ set (_compilerExe "${CMAKE_${_language}_COMPILER}")
+ endif()
+ if (NOT _compilerArg1)
+ set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1})
+ endif()
+ string (STRIP "${_compilerArg1}" _compilerArg1)
+ if ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
+ # compiler launcher is only supported for Makefile and Ninja
+ set (${_cmdVar} ${_compilerLauncher} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE)
+ else()
+ set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE)
+ endif()
+endfunction()
+
+macro (cotire_add_definitions_to_cmd _cmdVar _language)
+ foreach (_definition ${ARGN})
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ list (APPEND ${_cmdVar} "/D${_definition}")
+ else()
+ list (APPEND ${_cmdVar} "-D${_definition}")
+ endif()
+ endforeach()
+endmacro()
+
+function (cotire_add_includes_to_cmd _cmdVar _language _includesVar _systemIncludesVar)
+ set (_includeDirs ${${_includesVar}} ${${_systemIncludesVar}})
+ if (_includeDirs)
+ list (REMOVE_DUPLICATES _includeDirs)
+ foreach (_include ${_includeDirs})
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ file (TO_NATIVE_PATH "${_include}" _include)
+ list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_${_language}_SEP}${_include}")
+ else()
+ set (_index -1)
+ if ("${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" MATCHES ".+")
+ list (FIND ${_systemIncludesVar} "${_include}" _index)
+ endif()
+ if (_index GREATER -1)
+ list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}${_include}")
+ else()
+ list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_${_language}_SEP}${_include}")
+ endif()
+ endif()
+ endforeach()
+ endif()
+ set (${_cmdVar} ${${_cmdVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_frameworks_to_cmd _cmdVar _language _includesVar _systemIncludesVar)
+ if (APPLE)
+ set (_frameworkDirs "")
+ foreach (_include ${${_includesVar}})
+ if (IS_ABSOLUTE "${_include}" AND _include MATCHES "\\.framework$")
+ get_filename_component(_frameworkDir "${_include}" DIRECTORY)
+ list (APPEND _frameworkDirs "${_frameworkDir}")
+ endif()
+ endforeach()
+ set (_systemFrameworkDirs "")
+ foreach (_include ${${_systemIncludesVar}})
+ if (IS_ABSOLUTE "${_include}" AND _include MATCHES "\\.framework$")
+ get_filename_component(_frameworkDir "${_include}" DIRECTORY)
+ list (APPEND _systemFrameworkDirs "${_frameworkDir}")
+ endif()
+ endforeach()
+ if (_systemFrameworkDirs)
+ list (APPEND _frameworkDirs ${_systemFrameworkDirs})
+ endif()
+ if (_frameworkDirs)
+ list (REMOVE_DUPLICATES _frameworkDirs)
+ foreach (_frameworkDir ${_frameworkDirs})
+ set (_index -1)
+ if ("${CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG}" MATCHES ".+")
+ list (FIND _systemFrameworkDirs "${_frameworkDir}" _index)
+ endif()
+ if (_index GREATER -1)
+ list (APPEND ${_cmdVar} "${CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG}${_frameworkDir}")
+ else()
+ list (APPEND ${_cmdVar} "${CMAKE_${_language}_FRAMEWORK_SEARCH_FLAG}${_frameworkDir}")
+ endif()
+ endforeach()
+ endif()
+ endif()
+ set (${_cmdVar} ${${_cmdVar}} PARENT_SCOPE)
+endfunction()
+
+macro (cotire_add_compile_flags_to_cmd _cmdVar)
+ foreach (_flag ${ARGN})
+ list (APPEND ${_cmdVar} "${_flag}")
+ endforeach()
+endmacro()
+
+function (cotire_check_file_up_to_date _fileIsUpToDateVar _file)
+ if (EXISTS "${_file}")
+ set (_triggerFile "")
+ foreach (_dependencyFile ${ARGN})
+ if (EXISTS "${_dependencyFile}")
+ # IS_NEWER_THAN returns TRUE if both files have the same timestamp
+ # thus we do the comparison in both directions to exclude ties
+ if ("${_dependencyFile}" IS_NEWER_THAN "${_file}" AND
+ NOT "${_file}" IS_NEWER_THAN "${_dependencyFile}")
+ set (_triggerFile "${_dependencyFile}")
+ break()
+ endif()
+ endif()
+ endforeach()
+ if (_triggerFile)
+ if (COTIRE_VERBOSE)
+ get_filename_component(_fileName "${_file}" NAME)
+ message (STATUS "${_fileName} update triggered by ${_triggerFile} change.")
+ endif()
+ set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE)
+ else()
+ if (COTIRE_VERBOSE)
+ get_filename_component(_fileName "${_file}" NAME)
+ message (STATUS "${_fileName} is up-to-date.")
+ endif()
+ set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE)
+ endif()
+ else()
+ if (COTIRE_VERBOSE)
+ get_filename_component(_fileName "${_file}" NAME)
+ message (STATUS "${_fileName} does not exist yet.")
+ endif()
+ set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE)
+ endif()
+endfunction()
+
+macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar)
+ set (${_relPathVar} "")
+ foreach (_includeDir ${_includeDirs})
+ if (IS_DIRECTORY "${_includeDir}")
+ file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}")
+ if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")
+ string (LENGTH "${${_relPathVar}}" _closestLen)
+ string (LENGTH "${_relPath}" _relLen)
+ if (_closestLen EQUAL 0 OR _relLen LESS _closestLen)
+ set (${_relPathVar} "${_relPath}")
+ endif()
+ endif()
+ elseif ("${_includeDir}" STREQUAL "${_headerFile}")
+ # if path matches exactly, return short non-empty string
+ set (${_relPathVar} "1")
+ break()
+ endif()
+ endforeach()
+endmacro()
+
+macro (cotire_check_header_file_location _headerFile _insideIncludeDirs _outsideIncludeDirs _headerIsInside)
+ # check header path against ignored and honored include directories
+ cotire_find_closest_relative_path("${_headerFile}" "${_insideIncludeDirs}" _insideRelPath)
+ if (_insideRelPath)
+ # header is inside, but could be become outside if there is a shorter outside match
+ cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncludeDirs}" _outsideRelPath)
+ if (_outsideRelPath)
+ string (LENGTH "${_insideRelPath}" _insideRelPathLen)
+ string (LENGTH "${_outsideRelPath}" _outsideRelPathLen)
+ if (_outsideRelPathLen LESS _insideRelPathLen)
+ set (${_headerIsInside} FALSE)
+ else()
+ set (${_headerIsInside} TRUE)
+ endif()
+ else()
+ set (${_headerIsInside} TRUE)
+ endif()
+ else()
+ # header is outside
+ set (${_headerIsInside} FALSE)
+ endif()
+endmacro()
+
+macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar)
+ if (NOT EXISTS "${_headerFile}")
+ set (${_headerIsIgnoredVar} TRUE)
+ elseif (IS_DIRECTORY "${_headerFile}")
+ set (${_headerIsIgnoredVar} TRUE)
+ elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$")
+ # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path
+ # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation
+ # with the error message "error: no include path in which to search for header.h"
+ set (${_headerIsIgnoredVar} TRUE)
+ else()
+ set (${_headerIsIgnoredVar} FALSE)
+ endif()
+endmacro()
+
+macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar)
+ # check header file extension
+ cotire_get_source_file_extension("${_headerFile}" _headerFileExt)
+ set (${_headerIsIgnoredVar} FALSE)
+ if (_headerFileExt)
+ list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index)
+ if (_index GREATER -1)
+ set (${_headerIsIgnoredVar} TRUE)
+ endif()
+ endif()
+endmacro()
+
+macro (cotire_parse_line _line _headerFileVar _headerDepthVar)
+ if (MSVC)
+ # cl.exe /showIncludes output looks different depending on the language pack used, e.g.:
+ # English: "Note: including file: C:\directory\file"
+ # German: "Hinweis: Einlesen der Datei: C:\directory\file"
+ # We use a very general regular expression, relying on the presence of the : characters
+ if (_line MATCHES "( +)([a-zA-Z]:[^:]+)$")
+ # Visual Studio compiler output
+ string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar})
+ get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE)
+ else()
+ set (${_headerFileVar} "")
+ set (${_headerDepthVar} 0)
+ endif()
+ else()
+ if (_line MATCHES "^(\\.+) (.*)$")
+ # GCC like output
+ string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar})
+ if (IS_ABSOLUTE "${CMAKE_MATCH_2}")
+ set (${_headerFileVar} "${CMAKE_MATCH_2}")
+ else()
+ get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH)
+ endif()
+ else()
+ set (${_headerFileVar} "")
+ set (${_headerDepthVar} 0)
+ endif()
+ endif()
+endmacro()
+
+function (cotire_parse_includes _language _scanOutput _ignoredIncludeDirs _honoredIncludeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar)
+ if (WIN32)
+ # prevent CMake macro invocation errors due to backslash characters in Windows paths
+ string (REPLACE "\\" "/" _scanOutput "${_scanOutput}")
+ endif()
+ # canonize slashes
+ string (REPLACE "//" "/" _scanOutput "${_scanOutput}")
+ # prevent semicolon from being interpreted as a line separator
+ string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}")
+ # then separate lines
+ string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}")
+ list (LENGTH _scanOutput _len)
+ # remove duplicate lines to speed up parsing
+ list (REMOVE_DUPLICATES _scanOutput)
+ list (LENGTH _scanOutput _uniqueLen)
+ if (COTIRE_VERBOSE OR COTIRE_DEBUG)
+ message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes")
+ if (_ignoredExtensions)
+ message (STATUS "Ignored extensions: ${_ignoredExtensions}")
+ endif()
+ if (_ignoredIncludeDirs)
+ message (STATUS "Ignored paths: ${_ignoredIncludeDirs}")
+ endif()
+ if (_honoredIncludeDirs)
+ message (STATUS "Included paths: ${_honoredIncludeDirs}")
+ endif()
+ endif()
+ set (_sourceFiles ${ARGN})
+ set (_selectedIncludes "")
+ set (_unparsedLines "")
+ # stack keeps track of inside/outside project status of processed header files
+ set (_headerIsInsideStack "")
+ foreach (_line IN LISTS _scanOutput)
+ if (_line)
+ cotire_parse_line("${_line}" _headerFile _headerDepth)
+ if (_headerFile)
+ cotire_check_header_file_location("${_headerFile}" "${_ignoredIncludeDirs}" "${_honoredIncludeDirs}" _headerIsInside)
+ if (COTIRE_DEBUG)
+ message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}")
+ endif()
+ # update stack
+ list (LENGTH _headerIsInsideStack _stackLen)
+ if (_headerDepth GREATER _stackLen)
+ math (EXPR _stackLen "${_stackLen} + 1")
+ foreach (_index RANGE ${_stackLen} ${_headerDepth})
+ list (APPEND _headerIsInsideStack ${_headerIsInside})
+ endforeach()
+ else()
+ foreach (_index RANGE ${_headerDepth} ${_stackLen})
+ list (REMOVE_AT _headerIsInsideStack -1)
+ endforeach()
+ list (APPEND _headerIsInsideStack ${_headerIsInside})
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_headerIsInsideStack}")
+ endif()
+ # header is a candidate if it is outside project
+ if (NOT _headerIsInside)
+ # get parent header file's inside/outside status
+ if (_headerDepth GREATER 1)
+ math (EXPR _index "${_headerDepth} - 2")
+ list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside)
+ else()
+ set (_parentHeaderIsInside TRUE)
+ endif()
+ # select header file if parent header file is inside project
+ # (e.g., a project header file that includes a standard header file)
+ if (_parentHeaderIsInside)
+ cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored)
+ if (NOT _headerIsIgnored)
+ cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored)
+ if (NOT _headerIsIgnored)
+ list (APPEND _selectedIncludes "${_headerFile}")
+ else()
+ # fix header's inside status on stack, it is ignored by extension now
+ list (REMOVE_AT _headerIsInsideStack -1)
+ list (APPEND _headerIsInsideStack TRUE)
+ endif()
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}")
+ endif()
+ endif()
+ endif()
+ else()
+ if (MSVC)
+ # for cl.exe do not keep unparsed lines which solely consist of a source file name
+ string (FIND "${_sourceFiles}" "${_line}" _index)
+ if (_index LESS 0)
+ list (APPEND _unparsedLines "${_line}")
+ endif()
+ else()
+ list (APPEND _unparsedLines "${_line}")
+ endif()
+ endif()
+ endif()
+ endforeach()
+ list (REMOVE_DUPLICATES _selectedIncludes)
+ set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE)
+ set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE)
+endfunction()
+
+function (cotire_scan_includes _includesVar)
+ set(_options "")
+ set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_VERSION LANGUAGE UNPARSED_LINES SCAN_RESULT)
+ set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES
+ IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS INCLUDE_PRIORITY_PATH COMPILER_LAUNCHER)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
+ if (NOT _option_LANGUAGE)
+ set (_option_LANGUAGE "CXX")
+ endif()
+ if (NOT _option_COMPILER_ID)
+ set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
+ endif()
+ if (NOT _option_COMPILER_VERSION)
+ set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}")
+ endif()
+ cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_LAUNCHER}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}")
+ cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS})
+ cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS})
+ cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES)
+ cotire_add_frameworks_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES)
+ cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd)
+ # only consider existing source files for scanning
+ set (_existingSourceFiles "")
+ foreach (_sourceFile ${_sourceFiles})
+ if (EXISTS "${_sourceFile}")
+ list (APPEND _existingSourceFiles "${_sourceFile}")
+ endif()
+ endforeach()
+ if (NOT _existingSourceFiles)
+ set (${_includesVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ list (APPEND _cmd ${_existingSourceFiles})
+ if (COTIRE_VERBOSE)
+ message (STATUS "execute_process: ${_cmd}")
+ endif()
+ if (_option_COMPILER_ID MATCHES "MSVC")
+ # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
+ unset (ENV{VS_UNICODE_OUTPUT})
+ endif()
+ execute_process(
+ COMMAND ${_cmd}
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ RESULT_VARIABLE _result
+ OUTPUT_QUIET
+ ERROR_VARIABLE _output)
+ if (_result)
+ message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.")
+ endif()
+ cotire_parse_includes(
+ "${_option_LANGUAGE}" "${_output}"
+ "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}"
+ "${_option_IGNORE_EXTENSIONS}"
+ _includes _unparsedLines
+ ${_sourceFiles})
+ if (_option_INCLUDE_PRIORITY_PATH)
+ set (_sortedIncludes "")
+ foreach (_priorityPath ${_option_INCLUDE_PRIORITY_PATH})
+ foreach (_include ${_includes})
+ string (FIND ${_include} ${_priorityPath} _position)
+ if (_position GREATER -1)
+ list (APPEND _sortedIncludes ${_include})
+ endif()
+ endforeach()
+ endforeach()
+ if (_sortedIncludes)
+ list (INSERT _includes 0 ${_sortedIncludes})
+ list (REMOVE_DUPLICATES _includes)
+ endif()
+ endif()
+ set (${_includesVar} ${_includes} PARENT_SCOPE)
+ if (_option_UNPARSED_LINES)
+ set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE)
+ endif()
+ if (_option_SCAN_RESULT)
+ set (${_option_SCAN_RESULT} ${_result} PARENT_SCOPE)
+ endif()
+endfunction()
+
+macro (cotire_append_undefs _contentsVar)
+ set (_undefs ${ARGN})
+ if (_undefs)
+ list (REMOVE_DUPLICATES _undefs)
+ foreach (_definition ${_undefs})
+ list (APPEND ${_contentsVar} "#undef ${_definition}")
+ endforeach()
+ endif()
+endmacro()
+
+macro (cotire_comment_str _language _commentText _commentVar)
+ if ("${_language}" STREQUAL "CMAKE")
+ set (${_commentVar} "# ${_commentText}")
+ else()
+ set (${_commentVar} "/* ${_commentText} */")
+ endif()
+endmacro()
+
+function (cotire_write_file _language _file _contents _force)
+ get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME)
+ cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1)
+ cotire_comment_str("${_language}" "${_file}" _header2)
+ set (_contents "${_header1}\n${_header2}\n${_contents}")
+ if (COTIRE_DEBUG)
+ message (STATUS "${_contents}")
+ endif()
+ if (_force OR NOT EXISTS "${_file}")
+ file (WRITE "${_file}" "${_contents}")
+ else()
+ file (READ "${_file}" _oldContents)
+ if (NOT "${_oldContents}" STREQUAL "${_contents}")
+ file (WRITE "${_file}" "${_contents}")
+ else()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_file} unchanged")
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function (cotire_generate_unity_source _unityFile)
+ set(_options "")
+ set(_oneValueArgs LANGUAGE)
+ set(_multiValueArgs
+ DEPENDS SOURCES_COMPILE_DEFINITIONS
+ PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (_option_DEPENDS)
+ cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS})
+ if (_unityFileIsUpToDate)
+ return()
+ endif()
+ endif()
+ set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
+ if (NOT _option_PRE_UNDEFS)
+ set (_option_PRE_UNDEFS "")
+ endif()
+ if (NOT _option_SOURCES_PRE_UNDEFS)
+ set (_option_SOURCES_PRE_UNDEFS "")
+ endif()
+ if (NOT _option_POST_UNDEFS)
+ set (_option_POST_UNDEFS "")
+ endif()
+ if (NOT _option_SOURCES_POST_UNDEFS)
+ set (_option_SOURCES_POST_UNDEFS "")
+ endif()
+ set (_contents "")
+ if (_option_PROLOGUE)
+ list (APPEND _contents ${_option_PROLOGUE})
+ endif()
+ if (_option_LANGUAGE AND _sourceFiles)
+ if ("${_option_LANGUAGE}" STREQUAL "CXX")
+ list (APPEND _contents "#ifdef __cplusplus")
+ elseif ("${_option_LANGUAGE}" STREQUAL "C")
+ list (APPEND _contents "#ifndef __cplusplus")
+ endif()
+ endif()
+ set (_compileUndefinitions "")
+ foreach (_sourceFile ${_sourceFiles})
+ cotire_get_source_compile_definitions(
+ "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions
+ ${_option_SOURCES_COMPILE_DEFINITIONS})
+ cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS})
+ cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS})
+ if (_option_PRE_UNDEFS)
+ list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS})
+ endif()
+ if (_sourcePreUndefs)
+ list (APPEND _compileUndefinitions ${_sourcePreUndefs})
+ endif()
+ if (_compileUndefinitions)
+ cotire_append_undefs(_contents ${_compileUndefinitions})
+ set (_compileUndefinitions "")
+ endif()
+ if (_sourcePostUndefs)
+ list (APPEND _compileUndefinitions ${_sourcePostUndefs})
+ endif()
+ if (_option_POST_UNDEFS)
+ list (APPEND _compileUndefinitions ${_option_POST_UNDEFS})
+ endif()
+ foreach (_definition ${_compileDefinitions})
+ if (_definition MATCHES "^([a-zA-Z0-9_]+)=(.+)$")
+ list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}")
+ list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}")
+ else()
+ list (APPEND _contents "#define ${_definition}")
+ list (INSERT _compileUndefinitions 0 "${_definition}")
+ endif()
+ endforeach()
+ # use absolute path as source file location
+ get_filename_component(_sourceFileLocation "${_sourceFile}" ABSOLUTE)
+ if (WIN32)
+ file (TO_NATIVE_PATH "${_sourceFileLocation}" _sourceFileLocation)
+ endif()
+ list (APPEND _contents "#include \"${_sourceFileLocation}\"")
+ endforeach()
+ if (_compileUndefinitions)
+ cotire_append_undefs(_contents ${_compileUndefinitions})
+ set (_compileUndefinitions "")
+ endif()
+ if (_option_LANGUAGE AND _sourceFiles)
+ list (APPEND _contents "#endif")
+ endif()
+ if (_option_EPILOGUE)
+ list (APPEND _contents ${_option_EPILOGUE})
+ endif()
+ list (APPEND _contents "")
+ string (REPLACE ";" "\n" _contents "${_contents}")
+ if (COTIRE_VERBOSE)
+ message ("${_contents}")
+ endif()
+ cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE)
+endfunction()
+
+function (cotire_generate_prefix_header _prefixFile)
+ set(_options "")
+ set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_ID COMPILER_VERSION)
+ set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS
+ INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH
+ IGNORE_EXTENSIONS INCLUDE_PRIORITY_PATH COMPILER_LAUNCHER)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (NOT _option_COMPILER_ID)
+ set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
+ endif()
+ if (NOT _option_COMPILER_VERSION)
+ set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}")
+ endif()
+ if (_option_DEPENDS)
+ cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS})
+ if (_prefixFileIsUpToDate)
+ # create empty log file
+ set (_unparsedLinesFile "${_prefixFile}.log")
+ file (WRITE "${_unparsedLinesFile}" "")
+ return()
+ endif()
+ endif()
+ set (_prologue "")
+ set (_epilogue "")
+ if (_option_COMPILER_ID MATCHES "Clang")
+ set (_prologue "#pragma clang system_header")
+ elseif (_option_COMPILER_ID MATCHES "GNU")
+ set (_prologue "#pragma GCC system_header")
+ elseif (_option_COMPILER_ID MATCHES "MSVC")
+ set (_prologue "#pragma warning(push, 0)")
+ set (_epilogue "#pragma warning(pop)")
+ elseif (_option_COMPILER_ID MATCHES "Intel")
+ # Intel compiler requires hdrstop pragma to stop generating PCH file
+ set (_epilogue "#pragma hdrstop")
+ endif()
+ set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
+ cotire_scan_includes(_selectedHeaders ${_sourceFiles}
+ LANGUAGE "${_option_LANGUAGE}"
+ COMPILER_LAUNCHER "${_option_COMPILER_LAUNCHER}"
+ COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}"
+ COMPILER_ARG1 "${_option_COMPILER_ARG1}"
+ COMPILER_ID "${_option_COMPILER_ID}"
+ COMPILER_VERSION "${_option_COMPILER_VERSION}"
+ COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS}
+ COMPILE_FLAGS ${_option_COMPILE_FLAGS}
+ INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES}
+ SYSTEM_INCLUDE_DIRECTORIES ${_option_SYSTEM_INCLUDE_DIRECTORIES}
+ IGNORE_PATH ${_option_IGNORE_PATH}
+ INCLUDE_PATH ${_option_INCLUDE_PATH}
+ IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS}
+ INCLUDE_PRIORITY_PATH ${_option_INCLUDE_PRIORITY_PATH}
+ UNPARSED_LINES _unparsedLines
+ SCAN_RESULT _scanResult)
+ cotire_generate_unity_source("${_prefixFile}"
+ PROLOGUE ${_prologue} EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders})
+ set (_unparsedLinesFile "${_prefixFile}.log")
+ if (_unparsedLines)
+ if (COTIRE_VERBOSE OR _scanResult OR NOT _selectedHeaders)
+ list (LENGTH _unparsedLines _skippedLineCount)
+ message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFile}")
+ endif()
+ string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}")
+ endif()
+ file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}")
+endfunction()
+
+function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar)
+ set (_flags ${${_flagsVar}})
+ if (_compilerID MATCHES "MSVC")
+ # cl.exe options used
+ # /nologo suppresses display of sign-on banner
+ # /TC treat all files named on the command line as C source files
+ # /TP treat all files named on the command line as C++ source files
+ # /EP preprocess to stdout without #line directives
+ # /showIncludes list include files
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes)
+ else()
+ # return as a flag string
+ set (_flags "${_sourceFileType${_language}} /EP /showIncludes")
+ endif()
+ elseif (_compilerID MATCHES "GNU")
+ # GCC options used
+ # -H print the name of each header file used
+ # -E invoke preprocessor
+ # -fdirectives-only do not expand macros, requires GCC >= 4.3
+ if (_flags)
+ # append to list
+ list (APPEND _flags -H -E)
+ if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0")
+ list (APPEND _flags "-fdirectives-only")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "-H -E")
+ if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0")
+ set (_flags "${_flags} -fdirectives-only")
+ endif()
+ endif()
+ elseif (_compilerID MATCHES "Clang")
+ # Clang options used
+ # -H print the name of each header file used
+ # -E invoke preprocessor
+ # -fno-color-diagnostics don't prints diagnostics in color
+ if (_flags)
+ # append to list
+ list (APPEND _flags -H -E -fno-color-diagnostics)
+ else()
+ # return as a flag string
+ set (_flags "-H -E -fno-color-diagnostics")
+ endif()
+ elseif (_compilerID MATCHES "Intel")
+ if (WIN32)
+ # Windows Intel options used
+ # /nologo do not display compiler version information
+ # /QH display the include file order
+ # /EP preprocess to stdout, omitting #line directives
+ # /TC process all source or unrecognized file types as C source files
+ # /TP process all source or unrecognized file types as C++ source files
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH)
+ else()
+ # return as a flag string
+ set (_flags "${_sourceFileType${_language}} /EP /QH")
+ endif()
+ else()
+ # Linux / Mac OS X Intel options used
+ # -H print the name of each header file used
+ # -EP preprocess to stdout, omitting #line directives
+ # -Kc++ process all source or unrecognized file types as C++ source files
+ if (_flags)
+ # append to list
+ if ("${_language}" STREQUAL "CXX")
+ list (APPEND _flags -Kc++)
+ endif()
+ list (APPEND _flags -H -EP)
+ else()
+ # return as a flag string
+ if ("${_language}" STREQUAL "CXX")
+ set (_flags "-Kc++ ")
+ endif()
+ set (_flags "${_flags}-H -EP")
+ endif()
+ endif()
+ else()
+ message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
+ endif()
+ set (${_flagsVar} ${_flags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar)
+ set (_flags ${${_flagsVar}})
+ if (_compilerID MATCHES "MSVC")
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative)
+ # cl.exe options used
+ # /Yc creates a precompiled header file
+ # /Fp specifies precompiled header binary file name
+ # /FI forces inclusion of file
+ # /TC treat all files named on the command line as C source files
+ # /TP treat all files named on the command line as C++ source files
+ # /Zs syntax check only
+ # /Zm precompiled header memory allocation scaling factor
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}"
+ "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}")
+ if (COTIRE_PCH_MEMORY_SCALING_FACTOR)
+ list (APPEND _flags "/Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ if (COTIRE_PCH_MEMORY_SCALING_FACTOR)
+ set (_flags "${_flags} /Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}")
+ endif()
+ endif()
+ elseif (_compilerID MATCHES "GNU|Clang")
+ # GCC / Clang options used
+ # -x specify the source language
+ # -c compile but do not link
+ # -o place output in file
+ # note that we cannot use -w to suppress all warnings upon pre-compiling, because turning off a warning may
+ # alter compile flags as a side effect (e.g., -Wwrite-string implies -fconst-strings)
+ set (_xLanguage_C "c-header")
+ set (_xLanguage_CXX "c++-header")
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}")
+ else()
+ # return as a flag string
+ set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"")
+ endif()
+ elseif (_compilerID MATCHES "Intel")
+ if (WIN32)
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative)
+ # Windows Intel options used
+ # /nologo do not display compiler version information
+ # /Yc create a precompiled header (PCH) file
+ # /Fp specify a path or file name for precompiled header files
+ # /FI tells the preprocessor to include a specified file name as the header file
+ # /TC process all source or unrecognized file types as C source files
+ # /TP process all source or unrecognized file types as C++ source files
+ # /Zs syntax check only
+ # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}"
+ "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "/Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} /Wpch-messages")
+ endif()
+ endif()
+ else()
+ # Linux / Mac OS X Intel options used
+ # -pch-dir location for precompiled header files
+ # -pch-create name of the precompiled header (PCH) to create
+ # -Kc++ process all source or unrecognized file types as C++ source files
+ # -fsyntax-only check only for correct syntax
+ # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ get_filename_component(_pchDir "${_pchFile}" DIRECTORY)
+ get_filename_component(_pchName "${_pchFile}" NAME)
+ set (_xLanguage_C "c-header")
+ set (_xLanguage_CXX "c++-header")
+ if (_flags)
+ # append to list
+ if ("${_language}" STREQUAL "CXX")
+ list (APPEND _flags -Kc++)
+ endif()
+ list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-create" "${_pchName}" "-fsyntax-only" "${_hostFile}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "-Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} -Wpch-messages")
+ endif()
+ endif()
+ endif()
+ else()
+ message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
+ endif()
+ set (${_flagsVar} ${_flags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_prefix_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar)
+ set (_flags ${${_flagsVar}})
+ if (_compilerID MATCHES "MSVC")
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ # cl.exe options used
+ # /Yu uses a precompiled header file during build
+ # /Fp specifies precompiled header binary file name
+ # /FI forces inclusion of file
+ # /Zm precompiled header memory allocation scaling factor
+ if (_pchFile)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ if (_flags)
+ # append to list
+ list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}")
+ if (COTIRE_PCH_MEMORY_SCALING_FACTOR)
+ list (APPEND _flags "/Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ if (COTIRE_PCH_MEMORY_SCALING_FACTOR)
+ set (_flags "${_flags} /Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}")
+ endif()
+ endif()
+ else()
+ # no precompiled header, force inclusion of prefix header
+ if (_flags)
+ # append to list
+ list (APPEND _flags "/FI${_prefixFileNative}")
+ else()
+ # return as a flag string
+ set (_flags "/FI\"${_prefixFileNative}\"")
+ endif()
+ endif()
+ elseif (_compilerID MATCHES "GNU")
+ # GCC options used
+ # -include process include file as the first line of the primary source file
+ # -Winvalid-pch warns if precompiled header is found but cannot be used
+ # note: ccache requires the -include flag to be used in order to process precompiled header correctly
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-Winvalid-pch" "-include" "${_prefixFile}")
+ else()
+ # return as a flag string
+ set (_flags "-Winvalid-pch -include \"${_prefixFile}\"")
+ endif()
+ elseif (_compilerID MATCHES "Clang")
+ # Clang options used
+ # -include process include file as the first line of the primary source file
+ # -include-pch include precompiled header file
+ # -Qunused-arguments don't emit warning for unused driver arguments
+ # note: ccache requires the -include flag to be used in order to process precompiled header correctly
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-Qunused-arguments" "-include" "${_prefixFile}")
+ else()
+ # return as a flag string
+ set (_flags "-Qunused-arguments -include \"${_prefixFile}\"")
+ endif()
+ elseif (_compilerID MATCHES "Intel")
+ if (WIN32)
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ # Windows Intel options used
+ # /Yu use a precompiled header (PCH) file
+ # /Fp specify a path or file name for precompiled header files
+ # /FI tells the preprocessor to include a specified file name as the header file
+ # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ if (_pchFile)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ if (_flags)
+ # append to list
+ list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "/Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} /Wpch-messages")
+ endif()
+ endif()
+ else()
+ # no precompiled header, force inclusion of prefix header
+ if (_flags)
+ # append to list
+ list (APPEND _flags "/FI${_prefixFileNative}")
+ else()
+ # return as a flag string
+ set (_flags "/FI\"${_prefixFileNative}\"")
+ endif()
+ endif()
+ else()
+ # Linux / Mac OS X Intel options used
+ # -pch-dir location for precompiled header files
+ # -pch-use name of the precompiled header (PCH) to use
+ # -include process include file as the first line of the primary source file
+ # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ if (_pchFile)
+ get_filename_component(_pchDir "${_pchFile}" DIRECTORY)
+ get_filename_component(_pchName "${_pchFile}" NAME)
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-use" "${_pchName}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "-Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} -Wpch-messages")
+ endif()
+ endif()
+ else()
+ # no precompiled header, force inclusion of prefix header
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-include" "${_prefixFile}")
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\"")
+ endif()
+ endif()
+ endif()
+ else()
+ message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
+ endif()
+ set (${_flagsVar} ${_flags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile)
+ set(_options "")
+ set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_ID COMPILER_VERSION LANGUAGE)
+ set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES SYS COMPILER_LAUNCHER)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (NOT _option_LANGUAGE)
+ set (_option_LANGUAGE "CXX")
+ endif()
+ if (NOT _option_COMPILER_ID)
+ set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
+ endif()
+ if (NOT _option_COMPILER_VERSION)
+ set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}")
+ endif()
+ cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_LAUNCHER}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}")
+ cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS})
+ cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS})
+ cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES)
+ cotire_add_frameworks_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES)
+ cotire_add_pch_compilation_flags(
+ "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd)
+ if (COTIRE_VERBOSE)
+ message (STATUS "execute_process: ${_cmd}")
+ endif()
+ if (_option_COMPILER_ID MATCHES "MSVC")
+ # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
+ unset (ENV{VS_UNICODE_OUTPUT})
+ endif()
+ execute_process(
+ COMMAND ${_cmd}
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ RESULT_VARIABLE _result)
+ if (_result)
+ message (FATAL_ERROR "cotire: error ${_result} precompiling ${_prefixFile}.")
+ endif()
+endfunction()
+
+function (cotire_check_precompiled_header_support _language _target _msgVar)
+ set (_unsupportedCompiler
+ "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}")
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC")
+ # supported since Visual Studio C++ 6.0
+ # and CMake does not support an earlier version
+ set (${_msgVar} "" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU")
+ # GCC PCH support requires version >= 3.4
+ if ("${CMAKE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0")
+ set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}." PARENT_SCOPE)
+ else()
+ set (${_msgVar} "" PARENT_SCOPE)
+ endif()
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang")
+ # all Clang versions have PCH support
+ set (${_msgVar} "" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel")
+ # Intel PCH support requires version >= 8.0.0
+ if ("${CMAKE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0")
+ set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}." PARENT_SCOPE)
+ else()
+ set (${_msgVar} "" PARENT_SCOPE)
+ endif()
+ else()
+ set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE)
+ endif()
+ get_target_property(_launcher ${_target} ${_language}_COMPILER_LAUNCHER)
+ if (CMAKE_${_language}_COMPILER MATCHES "ccache" OR _launcher MATCHES "ccache")
+ if (DEFINED ENV{CCACHE_SLOPPINESS})
+ if (NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "pch_defines" OR NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "time_macros")
+ set (${_msgVar}
+ "ccache requires the environment variable CCACHE_SLOPPINESS to be set to \"pch_defines,time_macros\"."
+ PARENT_SCOPE)
+ endif()
+ else()
+ if (_launcher MATCHES "ccache")
+ get_filename_component(_ccacheExe "${_launcher}" REALPATH)
+ else()
+ get_filename_component(_ccacheExe "${CMAKE_${_language}_COMPILER}" REALPATH)
+ endif()
+ execute_process(
+ COMMAND "${_ccacheExe}" "--print-config"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+ RESULT_VARIABLE _result
+ OUTPUT_VARIABLE _ccacheConfig OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+ if (_result OR NOT
+ _ccacheConfig MATCHES "sloppiness.*=.*time_macros" OR NOT
+ _ccacheConfig MATCHES "sloppiness.*=.*pch_defines")
+ set (${_msgVar}
+ "ccache requires configuration setting \"sloppiness\" to be set to \"pch_defines,time_macros\"."
+ PARENT_SCOPE)
+ endif()
+ endif()
+ endif()
+ if (APPLE)
+ # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64)
+ cotire_get_configuration_types(_configs)
+ foreach (_config ${_configs})
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags)
+ cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags})
+ list (LENGTH _architectures _numberOfArchitectures)
+ if (_numberOfArchitectures GREATER 1)
+ string (REPLACE ";" ", " _architectureStr "${_architectures}")
+ set (${_msgVar}
+ "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})."
+ PARENT_SCOPE)
+ break()
+ endif()
+ endforeach()
+ endif()
+endfunction()
+
+macro (cotire_get_intermediate_dir _cotireDir)
+ # ${CMAKE_CFG_INTDIR} may reference a build-time variable when using a generator which supports configuration types
+ get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE)
+endmacro()
+
+macro (cotire_setup_file_extension_variables)
+ set (_unityFileExt_C ".c")
+ set (_unityFileExt_CXX ".cxx")
+ set (_prefixFileExt_C ".h")
+ set (_prefixFileExt_CXX ".hxx")
+ set (_prefixSourceFileExt_C ".c")
+ set (_prefixSourceFileExt_CXX ".cxx")
+endmacro()
+
+function (cotire_make_single_unity_source_file_path _language _target _unityFileVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _unityFileExt_${_language})
+ set (${_unityFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
+ set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}")
+ cotire_get_intermediate_dir(_baseDir)
+ set (_unityFile "${_baseDir}/${_unityFileName}")
+ set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _unityFileExt_${_language})
+ set (${_unityFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
+ cotire_get_intermediate_dir(_baseDir)
+ set (_startIndex 0)
+ set (_index 0)
+ set (_unityFiles "")
+ set (_sourceFiles ${ARGN})
+ foreach (_sourceFile ${_sourceFiles})
+ get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE)
+ math (EXPR _unityFileCount "${_index} - ${_startIndex}")
+ if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes))
+ if (_index GREATER 0)
+ # start new unity file segment
+ math (EXPR _endIndex "${_index} - 1")
+ set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}")
+ list (APPEND _unityFiles "${_baseDir}/${_unityFileName}")
+ endif()
+ set (_startIndex ${_index})
+ endif()
+ math (EXPR _index "${_index} + 1")
+ endforeach()
+ list (LENGTH _sourceFiles _numberOfSources)
+ if (_startIndex EQUAL 0)
+ # there is only a single unity file
+ cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles)
+ elseif (_startIndex LESS _numberOfSources)
+ # end with final unity file segment
+ math (EXPR _endIndex "${_index} - 1")
+ set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}")
+ list (APPEND _unityFiles "${_baseDir}/${_unityFileName}")
+ endif()
+ set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE)
+ if (COTIRE_DEBUG AND _unityFiles)
+ message (STATUS "unity files: ${_unityFiles}")
+ endif()
+endfunction()
+
+function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _unityFileExt_${_language})
+ set (${_prefixFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
+ set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
+ string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}")
+ string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}")
+ set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_prefix_header_to_source_file_path _language _prefixHeaderFile _prefixSourceFileVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _prefixSourceFileExt_${_language})
+ set (${_prefixSourceFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ string (REGEX REPLACE "${_prefixFileExt_${_language}}$" "${_prefixSourceFileExt_${_language}}" _prefixSourceFile "${_prefixHeaderFile}")
+ set (${_prefixSourceFileVar} "${_prefixSourceFile}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar)
+ cotire_setup_file_extension_variables()
+ if (NOT _language)
+ set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
+ set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}")
+ elseif (DEFINED _prefixFileExt_${_language})
+ set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
+ set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}")
+ else()
+ set (_prefixFileBaseName "")
+ set (_prefixFileName "")
+ endif()
+ set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE)
+ set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_make_prefix_file_path _language _target _prefixFileVar)
+ cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName)
+ set (${_prefixFileVar} "" PARENT_SCOPE)
+ if (_prefixFileName)
+ if (NOT _language)
+ set (_language "C")
+ endif()
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel|MSVC")
+ cotire_get_intermediate_dir(_baseDir)
+ set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE)
+ endif()
+ endif()
+endfunction()
+
+function (cotire_make_pch_file_path _language _target _pchFileVar)
+ cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName)
+ set (${_pchFileVar} "" PARENT_SCOPE)
+ if (_prefixFileBaseName AND _prefixFileName)
+ cotire_check_precompiled_header_support("${_language}" "${_target}" _msg)
+ if (NOT _msg)
+ if (XCODE)
+ # For Xcode, we completely hand off the compilation of the prefix header to the IDE
+ return()
+ endif()
+ cotire_get_intermediate_dir(_baseDir)
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC")
+ # MSVC uses the extension .pch added to the prefix header base name
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang")
+ # Clang looks for a precompiled header corresponding to the prefix header with the extension .pch appended
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.pch" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU")
+ # GCC looks for a precompiled header corresponding to the prefix header with the extension .gch appended
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel")
+ # Intel uses the extension .pchi added to the prefix header base name
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE)
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function (cotire_select_unity_source_files _unityFile _sourcesVar)
+ set (_sourceFiles ${ARGN})
+ if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)")
+ set (_startIndex ${CMAKE_MATCH_1})
+ set (_endIndex ${CMAKE_MATCH_2})
+ list (LENGTH _sourceFiles _numberOfSources)
+ if (NOT _startIndex LESS _numberOfSources)
+ math (EXPR _startIndex "${_numberOfSources} - 1")
+ endif()
+ if (NOT _endIndex LESS _numberOfSources)
+ math (EXPR _endIndex "${_numberOfSources} - 1")
+ endif()
+ set (_files "")
+ foreach (_index RANGE ${_startIndex} ${_endIndex})
+ list (GET _sourceFiles ${_index} _file)
+ list (APPEND _files "${_file}")
+ endforeach()
+ else()
+ set (_files ${_sourceFiles})
+ endif()
+ set (${_sourcesVar} ${_files} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar)
+ set (_dependencySources "")
+ # depend on target's generated source files
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${_targetSourceFiles})
+ if (_generatedSources)
+ # but omit all generated source files that have the COTIRE_EXCLUDED property set to true
+ cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources})
+ if (_excludedGeneratedSources)
+ list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources})
+ endif()
+ # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly
+ cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources})
+ if (_excludedNonDependencySources)
+ list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources})
+ endif()
+ if (_generatedSources)
+ list (APPEND _dependencySources ${_generatedSources})
+ endif()
+ endif()
+ if (COTIRE_DEBUG AND _dependencySources)
+ message (STATUS "${_language} ${_target} unity source dependencies: ${_dependencySources}")
+ endif()
+ set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar)
+ set (_dependencySources "")
+ # depend on target source files marked with custom COTIRE_DEPENDENCY property
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${_targetSourceFiles})
+ if (COTIRE_DEBUG AND _dependencySources)
+ message (STATUS "${_language} ${_target} prefix header dependencies: ${_dependencySources}")
+ endif()
+ set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE)
+endfunction()
+
+function (cotire_generate_target_script _language _configurations _target _targetScriptVar _targetConfigScriptVar)
+ set (_targetSources ${ARGN})
+ cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${_targetSources})
+ cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${_targetSources})
+ # set up variables to be configured
+ set (COTIRE_TARGET_LANGUAGE "${_language}")
+ get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH)
+ cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH)
+ get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH)
+ cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH)
+ get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS)
+ get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS)
+ get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES)
+ get_target_property(COTIRE_TARGET_INCLUDE_PRIORITY_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH)
+ cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${_targetSources})
+ cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${_targetSources})
+ set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}")
+ foreach (_config ${_configurations})
+ string (TOUPPER "${_config}" _upperConfig)
+ cotire_get_target_include_directories(
+ "${_config}" "${_language}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig} COTIRE_TARGET_SYSTEM_INCLUDE_DIRECTORIES_${_upperConfig})
+ cotire_get_target_compile_definitions(
+ "${_config}" "${_language}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig})
+ cotire_get_target_compiler_flags(
+ "${_config}" "${_language}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig})
+ cotire_get_source_files_compile_definitions(
+ "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${_targetSources})
+ endforeach()
+ get_target_property(COTIRE_TARGET_${_language}_COMPILER_LAUNCHER ${_target} ${_language}_COMPILER_LAUNCHER)
+ # set up COTIRE_TARGET_SOURCES
+ set (COTIRE_TARGET_SOURCES "")
+ foreach (_sourceFile ${_targetSources})
+ get_source_file_property(_generated "${_sourceFile}" GENERATED)
+ if (_generated)
+ # use absolute paths for generated files only, retrieving the LOCATION property is an expensive operation
+ get_source_file_property(_sourceLocation "${_sourceFile}" LOCATION)
+ list (APPEND COTIRE_TARGET_SOURCES "${_sourceLocation}")
+ else()
+ list (APPEND COTIRE_TARGET_SOURCES "${_sourceFile}")
+ endif()
+ endforeach()
+ # copy variable definitions to cotire target script
+ get_cmake_property(_vars VARIABLES)
+ string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}")
+ # omit COTIRE_*_INIT variables
+ string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+_INIT" _initVars "${_matchVars}")
+ if (_initVars)
+ list (REMOVE_ITEM _matchVars ${_initVars})
+ endif()
+ # omit COTIRE_VERBOSE which is passed as a CMake define on command line
+ list (REMOVE_ITEM _matchVars COTIRE_VERBOSE)
+ set (_contents "")
+ set (_contentsHasGeneratorExpressions FALSE)
+ foreach (_var IN LISTS _matchVars ITEMS
+ XCODE MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES
+ CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER_VERSION
+ CMAKE_${_language}_COMPILER_LAUNCHER CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1
+ CMAKE_INCLUDE_FLAG_${_language} CMAKE_INCLUDE_FLAG_${_language}_SEP
+ CMAKE_INCLUDE_SYSTEM_FLAG_${_language}
+ CMAKE_${_language}_FRAMEWORK_SEARCH_FLAG
+ CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG
+ CMAKE_${_language}_SOURCE_FILE_EXTENSIONS)
+ if (DEFINED ${_var})
+ string (REPLACE "\"" "\\\"" _value "${${_var}}")
+ set (_contents "${_contents}set (${_var} \"${_value}\")\n")
+ if (NOT _contentsHasGeneratorExpressions)
+ if ("${_value}" MATCHES "\\$<.*>")
+ set (_contentsHasGeneratorExpressions TRUE)
+ endif()
+ endif()
+ endif()
+ endforeach()
+ # generate target script file
+ get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME)
+ set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}")
+ cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE)
+ if (_contentsHasGeneratorExpressions)
+ # use file(GENERATE ...) to expand generator expressions in the target script at CMake generate-time
+ set (_configNameOrNoneGeneratorExpression "$<$<CONFIG:>:None>$<$<NOT:$<CONFIG:>>:$<CONFIGURATION>>")
+ set (_targetCotireConfigScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_configNameOrNoneGeneratorExpression}_${_moduleName}")
+ file (GENERATE OUTPUT "${_targetCotireConfigScript}" INPUT "${_targetCotireScript}")
+ else()
+ set (_targetCotireConfigScript "${_targetCotireScript}")
+ endif()
+ set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE)
+ set (${_targetConfigScriptVar} "${_targetCotireConfigScript}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_pch_file_compilation _language _target _targetScript _prefixFile _pchFile _hostFile)
+ set (_sourceFiles ${ARGN})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # for Visual Studio and Intel, we attach the precompiled header compilation to the host file
+ # the remaining files include the precompiled header, see cotire_setup_pch_file_inclusion
+ if (_sourceFiles)
+ set (_flags "")
+ cotire_add_pch_compilation_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags)
+ set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}")
+ # make object file generated from host file depend on prefix header
+ set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}")
+ # mark host file as cotired to prevent it from being used in another cotired target
+ set_property (SOURCE ${_hostFile} PROPERTY COTIRE_TARGET "${_target}")
+ endif()
+ elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
+ # for makefile based generator, we add a custom command to precompile the prefix header
+ if (_targetScript)
+ cotire_set_cmd_to_prologue(_cmds)
+ list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}")
+ if (MSVC_IDE)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileLogPath)
+ else()
+ file (RELATIVE_PATH _pchFileLogPath "${CMAKE_BINARY_DIR}" "${_pchFile}")
+ endif()
+ # make precompiled header compilation depend on the actual compiler executable used to force
+ # re-compilation when the compiler executable is updated. This prevents "created by a different GCC executable"
+ # warnings when the precompiled header is included.
+ get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" ABSOLUTE)
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} ${_realCompilerExe} IMPLICIT_DEPENDS ${_language} ${_prefixFile}")
+ endif()
+ set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE)
+ add_custom_command(
+ OUTPUT "${_pchFile}"
+ COMMAND ${_cmds}
+ DEPENDS "${_prefixFile}" "${_realCompilerExe}"
+ IMPLICIT_DEPENDS ${_language} "${_prefixFile}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ COMMENT "Building ${_language} precompiled header ${_pchFileLogPath}"
+ VERBATIM)
+ endif()
+ endif()
+endfunction()
+
+function (cotire_setup_pch_file_inclusion _language _target _wholeTarget _prefixFile _pchFile _hostFile)
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # for Visual Studio and Intel, we include the precompiled header in all but the host file
+ # the host file does the precompiled header compilation, see cotire_setup_pch_file_compilation
+ set (_sourceFiles ${ARGN})
+ list (LENGTH _sourceFiles _numberOfSourceFiles)
+ if (_numberOfSourceFiles GREATER 0)
+ # mark sources as cotired to prevent them from being used in another cotired target
+ set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
+ set (_flags "")
+ cotire_add_prefix_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _flags)
+ set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ # make object files generated from source files depend on precompiled header
+ set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}")
+ endif()
+ elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
+ set (_sourceFiles ${_hostFile} ${ARGN})
+ if (NOT _wholeTarget)
+ # for makefile based generator, we force the inclusion of the prefix header for a subset
+ # of the source files, if this is a multi-language target or has excluded files
+ set (_flags "")
+ cotire_add_prefix_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _flags)
+ set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ # mark sources as cotired to prevent them from being used in another cotired target
+ set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
+ endif()
+ # make object files generated from source files depend on precompiled header
+ set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}")
+ endif()
+endfunction()
+
+function (cotire_setup_prefix_file_inclusion _language _target _prefixFile)
+ set (_sourceFiles ${ARGN})
+ # force the inclusion of the prefix header for the given source files
+ set (_flags "")
+ set (_pchFile "")
+ cotire_add_prefix_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _flags)
+ set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ # mark sources as cotired to prevent them from being used in another cotired target
+ set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
+ # make object files generated from source files depend on prefix header
+ set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}")
+endfunction()
+
+function (cotire_get_first_set_property_value _propertyValueVar _type _object)
+ set (_properties ${ARGN})
+ foreach (_property ${_properties})
+ get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
+ if (_propertyValue)
+ set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+ set (${_propertyValueVar} "" PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_combine_command _language _targetScript _joinedFile _cmdsVar)
+ set (_files ${ARGN})
+ set (_filesPaths "")
+ foreach (_file ${_files})
+ get_filename_component(_filePath "${_file}" ABSOLUTE)
+ list (APPEND _filesPaths "${_filePath}")
+ endforeach()
+ cotire_set_cmd_to_prologue(_prefixCmd)
+ list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine")
+ if (_targetScript)
+ list (APPEND _prefixCmd "${_targetScript}")
+ endif()
+ list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths})
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}")
+ endif()
+ set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE)
+ if (MSVC_IDE)
+ file (TO_NATIVE_PATH "${_joinedFile}" _joinedFileLogPath)
+ else()
+ file (RELATIVE_PATH _joinedFileLogPath "${CMAKE_BINARY_DIR}" "${_joinedFile}")
+ endif()
+ get_filename_component(_joinedFileBaseName "${_joinedFile}" NAME_WE)
+ get_filename_component(_joinedFileExt "${_joinedFile}" EXT)
+ if (_language AND _joinedFileBaseName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$")
+ set (_comment "Generating ${_language} unity source ${_joinedFileLogPath}")
+ elseif (_language AND _joinedFileBaseName MATCHES "${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}$")
+ if (_joinedFileExt MATCHES "^\\.c")
+ set (_comment "Generating ${_language} prefix source ${_joinedFileLogPath}")
+ else()
+ set (_comment "Generating ${_language} prefix header ${_joinedFileLogPath}")
+ endif()
+ else()
+ set (_comment "Generating ${_joinedFileLogPath}")
+ endif()
+ add_custom_command(
+ OUTPUT "${_joinedFile}"
+ COMMAND ${_prefixCmd}
+ DEPENDS ${_files}
+ COMMENT "${_comment}"
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ VERBATIM)
+ list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd})
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_target_pch_usage _languages _target _wholeTarget)
+ if (XCODE)
+ # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers
+ set (_prefixFiles "")
+ foreach (_language ${_languages})
+ get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
+ if (_prefixFile)
+ list (APPEND _prefixFiles "${_prefixFile}")
+ endif()
+ endforeach()
+ set (_cmds ${ARGN})
+ list (LENGTH _prefixFiles _numberOfPrefixFiles)
+ if (_numberOfPrefixFiles GREATER 1)
+ # we also generate a generic, single prefix header which includes all language specific prefix headers
+ set (_language "")
+ set (_targetScript "")
+ cotire_make_prefix_file_path("${_language}" ${_target} _prefixHeader)
+ cotire_setup_combine_command("${_language}" "${_targetScript}" "${_prefixHeader}" _cmds ${_prefixFiles})
+ else()
+ set (_prefixHeader "${_prefixFiles}")
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}")
+ endif()
+ # because CMake PRE_BUILD command does not support dependencies,
+ # we check dependencies explicity in cotire script mode when the pre-build action is run
+ add_custom_command(
+ TARGET "${_target}"
+ PRE_BUILD ${_cmds}
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ COMMENT "Updating target ${_target} prefix headers"
+ VERBATIM)
+ # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++
+ set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES")
+ set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}")
+ elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
+ # for makefile based generator, we force inclusion of the prefix header for all target source files
+ # if this is a single-language target without any excluded files
+ if (_wholeTarget)
+ set (_language "${_languages}")
+ # for Visual Studio and Intel, precompiled header inclusion is always done on the source file level
+ # see cotire_setup_pch_file_inclusion
+ if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
+ if (_prefixFile)
+ get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER)
+ set (_options COMPILE_OPTIONS)
+ cotire_add_prefix_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _options)
+ set_property(TARGET ${_target} APPEND PROPERTY ${_options})
+ endif()
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function (cotire_setup_unity_generation_commands _language _target _targetScript _targetConfigScript _unityFiles _cmdsVar)
+ set (_dependencySources "")
+ cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN})
+ foreach (_unityFile ${_unityFiles})
+ set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE)
+ # set up compiled unity source dependencies via OBJECT_DEPENDS
+ # this ensures that missing source files are generated before the unity file is compiled
+ if (COTIRE_DEBUG AND _dependencySources)
+ message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}")
+ endif()
+ if (_dependencySources)
+ # the OBJECT_DEPENDS property requires a list of full paths
+ set (_objectDependsPaths "")
+ foreach (_sourceFile ${_dependencySources})
+ get_source_file_property(_sourceLocation "${_sourceFile}" LOCATION)
+ list (APPEND _objectDependsPaths "${_sourceLocation}")
+ endforeach()
+ set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_objectDependsPaths})
+ endif()
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC and Windows Intel
+ set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj")
+ endif()
+ cotire_set_cmd_to_prologue(_unityCmd)
+ list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetConfigScript}" "${_unityFile}")
+ if (CMAKE_VERSION VERSION_LESS "3.1.0")
+ set (_unityCmdDepends "${_targetScript}")
+ else()
+ # CMake 3.1.0 supports generator expressions in arguments to DEPENDS
+ set (_unityCmdDepends "${_targetConfigScript}")
+ endif()
+ if (MSVC_IDE)
+ file (TO_NATIVE_PATH "${_unityFile}" _unityFileLogPath)
+ else()
+ file (RELATIVE_PATH _unityFileLogPath "${CMAKE_BINARY_DIR}" "${_unityFile}")
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_unityCmdDepends}")
+ endif()
+ add_custom_command(
+ OUTPUT "${_unityFile}"
+ COMMAND ${_unityCmd}
+ DEPENDS ${_unityCmdDepends}
+ COMMENT "Generating ${_language} unity source ${_unityFileLogPath}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM)
+ list (APPEND ${_cmdsVar} COMMAND ${_unityCmd})
+ endforeach()
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_prefix_generation_command _language _target _targetScript _prefixFile _unityFiles _cmdsVar)
+ set (_sourceFiles ${ARGN})
+ set (_dependencySources "")
+ cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles})
+ cotire_set_cmd_to_prologue(_prefixCmd)
+ list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" ${_unityFiles})
+ set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE)
+ # make prefix header generation depend on the actual compiler executable used to force
+ # re-generation when the compiler executable is updated. This prevents "file not found"
+ # errors for compiler version specific system header files.
+ get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" ABSOLUTE)
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_unityFile} ${_dependencySources} ${_realCompilerExe}")
+ endif()
+ if (MSVC_IDE)
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileLogPath)
+ else()
+ file (RELATIVE_PATH _prefixFileLogPath "${CMAKE_BINARY_DIR}" "${_prefixFile}")
+ endif()
+ get_filename_component(_prefixFileExt "${_prefixFile}" EXT)
+ if (_prefixFileExt MATCHES "^\\.c")
+ set (_comment "Generating ${_language} prefix source ${_prefixFileLogPath}")
+ else()
+ set (_comment "Generating ${_language} prefix header ${_prefixFileLogPath}")
+ endif()
+ # prevent pre-processing errors upon generating the prefix header when a target's generated include file does not yet exist
+ # we do not add a file-level dependency for the target's generated files though, because we only want to depend on their existence
+ # thus we make the prefix header generation depend on a custom helper target which triggers the generation of the files
+ set (_preTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}_pre")
+ if (TARGET ${_preTargetName})
+ # custom helper target has already been generated while processing a different language
+ list (APPEND _dependencySources ${_preTargetName})
+ else()
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${_targetSourceFiles})
+ if (_generatedSources)
+ add_custom_target("${_preTargetName}" DEPENDS ${_generatedSources})
+ cotire_init_target("${_preTargetName}")
+ list (APPEND _dependencySources ${_preTargetName})
+ endif()
+ endif()
+ add_custom_command(
+ OUTPUT "${_prefixFile}" "${_prefixFile}.log"
+ COMMAND ${_prefixCmd}
+ DEPENDS ${_unityFiles} ${_dependencySources} "${_realCompilerExe}"
+ COMMENT "${_comment}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM)
+ list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd})
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_prefix_generation_from_unity_command _language _target _targetScript _prefixFile _unityFiles _cmdsVar)
+ set (_sourceFiles ${ARGN})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang")
+ # GNU and Clang require indirect compilation of the prefix header to make them honor the system_header pragma
+ cotire_prefix_header_to_source_file_path(${_language} "${_prefixFile}" _prefixSourceFile)
+ else()
+ set (_prefixSourceFile "${_prefixFile}")
+ endif()
+ cotire_setup_prefix_generation_command(
+ ${_language} ${_target} "${_targetScript}"
+ "${_prefixSourceFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang")
+ # set up generation of a prefix source file which includes the prefix header
+ cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixFile}" _cmds ${_prefixSourceFile})
+ endif()
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_prefix_generation_from_provided_command _language _target _targetScript _prefixFile _cmdsVar)
+ set (_prefixHeaderFiles ${ARGN})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang")
+ # GNU and Clang require indirect compilation of the prefix header to make them honor the system_header pragma
+ cotire_prefix_header_to_source_file_path(${_language} "${_prefixFile}" _prefixSourceFile)
+ else()
+ set (_prefixSourceFile "${_prefixFile}")
+ endif()
+ cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixSourceFile}" _cmds ${_prefixHeaderFiles})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang")
+ # set up generation of a prefix source file which includes the prefix header
+ cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixFile}" _cmds ${_prefixSourceFile})
+ endif()
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_init_cotire_target_properties _target)
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE)
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE)
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE)
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}")
+ cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}")
+ if (NOT _isRelative)
+ set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}")
+ endif()
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_LINK_LIBRARIES_INIT SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_LINK_LIBRARIES_INIT "COPY_UNITY")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET)
+ if (NOT _isSet)
+ if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}")
+ else()
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "")
+ endif()
+ endif()
+endfunction()
+
+function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar)
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
+ string (REPLACE ";" " " _languagesStr "${_languages}")
+ math (EXPR _numberOfExcludedFiles "${ARGC} - 4")
+ if (_numberOfExcludedFiles EQUAL 0)
+ set (_excludedStr "")
+ elseif (COTIRE_VERBOSE OR _numberOfExcludedFiles LESS 4)
+ string (REPLACE ";" ", " _excludedStr "excluding ${ARGN}")
+ else()
+ set (_excludedStr "excluding ${_numberOfExcludedFiles} files")
+ endif()
+ set (_targetMsg "")
+ if (NOT _languages)
+ set (_targetMsg "Target ${_target} cannot be cotired.")
+ if (_disableMsg)
+ set (_targetMsg "${_targetMsg} ${_disableMsg}")
+ endif()
+ elseif (NOT _targetUsePCH AND NOT _targetAddSCU)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.")
+ if (_disableMsg)
+ set (_targetMsg "${_targetMsg} ${_disableMsg}")
+ endif()
+ elseif (NOT _targetUsePCH)
+ if (_excludedStr)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header ${_excludedStr}.")
+ else()
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.")
+ endif()
+ if (_disableMsg)
+ set (_targetMsg "${_targetMsg} ${_disableMsg}")
+ endif()
+ elseif (NOT _targetAddSCU)
+ if (_excludedStr)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build ${_excludedStr}.")
+ else()
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.")
+ endif()
+ else()
+ if (_excludedStr)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired ${_excludedStr}.")
+ else()
+ set (_targetMsg "${_languagesStr} target ${_target} cotired.")
+ endif()
+ endif()
+ set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_choose_target_languages _target _targetLanguagesVar _wholeTargetVar)
+ set (_languages ${ARGN})
+ set (_allSourceFiles "")
+ set (_allExcludedSourceFiles "")
+ set (_allCotiredSourceFiles "")
+ set (_targetLanguages "")
+ set (_pchEligibleTargetLanguages "")
+ get_target_property(_targetType ${_target} TYPE)
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
+ set (_disableMsg "")
+ foreach (_language ${_languages})
+ get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER)
+ get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE)
+ if (_prefixHeader OR _unityBuildFile)
+ message (STATUS "cotire: target ${_target} has already been cotired.")
+ set (${_targetLanguagesVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ if (_targetUsePCH AND "${_language}" MATCHES "^C|CXX$" AND DEFINED CMAKE_${_language}_COMPILER_ID)
+ if (CMAKE_${_language}_COMPILER_ID)
+ cotire_check_precompiled_header_support("${_language}" "${_target}" _disableMsg)
+ if (_disableMsg)
+ set (_targetUsePCH FALSE)
+ endif()
+ endif()
+ endif()
+ set (_sourceFiles "")
+ set (_excludedSources "")
+ set (_cotiredSources "")
+ cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
+ if (_sourceFiles OR _excludedSources OR _cotiredSources)
+ list (APPEND _targetLanguages ${_language})
+ endif()
+ if (_sourceFiles)
+ list (APPEND _allSourceFiles ${_sourceFiles})
+ endif()
+ list (LENGTH _sourceFiles _numberOfSources)
+ if (NOT _numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
+ list (APPEND _pchEligibleTargetLanguages ${_language})
+ endif()
+ if (_excludedSources)
+ list (APPEND _allExcludedSourceFiles ${_excludedSources})
+ endif()
+ if (_cotiredSources)
+ list (APPEND _allCotiredSourceFiles ${_cotiredSources})
+ endif()
+ endforeach()
+ set (_targetMsgLevel STATUS)
+ if (NOT _targetLanguages)
+ string (REPLACE ";" " or " _languagesStr "${_languages}")
+ set (_disableMsg "No ${_languagesStr} source files.")
+ set (_targetUsePCH FALSE)
+ set (_targetAddSCU FALSE)
+ endif()
+ if (_targetUsePCH)
+ if (_allCotiredSourceFiles)
+ cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles})
+ list (REMOVE_DUPLICATES _cotireTargets)
+ string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}")
+ set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.")
+ set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},")
+ set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.")
+ set (_targetMsgLevel SEND_ERROR)
+ set (_targetUsePCH FALSE)
+ elseif (NOT _pchEligibleTargetLanguages)
+ set (_disableMsg "Too few applicable sources.")
+ set (_targetUsePCH FALSE)
+ elseif (XCODE AND _allExcludedSourceFiles)
+ # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target
+ set (_disableMsg "Exclusion of source files not supported for generator Xcode.")
+ set (_targetUsePCH FALSE)
+ elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY")
+ # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target
+ set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.")
+ set (_targetUsePCH FALSE)
+ endif()
+ endif()
+ set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH})
+ set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU})
+ cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles})
+ if (_targetMsg)
+ if (NOT DEFINED COTIREMSG_${_target})
+ set (COTIREMSG_${_target} "")
+ endif()
+ if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR
+ NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}")
+ # cache message to avoid redundant messages on re-configure
+ set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.")
+ message (${_targetMsgLevel} "${_targetMsg}")
+ endif()
+ endif()
+ list (LENGTH _targetLanguages _numberOfLanguages)
+ if (_numberOfLanguages GREATER 1 OR _allExcludedSourceFiles)
+ set (${_wholeTargetVar} FALSE PARENT_SCOPE)
+ else()
+ set (${_wholeTargetVar} TRUE PARENT_SCOPE)
+ endif()
+ set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE)
+endfunction()
+
+function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar)
+ set (_sourceFiles ${ARGN})
+ get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES)
+ if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)")
+ set (_numberOfThreads "${CMAKE_MATCH_2}")
+ if (NOT _numberOfThreads)
+ # use all available cores
+ ProcessorCount(_numberOfThreads)
+ endif()
+ list (LENGTH _sourceFiles _numberOfSources)
+ math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}")
+ elseif (NOT _maxIncludes MATCHES "[0-9]+")
+ set (_maxIncludes 0)
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_target} unity source max includes: ${_maxIncludes}")
+ endif()
+ set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE)
+endfunction()
+
+function (cotire_process_target_language _language _configurations _target _wholeTarget _cmdsVar)
+ set (${_cmdsVar} "" PARENT_SCOPE)
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ set (_sourceFiles "")
+ set (_excludedSources "")
+ set (_cotiredSources "")
+ cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
+ if (NOT _sourceFiles AND NOT _cotiredSources)
+ return()
+ endif()
+ set (_cmds "")
+ # check for user provided unity source file list
+ get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT)
+ if (NOT _unitySourceFiles)
+ set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources})
+ endif()
+ cotire_generate_target_script(
+ ${_language} "${_configurations}" ${_target} _targetScript _targetConfigScript ${_unitySourceFiles})
+ # set up unity files for parallel compilation
+ cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles})
+ cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles})
+ list (LENGTH _unityFiles _numberOfUnityFiles)
+ if (_numberOfUnityFiles EQUAL 0)
+ return()
+ elseif (_numberOfUnityFiles GREATER 1)
+ cotire_setup_unity_generation_commands(
+ ${_language} ${_target} "${_targetScript}" "${_targetConfigScript}" "${_unityFiles}" _cmds ${_unitySourceFiles})
+ endif()
+ # set up single unity file for prefix header generation
+ cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile)
+ cotire_setup_unity_generation_commands(
+ ${_language} ${_target} "${_targetScript}" "${_targetConfigScript}" "${_unityFile}" _cmds ${_unitySourceFiles})
+ cotire_make_prefix_file_path(${_language} ${_target} _prefixFile)
+ # set up prefix header
+ if (_prefixFile)
+ # check for user provided prefix header files
+ get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT)
+ if (_prefixHeaderFiles)
+ cotire_setup_prefix_generation_from_provided_command(
+ ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles})
+ else()
+ cotire_setup_prefix_generation_from_unity_command(
+ ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" "${_unityFile}" _cmds ${_unitySourceFiles})
+ endif()
+ # check if selected language has enough sources at all
+ list (LENGTH _sourceFiles _numberOfSources)
+ if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
+ set (_targetUsePCH FALSE)
+ else()
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ endif()
+ if (_targetUsePCH)
+ cotire_make_pch_file_path(${_language} ${_target} _pchFile)
+ if (_pchFile)
+ # first file in _sourceFiles is passed as the host file
+ cotire_setup_pch_file_compilation(
+ ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles})
+ cotire_setup_pch_file_inclusion(
+ ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles})
+ endif()
+ elseif (_prefixHeaderFiles)
+ # user provided prefix header must be included unconditionally
+ cotire_setup_prefix_file_inclusion(${_language} ${_target} "${_prefixFile}" ${_sourceFiles})
+ endif()
+ endif()
+ # mark target as cotired for language
+ set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}")
+ if (_prefixFile)
+ set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}")
+ if (_targetUsePCH AND _pchFile)
+ set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}")
+ endif()
+ endif()
+ set (${_cmdsVar} ${_cmds} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_clean_target _target)
+ set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}")
+ if (NOT TARGET "${_cleanTargetName}")
+ cotire_set_cmd_to_prologue(_cmds)
+ get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE)
+ list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}")
+ add_custom_target(${_cleanTargetName}
+ COMMAND ${_cmds}
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMMENT "Cleaning up target ${_target} cotire generated files"
+ VERBATIM)
+ cotire_init_target("${_cleanTargetName}")
+ endif()
+endfunction()
+
+function (cotire_setup_pch_target _languages _configurations _target)
+ if ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja")
+ # for makefile based generators, we add a custom target to trigger the generation of the cotire related files
+ set (_dependsFiles "")
+ foreach (_language ${_languages})
+ set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE)
+ if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # Visual Studio and Intel only create precompiled header as a side effect
+ list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER)
+ endif()
+ cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props})
+ if (_dependsFile)
+ list (APPEND _dependsFiles "${_dependsFile}")
+ endif()
+ endforeach()
+ if (_dependsFiles)
+ set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}")
+ add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles})
+ cotire_init_target("${_pchTargetName}")
+ cotire_add_to_pch_all_target(${_pchTargetName})
+ endif()
+ else()
+ # for other generators, we add the "clean all" target to clean up the precompiled header
+ cotire_setup_clean_all_target()
+ endif()
+endfunction()
+
+function (cotire_filter_object_libraries _target _objectLibrariesVar)
+ set (_objectLibraries "")
+ foreach (_source ${ARGN})
+ if (_source MATCHES "^\\$<TARGET_OBJECTS:.+>$")
+ list (APPEND _objectLibraries "${_source}")
+ endif()
+ endforeach()
+ set (${_objectLibrariesVar} ${_objectLibraries} PARENT_SCOPE)
+endfunction()
+
+function (cotire_collect_unity_target_sources _target _languages _unityTargetSourcesVar)
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ set (_unityTargetSources ${_targetSourceFiles})
+ foreach (_language ${_languages})
+ get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE)
+ if (_unityFiles)
+ # remove source files that are included in the unity source
+ set (_sourceFiles "")
+ set (_excludedSources "")
+ set (_cotiredSources "")
+ cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
+ if (_sourceFiles OR _cotiredSources)
+ list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources})
+ endif()
+ # add unity source files instead
+ list (APPEND _unityTargetSources ${_unityFiles})
+ endif()
+ endforeach()
+ get_target_property(_linkLibrariesStrategy ${_target} COTIRE_UNITY_LINK_LIBRARIES_INIT)
+ if ("${_linkLibrariesStrategy}" MATCHES "^COPY_UNITY$")
+ cotire_filter_object_libraries(${_target} _objectLibraries ${_targetSourceFiles})
+ if (_objectLibraries)
+ cotire_map_libraries("${_linkLibrariesStrategy}" _unityObjectLibraries ${_objectLibraries})
+ list (REMOVE_ITEM _unityTargetSources ${_objectLibraries})
+ list (APPEND _unityTargetSources ${_unityObjectLibraries})
+ endif()
+ endif()
+ set (${_unityTargetSourcesVar} ${_unityTargetSources} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_unity_target_pch_usage _languages _target)
+ foreach (_language ${_languages})
+ get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE)
+ if (_unityFiles)
+ get_property(_userPrefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT)
+ get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
+ if (_userPrefixFile AND _prefixFile)
+ # user provided prefix header must be included unconditionally by unity sources
+ cotire_setup_prefix_file_inclusion(${_language} ${_target} "${_prefixFile}" ${_unityFiles})
+ endif()
+ endif()
+ endforeach()
+endfunction()
+
+function (cotire_setup_unity_build_target _languages _configurations _target)
+ get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME)
+ if (NOT _unityTargetName)
+ set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}")
+ endif()
+ # determine unity target sub type
+ get_target_property(_targetType ${_target} TYPE)
+ if ("${_targetType}" STREQUAL "EXECUTABLE")
+ set (_unityTargetSubType "")
+ elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY")
+ set (_unityTargetSubType "${CMAKE_MATCH_1}")
+ else()
+ message (WARNING "cotire: target ${_target} has unknown target type ${_targetType}.")
+ return()
+ endif()
+ # determine unity target sources
+ set (_unityTargetSources "")
+ cotire_collect_unity_target_sources(${_target} "${_languages}" _unityTargetSources)
+ # handle automatic Qt processing
+ get_target_property(_targetAutoMoc ${_target} AUTOMOC)
+ get_target_property(_targetAutoUic ${_target} AUTOUIC)
+ get_target_property(_targetAutoRcc ${_target} AUTORCC)
+ if (_targetAutoMoc OR _targetAutoUic OR _targetAutoRcc)
+ # if the original target sources are subject to CMake's automatic Qt processing,
+ # also include implicitly generated <targetname>_automoc.cpp file
+ list (APPEND _unityTargetSources "${_target}_automoc.cpp")
+ set_property (SOURCE "${_target}_automoc.cpp" PROPERTY GENERATED TRUE)
+ endif()
+ # prevent AUTOMOC, AUTOUIC and AUTORCC properties from being set when the unity target is created
+ set (CMAKE_AUTOMOC OFF)
+ set (CMAKE_AUTOUIC OFF)
+ set (CMAKE_AUTORCC OFF)
+ if (COTIRE_DEBUG)
+ message (STATUS "add target ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}")
+ endif()
+ # generate unity target
+ if ("${_targetType}" STREQUAL "EXECUTABLE")
+ add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources})
+ else()
+ add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources})
+ endif()
+ if ("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
+ # depend on original target's automoc target, if it exists
+ if (TARGET ${_target}_automoc)
+ add_dependencies(${_unityTargetName} ${_target}_automoc)
+ endif()
+ else()
+ if (_targetAutoMoc OR _targetAutoUic OR _targetAutoRcc)
+ # depend on the original target's implicity generated <targetname>_automoc target
+ add_dependencies(${_unityTargetName} ${_target}_automoc)
+ endif()
+ endif()
+ # copy output location properties
+ set (_outputDirProperties
+ ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
+ LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
+ RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_<CONFIG>)
+ if (COTIRE_UNITY_OUTPUT_DIRECTORY)
+ set (_setDefaultOutputDir TRUE)
+ if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}")
+ set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}")
+ else()
+ # append relative COTIRE_UNITY_OUTPUT_DIRECTORY to target's actual output directory
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties})
+ cotire_resolve_config_properites("${_configurations}" _properties ${_outputDirProperties})
+ foreach (_property ${_properties})
+ get_property(_outputDir TARGET ${_target} PROPERTY ${_property})
+ if (_outputDir)
+ get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE)
+ set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}")
+ set (_setDefaultOutputDir FALSE)
+ endif()
+ endforeach()
+ if (_setDefaultOutputDir)
+ get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE)
+ endif()
+ endif()
+ if (_setDefaultOutputDir)
+ set_target_properties(${_unityTargetName} PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}"
+ LIBRARY_OUTPUT_DIRECTORY "${_outputDir}"
+ RUNTIME_OUTPUT_DIRECTORY "${_outputDir}")
+ endif()
+ else()
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ ${_outputDirProperties})
+ endif()
+ # copy output name
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_<CONFIG>
+ LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_<CONFIG>
+ OUTPUT_NAME OUTPUT_NAME_<CONFIG>
+ RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_<CONFIG>
+ PREFIX <CONFIG>_POSTFIX SUFFIX
+ IMPORT_PREFIX IMPORT_SUFFIX)
+ # copy compile stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ COMPILE_DEFINITIONS COMPILE_DEFINITIONS_<CONFIG>
+ COMPILE_FLAGS COMPILE_OPTIONS
+ Fortran_FORMAT Fortran_MODULE_DIRECTORY
+ INCLUDE_DIRECTORIES
+ INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
+ POSITION_INDEPENDENT_CODE
+ C_COMPILER_LAUNCHER CXX_COMPILER_LAUNCHER
+ C_INCLUDE_WHAT_YOU_USE CXX_INCLUDE_WHAT_YOU_USE
+ C_VISIBILITY_PRESET CXX_VISIBILITY_PRESET VISIBILITY_INLINES_HIDDEN
+ C_CLANG_TIDY CXX_CLANG_TIDY)
+ # copy compile features
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ C_EXTENSIONS C_STANDARD C_STANDARD_REQUIRED
+ CXX_EXTENSIONS CXX_STANDARD CXX_STANDARD_REQUIRED
+ COMPILE_FEATURES)
+ # copy interface stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ COMPATIBLE_INTERFACE_BOOL COMPATIBLE_INTERFACE_NUMBER_MAX COMPATIBLE_INTERFACE_NUMBER_MIN
+ COMPATIBLE_INTERFACE_STRING
+ INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS
+ INTERFACE_INCLUDE_DIRECTORIES INTERFACE_SOURCES
+ INTERFACE_POSITION_INDEPENDENT_CODE INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
+ INTERFACE_AUTOUIC_OPTIONS NO_SYSTEM_FROM_IMPORTED)
+ # copy link stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH
+ LINKER_LANGUAGE LINK_DEPENDS LINK_DEPENDS_NO_SHARED
+ LINK_FLAGS LINK_FLAGS_<CONFIG>
+ LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_<CONFIG>
+ LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_<CONFIG>
+ LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC
+ STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_<CONFIG>
+ NO_SONAME SOVERSION VERSION
+ LINK_WHAT_YOU_USE)
+ # copy cmake stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK)
+ # copy Apple platform specific stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ BUNDLE BUNDLE_EXTENSION FRAMEWORK FRAMEWORK_VERSION INSTALL_NAME_DIR
+ MACOSX_BUNDLE MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST MACOSX_RPATH
+ OSX_ARCHITECTURES OSX_ARCHITECTURES_<CONFIG> PRIVATE_HEADER PUBLIC_HEADER RESOURCE XCTEST
+ IOS_INSTALL_COMBINED)
+ # copy Windows platform specific stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ GNUtoMS
+ COMPILE_PDB_NAME COMPILE_PDB_NAME_<CONFIG>
+ COMPILE_PDB_OUTPUT_DIRECTORY COMPILE_PDB_OUTPUT_DIRECTORY_<CONFIG>
+ PDB_NAME PDB_NAME_<CONFIG> PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_<CONFIG>
+ VS_DESKTOP_EXTENSIONS_VERSION VS_DOTNET_REFERENCES VS_DOTNET_TARGET_FRAMEWORK_VERSION
+ VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_GLOBAL_ROOTNAMESPACE
+ VS_IOT_EXTENSIONS_VERSION VS_IOT_STARTUP_TASK
+ VS_KEYWORD VS_MOBILE_EXTENSIONS_VERSION
+ VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER
+ VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
+ VS_WINRT_COMPONENT VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES
+ WIN32_EXECUTABLE WINDOWS_EXPORT_ALL_SYMBOLS
+ DEPLOYMENT_REMOTE_DIRECTORY VS_CONFIGURATION_TYPE
+ VS_SDK_REFERENCES)
+ # copy Android platform specific stuff
+ cotire_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ ANDROID_API ANDROID_API_MIN ANDROID_GUI
+ ANDROID_ANT_ADDITIONAL_OPTIONS ANDROID_ARCH ANDROID_ASSETS_DIRECTORIES
+ ANDROID_JAR_DEPENDENCIES ANDROID_JAR_DIRECTORIES ANDROID_JAVA_SOURCE_DIR
+ ANDROID_NATIVE_LIB_DEPENDENCIES ANDROID_NATIVE_LIB_DIRECTORIES
+ ANDROID_PROCESS_MAX ANDROID_PROGUARD ANDROID_PROGUARD_CONFIG_PATH
+ ANDROID_SECURE_PROPS_PATH ANDROID_SKIP_ANT_STEP ANDROID_STL_TYPE)
+ # use output name from original target
+ get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME)
+ if (NOT _targetOutputName)
+ set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}")
+ endif()
+ # use export symbol from original target
+ cotire_get_target_export_symbol("${_target}" _defineSymbol)
+ if (_defineSymbol)
+ set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}")
+ if ("${_targetType}" STREQUAL "EXECUTABLE")
+ set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE)
+ endif()
+ endif()
+ cotire_init_target(${_unityTargetName})
+ cotire_add_to_unity_all_target(${_unityTargetName})
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}")
+endfunction(cotire_setup_unity_build_target)
+
+function (cotire_target _target)
+ set(_options "")
+ set(_oneValueArgs "")
+ set(_multiValueArgs LANGUAGES CONFIGURATIONS)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (NOT _option_LANGUAGES)
+ get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+ endif()
+ if (NOT _option_CONFIGURATIONS)
+ cotire_get_configuration_types(_option_CONFIGURATIONS)
+ endif()
+ # check if cotire can be applied to target at all
+ cotire_is_target_supported(${_target} _isSupported)
+ if (NOT _isSupported)
+ get_target_property(_imported ${_target} IMPORTED)
+ get_target_property(_targetType ${_target} TYPE)
+ if (_imported)
+ message (WARNING "cotire: imported ${_targetType} target ${_target} cannot be cotired.")
+ else()
+ message (STATUS "cotire: ${_targetType} target ${_target} cannot be cotired.")
+ endif()
+ return()
+ endif()
+ # resolve alias
+ get_target_property(_aliasName ${_target} ALIASED_TARGET)
+ if (_aliasName)
+ if (COTIRE_DEBUG)
+ message (STATUS "${_target} is an alias. Applying cotire to aliased target ${_aliasName} instead.")
+ endif()
+ set (_target ${_aliasName})
+ endif()
+ # check if target needs to be cotired for build type
+ # when using configuration types, the test is performed at build time
+ cotire_init_cotire_target_properties(${_target})
+ if (NOT CMAKE_CONFIGURATION_TYPES)
+ if (CMAKE_BUILD_TYPE)
+ list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index)
+ else()
+ list (FIND _option_CONFIGURATIONS "None" _index)
+ endif()
+ if (_index EQUAL -1)
+ if (COTIRE_DEBUG)
+ message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})")
+ endif()
+ return()
+ endif()
+ endif()
+ # when not using configuration types, immediately create cotire intermediate dir
+ if (NOT CMAKE_CONFIGURATION_TYPES)
+ cotire_get_intermediate_dir(_baseDir)
+ file (MAKE_DIRECTORY "${_baseDir}")
+ endif()
+ # choose languages that apply to the target
+ cotire_choose_target_languages("${_target}" _targetLanguages _wholeTarget ${_option_LANGUAGES})
+ if (NOT _targetLanguages)
+ return()
+ endif()
+ set (_cmds "")
+ foreach (_language ${_targetLanguages})
+ cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}" ${_target} ${_wholeTarget} _cmd)
+ if (_cmd)
+ list (APPEND _cmds ${_cmd})
+ endif()
+ endforeach()
+ get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
+ if (_targetAddSCU)
+ cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target})
+ endif()
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ if (_targetUsePCH)
+ cotire_setup_target_pch_usage("${_targetLanguages}" ${_target} ${_wholeTarget} ${_cmds})
+ cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target})
+ if (_targetAddSCU)
+ cotire_setup_unity_target_pch_usage("${_targetLanguages}" ${_target})
+ endif()
+ endif()
+ get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN)
+ if (_targetAddCleanTarget)
+ cotire_setup_clean_target(${_target})
+ endif()
+endfunction(cotire_target)
+
+function (cotire_map_libraries _strategy _mappedLibrariesVar)
+ set (_mappedLibraries "")
+ foreach (_library ${ARGN})
+ if (_library MATCHES "^\\$<LINK_ONLY:(.+)>$")
+ set (_libraryName "${CMAKE_MATCH_1}")
+ set (_linkOnly TRUE)
+ set (_objectLibrary FALSE)
+ elseif (_library MATCHES "^\\$<TARGET_OBJECTS:(.+)>$")
+ set (_libraryName "${CMAKE_MATCH_1}")
+ set (_linkOnly FALSE)
+ set (_objectLibrary TRUE)
+ else()
+ set (_libraryName "${_library}")
+ set (_linkOnly FALSE)
+ set (_objectLibrary FALSE)
+ endif()
+ if ("${_strategy}" MATCHES "COPY_UNITY")
+ cotire_is_target_supported(${_libraryName} _isSupported)
+ if (_isSupported)
+ # use target's corresponding unity target, if available
+ get_target_property(_libraryUnityTargetName ${_libraryName} COTIRE_UNITY_TARGET_NAME)
+ if (TARGET "${_libraryUnityTargetName}")
+ if (_linkOnly)
+ list (APPEND _mappedLibraries "$<LINK_ONLY:${_libraryUnityTargetName}>")
+ elseif (_objectLibrary)
+ list (APPEND _mappedLibraries "$<TARGET_OBJECTS:${_libraryUnityTargetName}>")
+ else()
+ list (APPEND _mappedLibraries "${_libraryUnityTargetName}")
+ endif()
+ else()
+ list (APPEND _mappedLibraries "${_library}")
+ endif()
+ else()
+ list (APPEND _mappedLibraries "${_library}")
+ endif()
+ else()
+ list (APPEND _mappedLibraries "${_library}")
+ endif()
+ endforeach()
+ list (REMOVE_DUPLICATES _mappedLibraries)
+ set (${_mappedLibrariesVar} ${_mappedLibraries} PARENT_SCOPE)
+endfunction()
+
+function (cotire_target_link_libraries _target)
+ cotire_is_target_supported(${_target} _isSupported)
+ if (NOT _isSupported)
+ return()
+ endif()
+ get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME)
+ if (TARGET "${_unityTargetName}")
+ get_target_property(_linkLibrariesStrategy ${_target} COTIRE_UNITY_LINK_LIBRARIES_INIT)
+ if (COTIRE_DEBUG)
+ message (STATUS "unity target ${_unityTargetName} link strategy: ${_linkLibrariesStrategy}")
+ endif()
+ if ("${_linkLibrariesStrategy}" MATCHES "^(COPY|COPY_UNITY)$")
+ get_target_property(_linkLibraries ${_target} LINK_LIBRARIES)
+ if (_linkLibraries)
+ cotire_map_libraries("${_linkLibrariesStrategy}" _unityLinkLibraries ${_linkLibraries})
+ set_target_properties(${_unityTargetName} PROPERTIES LINK_LIBRARIES "${_unityLinkLibraries}")
+ if (COTIRE_DEBUG)
+ message (STATUS "unity target ${_unityTargetName} link libraries: ${_unityLinkLibraries}")
+ endif()
+ endif()
+ get_target_property(_interfaceLinkLibraries ${_target} INTERFACE_LINK_LIBRARIES)
+ if (_interfaceLinkLibraries)
+ cotire_map_libraries("${_linkLibrariesStrategy}" _unityLinkInterfaceLibraries ${_interfaceLinkLibraries})
+ set_target_properties(${_unityTargetName} PROPERTIES INTERFACE_LINK_LIBRARIES "${_unityLinkInterfaceLibraries}")
+ if (COTIRE_DEBUG)
+ message (STATUS "unity target ${_unityTargetName} interface link libraries: ${_unityLinkInterfaceLibraries}")
+ endif()
+ endif()
+ endif()
+ endif()
+endfunction(cotire_target_link_libraries)
+
+function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName)
+ if (_targetName)
+ file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*")
+ else()
+ file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*")
+ endif()
+ # filter files in intermediate directory
+ set (_filesToRemove "")
+ foreach (_file ${_cotireFiles})
+ get_filename_component(_dir "${_file}" DIRECTORY)
+ get_filename_component(_dirName "${_dir}" NAME)
+ if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}")
+ list (APPEND _filesToRemove "${_file}")
+ endif()
+ endforeach()
+ if (_filesToRemove)
+ if (COTIRE_VERBOSE)
+ message (STATUS "cleaning up ${_filesToRemove}")
+ endif()
+ file (REMOVE ${_filesToRemove})
+ endif()
+endfunction()
+
+function (cotire_init_target _targetName)
+ if (COTIRE_TARGETS_FOLDER)
+ set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}")
+ endif()
+ set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_ALL TRUE)
+ if (MSVC_IDE)
+ set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE)
+ endif()
+endfunction()
+
+function (cotire_add_to_pch_all_target _pchTargetName)
+ set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}")
+ if (NOT TARGET "${_targetName}")
+ add_custom_target("${_targetName}"
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ VERBATIM)
+ cotire_init_target("${_targetName}")
+ endif()
+ cotire_setup_clean_all_target()
+ add_dependencies(${_targetName} ${_pchTargetName})
+endfunction()
+
+function (cotire_add_to_unity_all_target _unityTargetName)
+ set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}")
+ if (NOT TARGET "${_targetName}")
+ add_custom_target("${_targetName}"
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ VERBATIM)
+ cotire_init_target("${_targetName}")
+ endif()
+ cotire_setup_clean_all_target()
+ add_dependencies(${_targetName} ${_unityTargetName})
+endfunction()
+
+function (cotire_setup_clean_all_target)
+ set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}")
+ if (NOT TARGET "${_targetName}")
+ cotire_set_cmd_to_prologue(_cmds)
+ list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}")
+ add_custom_target(${_targetName}
+ COMMAND ${_cmds}
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMMENT "Cleaning up all cotire generated files"
+ VERBATIM)
+ cotire_init_target("${_targetName}")
+ endif()
+endfunction()
+
+function (cotire)
+ set(_options "")
+ set(_oneValueArgs "")
+ set(_multiValueArgs LANGUAGES CONFIGURATIONS)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ set (_targets ${_option_UNPARSED_ARGUMENTS})
+ foreach (_target ${_targets})
+ if (TARGET ${_target})
+ cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS})
+ else()
+ message (WARNING "cotire: ${_target} is not a target.")
+ endif()
+ endforeach()
+ foreach (_target ${_targets})
+ if (TARGET ${_target})
+ cotire_target_link_libraries(${_target})
+ endif()
+ endforeach()
+endfunction()
+
+if (CMAKE_SCRIPT_MODE_FILE)
+
+ # cotire is being run in script mode
+ # locate -P on command args
+ set (COTIRE_ARGC -1)
+ foreach (_index RANGE ${CMAKE_ARGC})
+ if (COTIRE_ARGC GREATER -1)
+ set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}")
+ math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1")
+ elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P")
+ set (COTIRE_ARGC 0)
+ endif()
+ endforeach()
+
+ # include target script if available
+ if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$")
+ # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES)
+ include("${COTIRE_ARGV2}")
+ endif()
+
+ if (COTIRE_DEBUG)
+ message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}")
+ endif()
+
+ if (NOT COTIRE_BUILD_TYPE)
+ set (COTIRE_BUILD_TYPE "None")
+ endif()
+ string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig)
+ set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}})
+ set (_systemIncludeDirs ${COTIRE_TARGET_SYSTEM_INCLUDE_DIRECTORIES_${_upperConfig}})
+ set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}})
+ set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}})
+ # check if target has been cotired for actual build type COTIRE_BUILD_TYPE
+ list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index)
+ if (_index GREATER -1)
+ set (_sources ${COTIRE_TARGET_SOURCES})
+ set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}})
+ else()
+ if (COTIRE_DEBUG)
+ message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})")
+ endif()
+ set (_sources "")
+ set (_sourcesDefinitions "")
+ endif()
+ set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS})
+ set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS})
+ set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS})
+ set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS})
+
+ if ("${COTIRE_ARGV1}" STREQUAL "unity")
+
+ if (XCODE)
+ # executing pre-build action under Xcode, check dependency on target script
+ set (_dependsOption DEPENDS "${COTIRE_ARGV2}")
+ else()
+ # executing custom command, no need to re-check for dependencies
+ set (_dependsOption "")
+ endif()
+
+ cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources})
+
+ cotire_generate_unity_source(
+ "${COTIRE_ARGV3}" ${_sources}
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions}
+ PRE_UNDEFS ${_targetPreUndefs}
+ POST_UNDEFS ${_targetPostUndefs}
+ SOURCES_PRE_UNDEFS ${_sourcesPreUndefs}
+ SOURCES_POST_UNDEFS ${_sourcesPostUndefs}
+ ${_dependsOption})
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "prefix")
+
+ if (XCODE)
+ # executing pre-build action under Xcode, check dependency on unity file and prefix dependencies
+ set (_dependsOption DEPENDS "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS})
+ else()
+ # executing custom command, no need to re-check for dependencies
+ set (_dependsOption "")
+ endif()
+
+ set (_files "")
+ foreach (_index RANGE 4 ${COTIRE_ARGC})
+ if (COTIRE_ARGV${_index})
+ list (APPEND _files "${COTIRE_ARGV${_index}}")
+ endif()
+ endforeach()
+
+ cotire_generate_prefix_header(
+ "${COTIRE_ARGV3}" ${_files}
+ COMPILER_LAUNCHER "${COTIRE_TARGET_${COTIRE_TARGET_LANGUAGE}_COMPILER_LAUNCHER}"
+ COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}"
+ COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1}
+ COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}"
+ COMPILER_VERSION "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}"
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}"
+ INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH}
+ IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}"
+ INCLUDE_PRIORITY_PATH ${COTIRE_TARGET_INCLUDE_PRIORITY_PATH}
+ INCLUDE_DIRECTORIES ${_includeDirs}
+ SYSTEM_INCLUDE_DIRECTORIES ${_systemIncludeDirs}
+ COMPILE_DEFINITIONS ${_compileDefinitions}
+ COMPILE_FLAGS ${_compileFlags}
+ ${_dependsOption})
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "precompile")
+
+ set (_files "")
+ foreach (_index RANGE 5 ${COTIRE_ARGC})
+ if (COTIRE_ARGV${_index})
+ list (APPEND _files "${COTIRE_ARGV${_index}}")
+ endif()
+ endforeach()
+
+ cotire_precompile_prefix_header(
+ "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}"
+ COMPILER_LAUNCHER "${COTIRE_TARGET_${COTIRE_TARGET_LANGUAGE}_COMPILER_LAUNCHER}"
+ COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}"
+ COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1}
+ COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}"
+ COMPILER_VERSION "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}"
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ INCLUDE_DIRECTORIES ${_includeDirs}
+ SYSTEM_INCLUDE_DIRECTORIES ${_systemIncludeDirs}
+ COMPILE_DEFINITIONS ${_compileDefinitions}
+ COMPILE_FLAGS ${_compileFlags})
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "combine")
+
+ if (COTIRE_TARGET_LANGUAGE)
+ set (_combinedFile "${COTIRE_ARGV3}")
+ set (_startIndex 4)
+ else()
+ set (_combinedFile "${COTIRE_ARGV2}")
+ set (_startIndex 3)
+ endif()
+ set (_files "")
+ foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC})
+ if (COTIRE_ARGV${_index})
+ list (APPEND _files "${COTIRE_ARGV${_index}}")
+ endif()
+ endforeach()
+
+ if (XCODE)
+ # executing pre-build action under Xcode, check dependency on files to be combined
+ set (_dependsOption DEPENDS ${_files})
+ else()
+ # executing custom command, no need to re-check for dependencies
+ set (_dependsOption "")
+ endif()
+
+ if (COTIRE_TARGET_LANGUAGE)
+ cotire_generate_unity_source(
+ "${_combinedFile}" ${_files}
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ ${_dependsOption})
+ else()
+ cotire_generate_unity_source("${_combinedFile}" ${_files} ${_dependsOption})
+ endif()
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup")
+
+ cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}")
+
+ else()
+ message (FATAL_ERROR "cotire: unknown command \"${COTIRE_ARGV1}\".")
+ endif()
+
+else()
+
+ # cotire is being run in include mode
+ # set up all variable and property definitions
+
+ if (NOT DEFINED COTIRE_DEBUG_INIT)
+ if (DEFINED COTIRE_DEBUG)
+ set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG})
+ else()
+ set (COTIRE_DEBUG_INIT FALSE)
+ endif()
+ endif()
+ option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT})
+
+ if (NOT DEFINED COTIRE_VERBOSE_INIT)
+ if (DEFINED COTIRE_VERBOSE)
+ set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE})
+ else()
+ set (COTIRE_VERBOSE_INIT FALSE)
+ endif()
+ endif()
+ option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT})
+
+ set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING
+ "Ignore headers with the listed file extensions from the generated prefix header.")
+
+ set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING
+ "Ignore headers from these directories when generating the prefix header.")
+
+ set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING
+ "Ignore sources with the listed file extensions from the generated unity source.")
+
+ set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING
+ "Minimum number of sources in target required to enable use of precompiled header.")
+
+ if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT)
+ if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES)
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES})
+ elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio")
+ # enable parallelization for generators that run multiple jobs by default
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j")
+ else()
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0")
+ endif()
+ endif()
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING
+ "Maximum number of source files to include in a single unity source file.")
+
+ if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX)
+ set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix")
+ endif()
+ if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX)
+ set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity")
+ endif()
+ if (NOT COTIRE_INTDIR)
+ set (COTIRE_INTDIR "cotire")
+ endif()
+ if (NOT COTIRE_PCH_ALL_TARGET_NAME)
+ set (COTIRE_PCH_ALL_TARGET_NAME "all_pch")
+ endif()
+ if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME)
+ set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity")
+ endif()
+ if (NOT COTIRE_CLEAN_ALL_TARGET_NAME)
+ set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire")
+ endif()
+ if (NOT COTIRE_CLEAN_TARGET_SUFFIX)
+ set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire")
+ endif()
+ if (NOT COTIRE_PCH_TARGET_SUFFIX)
+ set (COTIRE_PCH_TARGET_SUFFIX "_pch")
+ endif()
+ if (MSVC)
+ # MSVC default PCH memory scaling factor of 100 percent (75 MB) is too small for template heavy C++ code
+ # use a bigger default factor of 170 percent (128 MB)
+ if (NOT DEFINED COTIRE_PCH_MEMORY_SCALING_FACTOR)
+ set (COTIRE_PCH_MEMORY_SCALING_FACTOR "170")
+ endif()
+ endif()
+ if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX)
+ set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity")
+ endif()
+ if (NOT DEFINED COTIRE_TARGETS_FOLDER)
+ set (COTIRE_TARGETS_FOLDER "cotire")
+ endif()
+ if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY)
+ if ("${CMAKE_GENERATOR}" MATCHES "Ninja")
+ # generated Ninja build files do not work if the unity target produces the same output file as the cotired target
+ set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity")
+ else()
+ set (COTIRE_UNITY_OUTPUT_DIRECTORY "")
+ endif()
+ endif()
+
+ # define cotire cache variables
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH"
+ BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "The variable can be set to a semicolon separated list of include directories."
+ "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header."
+ "If not defined, defaults to empty list."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS"
+ BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header."
+ FULL_DOCS
+ "The variable can be set to a semicolon separated list of file extensions."
+ "If a header file extension matches one in the list, it will be excluded from the generated prefix header."
+ "Includes with an extension in CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS are always ignored."
+ "If not defined, defaults to inc;inl;ipp."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS"
+ BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source."
+ FULL_DOCS
+ "The variable can be set to a semicolon separated list of file extensions."
+ "If a source file extension matches one in the list, it will be excluded from the generated unity source file."
+ "Source files with an extension in CMAKE_<LANG>_IGNORE_EXTENSIONS are always excluded."
+ "If not defined, defaults to m;mm."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES"
+ BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header."
+ FULL_DOCS
+ "The variable can be set to an integer > 0."
+ "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target."
+ "If not defined, defaults to 3."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES"
+ BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
+ FULL_DOCS
+ "This may be set to an integer >= 0."
+ "If 0, cotire will only create a single unity source file."
+ "If a target contains more than that number of source files, cotire will create multiple unity source files for it."
+ "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores."
+ "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs."
+ "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES."
+ "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise."
+ )
+
+ # define cotire directory properties
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER"
+ BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header."
+ FULL_DOCS
+ "See target property COTIRE_ENABLE_PRECOMPILED_HEADER."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD"
+ BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory."
+ FULL_DOCS
+ "See target property COTIRE_ADD_UNITY_BUILD."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_ADD_CLEAN"
+ BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory."
+ FULL_DOCS
+ "See target property COTIRE_ADD_CLEAN."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH"
+ BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH"
+ BRIEF_DOCS "Honor headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH"
+ BRIEF_DOCS "Header paths matching one of these directories are put at the top of the prefix header."
+ FULL_DOCS
+ "See target property COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES"
+ BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_LINK_LIBRARIES_INIT"
+ BRIEF_DOCS "Define strategy for setting up the unity target's link libraries."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_LINK_LIBRARIES_INIT."
+ )
+
+ # define cotire target properties
+
+ define_property(
+ TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED
+ BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header."
+ "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target."
+ "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header."
+ "The target name will be set to this target's name with the suffix _pch appended."
+ "Inherited from directory."
+ "Defaults to TRUE."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED
+ BRIEF_DOCS "Add a new target that performs a unity build for this target."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources."
+ "Most of the relevant target properties will be copied from this target to the new unity build target."
+ "Target dependencies and linked libraries have to be manually set up for the new unity build target."
+ "The unity target name will be set to this target's name with the suffix _unity appended."
+ "Inherited from directory."
+ "Defaults to TRUE."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED
+ BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)."
+ "The clean target name will be set to this target's name with the suffix _clean_cotire appended."
+ "Inherited from directory."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED
+ BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "The property can be set to a list of directories."
+ "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header."
+ "Inherited from directory."
+ "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED
+ BRIEF_DOCS "Honor headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "The property can be set to a list of directories."
+ "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header."
+ "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH,"
+ "the option which yields the closer relative path match wins."
+ "Inherited from directory."
+ "If not set, this property is initialized to the empty list."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH" INHERITED
+ BRIEF_DOCS "Header paths matching one of these directories are put at the top of prefix header."
+ FULL_DOCS
+ "The property can be set to a list of directories."
+ "Header file paths matching one of these directories will be inserted at the beginning of the generated prefix header."
+ "Header files are sorted according to the order of the directories in the property."
+ "If not set, this property is initialized to the empty list."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file before each target source file."
+ "Inherited from directory."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file after each target source file."
+ "Inherited from directory."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED
+ BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
+ FULL_DOCS
+ "This may be set to an integer > 0."
+ "If a target contains more than that number of source files, cotire will create multiple unity build files for it."
+ "If not set, cotire will only create a single unity source file."
+ "Inherited from directory."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE_INIT"
+ BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one."
+ FULL_DOCS
+ "If set, cotire will only add the given file(s) to the generated unity source file."
+ "If not set, cotire will add all the target source files to the generated unity source file."
+ "The property can be set to a user provided unity source file."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER_INIT"
+ BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one."
+ FULL_DOCS
+ "If set, cotire will add the given header file(s) to the generated prefix header file."
+ "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file."
+ "The property can be set to a user provided prefix header file (e.g., stdafx.h)."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_LINK_LIBRARIES_INIT" INHERITED
+ BRIEF_DOCS "Define strategy for setting up unity target's link libraries."
+ FULL_DOCS
+ "If this property is empty or set to NONE, the generated unity target's link libraries have to be set up manually."
+ "If this property is set to COPY, the unity target's link libraries will be copied from this target."
+ "If this property is set to COPY_UNITY, the unity target's link libraries will be copied from this target with considering existing unity targets."
+ "Inherited from directory."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE"
+ BRIEF_DOCS "Read-only property. The generated <LANG> unity source file(s)."
+ FULL_DOCS
+ "cotire sets this property to the path of the generated <LANG> single computation unit source file for the target."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER"
+ BRIEF_DOCS "Read-only property. The generated <LANG> prefix header file."
+ FULL_DOCS
+ "cotire sets this property to the full path of the generated <LANG> language prefix header for the target."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_PRECOMPILED_HEADER"
+ BRIEF_DOCS "Read-only property. The generated <LANG> precompiled header file."
+ FULL_DOCS
+ "cotire sets this property to the full path of the generated <LANG> language precompiled header binary for the target."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME"
+ BRIEF_DOCS "The name of the generated unity build target corresponding to this target."
+ FULL_DOCS
+ "This property can be set to the desired name of the unity target that will be created by cotire."
+ "If not set, the unity target name will be set to this target's name with the suffix _unity appended."
+ "After this target has been processed by cotire, the property is set to the actual name of the generated unity target."
+ "Defaults to empty string."
+ )
+
+ # define cotire source properties
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_EXCLUDED"
+ BRIEF_DOCS "Do not modify source file's build command."
+ FULL_DOCS
+ "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header."
+ "The source file will also be excluded from the generated unity source file."
+ "Source files that have their COMPILE_FLAGS property set will be excluded by default."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_DEPENDENCY"
+ BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file."
+ FULL_DOCS
+ "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file."
+ "If the file is modified, cotire will re-generate the prefix header source upon build."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file before this file is included."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file after this file is included."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE"
+ BRIEF_DOCS "Start a new unity source file which includes this source file as the first one."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire will complete the current unity file and start a new one."
+ "The new unity source file will include this source file as the first one."
+ "This property essentially works as a separator for unity source files."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_TARGET"
+ BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target."
+ FULL_DOCS
+ "cotire sets this property to the name of target, that the source file's build command has been altered for."
+ "Defaults to empty string."
+ )
+
+ message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.")
+
+endif()
diff --git a/doc/Doxyfile b/doc/Doxyfile
index b4f5786..27806a6 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -5,7 +5,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "JSON for Modern C++"
-PROJECT_NUMBER = 2.1.0
+PROJECT_NUMBER = 2.1.1
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY = .
diff --git a/doc/Makefile b/doc/Makefile
index 04a5cc7..37a0e19 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -53,11 +53,11 @@
# create Doxygen documentation
doxygen: create_output create_links
doxygen
- $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType >@@g' html/*.html
- $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType >@@g' html/*.html
- $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
- $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
- $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType >@@g' html/*.html
+ $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
+ $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberFloatType, AllocatorType JSONSerializer >@@g' html/*.html
+ $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
+ $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer >@@g' html/*.html
+ $(SED) -i 's@< ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType JSONSerializer >@@g' html/*.html
upload: clean doxygen check_output
cd html ; ../scripts/git-update-ghpages nlohmann/json
diff --git a/doc/examples/meta.output b/doc/examples/meta.output
index f361bb6..933aac0 100644
--- a/doc/examples/meta.output
+++ b/doc/examples/meta.output
@@ -11,7 +11,7 @@
"version": {
"major": 2,
"minor": 1,
- "patch": 0,
- "string": "2.1.0"
+ "patch": 1,
+ "string": "2.1.1"
}
}
diff --git a/doc/index.md b/doc/index.md
index 2f12767..9493412 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -277,4 +277,4 @@
@author [Niels Lohmann](http://nlohmann.me)
@see https://github.com/nlohmann/json to download the source code
-@version 2.1.0
+@version 2.1.1
diff --git a/doc/json.gif b/doc/json.gif
index 9d05cfd..2fbcde6 100644
--- a/doc/json.gif
+++ b/doc/json.gif
Binary files differ
diff --git a/doc/usages/ios.png b/doc/usages/ios.png
new file mode 100755
index 0000000..1d2c1b8
--- /dev/null
+++ b/doc/usages/ios.png
Binary files differ
diff --git a/doc/usages/macos.png b/doc/usages/macos.png
new file mode 100644
index 0000000..107b5f0
--- /dev/null
+++ b/doc/usages/macos.png
Binary files differ
diff --git a/src/json.hpp b/src/json.hpp
index 5fdd83d..6dfc183 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -29,22 +29,22 @@
#ifndef NLOHMANN_JSON_HPP
#define NLOHMANN_JSON_HPP
-#include <algorithm> // all_of, for_each, transform
+#include <algorithm> // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform
#include <array> // array
#include <cassert> // assert
#include <cctype> // isdigit
#include <ciso646> // and, not, or
-#include <cmath> // isfinite, ldexp, signbit
+#include <cmath> // isfinite, labs, ldexp, signbit
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <cstdint> // int64_t, uint64_t
-#include <cstdlib> // strtod, strtof, strtold, strtoul
+#include <cstdlib> // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull
#include <cstring> // strlen
#include <forward_list> // forward_list
#include <functional> // function, hash, less
#include <initializer_list> // initializer_list
#include <iomanip> // setw
#include <iostream> // istream, ostream
-#include <iterator> // advance, begin, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
+#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
#include <limits> // numeric_limits
#include <locale> // locale
#include <map> // map
@@ -53,7 +53,7 @@
#include <sstream> // stringstream
#include <stdexcept> // domain_error, invalid_argument, out_of_range
#include <string> // getline, stoi, string, to_string
-#include <type_traits> // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference
+#include <type_traits> // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type
#include <utility> // declval, forward, make_pair, move, pair, swap
#include <vector> // vector
@@ -523,8 +523,9 @@
// to_json //
/////////////
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+template<typename BasicJsonType, typename T, enable_if_t<
+ std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+void to_json(BasicJsonType& j, T b) noexcept
{
external_constructor<value_t::boolean>::construct(j, b);
}
@@ -672,7 +673,7 @@
enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
void from_json(const BasicJsonType& j, UnscopedEnumType& e)
{
- typename std::underlying_type<UnscopedEnumType>::type val = e;
+ typename std::underlying_type<UnscopedEnumType>::type val;
get_arithmetic_value(j, val);
e = static_cast<UnscopedEnumType>(val);
}
@@ -916,7 +917,7 @@
@ref basic_json class (either explicit or via conversion operators).
@param[in] j JSON value to read from
- @param[in, out] val value to write to
+ @param[in,out] val value to write to
*/
template<typename BasicJsonType, typename ValueType>
static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
@@ -931,7 +932,7 @@
This function is usually called by the constructors of the @ref basic_json
class.
- @param[in, out] j JSON value to write to
+ @param[in,out] j JSON value to write to
@param[in] val value to read from
*/
template<typename BasicJsonType, typename ValueType>
@@ -1106,7 +1107,7 @@
/*!
@brief returns version information on the library
- This function returns a JSON object with infiormation about the library,
+ This function returns a JSON object with information about the library,
including the version number and information on the platform and compiler.
@return JSON object holding version information
@@ -1135,10 +1136,10 @@
result["url"] = "https://github.com/nlohmann/json";
result["version"] =
{
- {"string", "2.1.0"},
+ {"string", "2.1.1"},
{"major", 2},
{"minor", 1},
- {"patch", 0},
+ {"patch", 1}
};
#ifdef _WIN32
@@ -1746,7 +1747,7 @@
{
if (t == value_t::null)
{
- JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.0")); // LCOV_EXCL_LINE
+ JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE
}
break;
}
@@ -2646,14 +2647,6 @@
string_t dump(const int indent = -1) const
{
std::stringstream ss;
- // fix locale problems
- ss.imbue(std::locale::classic());
-
- // 6, 15 or 16 digits of precision allows round-trip IEEE 754
- // string->float->string, string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- ss.precision(std::numeric_limits<double>::digits10);
if (indent >= 0)
{
@@ -3036,10 +3029,8 @@
{
return m_value.boolean;
}
- else
- {
- JSON_THROW(std::domain_error("type must be boolean, but is " + type_name()));
- }
+
+ JSON_THROW(std::domain_error("type must be boolean, but is " + type_name()));
}
/// get a pointer to the value (object)
@@ -3151,8 +3142,8 @@
return *ptr;
}
- throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
- obj.type_name());
+ JSON_THROW(std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
+ obj.type_name()));
}
public:
@@ -3419,7 +3410,7 @@
/*!
@brief get a reference value (implicit)
- Implict reference access to the internally stored JSON value. No copies
+ Implicit reference access to the internally stored JSON value. No copies
are made.
@warning Writing data to the referee of the result yields an undefined
@@ -3494,7 +3485,7 @@
template < typename ValueType, typename std::enable_if <
not std::is_pointer<ValueType>::value and
not std::is_same<ValueType, typename string_t::value_type>::value
-#ifndef _MSC_VER // fix for issue #167 operator<< abiguity under VS2015
+#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif
, int >::type = 0 >
@@ -4287,7 +4278,7 @@
@complexity The complexity depends on the type:
- objects: amortized constant
- - arrays: linear in distance between pos and the end of the container
+ - arrays: linear in distance between @a pos and the end of the container
- strings: linear in the length of the string
- other types: constant
@@ -5539,7 +5530,7 @@
@throw std::domain_error if @a pos is not an iterator of *this; example:
`"iterator does not fit current value"`
- @complexity Constant plus linear in the distance between pos and end of
+ @complexity Constant plus linear in the distance between @a pos and end of
the container.
@liveexample{The example shows how `insert()` is used.,insert}
@@ -5965,34 +5956,24 @@
/*!
@brief comparison: equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__equal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
{
- return v.is_null();
+ return (lhs == basic_json(rhs));
}
/*!
@brief comparison: equal
- @copydoc operator==(const_reference, std::nullptr_t)
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
{
- return v.is_null();
+ return (basic_json(lhs) == rhs);
}
/*!
@@ -6018,34 +5999,24 @@
/*!
@brief comparison: not equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `not v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is not null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__notequal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
{
- return not v.is_null();
+ return (lhs != basic_json(rhs));
}
/*!
@brief comparison: not equal
- @copydoc operator!=(const_reference, std::nullptr_t)
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
{
- return not v.is_null();
+ return (basic_json(lhs) != rhs);
}
/*!
@@ -6236,10 +6207,6 @@
`std::setw(4)` on @a o sets the indentation level to `4` and the
serialization result is the same as calling `dump(4)`.
- @note During serializaion, the locale and the precision of the output
- stream @a o are changed. The original values are restored when the
- function returns.
-
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@@ -6261,22 +6228,9 @@
// reset width to 0 for subsequent calls to this stream
o.width(0);
- // fix locale problems
- const auto old_locale = o.imbue(std::locale::classic());
- // set precision
-
- // 6, 15 or 16 digits of precision allows round-trip IEEE 754
- // string->float->string, string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- const auto old_precision = o.precision(std::numeric_limits<double>::digits10);
-
// do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
- // reset locale and precision
- o.imbue(old_locale);
- o.precision(old_precision);
return o;
}
@@ -6583,6 +6537,11 @@
/// @{
private:
+ /*!
+ @note Some code in the switch cases has been copied, because otherwise
+ copilers would complain about implicit fallthrough and there is no
+ portable attribute to mute such warnings.
+ */
template<typename T>
static void add_to_vector(std::vector<uint8_t>& vec, size_t bytes, const T number)
{
@@ -6592,24 +6551,31 @@
{
case 8:
{
- vec.push_back(static_cast<uint8_t>((number >> 070) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 060) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 050) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 040) & 0xff));
- // intentional fall-through
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 070) & 0xff));
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 060) & 0xff));
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 050) & 0xff));
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 040) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
}
case 4:
{
vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff));
vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff));
- // intentional fall-through
+ vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
}
case 2:
{
vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
- // intentional fall-through
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
}
case 1:
@@ -6714,25 +6680,25 @@
// positive fixnum
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT8_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint8_t>::max())
{
// uint 8
v.push_back(0xcc);
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT16_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint16_t>::max())
{
// uint 16
v.push_back(0xcd);
add_to_vector(v, 2, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT32_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint32_t>::max())
{
// uint 32
v.push_back(0xce);
add_to_vector(v, 4, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT64_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint64_t>::max())
{
// uint 64
v.push_back(0xcf);
@@ -6746,25 +6712,25 @@
// negative fixnum
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int8_t>::min() and j.m_value.number_integer <= std::numeric_limits<int8_t>::max())
{
// int 8
v.push_back(0xd0);
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int16_t>::min() and j.m_value.number_integer <= std::numeric_limits<int16_t>::max())
{
// int 16
v.push_back(0xd1);
add_to_vector(v, 2, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int32_t>::min() and j.m_value.number_integer <= std::numeric_limits<int32_t>::max())
{
// int 32
v.push_back(0xd2);
add_to_vector(v, 4, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int64_t>::min() and j.m_value.number_integer <= std::numeric_limits<int64_t>::max())
{
// int 64
v.push_back(0xd3);
@@ -6781,25 +6747,25 @@
// positive fixnum
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT8_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint8_t>::max())
{
// uint 8
v.push_back(0xcc);
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT16_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint16_t>::max())
{
// uint 16
v.push_back(0xcd);
add_to_vector(v, 2, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT32_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint32_t>::max())
{
// uint 32
v.push_back(0xce);
add_to_vector(v, 4, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT64_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint64_t>::max())
{
// uint 64
v.push_back(0xcf);
@@ -6956,19 +6922,19 @@
{
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer <= UINT8_MAX)
+ else if (j.m_value.number_integer <= std::numeric_limits<uint8_t>::max())
{
v.push_back(0x18);
// one-byte uint8_t
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer <= UINT16_MAX)
+ else if (j.m_value.number_integer <= std::numeric_limits<uint16_t>::max())
{
v.push_back(0x19);
// two-byte uint16_t
add_to_vector(v, 2, j.m_value.number_integer);
}
- else if (j.m_value.number_integer <= UINT32_MAX)
+ else if (j.m_value.number_integer <= std::numeric_limits<uint32_t>::max())
{
v.push_back(0x1a);
// four-byte uint32_t
@@ -6990,19 +6956,19 @@
{
v.push_back(static_cast<uint8_t>(0x20 + positive_number));
}
- else if (positive_number <= UINT8_MAX)
+ else if (positive_number <= std::numeric_limits<uint8_t>::max())
{
// int 8
v.push_back(0x38);
add_to_vector(v, 1, positive_number);
}
- else if (positive_number <= UINT16_MAX)
+ else if (positive_number <= std::numeric_limits<uint16_t>::max())
{
// int 16
v.push_back(0x39);
add_to_vector(v, 2, positive_number);
}
- else if (positive_number <= UINT32_MAX)
+ else if (positive_number <= std::numeric_limits<uint32_t>::max())
{
// int 32
v.push_back(0x3a);
@@ -7068,7 +7034,7 @@
const auto N = j.m_value.string->size();
if (N <= 0x17)
{
- v.push_back(0x60 + N); // 1 byte for string + size
+ v.push_back(0x60 + static_cast<uint8_t>(N)); // 1 byte for string + size
}
else if (N <= 0xff)
{
@@ -7104,7 +7070,7 @@
const auto N = j.m_value.array->size();
if (N <= 0x17)
{
- v.push_back(0x80 + N); // 1 byte for array + size
+ v.push_back(0x80 + static_cast<uint8_t>(N)); // 1 byte for array + size
}
else if (N <= 0xff)
{
@@ -7142,7 +7108,7 @@
const auto N = j.m_value.object->size();
if (N <= 0x17)
{
- v.push_back(0xa0 + N); // 1 byte for object + size
+ v.push_back(0xa0 + static_cast<uint8_t>(N)); // 1 byte for object + size
}
else if (N <= 0xff)
{
@@ -7911,7 +7877,9 @@
}
else
{
- val = mant == 0 ? INFINITY : NAN;
+ val = mant == 0
+ ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
}
return (half & 0x8000) != 0 ? -val : val;
}
@@ -7964,9 +7932,11 @@
vector in MessagePack format.,to_msgpack}
@sa http://msgpack.org
- @sa @ref from_msgpack(const std::vector<uint8_t>&) for the analogous
- deserialization
+ @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
@sa @ref to_cbor(const basic_json& for the related CBOR format
+
+ @since version 2.0.9
*/
static std::vector<uint8_t> to_msgpack(const basic_json& j)
{
@@ -7982,6 +7952,7 @@
serialization format.
@param[in] v a byte vector in MessagePack format
+ @param[in] start_index the index to start reading from @a v (0 by default)
@return deserialized JSON value
@throw std::invalid_argument if unsupported features from MessagePack were
@@ -7995,11 +7966,15 @@
@sa http://msgpack.org
@sa @ref to_msgpack(const basic_json&) for the analogous serialization
- @sa @ref from_cbor(const std::vector<uint8_t>&) for the related CBOR format
+ @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ related CBOR format
+
+ @since version 2.0.9, parameter @a start_index since 2.1.1
*/
- static basic_json from_msgpack(const std::vector<uint8_t>& v)
+ static basic_json from_msgpack(const std::vector<uint8_t>& v,
+ const size_t start_index = 0)
{
- size_t i = 0;
+ size_t i = start_index;
return from_msgpack_internal(v, i);
}
@@ -8020,9 +7995,11 @@
vector in CBOR format.,to_cbor}
@sa http://cbor.io
- @sa @ref from_cbor(const std::vector<uint8_t>&) for the analogous
- deserialization
+ @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
@sa @ref to_msgpack(const basic_json& for the related MessagePack format
+
+ @since version 2.0.9
*/
static std::vector<uint8_t> to_cbor(const basic_json& j)
{
@@ -8038,6 +8015,7 @@
(Concise Binary Object Representation) serialization format.
@param[in] v a byte vector in CBOR format
+ @param[in] start_index the index to start reading from @a v (0 by default)
@return deserialized JSON value
@throw std::invalid_argument if unsupported features from CBOR were used in
@@ -8051,12 +8029,15 @@
@sa http://cbor.io
@sa @ref to_cbor(const basic_json&) for the analogous serialization
- @sa @ref from_msgpack(const std::vector<uint8_t>&) for the related
- MessagePack format
+ @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
+ related MessagePack format
+
+ @since version 2.0.9, parameter @a start_index since 2.1.1
*/
- static basic_json from_cbor(const std::vector<uint8_t>& v)
+ static basic_json from_cbor(const std::vector<uint8_t>& v,
+ const size_t start_index = 0)
{
- size_t i = 0;
+ size_t i = start_index;
return from_cbor_internal(v, i);
}
@@ -8266,6 +8247,154 @@
return result;
}
+
+ /*!
+ @brief locale-independent serialization for built-in arithmetic types
+ */
+ struct numtostr
+ {
+ public:
+ template<typename NumberType>
+ numtostr(NumberType value)
+ {
+ x_write(value, std::is_integral<NumberType>());
+ }
+
+ const char* c_str() const
+ {
+ return m_buf.data();
+ }
+
+ private:
+ /// a (hopefully) large enough character buffer
+ std::array < char, 64 > m_buf{{}};
+
+ template<typename NumberType>
+ void x_write(NumberType x, /*is_integral=*/std::true_type)
+ {
+ // special case for "0"
+ if (x == 0)
+ {
+ m_buf[0] = '0';
+ return;
+ }
+
+ const bool is_negative = x < 0;
+ size_t i = 0;
+
+ // spare 1 byte for '\0'
+ while (x != 0 and i < m_buf.size() - 1)
+ {
+ const auto digit = std::labs(static_cast<long>(x % 10));
+ m_buf[i++] = static_cast<char>('0' + digit);
+ x /= 10;
+ }
+
+ // make sure the number has been processed completely
+ assert(x == 0);
+
+ if (is_negative)
+ {
+ // make sure there is capacity for the '-'
+ assert(i < m_buf.size() - 2);
+ m_buf[i++] = '-';
+ }
+
+ std::reverse(m_buf.begin(), m_buf.begin() + i);
+ }
+
+ template<typename NumberType>
+ void x_write(NumberType x, /*is_integral=*/std::false_type)
+ {
+ // special case for 0.0 and -0.0
+ if (x == 0)
+ {
+ size_t i = 0;
+ if (std::signbit(x))
+ {
+ m_buf[i++] = '-';
+ }
+ m_buf[i++] = '0';
+ m_buf[i++] = '.';
+ m_buf[i] = '0';
+ return;
+ }
+
+ // get number of digits for a text -> float -> text round-trip
+ static constexpr auto d = std::numeric_limits<NumberType>::digits10;
+
+ // the actual conversion
+ const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+
+ // negative value indicates an error
+ assert(written_bytes > 0);
+ // check if buffer was large enough
+ assert(static_cast<size_t>(written_bytes) < m_buf.size());
+
+ // read information from locale
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ const char thousands_sep = !loc->thousands_sep ? '\0'
+ : loc->thousands_sep[0];
+
+ const char decimal_point = !loc->decimal_point ? '\0'
+ : loc->decimal_point[0];
+
+ // erase thousands separator
+ if (thousands_sep != '\0')
+ {
+ const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
+ std::fill(end, m_buf.end(), '\0');
+ }
+
+ // convert decimal point to '.'
+ if (decimal_point != '\0' and decimal_point != '.')
+ {
+ for (auto& c : m_buf)
+ {
+ if (c == decimal_point)
+ {
+ c = '.';
+ break;
+ }
+ }
+ }
+
+ // determine if need to append ".0"
+ size_t i = 0;
+ bool value_is_int_like = true;
+ for (i = 0; i < m_buf.size(); ++i)
+ {
+ // break when end of number is reached
+ if (m_buf[i] == '\0')
+ {
+ break;
+ }
+
+ // check if we find non-int character
+ value_is_int_like = value_is_int_like and m_buf[i] != '.' and
+ m_buf[i] != 'e' and m_buf[i] != 'E';
+ }
+
+ if (value_is_int_like)
+ {
+ // there must be 2 bytes left for ".0"
+ assert((i + 2) < m_buf.size());
+ // we write to the end of the number
+ assert(m_buf[i] == '\0');
+ assert(m_buf[i - 1] != '\0');
+
+ // add ".0"
+ m_buf[i] = '.';
+ m_buf[i + 1] = '0';
+
+ // the resulting string is properly terminated
+ assert(m_buf[i + 2] == '\0');
+ }
+ }
+ };
+
+
/*!
@brief internal implementation of the serialization function
@@ -8385,27 +8514,19 @@
case value_t::number_integer:
{
- o << m_value.number_integer;
+ o << numtostr(m_value.number_integer).c_str();
return;
}
case value_t::number_unsigned:
{
- o << m_value.number_unsigned;
+ o << numtostr(m_value.number_unsigned).c_str();
return;
}
case value_t::number_float:
{
- if (m_value.number_float == 0)
- {
- // special case for zero to get "0.0"/"-0.0"
- o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
- }
- else
- {
- o << m_value.number_float;
- }
+ o << numtostr(m_value.number_float).c_str();
return;
}
@@ -8534,10 +8655,11 @@
return *this;
}
- primitive_iterator_t& operator++(int)
+ primitive_iterator_t operator++(int)
{
+ auto result = *this;
m_it++;
- return *this;
+ return result;
}
primitive_iterator_t& operator--()
@@ -8546,10 +8668,11 @@
return *this;
}
- primitive_iterator_t& operator--(int)
+ primitive_iterator_t operator--(int)
{
+ auto result = *this;
m_it--;
- return *this;
+ return result;
}
primitive_iterator_t& operator+=(difference_type n)
@@ -9444,7 +9567,9 @@
literal_false, ///< the `false` literal
literal_null, ///< the `null` literal
value_string, ///< a string -- use get_string() for actual value
- value_number, ///< a number -- use get_number() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number() for actual value
+ value_integer, ///< a signed integer -- use get_number() for actual value
+ value_float, ///< an floating point number -- use get_number() for actual value
begin_array, ///< the character for array begin `[`
begin_object, ///< the character for object begin `{`
end_array, ///< the character for array end `]`
@@ -9596,7 +9721,9 @@
return "null literal";
case token_type::value_string:
return "string literal";
- case token_type::value_number:
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
return "number literal";
case token_type::begin_array:
return "'['";
@@ -9869,37 +9996,47 @@
}
if (yych <= '0')
{
- goto basic_json_parser_13;
+ goto basic_json_parser_43;
}
if (yych <= '9')
{
- goto basic_json_parser_15;
+ goto basic_json_parser_45;
}
goto basic_json_parser_5;
basic_json_parser_13:
yyaccept = 1;
yych = *(m_marker = ++m_cursor);
- if (yych <= 'D')
+ if (yych <= '9')
{
if (yych == '.')
{
- goto basic_json_parser_43;
+ goto basic_json_parser_47;
+ }
+ if (yych >= '0')
+ {
+ goto basic_json_parser_48;
}
}
else
{
if (yych <= 'E')
{
- goto basic_json_parser_44;
+ if (yych >= 'E')
+ {
+ goto basic_json_parser_51;
+ }
}
- if (yych == 'e')
+ else
{
- goto basic_json_parser_44;
+ if (yych == 'e')
+ {
+ goto basic_json_parser_51;
+ }
}
}
basic_json_parser_14:
{
- last_token_type = token_type::value_number;
+ last_token_type = token_type::value_unsigned;
break;
}
basic_json_parser_15:
@@ -9918,7 +10055,7 @@
{
if (yych == '.')
{
- goto basic_json_parser_43;
+ goto basic_json_parser_47;
}
goto basic_json_parser_14;
}
@@ -9926,11 +10063,11 @@
{
if (yych <= 'E')
{
- goto basic_json_parser_44;
+ goto basic_json_parser_51;
}
if (yych == 'e')
{
- goto basic_json_parser_44;
+ goto basic_json_parser_51;
}
goto basic_json_parser_14;
}
@@ -9957,7 +10094,7 @@
yych = *(m_marker = ++m_cursor);
if (yych == 'a')
{
- goto basic_json_parser_45;
+ goto basic_json_parser_52;
}
goto basic_json_parser_5;
basic_json_parser_24:
@@ -9965,7 +10102,7 @@
yych = *(m_marker = ++m_cursor);
if (yych == 'u')
{
- goto basic_json_parser_46;
+ goto basic_json_parser_53;
}
goto basic_json_parser_5;
basic_json_parser_25:
@@ -9973,7 +10110,7 @@
yych = *(m_marker = ++m_cursor);
if (yych == 'r')
{
- goto basic_json_parser_47;
+ goto basic_json_parser_54;
}
goto basic_json_parser_5;
basic_json_parser_26:
@@ -10055,13 +10192,27 @@
}
basic_json_parser_32:
m_cursor = m_marker;
- if (yyaccept == 0)
+ if (yyaccept <= 1)
{
- goto basic_json_parser_5;
+ if (yyaccept == 0)
+ {
+ goto basic_json_parser_5;
+ }
+ else
+ {
+ goto basic_json_parser_14;
+ }
}
else
{
- goto basic_json_parser_14;
+ if (yyaccept == 2)
+ {
+ goto basic_json_parser_44;
+ }
+ else
+ {
+ goto basic_json_parser_58;
+ }
}
basic_json_parser_33:
++m_cursor;
@@ -10142,7 +10293,7 @@
}
if (yych <= 'u')
{
- goto basic_json_parser_48;
+ goto basic_json_parser_55;
}
goto basic_json_parser_32;
}
@@ -10261,6 +10412,81 @@
}
goto basic_json_parser_32;
basic_json_parser_43:
+ yyaccept = 2;
+ yych = *(m_marker = ++m_cursor);
+ if (yych <= '9')
+ {
+ if (yych == '.')
+ {
+ goto basic_json_parser_47;
+ }
+ if (yych >= '0')
+ {
+ goto basic_json_parser_48;
+ }
+ }
+ else
+ {
+ if (yych <= 'E')
+ {
+ if (yych >= 'E')
+ {
+ goto basic_json_parser_51;
+ }
+ }
+ else
+ {
+ if (yych == 'e')
+ {
+ goto basic_json_parser_51;
+ }
+ }
+ }
+basic_json_parser_44:
+ {
+ last_token_type = token_type::value_integer;
+ break;
+ }
+basic_json_parser_45:
+ yyaccept = 2;
+ m_marker = ++m_cursor;
+ if ((m_limit - m_cursor) < 3)
+ {
+ fill_line_buffer(3); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
+ if (yych <= '9')
+ {
+ if (yych == '.')
+ {
+ goto basic_json_parser_47;
+ }
+ if (yych <= '/')
+ {
+ goto basic_json_parser_44;
+ }
+ goto basic_json_parser_45;
+ }
+ else
+ {
+ if (yych <= 'E')
+ {
+ if (yych <= 'D')
+ {
+ goto basic_json_parser_44;
+ }
+ goto basic_json_parser_51;
+ }
+ else
+ {
+ if (yych == 'e')
+ {
+ goto basic_json_parser_51;
+ }
+ goto basic_json_parser_44;
+ }
+ }
+basic_json_parser_47:
yych = *++m_cursor;
if (yych <= '/')
{
@@ -10268,53 +10494,6 @@
}
if (yych <= '9')
{
- goto basic_json_parser_49;
- }
- goto basic_json_parser_32;
-basic_json_parser_44:
- yych = *++m_cursor;
- if (yych <= ',')
- {
- if (yych == '+')
- {
- goto basic_json_parser_51;
- }
- goto basic_json_parser_32;
- }
- else
- {
- if (yych <= '-')
- {
- goto basic_json_parser_51;
- }
- if (yych <= '/')
- {
- goto basic_json_parser_32;
- }
- if (yych <= '9')
- {
- goto basic_json_parser_52;
- }
- goto basic_json_parser_32;
- }
-basic_json_parser_45:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_54;
- }
- goto basic_json_parser_32;
-basic_json_parser_46:
- yych = *++m_cursor;
- if (yych == 'l')
- {
- goto basic_json_parser_55;
- }
- goto basic_json_parser_32;
-basic_json_parser_47:
- yych = *++m_cursor;
- if (yych == 'u')
- {
goto basic_json_parser_56;
}
goto basic_json_parser_32;
@@ -10325,6 +10504,73 @@
fill_line_buffer(1); // LCOV_EXCL_LINE
}
yych = *m_cursor;
+ if (yych <= '/')
+ {
+ goto basic_json_parser_50;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_48;
+ }
+basic_json_parser_50:
+ {
+ last_token_type = token_type::parse_error;
+ break;
+ }
+basic_json_parser_51:
+ yych = *++m_cursor;
+ if (yych <= ',')
+ {
+ if (yych == '+')
+ {
+ goto basic_json_parser_59;
+ }
+ goto basic_json_parser_32;
+ }
+ else
+ {
+ if (yych <= '-')
+ {
+ goto basic_json_parser_59;
+ }
+ if (yych <= '/')
+ {
+ goto basic_json_parser_32;
+ }
+ if (yych <= '9')
+ {
+ goto basic_json_parser_60;
+ }
+ goto basic_json_parser_32;
+ }
+basic_json_parser_52:
+ yych = *++m_cursor;
+ if (yych == 'l')
+ {
+ goto basic_json_parser_62;
+ }
+ goto basic_json_parser_32;
+basic_json_parser_53:
+ yych = *++m_cursor;
+ if (yych == 'l')
+ {
+ goto basic_json_parser_63;
+ }
+ goto basic_json_parser_32;
+basic_json_parser_54:
+ yych = *++m_cursor;
+ if (yych == 'u')
+ {
+ goto basic_json_parser_64;
+ }
+ goto basic_json_parser_32;
+basic_json_parser_55:
+ ++m_cursor;
+ if (m_limit <= m_cursor)
+ {
+ fill_line_buffer(1); // LCOV_EXCL_LINE
+ }
+ yych = *m_cursor;
if (yych <= '@')
{
if (yych <= '/')
@@ -10333,7 +10579,7 @@
}
if (yych <= '9')
{
- goto basic_json_parser_57;
+ goto basic_json_parser_65;
}
goto basic_json_parser_32;
}
@@ -10341,7 +10587,7 @@
{
if (yych <= 'F')
{
- goto basic_json_parser_57;
+ goto basic_json_parser_65;
}
if (yych <= '`')
{
@@ -10349,12 +10595,12 @@
}
if (yych <= 'f')
{
- goto basic_json_parser_57;
+ goto basic_json_parser_65;
}
goto basic_json_parser_32;
}
-basic_json_parser_49:
- yyaccept = 1;
+basic_json_parser_56:
+ yyaccept = 3;
m_marker = ++m_cursor;
if ((m_limit - m_cursor) < 3)
{
@@ -10365,27 +10611,30 @@
{
if (yych <= '/')
{
- goto basic_json_parser_14;
+ goto basic_json_parser_58;
}
if (yych <= '9')
{
- goto basic_json_parser_49;
+ goto basic_json_parser_56;
}
- goto basic_json_parser_14;
}
else
{
if (yych <= 'E')
{
- goto basic_json_parser_44;
+ goto basic_json_parser_51;
}
if (yych == 'e')
{
- goto basic_json_parser_44;
+ goto basic_json_parser_51;
}
- goto basic_json_parser_14;
}
-basic_json_parser_51:
+basic_json_parser_58:
+ {
+ last_token_type = token_type::value_float;
+ break;
+ }
+basic_json_parser_59:
yych = *++m_cursor;
if (yych <= '/')
{
@@ -10395,7 +10644,7 @@
{
goto basic_json_parser_32;
}
-basic_json_parser_52:
+basic_json_parser_60:
++m_cursor;
if (m_limit <= m_cursor)
{
@@ -10404,35 +10653,35 @@
yych = *m_cursor;
if (yych <= '/')
{
- goto basic_json_parser_14;
+ goto basic_json_parser_58;
}
if (yych <= '9')
{
- goto basic_json_parser_52;
+ goto basic_json_parser_60;
}
- goto basic_json_parser_14;
-basic_json_parser_54:
+ goto basic_json_parser_58;
+basic_json_parser_62:
yych = *++m_cursor;
if (yych == 's')
{
- goto basic_json_parser_58;
+ goto basic_json_parser_66;
}
goto basic_json_parser_32;
-basic_json_parser_55:
+basic_json_parser_63:
yych = *++m_cursor;
if (yych == 'l')
{
- goto basic_json_parser_59;
+ goto basic_json_parser_67;
}
goto basic_json_parser_32;
-basic_json_parser_56:
+basic_json_parser_64:
yych = *++m_cursor;
if (yych == 'e')
{
- goto basic_json_parser_61;
+ goto basic_json_parser_69;
}
goto basic_json_parser_32;
-basic_json_parser_57:
+basic_json_parser_65:
++m_cursor;
if (m_limit <= m_cursor)
{
@@ -10447,7 +10696,7 @@
}
if (yych <= '9')
{
- goto basic_json_parser_63;
+ goto basic_json_parser_71;
}
goto basic_json_parser_32;
}
@@ -10455,7 +10704,7 @@
{
if (yych <= 'F')
{
- goto basic_json_parser_63;
+ goto basic_json_parser_71;
}
if (yych <= '`')
{
@@ -10463,30 +10712,30 @@
}
if (yych <= 'f')
{
- goto basic_json_parser_63;
+ goto basic_json_parser_71;
}
goto basic_json_parser_32;
}
-basic_json_parser_58:
+basic_json_parser_66:
yych = *++m_cursor;
if (yych == 'e')
{
- goto basic_json_parser_64;
+ goto basic_json_parser_72;
}
goto basic_json_parser_32;
-basic_json_parser_59:
+basic_json_parser_67:
++m_cursor;
{
last_token_type = token_type::literal_null;
break;
}
-basic_json_parser_61:
+basic_json_parser_69:
++m_cursor;
{
last_token_type = token_type::literal_true;
break;
}
-basic_json_parser_63:
+basic_json_parser_71:
++m_cursor;
if (m_limit <= m_cursor)
{
@@ -10501,7 +10750,7 @@
}
if (yych <= '9')
{
- goto basic_json_parser_66;
+ goto basic_json_parser_74;
}
goto basic_json_parser_32;
}
@@ -10509,7 +10758,7 @@
{
if (yych <= 'F')
{
- goto basic_json_parser_66;
+ goto basic_json_parser_74;
}
if (yych <= '`')
{
@@ -10517,17 +10766,17 @@
}
if (yych <= 'f')
{
- goto basic_json_parser_66;
+ goto basic_json_parser_74;
}
goto basic_json_parser_32;
}
-basic_json_parser_64:
+basic_json_parser_72:
++m_cursor;
{
last_token_type = token_type::literal_false;
break;
}
-basic_json_parser_66:
+basic_json_parser_74:
++m_cursor;
if (m_limit <= m_cursor)
{
@@ -10624,7 +10873,7 @@
if (m_stream == nullptr or m_stream->eof())
{
// m_start may or may not be pointing into m_line_buffer at
- // this point. We trust the standand library to do the right
+ // this point. We trust the standard library to do the right
// thing. See http://stackoverflow.com/q/28142011/266378
m_line_buffer.assign(m_start, m_limit);
@@ -10712,7 +10961,7 @@
m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This
can be rephrased to m_cursor - m_start - 2 > x. With the
precondition, we x <= 0, meaning that the loop condition holds
- indefinitly if i is always decreased. However, observe that the value
+ indefinitely if i is always decreased. However, observe that the value
of i is strictly increasing with each iteration, as it is incremented
by 1 in the iteration expression and never decremented inside the loop
body. Hence, the loop condition will eventually be false which
@@ -10838,59 +11087,155 @@
return result;
}
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- long double str_to_float_t(long double* /* type */, char** endptr) const
- {
- return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
/*!
- @brief parse floating point number
+ @brief parse string into a built-in arithmetic type as if the current
+ locale is POSIX.
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
+ @note in floating-point case strtod may parse past the token's end -
+ this is not an error
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
+ @note any leading blanks are not handled
*/
- double str_to_float_t(double* /* type */, char** endptr) const
+ struct strtonum
{
- return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
+ public:
+ strtonum(const char* start, const char* end)
+ : m_start(start), m_end(end)
+ {}
- /*!
- @brief parse floating point number
+ /*!
+ @return true iff parsed successfully as number of type T
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
+ @param[in,out] val shall contain parsed value, or undefined value
+ if could not parse
+ */
+ template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
+ bool to(T& val) const
+ {
+ return parse(val, std::is_integral<T>());
+ }
- @param[in,out] endptr recieves a pointer to the first character after
- the number
+ private:
+ const char* const m_start = nullptr;
+ const char* const m_end = nullptr;
- @return the floating point number
- */
- float str_to_float_t(float* /* type */, char** endptr) const
- {
- return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
+ // floating-point conversion
+
+ // overloaded wrappers for strtod/strtof/strtold
+ // that will be called from parse<floating_point_t>
+ static void strtof(float& f, const char* str, char** endptr)
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ static void strtof(double& f, const char* str, char** endptr)
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ static void strtof(long double& f, const char* str, char** endptr)
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ template<typename T>
+ bool parse(T& value, /*is_integral=*/std::false_type) const
+ {
+ // replace decimal separator with locale-specific version,
+ // when necessary; data will point to either the original
+ // string, or buf, or tempstr containing the fixed string.
+ std::string tempstr;
+ std::array<char, 64> buf;
+ const size_t len = static_cast<size_t>(m_end - m_start);
+
+ // lexer will reject empty numbers
+ assert(len > 0);
+
+ // since dealing with strtod family of functions, we're
+ // getting the decimal point char from the C locale facilities
+ // instead of C++'s numpunct facet of the current std::locale
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0];
+
+ const char* data = m_start;
+
+ if (decimal_point_char != '.')
+ {
+ const size_t ds_pos = static_cast<size_t>(std::find(m_start, m_end, '.') - m_start);
+
+ if (ds_pos != len)
+ {
+ // copy the data into the local buffer or tempstr, if
+ // buffer is too small; replace decimal separator, and
+ // update data to point to the modified bytes
+ if ((len + 1) < buf.size())
+ {
+ std::copy(m_start, m_end, buf.begin());
+ buf[len] = 0;
+ buf[ds_pos] = decimal_point_char;
+ data = buf.data();
+ }
+ else
+ {
+ tempstr.assign(m_start, m_end);
+ tempstr[ds_pos] = decimal_point_char;
+ data = tempstr.c_str();
+ }
+ }
+ }
+
+ char* endptr = nullptr;
+ value = 0;
+ // this calls appropriate overload depending on T
+ strtof(value, data, &endptr);
+
+ // parsing was successful iff strtof parsed exactly the number
+ // of characters determined by the lexer (len)
+ const bool ok = (endptr == (data + len));
+
+ if (ok and (value == static_cast<T>(0.0)) and (*data == '-'))
+ {
+ // some implementations forget to negate the zero
+ value = -0.0;
+ }
+
+ return ok;
+ }
+
+ // integral conversion
+
+ signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const
+ {
+ return std::strtoll(m_start, endptr, 10);
+ }
+
+ unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const
+ {
+ return std::strtoull(m_start, endptr, 10);
+ }
+
+ template<typename T>
+ bool parse(T& value, /*is_integral=*/std::true_type) const
+ {
+ char* endptr = nullptr;
+ errno = 0; // these are thread-local
+ const auto x = parse_integral(&endptr, std::is_signed<T>());
+
+ // called right overload?
+ static_assert(std::is_signed<T>() == std::is_signed<decltype(x)>(), "");
+
+ value = static_cast<T>(x);
+
+ return (x == static_cast<decltype(x)>(value)) // x fits into destination T
+ and (x < 0) == (value < 0) // preserved sign
+ //and ((x != 0) or is_integral()) // strto[u]ll did nto fail
+ and (errno == 0) // strto[u]ll did not overflow
+ and (m_start < m_end) // token was not empty
+ and (endptr == m_end); // parsed entire token exactly
+ }
+ };
/*!
@brief return number value for number tokens
@@ -10899,125 +11244,84 @@
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
- This function parses the integer component up to the radix point or
- exponent while collecting information about the 'floating point
- representation', which it stores in the result parameter. If there is
- no radix point or exponent, and the number can fit into a @ref
- number_integer_t or @ref number_unsigned_t then it sets the result
- parameter accordingly.
+ integral numbers that don't fit into the the range of the respective
+ type are parsed as number_float_t
- If the number is a floating point number the number is then parsed
- using @a std:strtod (or @a std:strtof or @a std::strtold).
+ floating-point values do not satisfy std::isfinite predicate
+ are converted to value_t::null
- @param[out] result @ref basic_json object to receive the number, or
- NAN if the conversion read past the current token. The latter case
- needs to be treated by the caller function.
+ throws if the entire string [m_start .. m_cursor) cannot be
+ interpreted as a number
+
+ @param[out] result @ref basic_json object to receive the number.
+ @param[in] token the type of the number token
*/
- void get_number(basic_json& result) const
+ bool get_number(basic_json& result, const token_type token) const
{
assert(m_start != nullptr);
+ assert(m_start < m_cursor);
+ assert((token == token_type::value_unsigned) or
+ (token == token_type::value_integer) or
+ (token == token_type::value_float));
- const lexer::lexer_char_t* curptr = m_start;
+ strtonum num_converter(reinterpret_cast<const char*>(m_start),
+ reinterpret_cast<const char*>(m_cursor));
- // accumulate the integer conversion result (unsigned for now)
- number_unsigned_t value = 0;
-
- // maximum absolute value of the relevant integer type
- number_unsigned_t max;
-
- // temporarily store the type to avoid unecessary bitfield access
- value_t type;
-
- // look for sign
- if (*curptr == '-')
+ switch (token)
{
- type = value_t::number_integer;
- max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
- curptr++;
- }
- else
- {
- type = value_t::number_unsigned;
- max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
- }
-
- // count the significant figures
- for (; curptr < m_cursor; curptr++)
- {
- // quickly skip tests if a digit
- if (*curptr < '0' or* curptr > '9')
+ case lexer::token_type::value_unsigned:
{
- if (*curptr == '.')
+ number_unsigned_t val;
+ if (num_converter.to(val))
{
- // don't count '.' but change to float
- type = value_t::number_float;
- continue;
+ // parsing successful
+ result.m_type = value_t::number_unsigned;
+ result.m_value = val;
+ return true;
}
- // assume exponent (if not then will fail parse): change to
- // float, stop counting and record exponent details
- type = value_t::number_float;
break;
}
- // skip if definitely not an integer
- if (type != value_t::number_float)
+ case lexer::token_type::value_integer:
{
- auto digit = static_cast<number_unsigned_t>(*curptr - '0');
-
- // overflow if value * 10 + digit > max, move terms around
- // to avoid overflow in intermediate values
- if (value > (max - digit) / 10)
+ number_integer_t val;
+ if (num_converter.to(val))
{
- // overflow
- type = value_t::number_float;
+ // parsing successful
+ result.m_type = value_t::number_integer;
+ result.m_value = val;
+ return true;
}
- else
- {
- // no overflow
- value = value * 10 + digit;
- }
+ break;
+ }
+
+ default:
+ {
+ break;
}
}
- // save the value (if not a float)
- if (type == value_t::number_unsigned)
+ // parse float (either explicitly or because a previous conversion
+ // failed)
+ number_float_t val;
+ if (num_converter.to(val))
{
- result.m_value.number_unsigned = value;
- }
- else if (type == value_t::number_integer)
- {
- // invariant: if we parsed a '-', the absolute value is between
- // 0 (we allow -0) and max == -INT64_MIN
- assert(value >= 0);
- assert(value <= max);
-
- if (value == max)
- {
- // we cannot simply negate value (== max == -INT64_MIN),
- // see https://github.com/nlohmann/json/issues/389
- result.m_value.number_integer = static_cast<number_integer_t>(INT64_MIN);
- }
- else
- {
- // all other values can be negated safely
- result.m_value.number_integer = -static_cast<number_integer_t>(value);
- }
- }
- else
- {
- // parse with strtod
- result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), nullptr);
+ // parsing successful
+ result.m_type = value_t::number_float;
+ result.m_value = val;
// replace infinity and NAN by null
if (not std::isfinite(result.m_value.number_float))
{
- type = value_t::null;
+ result.m_type = value_t::null;
result.m_value = basic_json::json_value();
}
+
+ return true;
}
- // save the type
- result.m_type = type;
+ // couldn't parse number in any format
+ return false;
}
private:
@@ -11261,9 +11565,11 @@
break;
}
- case lexer::token_type::value_number:
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
{
- m_lexer.get_number(result);
+ m_lexer.get_number(result, last_token);
get_token();
break;
}
@@ -11558,7 +11864,7 @@
if (reference_token == "-")
{
- // explicityly treat "-" as index beyond the end
+ // explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
@@ -11597,9 +11903,9 @@
if (reference_token == "-")
{
// "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
+ JSON_THROW(std::out_of_range("array index '-' (" +
+ std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
@@ -11649,9 +11955,9 @@
if (reference_token == "-")
{
// "-" cannot be used for const access
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
+ JSON_THROW(std::out_of_range("array index '-' (" +
+ std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
@@ -11693,9 +11999,9 @@
if (reference_token == "-")
{
// "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
+ JSON_THROW(std::out_of_range("array index '-' (" +
+ std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
@@ -12065,7 +12371,7 @@
primitive. The original JSON value can be restored using the @ref
unflatten() function.
- @return an object that maps JSON pointers to primitve values
+ @return an object that maps JSON pointers to primitive values
@note Empty objects and arrays are flattened to `null` and will not be
reconstructed correctly by the @ref unflatten() function.
@@ -12132,7 +12438,7 @@
[JSON Patch](http://jsonpatch.com) defines a JSON document structure for
expressing a sequence of operations to apply to a JSON) document. With
- this funcion, a JSON Patch is applied to the current JSON value by
+ this function, a JSON Patch is applied to the current JSON value by
executing all operations from the patch.
@param[in] json_patch JSON patch document
@@ -12300,7 +12606,7 @@
JSON_THROW(std::invalid_argument("JSON patch must be an array of objects"));
}
- // iterate and apply th eoperations
+ // iterate and apply the operations
for (const auto& val : json_patch)
{
// wrapper to get a value for an operation
@@ -12439,8 +12745,8 @@
@note Currently, only `remove`, `add`, and `replace` operations are
generated.
- @param[in] source JSON value to copare from
- @param[in] target JSON value to copare against
+ @param[in] source JSON value to compare from
+ @param[in] target JSON value to compare against
@param[in] path helper value to create JSON pointers
@return a JSON patch to convert the @a source to @a target
@@ -12689,9 +12995,9 @@
#endif
// clean up
-#undef JSON_THROW
-#undef JSON_TRY
#undef JSON_CATCH
#undef JSON_DEPRECATED
+#undef JSON_THROW
+#undef JSON_TRY
#endif
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 068b876..a42daba 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -29,22 +29,22 @@
#ifndef NLOHMANN_JSON_HPP
#define NLOHMANN_JSON_HPP
-#include <algorithm> // all_of, for_each, transform
+#include <algorithm> // all_of, copy, fill, find, for_each, none_of, remove, reverse, transform
#include <array> // array
#include <cassert> // assert
#include <cctype> // isdigit
#include <ciso646> // and, not, or
-#include <cmath> // isfinite, ldexp, signbit
+#include <cmath> // isfinite, labs, ldexp, signbit
#include <cstddef> // nullptr_t, ptrdiff_t, size_t
#include <cstdint> // int64_t, uint64_t
-#include <cstdlib> // strtod, strtof, strtold, strtoul
+#include <cstdlib> // abort, strtod, strtof, strtold, strtoul, strtoll, strtoull
#include <cstring> // strlen
#include <forward_list> // forward_list
#include <functional> // function, hash, less
#include <initializer_list> // initializer_list
#include <iomanip> // setw
#include <iostream> // istream, ostream
-#include <iterator> // advance, begin, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
+#include <iterator> // advance, begin, back_inserter, bidirectional_iterator_tag, distance, end, inserter, iterator, iterator_traits, next, random_access_iterator_tag, reverse_iterator
#include <limits> // numeric_limits
#include <locale> // locale
#include <map> // map
@@ -53,7 +53,7 @@
#include <sstream> // stringstream
#include <stdexcept> // domain_error, invalid_argument, out_of_range
#include <string> // getline, stoi, string, to_string
-#include <type_traits> // add_pointer, enable_if, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_floating_point, is_integral, is_nothrow_move_assignable, std::is_nothrow_move_constructible, std::is_pointer, std::is_reference, std::is_same, remove_const, remove_pointer, remove_reference
+#include <type_traits> // add_pointer, conditional, decay, enable_if, false_type, integral_constant, is_arithmetic, is_base_of, is_const, is_constructible, is_convertible, is_default_constructible, is_enum, is_floating_point, is_integral, is_nothrow_move_assignable, is_nothrow_move_constructible, is_pointer, is_reference, is_same, is_scalar, is_signed, remove_const, remove_cv, remove_pointer, remove_reference, true_type, underlying_type
#include <utility> // declval, forward, make_pair, move, pair, swap
#include <vector> // vector
@@ -523,8 +523,9 @@
// to_json //
/////////////
-template<typename BasicJsonType>
-void to_json(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+template<typename BasicJsonType, typename T, enable_if_t<
+ std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+void to_json(BasicJsonType& j, T b) noexcept
{
external_constructor<value_t::boolean>::construct(j, b);
}
@@ -672,7 +673,7 @@
enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0>
void from_json(const BasicJsonType& j, UnscopedEnumType& e)
{
- typename std::underlying_type<UnscopedEnumType>::type val = e;
+ typename std::underlying_type<UnscopedEnumType>::type val;
get_arithmetic_value(j, val);
e = static_cast<UnscopedEnumType>(val);
}
@@ -916,7 +917,7 @@
@ref basic_json class (either explicit or via conversion operators).
@param[in] j JSON value to read from
- @param[in, out] val value to write to
+ @param[in,out] val value to write to
*/
template<typename BasicJsonType, typename ValueType>
static void from_json(BasicJsonType&& j, ValueType& val) noexcept(
@@ -931,7 +932,7 @@
This function is usually called by the constructors of the @ref basic_json
class.
- @param[in, out] j JSON value to write to
+ @param[in,out] j JSON value to write to
@param[in] val value to read from
*/
template<typename BasicJsonType, typename ValueType>
@@ -1106,7 +1107,7 @@
/*!
@brief returns version information on the library
- This function returns a JSON object with infiormation about the library,
+ This function returns a JSON object with information about the library,
including the version number and information on the platform and compiler.
@return JSON object holding version information
@@ -1135,10 +1136,10 @@
result["url"] = "https://github.com/nlohmann/json";
result["version"] =
{
- {"string", "2.1.0"},
+ {"string", "2.1.1"},
{"major", 2},
{"minor", 1},
- {"patch", 0},
+ {"patch", 1}
};
#ifdef _WIN32
@@ -1746,7 +1747,7 @@
{
if (t == value_t::null)
{
- JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.0")); // LCOV_EXCL_LINE
+ JSON_THROW(std::domain_error("961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE
}
break;
}
@@ -2646,14 +2647,6 @@
string_t dump(const int indent = -1) const
{
std::stringstream ss;
- // fix locale problems
- ss.imbue(std::locale::classic());
-
- // 6, 15 or 16 digits of precision allows round-trip IEEE 754
- // string->float->string, string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- ss.precision(std::numeric_limits<double>::digits10);
if (indent >= 0)
{
@@ -3036,10 +3029,8 @@
{
return m_value.boolean;
}
- else
- {
- JSON_THROW(std::domain_error("type must be boolean, but is " + type_name()));
- }
+
+ JSON_THROW(std::domain_error("type must be boolean, but is " + type_name()));
}
/// get a pointer to the value (object)
@@ -3151,8 +3142,8 @@
return *ptr;
}
- throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
- obj.type_name());
+ JSON_THROW(std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
+ obj.type_name()));
}
public:
@@ -3419,7 +3410,7 @@
/*!
@brief get a reference value (implicit)
- Implict reference access to the internally stored JSON value. No copies
+ Implicit reference access to the internally stored JSON value. No copies
are made.
@warning Writing data to the referee of the result yields an undefined
@@ -3494,7 +3485,7 @@
template < typename ValueType, typename std::enable_if <
not std::is_pointer<ValueType>::value and
not std::is_same<ValueType, typename string_t::value_type>::value
-#ifndef _MSC_VER // fix for issue #167 operator<< abiguity under VS2015
+#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
#endif
, int >::type = 0 >
@@ -4287,7 +4278,7 @@
@complexity The complexity depends on the type:
- objects: amortized constant
- - arrays: linear in distance between pos and the end of the container
+ - arrays: linear in distance between @a pos and the end of the container
- strings: linear in the length of the string
- other types: constant
@@ -5539,7 +5530,7 @@
@throw std::domain_error if @a pos is not an iterator of *this; example:
`"iterator does not fit current value"`
- @complexity Constant plus linear in the distance between pos and end of
+ @complexity Constant plus linear in the distance between @a pos and end of
the container.
@liveexample{The example shows how `insert()` is used.,insert}
@@ -5965,34 +5956,24 @@
/*!
@brief comparison: equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__equal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept
{
- return v.is_null();
+ return (lhs == basic_json(rhs));
}
/*!
@brief comparison: equal
- @copydoc operator==(const_reference, std::nullptr_t)
+ @copydoc operator==(const_reference, const_reference)
*/
- friend bool operator==(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept
{
- return v.is_null();
+ return (basic_json(lhs) == rhs);
}
/*!
@@ -6018,34 +5999,24 @@
/*!
@brief comparison: not equal
-
- The functions compares the given JSON value against a null pointer. As the
- null pointer can be used to initialize a JSON value to null, a comparison
- of JSON value @a v with a null pointer should be equivalent to call
- `not v.is_null()`.
-
- @param[in] v JSON value to consider
- @return whether @a v is not null
-
- @complexity Constant.
-
- @liveexample{The example compares several JSON types to the null pointer.
- ,operator__notequal__nullptr_t}
-
- @since version 1.0.0
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(const_reference v, std::nullptr_t) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept
{
- return not v.is_null();
+ return (lhs != basic_json(rhs));
}
/*!
@brief comparison: not equal
- @copydoc operator!=(const_reference, std::nullptr_t)
+ @copydoc operator!=(const_reference, const_reference)
*/
- friend bool operator!=(std::nullptr_t, const_reference v) noexcept
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept
{
- return not v.is_null();
+ return (basic_json(lhs) != rhs);
}
/*!
@@ -6236,10 +6207,6 @@
`std::setw(4)` on @a o sets the indentation level to `4` and the
serialization result is the same as calling `dump(4)`.
- @note During serializaion, the locale and the precision of the output
- stream @a o are changed. The original values are restored when the
- function returns.
-
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@@ -6261,22 +6228,9 @@
// reset width to 0 for subsequent calls to this stream
o.width(0);
- // fix locale problems
- const auto old_locale = o.imbue(std::locale::classic());
- // set precision
-
- // 6, 15 or 16 digits of precision allows round-trip IEEE 754
- // string->float->string, string->double->string or string->long
- // double->string; to be safe, we read this value from
- // std::numeric_limits<number_float_t>::digits10
- const auto old_precision = o.precision(std::numeric_limits<double>::digits10);
-
// do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
- // reset locale and precision
- o.imbue(old_locale);
- o.precision(old_precision);
return o;
}
@@ -6583,6 +6537,11 @@
/// @{
private:
+ /*!
+ @note Some code in the switch cases has been copied, because otherwise
+ copilers would complain about implicit fallthrough and there is no
+ portable attribute to mute such warnings.
+ */
template<typename T>
static void add_to_vector(std::vector<uint8_t>& vec, size_t bytes, const T number)
{
@@ -6592,24 +6551,31 @@
{
case 8:
{
- vec.push_back(static_cast<uint8_t>((number >> 070) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 060) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 050) & 0xff));
- vec.push_back(static_cast<uint8_t>((number >> 040) & 0xff));
- // intentional fall-through
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 070) & 0xff));
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 060) & 0xff));
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 050) & 0xff));
+ vec.push_back(static_cast<uint8_t>((static_cast<uint64_t>(number) >> 040) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff));
+ vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
}
case 4:
{
vec.push_back(static_cast<uint8_t>((number >> 030) & 0xff));
vec.push_back(static_cast<uint8_t>((number >> 020) & 0xff));
- // intentional fall-through
+ vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
}
case 2:
{
vec.push_back(static_cast<uint8_t>((number >> 010) & 0xff));
- // intentional fall-through
+ vec.push_back(static_cast<uint8_t>(number & 0xff));
+ break;
}
case 1:
@@ -6714,25 +6680,25 @@
// positive fixnum
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT8_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint8_t>::max())
{
// uint 8
v.push_back(0xcc);
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT16_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint16_t>::max())
{
// uint 16
v.push_back(0xcd);
add_to_vector(v, 2, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT32_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint32_t>::max())
{
// uint 32
v.push_back(0xce);
add_to_vector(v, 4, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT64_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint64_t>::max())
{
// uint 64
v.push_back(0xcf);
@@ -6746,25 +6712,25 @@
// negative fixnum
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT8_MIN and j.m_value.number_integer <= INT8_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int8_t>::min() and j.m_value.number_integer <= std::numeric_limits<int8_t>::max())
{
// int 8
v.push_back(0xd0);
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT16_MIN and j.m_value.number_integer <= INT16_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int16_t>::min() and j.m_value.number_integer <= std::numeric_limits<int16_t>::max())
{
// int 16
v.push_back(0xd1);
add_to_vector(v, 2, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT32_MIN and j.m_value.number_integer <= INT32_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int32_t>::min() and j.m_value.number_integer <= std::numeric_limits<int32_t>::max())
{
// int 32
v.push_back(0xd2);
add_to_vector(v, 4, j.m_value.number_integer);
}
- else if (j.m_value.number_integer >= INT64_MIN and j.m_value.number_integer <= INT64_MAX)
+ else if (j.m_value.number_integer >= std::numeric_limits<int64_t>::min() and j.m_value.number_integer <= std::numeric_limits<int64_t>::max())
{
// int 64
v.push_back(0xd3);
@@ -6781,25 +6747,25 @@
// positive fixnum
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT8_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint8_t>::max())
{
// uint 8
v.push_back(0xcc);
add_to_vector(v, 1, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT16_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint16_t>::max())
{
// uint 16
v.push_back(0xcd);
add_to_vector(v, 2, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT32_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint32_t>::max())
{
// uint 32
v.push_back(0xce);
add_to_vector(v, 4, j.m_value.number_unsigned);
}
- else if (j.m_value.number_unsigned <= UINT64_MAX)
+ else if (j.m_value.number_unsigned <= std::numeric_limits<uint64_t>::max())
{
// uint 64
v.push_back(0xcf);
@@ -6956,19 +6922,19 @@
{
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer <= UINT8_MAX)
+ else if (j.m_value.number_integer <= std::numeric_limits<uint8_t>::max())
{
v.push_back(0x18);
// one-byte uint8_t
add_to_vector(v, 1, j.m_value.number_integer);
}
- else if (j.m_value.number_integer <= UINT16_MAX)
+ else if (j.m_value.number_integer <= std::numeric_limits<uint16_t>::max())
{
v.push_back(0x19);
// two-byte uint16_t
add_to_vector(v, 2, j.m_value.number_integer);
}
- else if (j.m_value.number_integer <= UINT32_MAX)
+ else if (j.m_value.number_integer <= std::numeric_limits<uint32_t>::max())
{
v.push_back(0x1a);
// four-byte uint32_t
@@ -6990,19 +6956,19 @@
{
v.push_back(static_cast<uint8_t>(0x20 + positive_number));
}
- else if (positive_number <= UINT8_MAX)
+ else if (positive_number <= std::numeric_limits<uint8_t>::max())
{
// int 8
v.push_back(0x38);
add_to_vector(v, 1, positive_number);
}
- else if (positive_number <= UINT16_MAX)
+ else if (positive_number <= std::numeric_limits<uint16_t>::max())
{
// int 16
v.push_back(0x39);
add_to_vector(v, 2, positive_number);
}
- else if (positive_number <= UINT32_MAX)
+ else if (positive_number <= std::numeric_limits<uint32_t>::max())
{
// int 32
v.push_back(0x3a);
@@ -7068,7 +7034,7 @@
const auto N = j.m_value.string->size();
if (N <= 0x17)
{
- v.push_back(0x60 + N); // 1 byte for string + size
+ v.push_back(0x60 + static_cast<uint8_t>(N)); // 1 byte for string + size
}
else if (N <= 0xff)
{
@@ -7104,7 +7070,7 @@
const auto N = j.m_value.array->size();
if (N <= 0x17)
{
- v.push_back(0x80 + N); // 1 byte for array + size
+ v.push_back(0x80 + static_cast<uint8_t>(N)); // 1 byte for array + size
}
else if (N <= 0xff)
{
@@ -7142,7 +7108,7 @@
const auto N = j.m_value.object->size();
if (N <= 0x17)
{
- v.push_back(0xa0 + N); // 1 byte for object + size
+ v.push_back(0xa0 + static_cast<uint8_t>(N)); // 1 byte for object + size
}
else if (N <= 0xff)
{
@@ -7911,7 +7877,9 @@
}
else
{
- val = mant == 0 ? INFINITY : NAN;
+ val = mant == 0
+ ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
}
return (half & 0x8000) != 0 ? -val : val;
}
@@ -7964,9 +7932,11 @@
vector in MessagePack format.,to_msgpack}
@sa http://msgpack.org
- @sa @ref from_msgpack(const std::vector<uint8_t>&) for the analogous
- deserialization
+ @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
@sa @ref to_cbor(const basic_json& for the related CBOR format
+
+ @since version 2.0.9
*/
static std::vector<uint8_t> to_msgpack(const basic_json& j)
{
@@ -7982,6 +7952,7 @@
serialization format.
@param[in] v a byte vector in MessagePack format
+ @param[in] start_index the index to start reading from @a v (0 by default)
@return deserialized JSON value
@throw std::invalid_argument if unsupported features from MessagePack were
@@ -7995,11 +7966,15 @@
@sa http://msgpack.org
@sa @ref to_msgpack(const basic_json&) for the analogous serialization
- @sa @ref from_cbor(const std::vector<uint8_t>&) for the related CBOR format
+ @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ related CBOR format
+
+ @since version 2.0.9, parameter @a start_index since 2.1.1
*/
- static basic_json from_msgpack(const std::vector<uint8_t>& v)
+ static basic_json from_msgpack(const std::vector<uint8_t>& v,
+ const size_t start_index = 0)
{
- size_t i = 0;
+ size_t i = start_index;
return from_msgpack_internal(v, i);
}
@@ -8020,9 +7995,11 @@
vector in CBOR format.,to_cbor}
@sa http://cbor.io
- @sa @ref from_cbor(const std::vector<uint8_t>&) for the analogous
- deserialization
+ @sa @ref from_cbor(const std::vector<uint8_t>&, const size_t) for the
+ analogous deserialization
@sa @ref to_msgpack(const basic_json& for the related MessagePack format
+
+ @since version 2.0.9
*/
static std::vector<uint8_t> to_cbor(const basic_json& j)
{
@@ -8038,6 +8015,7 @@
(Concise Binary Object Representation) serialization format.
@param[in] v a byte vector in CBOR format
+ @param[in] start_index the index to start reading from @a v (0 by default)
@return deserialized JSON value
@throw std::invalid_argument if unsupported features from CBOR were used in
@@ -8051,12 +8029,15 @@
@sa http://cbor.io
@sa @ref to_cbor(const basic_json&) for the analogous serialization
- @sa @ref from_msgpack(const std::vector<uint8_t>&) for the related
- MessagePack format
+ @sa @ref from_msgpack(const std::vector<uint8_t>&, const size_t) for the
+ related MessagePack format
+
+ @since version 2.0.9, parameter @a start_index since 2.1.1
*/
- static basic_json from_cbor(const std::vector<uint8_t>& v)
+ static basic_json from_cbor(const std::vector<uint8_t>& v,
+ const size_t start_index = 0)
{
- size_t i = 0;
+ size_t i = start_index;
return from_cbor_internal(v, i);
}
@@ -8266,6 +8247,154 @@
return result;
}
+
+ /*!
+ @brief locale-independent serialization for built-in arithmetic types
+ */
+ struct numtostr
+ {
+ public:
+ template<typename NumberType>
+ numtostr(NumberType value)
+ {
+ x_write(value, std::is_integral<NumberType>());
+ }
+
+ const char* c_str() const
+ {
+ return m_buf.data();
+ }
+
+ private:
+ /// a (hopefully) large enough character buffer
+ std::array < char, 64 > m_buf{{}};
+
+ template<typename NumberType>
+ void x_write(NumberType x, /*is_integral=*/std::true_type)
+ {
+ // special case for "0"
+ if (x == 0)
+ {
+ m_buf[0] = '0';
+ return;
+ }
+
+ const bool is_negative = x < 0;
+ size_t i = 0;
+
+ // spare 1 byte for '\0'
+ while (x != 0 and i < m_buf.size() - 1)
+ {
+ const auto digit = std::labs(static_cast<long>(x % 10));
+ m_buf[i++] = static_cast<char>('0' + digit);
+ x /= 10;
+ }
+
+ // make sure the number has been processed completely
+ assert(x == 0);
+
+ if (is_negative)
+ {
+ // make sure there is capacity for the '-'
+ assert(i < m_buf.size() - 2);
+ m_buf[i++] = '-';
+ }
+
+ std::reverse(m_buf.begin(), m_buf.begin() + i);
+ }
+
+ template<typename NumberType>
+ void x_write(NumberType x, /*is_integral=*/std::false_type)
+ {
+ // special case for 0.0 and -0.0
+ if (x == 0)
+ {
+ size_t i = 0;
+ if (std::signbit(x))
+ {
+ m_buf[i++] = '-';
+ }
+ m_buf[i++] = '0';
+ m_buf[i++] = '.';
+ m_buf[i] = '0';
+ return;
+ }
+
+ // get number of digits for a text -> float -> text round-trip
+ static constexpr auto d = std::numeric_limits<NumberType>::digits10;
+
+ // the actual conversion
+ const auto written_bytes = snprintf(m_buf.data(), m_buf.size(), "%.*g", d, x);
+
+ // negative value indicates an error
+ assert(written_bytes > 0);
+ // check if buffer was large enough
+ assert(static_cast<size_t>(written_bytes) < m_buf.size());
+
+ // read information from locale
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ const char thousands_sep = !loc->thousands_sep ? '\0'
+ : loc->thousands_sep[0];
+
+ const char decimal_point = !loc->decimal_point ? '\0'
+ : loc->decimal_point[0];
+
+ // erase thousands separator
+ if (thousands_sep != '\0')
+ {
+ const auto end = std::remove(m_buf.begin(), m_buf.begin() + written_bytes, thousands_sep);
+ std::fill(end, m_buf.end(), '\0');
+ }
+
+ // convert decimal point to '.'
+ if (decimal_point != '\0' and decimal_point != '.')
+ {
+ for (auto& c : m_buf)
+ {
+ if (c == decimal_point)
+ {
+ c = '.';
+ break;
+ }
+ }
+ }
+
+ // determine if need to append ".0"
+ size_t i = 0;
+ bool value_is_int_like = true;
+ for (i = 0; i < m_buf.size(); ++i)
+ {
+ // break when end of number is reached
+ if (m_buf[i] == '\0')
+ {
+ break;
+ }
+
+ // check if we find non-int character
+ value_is_int_like = value_is_int_like and m_buf[i] != '.' and
+ m_buf[i] != 'e' and m_buf[i] != 'E';
+ }
+
+ if (value_is_int_like)
+ {
+ // there must be 2 bytes left for ".0"
+ assert((i + 2) < m_buf.size());
+ // we write to the end of the number
+ assert(m_buf[i] == '\0');
+ assert(m_buf[i - 1] != '\0');
+
+ // add ".0"
+ m_buf[i] = '.';
+ m_buf[i + 1] = '0';
+
+ // the resulting string is properly terminated
+ assert(m_buf[i + 2] == '\0');
+ }
+ }
+ };
+
+
/*!
@brief internal implementation of the serialization function
@@ -8385,27 +8514,19 @@
case value_t::number_integer:
{
- o << m_value.number_integer;
+ o << numtostr(m_value.number_integer).c_str();
return;
}
case value_t::number_unsigned:
{
- o << m_value.number_unsigned;
+ o << numtostr(m_value.number_unsigned).c_str();
return;
}
case value_t::number_float:
{
- if (m_value.number_float == 0)
- {
- // special case for zero to get "0.0"/"-0.0"
- o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
- }
- else
- {
- o << m_value.number_float;
- }
+ o << numtostr(m_value.number_float).c_str();
return;
}
@@ -8534,10 +8655,11 @@
return *this;
}
- primitive_iterator_t& operator++(int)
+ primitive_iterator_t operator++(int)
{
+ auto result = *this;
m_it++;
- return *this;
+ return result;
}
primitive_iterator_t& operator--()
@@ -8546,10 +8668,11 @@
return *this;
}
- primitive_iterator_t& operator--(int)
+ primitive_iterator_t operator--(int)
{
+ auto result = *this;
m_it--;
- return *this;
+ return result;
}
primitive_iterator_t& operator+=(difference_type n)
@@ -9444,7 +9567,9 @@
literal_false, ///< the `false` literal
literal_null, ///< the `null` literal
value_string, ///< a string -- use get_string() for actual value
- value_number, ///< a number -- use get_number() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number() for actual value
+ value_integer, ///< a signed integer -- use get_number() for actual value
+ value_float, ///< an floating point number -- use get_number() for actual value
begin_array, ///< the character for array begin `[`
begin_object, ///< the character for object begin `{`
end_array, ///< the character for array end `]`
@@ -9596,7 +9721,9 @@
return "null literal";
case token_type::value_string:
return "string literal";
- case token_type::value_number:
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
return "number literal";
case token_type::begin_array:
return "'['";
@@ -9684,18 +9811,24 @@
"false" { last_token_type = token_type::literal_false; break; }
// number
- decimal_point = ".";
- digit = [0-9];
- digit_1_9 = [1-9];
- e = "e" | "E";
- minus = "-";
- plus = "+";
- zero = "0";
- exp = e (minus | plus)? digit+;
- frac = decimal_point digit+;
- int = (zero | digit_1_9 digit*);
- number = minus? int frac? exp?;
- number { last_token_type = token_type::value_number; break; }
+ decimal_point = ".";
+ digit = [0-9];
+ digit_1_9 = [1-9];
+ e = "e" | "E";
+ minus = "-";
+ plus = "+";
+ zero = "0";
+ exp = e (minus | plus)? digit+;
+ frac = decimal_point digit+;
+ int = (zero | digit_1_9 digit*);
+ invalid_int = minus? "0" digit+;
+ invalid_int { last_token_type = token_type::parse_error; break; }
+ number_unsigned = int;
+ number_unsigned { last_token_type = token_type::value_unsigned; break; }
+ number_integer = minus int;
+ number_integer { last_token_type = token_type::value_integer; break; }
+ number_float = minus? int frac? exp?;
+ number_float { last_token_type = token_type::value_float; break; }
// string
quotation_mark = "\"";
@@ -9774,7 +9907,7 @@
if (m_stream == nullptr or m_stream->eof())
{
// m_start may or may not be pointing into m_line_buffer at
- // this point. We trust the standand library to do the right
+ // this point. We trust the standard library to do the right
// thing. See http://stackoverflow.com/q/28142011/266378
m_line_buffer.assign(m_start, m_limit);
@@ -9862,7 +9995,7 @@
m_start + 1 + x < m_cursor - 1 must hold to loop indefinitely. This
can be rephrased to m_cursor - m_start - 2 > x. With the
precondition, we x <= 0, meaning that the loop condition holds
- indefinitly if i is always decreased. However, observe that the value
+ indefinitely if i is always decreased. However, observe that the value
of i is strictly increasing with each iteration, as it is incremented
by 1 in the iteration expression and never decremented inside the loop
body. Hence, the loop condition will eventually be false which
@@ -9988,59 +10121,155 @@
return result;
}
- /*!
- @brief parse floating point number
-
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
-
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
- */
- long double str_to_float_t(long double* /* type */, char** endptr) const
- {
- return std::strtold(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
/*!
- @brief parse floating point number
+ @brief parse string into a built-in arithmetic type as if the current
+ locale is POSIX.
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
+ @note in floating-point case strtod may parse past the token's end -
+ this is not an error
- @param[in,out] endptr recieves a pointer to the first character after
- the number
-
- @return the floating point number
+ @note any leading blanks are not handled
*/
- double str_to_float_t(double* /* type */, char** endptr) const
+ struct strtonum
{
- return std::strtod(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
+ public:
+ strtonum(const char* start, const char* end)
+ : m_start(start), m_end(end)
+ {}
- /*!
- @brief parse floating point number
+ /*!
+ @return true iff parsed successfully as number of type T
- This function (and its overloads) serves to select the most approprate
- standard floating point number parsing function based on the type
- supplied via the first parameter. Set this to @a
- static_cast<number_float_t*>(nullptr).
+ @param[in,out] val shall contain parsed value, or undefined value
+ if could not parse
+ */
+ template<typename T, typename = typename std::enable_if<std::is_arithmetic<T>::value>::type>
+ bool to(T& val) const
+ {
+ return parse(val, std::is_integral<T>());
+ }
- @param[in,out] endptr recieves a pointer to the first character after
- the number
+ private:
+ const char* const m_start = nullptr;
+ const char* const m_end = nullptr;
- @return the floating point number
- */
- float str_to_float_t(float* /* type */, char** endptr) const
- {
- return std::strtof(reinterpret_cast<typename string_t::const_pointer>(m_start), endptr);
- }
+ // floating-point conversion
+
+ // overloaded wrappers for strtod/strtof/strtold
+ // that will be called from parse<floating_point_t>
+ static void strtof(float& f, const char* str, char** endptr)
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ static void strtof(double& f, const char* str, char** endptr)
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ static void strtof(long double& f, const char* str, char** endptr)
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ template<typename T>
+ bool parse(T& value, /*is_integral=*/std::false_type) const
+ {
+ // replace decimal separator with locale-specific version,
+ // when necessary; data will point to either the original
+ // string, or buf, or tempstr containing the fixed string.
+ std::string tempstr;
+ std::array<char, 64> buf;
+ const size_t len = static_cast<size_t>(m_end - m_start);
+
+ // lexer will reject empty numbers
+ assert(len > 0);
+
+ // since dealing with strtod family of functions, we're
+ // getting the decimal point char from the C locale facilities
+ // instead of C++'s numpunct facet of the current std::locale
+ const auto loc = localeconv();
+ assert(loc != nullptr);
+ const char decimal_point_char = (loc->decimal_point == nullptr) ? '.' : loc->decimal_point[0];
+
+ const char* data = m_start;
+
+ if (decimal_point_char != '.')
+ {
+ const size_t ds_pos = static_cast<size_t>(std::find(m_start, m_end, '.') - m_start);
+
+ if (ds_pos != len)
+ {
+ // copy the data into the local buffer or tempstr, if
+ // buffer is too small; replace decimal separator, and
+ // update data to point to the modified bytes
+ if ((len + 1) < buf.size())
+ {
+ std::copy(m_start, m_end, buf.begin());
+ buf[len] = 0;
+ buf[ds_pos] = decimal_point_char;
+ data = buf.data();
+ }
+ else
+ {
+ tempstr.assign(m_start, m_end);
+ tempstr[ds_pos] = decimal_point_char;
+ data = tempstr.c_str();
+ }
+ }
+ }
+
+ char* endptr = nullptr;
+ value = 0;
+ // this calls appropriate overload depending on T
+ strtof(value, data, &endptr);
+
+ // parsing was successful iff strtof parsed exactly the number
+ // of characters determined by the lexer (len)
+ const bool ok = (endptr == (data + len));
+
+ if (ok and (value == static_cast<T>(0.0)) and (*data == '-'))
+ {
+ // some implementations forget to negate the zero
+ value = -0.0;
+ }
+
+ return ok;
+ }
+
+ // integral conversion
+
+ signed long long parse_integral(char** endptr, /*is_signed*/std::true_type) const
+ {
+ return std::strtoll(m_start, endptr, 10);
+ }
+
+ unsigned long long parse_integral(char** endptr, /*is_signed*/std::false_type) const
+ {
+ return std::strtoull(m_start, endptr, 10);
+ }
+
+ template<typename T>
+ bool parse(T& value, /*is_integral=*/std::true_type) const
+ {
+ char* endptr = nullptr;
+ errno = 0; // these are thread-local
+ const auto x = parse_integral(&endptr, std::is_signed<T>());
+
+ // called right overload?
+ static_assert(std::is_signed<T>() == std::is_signed<decltype(x)>(), "");
+
+ value = static_cast<T>(x);
+
+ return (x == static_cast<decltype(x)>(value)) // x fits into destination T
+ and (x < 0) == (value < 0) // preserved sign
+ //and ((x != 0) or is_integral()) // strto[u]ll did nto fail
+ and (errno == 0) // strto[u]ll did not overflow
+ and (m_start < m_end) // token was not empty
+ and (endptr == m_end); // parsed entire token exactly
+ }
+ };
/*!
@brief return number value for number tokens
@@ -10049,125 +10278,84 @@
number type (either integer, unsigned integer or floating point),
which is passed back to the caller via the result parameter.
- This function parses the integer component up to the radix point or
- exponent while collecting information about the 'floating point
- representation', which it stores in the result parameter. If there is
- no radix point or exponent, and the number can fit into a @ref
- number_integer_t or @ref number_unsigned_t then it sets the result
- parameter accordingly.
+ integral numbers that don't fit into the the range of the respective
+ type are parsed as number_float_t
- If the number is a floating point number the number is then parsed
- using @a std:strtod (or @a std:strtof or @a std::strtold).
+ floating-point values do not satisfy std::isfinite predicate
+ are converted to value_t::null
- @param[out] result @ref basic_json object to receive the number, or
- NAN if the conversion read past the current token. The latter case
- needs to be treated by the caller function.
+ throws if the entire string [m_start .. m_cursor) cannot be
+ interpreted as a number
+
+ @param[out] result @ref basic_json object to receive the number.
+ @param[in] token the type of the number token
*/
- void get_number(basic_json& result) const
+ bool get_number(basic_json& result, const token_type token) const
{
assert(m_start != nullptr);
+ assert(m_start < m_cursor);
+ assert((token == token_type::value_unsigned) or
+ (token == token_type::value_integer) or
+ (token == token_type::value_float));
- const lexer::lexer_char_t* curptr = m_start;
+ strtonum num_converter(reinterpret_cast<const char*>(m_start),
+ reinterpret_cast<const char*>(m_cursor));
- // accumulate the integer conversion result (unsigned for now)
- number_unsigned_t value = 0;
-
- // maximum absolute value of the relevant integer type
- number_unsigned_t max;
-
- // temporarily store the type to avoid unecessary bitfield access
- value_t type;
-
- // look for sign
- if (*curptr == '-')
+ switch (token)
{
- type = value_t::number_integer;
- max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
- curptr++;
- }
- else
- {
- type = value_t::number_unsigned;
- max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
- }
-
- // count the significant figures
- for (; curptr < m_cursor; curptr++)
- {
- // quickly skip tests if a digit
- if (*curptr < '0' or* curptr > '9')
+ case lexer::token_type::value_unsigned:
{
- if (*curptr == '.')
+ number_unsigned_t val;
+ if (num_converter.to(val))
{
- // don't count '.' but change to float
- type = value_t::number_float;
- continue;
+ // parsing successful
+ result.m_type = value_t::number_unsigned;
+ result.m_value = val;
+ return true;
}
- // assume exponent (if not then will fail parse): change to
- // float, stop counting and record exponent details
- type = value_t::number_float;
break;
}
- // skip if definitely not an integer
- if (type != value_t::number_float)
+ case lexer::token_type::value_integer:
{
- auto digit = static_cast<number_unsigned_t>(*curptr - '0');
-
- // overflow if value * 10 + digit > max, move terms around
- // to avoid overflow in intermediate values
- if (value > (max - digit) / 10)
+ number_integer_t val;
+ if (num_converter.to(val))
{
- // overflow
- type = value_t::number_float;
+ // parsing successful
+ result.m_type = value_t::number_integer;
+ result.m_value = val;
+ return true;
}
- else
- {
- // no overflow
- value = value * 10 + digit;
- }
+ break;
+ }
+
+ default:
+ {
+ break;
}
}
- // save the value (if not a float)
- if (type == value_t::number_unsigned)
+ // parse float (either explicitly or because a previous conversion
+ // failed)
+ number_float_t val;
+ if (num_converter.to(val))
{
- result.m_value.number_unsigned = value;
- }
- else if (type == value_t::number_integer)
- {
- // invariant: if we parsed a '-', the absolute value is between
- // 0 (we allow -0) and max == -INT64_MIN
- assert(value >= 0);
- assert(value <= max);
-
- if (value == max)
- {
- // we cannot simply negate value (== max == -INT64_MIN),
- // see https://github.com/nlohmann/json/issues/389
- result.m_value.number_integer = static_cast<number_integer_t>(INT64_MIN);
- }
- else
- {
- // all other values can be negated safely
- result.m_value.number_integer = -static_cast<number_integer_t>(value);
- }
- }
- else
- {
- // parse with strtod
- result.m_value.number_float = str_to_float_t(static_cast<number_float_t*>(nullptr), nullptr);
+ // parsing successful
+ result.m_type = value_t::number_float;
+ result.m_value = val;
// replace infinity and NAN by null
if (not std::isfinite(result.m_value.number_float))
{
- type = value_t::null;
+ result.m_type = value_t::null;
result.m_value = basic_json::json_value();
}
+
+ return true;
}
- // save the type
- result.m_type = type;
+ // couldn't parse number in any format
+ return false;
}
private:
@@ -10411,9 +10599,11 @@
break;
}
- case lexer::token_type::value_number:
+ case lexer::token_type::value_unsigned:
+ case lexer::token_type::value_integer:
+ case lexer::token_type::value_float:
{
- m_lexer.get_number(result);
+ m_lexer.get_number(result, last_token);
get_token();
break;
}
@@ -10708,7 +10898,7 @@
if (reference_token == "-")
{
- // explicityly treat "-" as index beyond the end
+ // explicitly treat "-" as index beyond the end
ptr = &ptr->operator[](ptr->m_value.array->size());
}
else
@@ -10747,9 +10937,9 @@
if (reference_token == "-")
{
// "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
+ JSON_THROW(std::out_of_range("array index '-' (" +
+ std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
@@ -10799,9 +10989,9 @@
if (reference_token == "-")
{
// "-" cannot be used for const access
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
+ JSON_THROW(std::out_of_range("array index '-' (" +
+ std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
@@ -10843,9 +11033,9 @@
if (reference_token == "-")
{
// "-" always fails the range check
- throw std::out_of_range("array index '-' (" +
- std::to_string(ptr->m_value.array->size()) +
- ") is out of range");
+ JSON_THROW(std::out_of_range("array index '-' (" +
+ std::to_string(ptr->m_value.array->size()) +
+ ") is out of range"));
}
// error condition (cf. RFC 6901, Sect. 4)
@@ -11215,7 +11405,7 @@
primitive. The original JSON value can be restored using the @ref
unflatten() function.
- @return an object that maps JSON pointers to primitve values
+ @return an object that maps JSON pointers to primitive values
@note Empty objects and arrays are flattened to `null` and will not be
reconstructed correctly by the @ref unflatten() function.
@@ -11282,7 +11472,7 @@
[JSON Patch](http://jsonpatch.com) defines a JSON document structure for
expressing a sequence of operations to apply to a JSON) document. With
- this funcion, a JSON Patch is applied to the current JSON value by
+ this function, a JSON Patch is applied to the current JSON value by
executing all operations from the patch.
@param[in] json_patch JSON patch document
@@ -11450,7 +11640,7 @@
JSON_THROW(std::invalid_argument("JSON patch must be an array of objects"));
}
- // iterate and apply th eoperations
+ // iterate and apply the operations
for (const auto& val : json_patch)
{
// wrapper to get a value for an operation
@@ -11589,8 +11779,8 @@
@note Currently, only `remove`, `add`, and `replace` operations are
generated.
- @param[in] source JSON value to copare from
- @param[in] target JSON value to copare against
+ @param[in] source JSON value to compare from
+ @param[in] target JSON value to compare against
@param[in] path helper value to create JSON pointers
@return a JSON patch to convert the @a source to @a target
@@ -11839,9 +12029,9 @@
#endif
// clean up
-#undef JSON_THROW
-#undef JSON_TRY
#undef JSON_CATCH
#undef JSON_DEPRECATED
+#undef JSON_THROW
+#undef JSON_TRY
#endif
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0ceb6bf..25e345c 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,8 +1,16 @@
+add_library(catch_main OBJECT
+ "src/unit.cpp"
+)
+set_target_properties(catch_main PROPERTIES
+ CXX_STANDARD 11
+ CXX_STANDARD_REQUIRED ON
+)
+target_include_directories(catch_main PRIVATE "thirdparty/catch")
+
# The unit test executable.
set(JSON_UNITTEST_TARGET_NAME "json_unit")
add_executable(${JSON_UNITTEST_TARGET_NAME}
- "thirdparty/catch/catch.hpp"
- "src/unit.cpp"
+ $<TARGET_OBJECTS:catch_main>
"src/unit-algorithms.cpp"
"src/unit-allocator.cpp"
"src/unit-capacity.cpp"
@@ -43,13 +51,33 @@
set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON
- COMPILE_DEFINITIONS "$<$<CXX_COMPILER_ID:MSVC>:_SCL_SECURE_NO_WARNINGS>"
- COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:MSVC>:/EHsc;$<$<CONFIG:Release>:/Od>>"
)
+if(MSVC)
+ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES
+ COMPILE_DEFINITIONS "_SCL_SECURE_NO_WARNINGS"
+ COMPILE_OPTIONS "/EHsc;$<$<CONFIG:Release>:/Od>"
+ )
+endif()
+
target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src" "thirdparty/catch")
target_link_libraries(${JSON_UNITTEST_TARGET_NAME} ${JSON_TARGET_NAME})
+include(cotire OPTIONAL)
+
+if(COMMAND cotire)
+ set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES
+ COTIRE_ADD_UNITY_BUILD FALSE
+ COTIRE_CXX_PREFIX_HEADER_INIT "src/prefix.hpp"
+ )
+ # HACK - CMAKE_INCLUDE_SYSTEM_FLAG_CXX has a trailing space, which Cotire doesn't strip
+ # Technically, this fix should go in cotire.cmake. TODO - submit a pull request upstream.
+ if (CMAKE_INCLUDE_SYSTEM_FLAG_CXX)
+ string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_CXX}" CMAKE_INCLUDE_SYSTEM_FLAG_CXX)
+ endif()
+ cotire(${JSON_UNITTEST_TARGET_NAME})
+endif()
+
add_test(NAME "${JSON_UNITTEST_TARGET_NAME}_default"
COMMAND ${JSON_UNITTEST_TARGET_NAME}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
diff --git a/test/Makefile b/test/Makefile
index 0b235ba..8f4e215 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -71,9 +71,9 @@
# individual test cases
##############################################################################
-test-%: src/unit-%.cpp ../src/json.hpp thirdparty/catch/catch.hpp
+test-%: src/unit-%.o src/unit.o ../src/json.hpp thirdparty/catch/catch.hpp
@echo "[CXXLD] $@"
- @$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -DCATCH_CONFIG_MAIN $< -o $@
+ @$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $< src/unit.o -o $@
TEST_PATTERN ?= "*"
TEST_PREFIX = ""
diff --git a/test/src/fuzzer-driver_afl.cpp b/test/src/fuzzer-driver_afl.cpp
index b1699f3..92e9147 100644
--- a/test/src/fuzzer-driver_afl.cpp
+++ b/test/src/fuzzer-driver_afl.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (fuzz test support)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
diff --git a/test/src/fuzzer-parse_cbor.cpp b/test/src/fuzzer-parse_cbor.cpp
index 1b6664f..30fa697 100644
--- a/test/src/fuzzer-parse_cbor.cpp
+++ b/test/src/fuzzer-parse_cbor.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (fuzz test support)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
This file implements a parser test suitable for fuzz testing. Given a byte
diff --git a/test/src/fuzzer-parse_json.cpp b/test/src/fuzzer-parse_json.cpp
index 40ccf58..bd2e5e3 100644
--- a/test/src/fuzzer-parse_json.cpp
+++ b/test/src/fuzzer-parse_json.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (fuzz test support)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
This file implements a parser test suitable for fuzz testing. Given a byte
diff --git a/test/src/fuzzer-parse_msgpack.cpp b/test/src/fuzzer-parse_msgpack.cpp
index 381dc75..bf2fcab 100644
--- a/test/src/fuzzer-parse_msgpack.cpp
+++ b/test/src/fuzzer-parse_msgpack.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (fuzz test support)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
This file implements a parser test suitable for fuzz testing. Given a byte
diff --git a/test/src/prefix.hpp b/test/src/prefix.hpp
new file mode 100644
index 0000000..9df98c7
--- /dev/null
+++ b/test/src/prefix.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "catch.hpp"
+
+#define private public
+#include "json.hpp"
+using nlohmann::json;
diff --git a/test/src/unit-algorithms.cpp b/test/src/unit-algorithms.cpp
index 3a73c10..8386238 100644
--- a/test/src/unit-algorithms.cpp
+++ b/test/src/unit-algorithms.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-allocator.cpp b/test/src/unit-allocator.cpp
index ae45ac2..903d595 100644
--- a/test/src/unit-allocator.cpp
+++ b/test/src/unit-allocator.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -59,13 +59,13 @@
bad_allocator>;
// creating an object should throw
- CHECK_THROWS_AS(bad_json j(bad_json::value_t::object), std::bad_alloc);
+ CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc);
}
}
-bool next_construct_fails = false;
-bool next_destroy_fails = false;
-bool next_deallocate_fails = false;
+static bool next_construct_fails = false;
+static bool next_destroy_fails = false;
+static bool next_deallocate_fails = false;
template<class T>
struct my_allocator : std::allocator<T>
@@ -141,39 +141,27 @@
{
next_construct_fails = false;
auto t = my_json::value_t::object;
- auto clean_up = [](my_json::json_value & j)
- {
- my_allocator_clean_up(j.object);
- };
- CHECK_NOTHROW(my_json::json_value j(t); clean_up(j));
+ CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc);
+ CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc);
next_construct_fails = false;
}
SECTION("array")
{
next_construct_fails = false;
auto t = my_json::value_t::array;
- auto clean_up = [](my_json::json_value & j)
- {
- my_allocator_clean_up(j.array);
- };
- CHECK_NOTHROW(my_json::json_value j(t); clean_up(j));
+ CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc);
+ CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc);
next_construct_fails = false;
}
SECTION("string")
{
next_construct_fails = false;
auto t = my_json::value_t::string;
- auto clean_up = [](my_json::json_value & j)
- {
- my_allocator_clean_up(j.string);
- };
- CHECK_NOTHROW(my_json::json_value j(t); clean_up(j));
+ CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value j(t), std::bad_alloc);
+ CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc);
next_construct_fails = false;
}
}
@@ -182,13 +170,9 @@
{
next_construct_fails = false;
my_json::string_t v("foo");
- auto clean_up = [](my_json::json_value & j)
- {
- my_allocator_clean_up(j.string);
- };
- CHECK_NOTHROW(my_json::json_value j(v); clean_up(j));
+ CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json::json_value j(v), std::bad_alloc);
+ CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc);
next_construct_fails = false;
}
@@ -222,9 +206,9 @@
{
next_construct_fails = false;
std::map<std::string, std::string> v {{"foo", "bar"}};
- CHECK_NOTHROW(my_json j(v));
+ CHECK_NOTHROW(my_json(v));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json j(v), std::bad_alloc);
+ CHECK_THROWS_AS(my_json(v), std::bad_alloc);
next_construct_fails = false;
}
@@ -232,18 +216,18 @@
{
next_construct_fails = false;
std::vector<std::string> v {"foo", "bar", "baz"};
- CHECK_NOTHROW(my_json j(v));
+ CHECK_NOTHROW(my_json(v));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json j(v), std::bad_alloc);
+ CHECK_THROWS_AS(my_json(v), std::bad_alloc);
next_construct_fails = false;
}
SECTION("basic_json(const typename string_t::value_type*)")
{
next_construct_fails = false;
- CHECK_NOTHROW(my_json v("foo"));
+ CHECK_NOTHROW(my_json("foo"));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json v("foo"), std::bad_alloc);
+ CHECK_THROWS_AS(my_json("foo"), std::bad_alloc);
next_construct_fails = false;
}
@@ -251,9 +235,9 @@
{
next_construct_fails = false;
std::string s("foo");
- CHECK_NOTHROW(my_json v(s));
+ CHECK_NOTHROW(my_json(s));
next_construct_fails = true;
- CHECK_THROWS_AS(my_json v(s), std::bad_alloc);
+ CHECK_THROWS_AS(my_json(s), std::bad_alloc);
next_construct_fails = false;
}
}
diff --git a/test/src/unit-capacity.cpp b/test/src/unit-capacity.cpp
index 44a35b9..8f2c311 100644
--- a/test/src/unit-capacity.cpp
+++ b/test/src/unit-capacity.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp
index 539d408..84b280b 100644
--- a/test/src/unit-cbor.cpp
+++ b/test/src/unit-cbor.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -237,7 +237,7 @@
const auto result = json::to_cbor(j);
CHECK(result == expected);
- int16_t restored = -1 - ((result[1] << 8) + result[2]);
+ int16_t restored = static_cast<int16_t>(-1 - ((result[1] << 8) + result[2]));
CHECK(restored == -9263);
// roundtrip
@@ -1184,6 +1184,10 @@
// compare parsed JSON values
CHECK(j1 == j2);
+
+ // check with different start index
+ packed.insert(packed.begin(), 5, 0xff);
+ CHECK(j1 == json::from_cbor(packed, 5));
}
}
@@ -1267,7 +1271,7 @@
SECTION("improve code coverage")
{
// exotic edge case
- CHECK_THROWS_AS(json::check_length(0xffffffffffffffff, 0xfffffffffffffff0, 0xff), std::out_of_range);
+ CHECK_THROWS_AS(json::check_length(0xffffffffffffffffull, 0xfffffffffffffff0ull, 0xff), std::out_of_range);
}
}
diff --git a/test/src/unit-class_const_iterator.cpp b/test/src/unit-class_const_iterator.cpp
index 1a05bec..840549a 100644
--- a/test/src/unit-class_const_iterator.cpp
+++ b/test/src/unit-class_const_iterator.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-class_iterator.cpp b/test/src/unit-class_iterator.cpp
index e3ef6a0..0238c33 100644
--- a/test/src/unit-class_iterator.cpp
+++ b/test/src/unit-class_iterator.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp
index ac43de8..0b019bf 100644
--- a/test/src/unit-class_lexer.cpp
+++ b/test/src/unit-class_lexer.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -65,25 +65,37 @@
SECTION("numbers")
{
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
- 1).scan() == json::lexer::token_type::value_number));
+ 1).scan() == json::lexer::token_type::value_unsigned));
+
+ CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("-0"),
+ 2).scan() == json::lexer::token_type::value_integer));
+ CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("-1"),
+ 2).scan() == json::lexer::token_type::value_integer));
+
+ CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1.1"),
+ 3).scan() == json::lexer::token_type::value_float));
+ CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("-1.1"),
+ 4).scan() == json::lexer::token_type::value_float));
+ CHECK((json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1E10"),
+ 4).scan() == json::lexer::token_type::value_float));
}
SECTION("whitespace")
@@ -109,7 +121,9 @@
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"));
- CHECK((json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"));
+ CHECK((json::lexer::token_type_name(json::lexer::token_type::value_unsigned) == "number literal"));
+ CHECK((json::lexer::token_type_name(json::lexer::token_type::value_integer) == "number literal"));
+ CHECK((json::lexer::token_type_name(json::lexer::token_type::value_float) == "number literal"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"));
CHECK((json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"));
diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp
index 3dfad5b..e3ad372 100644
--- a/test/src/unit-class_parser.cpp
+++ b/test/src/unit-class_parser.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -101,6 +101,7 @@
CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'");
// improve code coverage
CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument);
+ CHECK_THROWS_AS(json::parser("[-4:1,]").parse(), std::invalid_argument);
// unescaped control characters
CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument);
@@ -269,6 +270,11 @@
}
}
+ SECTION("overflow")
+ {
+ CHECK(json::parser("1.18973e+4932").parse() == json());
+ }
+
SECTION("invalid numbers")
{
CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument);
@@ -293,7 +299,9 @@
CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("01").parse(),
- "parse error - unexpected number literal; expected end of input");
+ "parse error - unexpected '01'");
+ CHECK_THROWS_WITH(json::parser("-01").parse(),
+ "parse error - unexpected '-01'");
CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("1.").parse(),
"parse error - unexpected '.'; expected end of input");
@@ -596,6 +604,32 @@
"missing or wrong low surrogate");
}
+ SECTION("tests found by mutate++")
+ {
+ // test case to make sure no comma preceeds the first key
+ CHECK_THROWS_AS(json::parser("{,\"key\": false}").parse(), std::invalid_argument);
+ // test case to make sure an object is properly closed
+ CHECK_THROWS_AS(json::parser("[{\"key\": false true]").parse(), std::invalid_argument);
+
+ // test case to make sure the callback is properly evaluated after reading a key
+ {
+ json::parser_callback_t cb = [](int, json::parse_event_t event, json&)
+ {
+ if (event == json::parse_event_t::key)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ };
+
+ json x = json::parse("{\"key\": false}", cb);
+ CHECK(x == json::object());
+ }
+ }
+
SECTION("callback function")
{
auto s_object = R"(
diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp
index f1e1aeb..ac24367 100644
--- a/test/src/unit-comparison.cpp
+++ b/test/src/unit-comparison.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp
index d65f740..79be78c 100644
--- a/test/src/unit-concepts.cpp
+++ b/test/src/unit-concepts.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -72,8 +72,8 @@
// X::size_type must return an unsigned integer
CHECK((std::is_unsigned<json::size_type>::value));
// X::size_type can represent any non-negative value of X::difference_type
- CHECK(std::numeric_limits<json::difference_type>::max() <=
- std::numeric_limits<json::size_type>::max());
+ CHECK(static_cast<size_t>(std::numeric_limits<json::difference_type>::max()) <=
+ static_cast<size_t>(std::numeric_limits<json::size_type>::max()));
// the expression "X u" has the post-condition "u.empty()"
{
diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp
index 93546c5..6bba701 100644
--- a/test/src/unit-constructor1.cpp
+++ b/test/src/unit-constructor1.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -725,7 +725,7 @@
SECTION("long double")
{
- long double n = 42.23;
+ long double n = 42.23l;
json j(n);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp
index 95cb87d..7259edf 100644
--- a/test/src/unit-constructor2.cpp
+++ b/test/src/unit-constructor2.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp
index a9355da..891dbc1 100644
--- a/test/src/unit-convenience.cpp
+++ b/test/src/unit-convenience.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp
index fca807a..106e37d 100644
--- a/test/src/unit-conversions.cpp
+++ b/test/src/unit-conversions.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -972,14 +972,14 @@
auto m5 = j5.get<std::list<std::string>>();
}
- //SECTION("std::forward_list")
- //{
- // auto m1 = j1.get<std::forward_list<int>>();
- // auto m2 = j2.get<std::forward_list<unsigned int>>();
- // auto m3 = j3.get<std::forward_list<double>>();
- // auto m4 = j4.get<std::forward_list<bool>>();
- // auto m5 = j5.get<std::forward_list<std::string>>();
- //}
+ SECTION("std::forward_list")
+ {
+ auto m1 = j1.get<std::forward_list<int>>();
+ auto m2 = j2.get<std::forward_list<unsigned int>>();
+ auto m3 = j3.get<std::forward_list<double>>();
+ auto m4 = j4.get<std::forward_list<bool>>();
+ auto m5 = j5.get<std::forward_list<std::string>>();
+ }
SECTION("std::vector")
{
diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp
index aaf4627..9028fdf 100644
--- a/test/src/unit-deserialization.cpp
+++ b/test/src/unit-deserialization.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp
index 5f264ac..f0763c5 100644
--- a/test/src/unit-element_access1.cpp
+++ b/test/src/unit-element_access1.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp
index 353d66d..8e91e89 100644
--- a/test/src/unit-element_access2.cpp
+++ b/test/src/unit-element_access2.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp
index 2e3d075..aead125 100644
--- a/test/src/unit-inspection.cpp
+++ b/test/src/unit-inspection.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -250,17 +250,31 @@
ss.str(std::string());
// use stringstream for JSON serialization
- json j_number = 3.141592653589793;
+ json j_number = 3.14159265358979;
ss << j_number;
// check that precision has been overridden during serialization
- CHECK(ss.str() == "3.141592653589793");
+ CHECK(ss.str() == "3.14159265358979");
// check that precision has been restored
CHECK(ss.precision() == 3);
}
}
+ SECTION("round trips")
+ {
+ for (const auto& s :
+ {"3.141592653589793", "1000000000000000010E5"
+ })
+ {
+ json j1 = json::parse(s);
+ std::string s1 = j1.dump();
+ json j2 = json::parse(s1);
+ std::string s2 = j2.dump();
+ CHECK(s1 == s2);
+ }
+ }
+
SECTION("return the type of the object (explicit)")
{
SECTION("null")
diff --git a/test/src/unit-iterator_wrapper.cpp b/test/src/unit-iterator_wrapper.cpp
index 79def90..b8e0065 100644
--- a/test/src/unit-iterator_wrapper.cpp
+++ b/test/src/unit-iterator_wrapper.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-iterators1.cpp b/test/src/unit-iterators1.cpp
index 78ea3db..eebee11 100644
--- a/test/src/unit-iterators1.cpp
+++ b/test/src/unit-iterators1.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-iterators2.cpp b/test/src/unit-iterators2.cpp
index ef806ad..b2bc4a3 100644
--- a/test/src/unit-iterators2.cpp
+++ b/test/src/unit-iterators2.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-json_patch.cpp b/test/src/unit-json_patch.cpp
index 837691d..05ed750 100644
--- a/test/src/unit-json_patch.cpp
+++ b/test/src/unit-json_patch.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index 24aa4a7..495d706 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-meta.cpp b/test/src/unit-meta.cpp
index 28c1a9d..8c614a6 100644
--- a/test/src/unit-meta.cpp
+++ b/test/src/unit-meta.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -36,5 +36,12 @@
SECTION("version()")
{
CHECK(json::meta()["name"] == "JSON for Modern C++");
+ CHECK(json::meta()["version"] == json(
+ {
+ {"string", "2.1.1"},
+ {"major", 2},
+ {"minor", 1},
+ {"patch", 1}
+ }));
}
}
diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp
index 80b1a71..0edc9a1 100644
--- a/test/src/unit-modifiers.cpp
+++ b/test/src/unit-modifiers.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp
index cad78ae..3a2ae5b 100644
--- a/test/src/unit-msgpack.cpp
+++ b/test/src/unit-msgpack.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -342,7 +342,7 @@
const auto result = json::to_msgpack(j);
CHECK(result == expected);
- int16_t restored = (result[1] << 8) + result[2];
+ int16_t restored = static_cast<int16_t>((result[1] << 8) + result[2]);
CHECK(restored == -9263);
// roundtrip
@@ -374,7 +374,7 @@
// check individual bytes
CHECK(result[0] == 0xd1);
- int16_t restored = (result[1] << 8) + result[2];
+ int16_t restored = static_cast<int16_t>((result[1] << 8) + result[2]);
CHECK(restored == i);
// roundtrip
@@ -389,7 +389,7 @@
numbers.push_back(-65536);
numbers.push_back(-77777);
numbers.push_back(-1048576);
- numbers.push_back(-2147483648);
+ numbers.push_back(-2147483648ll);
for (auto i : numbers)
{
CAPTURE(i);
@@ -1039,6 +1039,10 @@
// compare parsed JSON values
CHECK(j1 == j2);
+
+ // check with different start index
+ packed.insert(packed.begin(), 5, 0xff);
+ CHECK(j1 == json::from_msgpack(packed, 5));
}
}
diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp
index ddd8102..898e779 100644
--- a/test/src/unit-noexcept.cpp
+++ b/test/src/unit-noexcept.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp
index 5611a75..5f70780 100644
--- a/test/src/unit-pointer_access.cpp
+++ b/test/src/unit-pointer_access.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-readme.cpp b/test/src/unit-readme.cpp
index 061aadb..1b22f1b 100644
--- a/test/src/unit-readme.cpp
+++ b/test/src/unit-readme.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -163,7 +163,7 @@
j.clear(); // the array is empty again
// comparison
- j == "[\"foo\", 1, true]"_json; // true
+ bool x = (j == "[\"foo\", 1, true]"_json); // true
// create an object
json o;
diff --git a/test/src/unit-reference_access.cpp b/test/src/unit-reference_access.cpp
index 6281572..e73ec07 100644
--- a/test/src/unit-reference_access.cpp
+++ b/test/src/unit-reference_access.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp
index 17edbd5..83bb371 100644
--- a/test/src/unit-regression.cpp
+++ b/test/src/unit-regression.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -350,8 +350,8 @@
// double
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> j_double =
- 1.23e35f;
- CHECK(j_double.get<double>() == 1.23e35f);
+ 1.23e35;
+ CHECK(j_double.get<double>() == 1.23e35);
// long double
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double>
@@ -361,8 +361,8 @@
SECTION("issue #228 - double values are serialized with commas as decimal points")
{
- json j1a = 23.42;
- json j1b = json::parse("23.42");
+ json j1a = 2312.42;
+ json j1b = json::parse("2312.42");
json j2a = 2342e-2;
//issue #230
@@ -380,33 +380,88 @@
{
return ',';
}
+
+ char do_thousands_sep() const
+ {
+ return '.';
+ }
+
+ std::string do_grouping() const
+ {
+ return "\03";
+ }
};
// change locale to mess with decimal points
- std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator));
+ auto orig_locale = std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator));
- CHECK(j1a.dump() == "23.42");
- CHECK(j1b.dump() == "23.42");
+ CHECK(j1a.dump() == "2312.42");
+ CHECK(j1b.dump() == "2312.42");
// check if locale is properly reset
std::stringstream ss;
ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator));
- ss << 47.11;
- CHECK(ss.str() == "47,11");
+ ss << 4712.11;
+ CHECK(ss.str() == "4.712,11");
ss << j1a;
- CHECK(ss.str() == "47,1123.42");
+ CHECK(ss.str() == "4.712,112312.42");
ss << 47.11;
- CHECK(ss.str() == "47,1123.4247,11");
+ CHECK(ss.str() == "4.712,112312.4247,11");
CHECK(j2a.dump() == "23.42");
//issue #230
//CHECK(j2b.dump() == "23.42");
- CHECK(j3a.dump() == "10000");
- CHECK(j3b.dump() == "10000");
- CHECK(j3c.dump() == "10000");
+ CHECK(j3a.dump() == "10000.0");
+ CHECK(j3b.dump() == "10000.0");
+ CHECK(j3c.dump() == "10000.0");
//CHECK(j3b.dump() == "1E04"); // roundtrip error
//CHECK(j3c.dump() == "1e04"); // roundtrip error
+
+ std::locale::global(orig_locale);
+ }
+
+ SECTION("issue #378 - locale-independent num-to-str")
+ {
+ setlocale(LC_NUMERIC, "de_DE.UTF-8");
+
+ // Verify that snprintf uses special decimal and grouping characters.
+ // Disabled, because can't trigger locale-specific behavior in AppVeyor
+#ifndef _MSC_VER
+ {
+ std::array<char, 64> buf;
+ std::snprintf(buf.data(), buf.size(), "%.2f", 12345.67);
+ CHECK(strcmp(buf.data(), "12345,67") == 0);
+ }
+#endif
+
+ // verify that dumped correctly with '.' and no grouping
+ const json j1 = 12345.67;
+ CHECK(json(12345.67).dump() == "12345.67");
+ setlocale(LC_NUMERIC, "C");
+ }
+
+ SECTION("issue #379 - locale-independent str-to-num")
+ {
+ setlocale(LC_NUMERIC, "de_DE.UTF-8");
+
+ // disabled, because locale-specific beharivor is not
+ // triggered in AppVeyor for some reason
+#ifndef _MSC_VER
+ {
+ // verify that strtod now uses commas as decimal-separator
+ CHECK(std::strtod("3,14", nullptr) == 3.14);
+
+ // verify that strtod does not understand dots as decimal separator
+ CHECK(std::strtod("3.14", nullptr) == 3);
+ }
+#endif
+
+ // verify that parsed correctly despite using strtod internally
+ CHECK(json::parse("3.14").get<double>() == 3.14);
+
+ // check a different code path
+ CHECK(json::parse("1.000000000000000000000000000000000000000000000000000000000000000000000000").get<double>() == 1.0);
}
SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator")
@@ -586,7 +641,7 @@
CHECK_THROWS_AS(json::from_msgpack(vec1), std::out_of_range);
// more test cases for MessagePack
- for (uint8_t b :
+ for (auto b :
{
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // fixmap
0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // fixarray
@@ -594,12 +649,12 @@
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf
})
{
- std::vector<uint8_t> vec(1, b);
+ std::vector<uint8_t> vec(1, static_cast<uint8_t>(b));
CHECK_THROWS_AS(json::from_msgpack(vec), std::out_of_range);
}
// more test cases for CBOR
- for (uint8_t b :
+ for (auto b :
{
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // UTF-8 string
@@ -609,7 +664,7 @@
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7 // map
})
{
- std::vector<uint8_t> vec(1, b);
+ std::vector<uint8_t> vec(1, static_cast<uint8_t>(b));
CHECK_THROWS_AS(json::from_cbor(vec), std::out_of_range);
}
@@ -672,6 +727,24 @@
CHECK_THROWS_AS(json::from_cbor(vec3), std::out_of_range);
}
+ SECTION("issue #414 - compare with literal 0)")
+ {
+#define CHECK_TYPE(v) \
+ CHECK((json(v) == v));\
+ CHECK((v == json(v)));\
+ CHECK_FALSE((json(v) != v));\
+ CHECK_FALSE((v != json(v)));
+
+ CHECK_TYPE(nullptr);
+ CHECK_TYPE(0);
+ CHECK_TYPE(0u);
+ CHECK_TYPE(0L);
+ CHECK_TYPE(0.0);
+ CHECK_TYPE("");
+
+#undef CHECK_TYPE
+ }
+
SECTION("issue #416 - Use-of-uninitialized-value (OSS-Fuzz issue 377)")
{
// original test case
@@ -698,4 +771,25 @@
};
CHECK_THROWS_AS(json::from_cbor(vec2), std::out_of_range);
}
+
+ SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)")
+ {
+ std::vector<uint8_t> vec = {'-', '0', '1', '2', '2', '7', '4'};
+ CHECK_THROWS_AS(json::parse(vec), std::invalid_argument);
+ }
+
+ SECTION("issue #454 - doubles are printed as integers")
+ {
+ json j = R"({"bool_value":true,"double_value":2.0,"int_value":10,"level1":{"list_value":[3,"hi",false],"tmp":5.0},"string_value":"hello"})"_json;
+ CHECK(j["double_value"].is_number_float());
+ }
+
+ SECTION("issue #465 - roundtrip error while parsing 1000000000000000010E5")
+ {
+ json j1 = json::parse("1000000000000000010E5");
+ std::string s1 = j1.dump();
+ json j2 = json::parse(s1);
+ std::string s2 = j2.dump();
+ CHECK(s1 == s2);
+ }
}
diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp
index b8bb645..3df56cd 100644
--- a/test/src/unit-serialization.cpp
+++ b/test/src/unit-serialization.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit-testsuites.cpp b/test/src/unit-testsuites.cpp
index 9a9de12..e83d5b7 100644
--- a/test/src/unit-testsuites.cpp
+++ b/test/src/unit-testsuites.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -816,13 +816,58 @@
}
}
+std::string trim(const std::string& str);
+
+// from http://stackoverflow.com/a/25829178/266378
+std::string trim(const std::string& str)
+{
+ size_t first = str.find_first_not_of(' ');
+ if (std::string::npos == first)
+ {
+ return str;
+ }
+ size_t last = str.find_last_not_of(' ');
+ return str.substr(first, (last - first + 1));
+}
+
TEST_CASE("Big List of Naughty Strings")
{
// test from https://github.com/minimaxir/big-list-of-naughty-strings
- SECTION("blns.json")
+ SECTION("parsing blns.json")
{
std::ifstream f("test/data/big-list-of-naughty-strings/blns.json");
json j;
CHECK_NOTHROW(j << f);
}
+
+ // check if parsed strings roundtrip
+ // https://www.reddit.com/r/cpp/comments/5qpbie/json_form_modern_c_version_210/dd12mpq/
+ SECTION("roundtripping")
+ {
+ std::ifstream f("test/data/big-list-of-naughty-strings/blns.json");
+
+ while (not f.eof())
+ {
+ // read line
+ std::string line;
+ getline(f, line);
+
+ // trim whitespace
+ line = trim(line);
+
+ // remove trailing comma
+ line = line.substr(0, line.find_last_of(","));
+
+ // discard lines without at least two characters (quotes)
+ if (line.size() < 2)
+ {
+ continue;
+ }
+
+ // check roundtrip
+ CAPTURE(line);
+ json j = json::parse(line);
+ CHECK(j.dump() == line);
+ }
+ }
}
diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp
index 347ea37..a810308 100644
--- a/test/src/unit-udt.cpp
+++ b/test/src/unit-udt.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -26,16 +26,17 @@
SOFTWARE.
*/
-#include <array>
-#include <map>
-#include <string>
-#include <memory>
#include "catch.hpp"
#include "json.hpp"
using nlohmann::json;
+#include <array>
+#include <map>
+#include <string>
+#include <memory>
+
namespace udt
{
enum class country
diff --git a/test/src/unit-unicode.cpp b/test/src/unit-unicode.cpp
index 89e828f..c429b91 100644
--- a/test/src/unit-unicode.cpp
+++ b/test/src/unit-unicode.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index c89a0e8..38f2d5b 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -1,7 +1,7 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
-| | |__ | | | | | | version 2.1.0
+| | |__ | | | | | | version 2.1.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
diff --git a/test/thirdparty/catch/catch.hpp b/test/thirdparty/catch/catch.hpp
index 1d49e73..2a09fd1 100644
--- a/test/thirdparty/catch/catch.hpp
+++ b/test/thirdparty/catch/catch.hpp
@@ -1,6 +1,6 @@
/*
- * Catch v1.6.0
- * Generated: 2017-01-11 16:38:09.405017
+ * Catch v1.7.2
+ * Generated: 2017-02-13 15:57:33.350226
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -74,11 +74,14 @@
// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
// ****************
// Note to maintainers: if new toggles are added please document them
// in configuration.md, too
@@ -158,6 +161,8 @@
// Visual C++
#ifdef _MSC_VER
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
#if (_MSC_VER >= 1600)
# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
@@ -167,6 +172,7 @@
#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
#endif
#endif // _MSC_VER
@@ -235,6 +241,9 @@
# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
# endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
#endif // __cplusplus >= 201103L
@@ -266,12 +275,21 @@
#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
# define CATCH_CONFIG_CPP11_UNIQUE_PTR
#endif
-#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for
+// analytics) because, at time of writing, __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__)
# define CATCH_CONFIG_COUNTER
#endif
#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
# define CATCH_CONFIG_CPP11_SHUFFLE
#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+# define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
@@ -319,7 +337,6 @@
#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
#include <sstream>
-#include <stdexcept>
#include <algorithm>
namespace Catch {
@@ -374,7 +391,9 @@
}
bool startsWith( std::string const& s, std::string const& prefix );
+ bool startsWith( std::string const& s, char prefix );
bool endsWith( std::string const& s, std::string const& suffix );
+ bool endsWith( std::string const& s, char suffix );
bool contains( std::string const& s, std::string const& infix );
void toLowerInPlace( std::string& s );
std::string toLower( std::string const& s );
@@ -394,8 +413,8 @@
SourceLineInfo();
SourceLineInfo( char const* _file, std::size_t _line );
- SourceLineInfo( SourceLineInfo const& other );
# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+ SourceLineInfo(SourceLineInfo const& other) = default;
SourceLineInfo( SourceLineInfo && ) = default;
SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
SourceLineInfo& operator = ( SourceLineInfo && ) = default;
@@ -404,7 +423,7 @@
bool operator == ( SourceLineInfo const& other ) const;
bool operator < ( SourceLineInfo const& other ) const;
- std::string file;
+ char const* file;
std::size_t line;
};
@@ -438,8 +457,6 @@
#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
-#include <ostream>
-
namespace Catch {
class NotImplementedException : public std::exception
@@ -572,10 +589,6 @@
#pragma clang diagnostic pop
#endif
-#include <memory>
-#include <vector>
-#include <stdlib.h>
-
namespace Catch {
class TestCase;
@@ -839,6 +852,27 @@
namespace Catch {
+ struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+ struct DecomposedExpression
+ {
+ virtual ~DecomposedExpression() {}
+ virtual bool isBinaryExpression() const {
+ return false;
+ }
+ virtual void reconstructExpression( std::string& dest ) const = 0;
+
+ // Only simple binary comparisons can be decomposed.
+ // If more complex check is required then wrap sub-expressions in parentheses.
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+ template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+ };
+
struct AssertionInfo
{
AssertionInfo() {}
@@ -855,11 +889,41 @@
struct AssertionResultData
{
- AssertionResultData() : resultType( ResultWas::Unknown ) {}
+ AssertionResultData() : decomposedExpression( CATCH_NULL )
+ , resultType( ResultWas::Unknown )
+ , negated( false )
+ , parenthesized( false ) {}
- std::string reconstructedExpression;
+ void negate( bool parenthesize ) {
+ negated = !negated;
+ parenthesized = parenthesize;
+ if( resultType == ResultWas::Ok )
+ resultType = ResultWas::ExpressionFailed;
+ else if( resultType == ResultWas::ExpressionFailed )
+ resultType = ResultWas::Ok;
+ }
+
+ std::string const& reconstructExpression() const {
+ if( decomposedExpression != CATCH_NULL ) {
+ decomposedExpression->reconstructExpression( reconstructedExpression );
+ if( parenthesized ) {
+ reconstructedExpression.insert( 0, 1, '(' );
+ reconstructedExpression.append( 1, ')' );
+ }
+ if( negated ) {
+ reconstructedExpression.insert( 0, 1, '!' );
+ }
+ decomposedExpression = CATCH_NULL;
+ }
+ return reconstructedExpression;
+ }
+
+ mutable DecomposedExpression const* decomposedExpression;
+ mutable std::string reconstructedExpression;
std::string message;
ResultWas::OfType resultType;
+ bool negated;
+ bool parenthesized;
};
class AssertionResult {
@@ -886,6 +950,8 @@
std::string getMessage() const;
SourceLineInfo getSourceInfo() const;
std::string getTestMacroName() const;
+ void discardDecomposedExpression() const;
+ void expandDecomposedExpression() const;
protected:
AssertionInfo m_info;
@@ -1072,7 +1138,7 @@
{
return m_caseSensitivity == CaseSensitive::No
? " (case insensitive)"
- : "";
+ : std::string();
}
CaseSensitive::Choice m_caseSensitivity;
std::string m_str;
@@ -1090,7 +1156,7 @@
return m_data.m_str == m_data.adjustString( expr );;
}
virtual std::string toString() const {
- return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+ return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
}
CasedString m_data;
@@ -1107,7 +1173,7 @@
return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos;
}
virtual std::string toString() const {
- return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+ return "contains: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
}
CasedString m_data;
@@ -1125,7 +1191,7 @@
return startsWith( m_data.adjustString( expr ), m_data.m_str );
}
virtual std::string toString() const {
- return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+ return "starts with: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
}
CasedString m_data;
@@ -1142,7 +1208,7 @@
return endsWith( m_data.adjustString( expr ), m_data.m_str );
}
virtual std::string toString() const {
- return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+ return "ends with: \"" + m_data.m_str + '"' + m_data.toStringSuffix();
}
CasedString m_data;
@@ -1217,22 +1283,20 @@
template<typename T> class ExpressionLhs;
- struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
-
struct CopyableStream {
CopyableStream() {}
CopyableStream( CopyableStream const& other ) {
oss << other.oss.str();
}
CopyableStream& operator=( CopyableStream const& other ) {
- oss.str("");
+ oss.str(std::string());
oss << other.oss.str();
return *this;
}
std::ostringstream oss;
};
- class ResultBuilder {
+ class ResultBuilder : public DecomposedExpression {
public:
ResultBuilder( char const* macroName,
SourceLineInfo const& lineInfo,
@@ -1250,19 +1314,15 @@
return *this;
}
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
-
ResultBuilder& setResultType( ResultWas::OfType result );
ResultBuilder& setResultType( bool result );
- ResultBuilder& setLhs( std::string const& lhs );
- ResultBuilder& setRhs( std::string const& rhs );
- ResultBuilder& setOp( std::string const& op );
- void endExpression();
+ void endExpression( DecomposedExpression const& expr );
- std::string reconstructExpression() const;
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
AssertionResult build() const;
+ AssertionResult build( DecomposedExpression const& expr ) const;
void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
void captureResult( ResultWas::OfType resultType );
@@ -1274,14 +1334,12 @@
bool shouldDebugBreak() const;
bool allowThrows() const;
+ template<typename ArgT, typename MatcherT>
+ void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
private:
AssertionInfo m_assertionInfo;
AssertionResultData m_data;
- struct ExprComponents {
- ExprComponents() : testFalse( false ) {}
- bool testFalse;
- std::string lhs, rhs, op;
- } m_exprComponents;
CopyableStream m_stream;
bool m_shouldDebugBreak;
@@ -1803,90 +1861,155 @@
namespace Catch {
-// Wraps the LHS of an expression and captures the operator and RHS (if any) -
-// wrapping them all in a ResultBuilder object
-template<typename T>
-class ExpressionLhs {
- ExpressionLhs& operator = ( ExpressionLhs const& );
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
-# endif
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
public:
- ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
- ExpressionLhs( ExpressionLhs const& ) = default;
- ExpressionLhs( ExpressionLhs && ) = default;
-# endif
+ ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
template<typename RhsT>
- ResultBuilder& operator == ( RhsT const& rhs ) {
+ BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+ operator == ( RhsT const& rhs ) {
return captureExpression<Internal::IsEqualTo>( rhs );
}
template<typename RhsT>
- ResultBuilder& operator != ( RhsT const& rhs ) {
+ BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+ operator != ( RhsT const& rhs ) {
return captureExpression<Internal::IsNotEqualTo>( rhs );
}
template<typename RhsT>
- ResultBuilder& operator < ( RhsT const& rhs ) {
+ BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+ operator < ( RhsT const& rhs ) {
return captureExpression<Internal::IsLessThan>( rhs );
}
template<typename RhsT>
- ResultBuilder& operator > ( RhsT const& rhs ) {
+ BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+ operator > ( RhsT const& rhs ) {
return captureExpression<Internal::IsGreaterThan>( rhs );
}
template<typename RhsT>
- ResultBuilder& operator <= ( RhsT const& rhs ) {
+ BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+ operator <= ( RhsT const& rhs ) {
return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
}
template<typename RhsT>
- ResultBuilder& operator >= ( RhsT const& rhs ) {
+ BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+ operator >= ( RhsT const& rhs ) {
return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
}
- ResultBuilder& operator == ( bool rhs ) {
+ BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
return captureExpression<Internal::IsEqualTo>( rhs );
}
- ResultBuilder& operator != ( bool rhs ) {
+ BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
return captureExpression<Internal::IsNotEqualTo>( rhs );
}
void endExpression() {
- bool value = m_lhs ? true : false;
+ m_truthy = m_lhs ? true : false;
m_rb
- .setLhs( Catch::toString( value ) )
- .setResultType( value )
- .endExpression();
+ .setResultType( m_truthy )
+ .endExpression( *this );
}
- // Only simple binary expressions are allowed on the LHS.
- // If more complex compositions are required then place the sub expression in parentheses
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
- template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ dest = Catch::toString( m_truthy );
+ }
private:
template<Internal::Operator Op, typename RhsT>
- ResultBuilder& captureExpression( RhsT const& rhs ) {
- return m_rb
- .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
- .setLhs( Catch::toString( m_lhs ) )
- .setRhs( Catch::toString( rhs ) )
- .setOp( Internal::OperatorTraits<Op>::getName() );
+ BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+ return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+ }
+
+ template<Internal::Operator Op>
+ BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+ return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
}
private:
ResultBuilder& m_rb;
T m_lhs;
+ bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+ BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+ : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+ void endExpression() const {
+ m_rb
+ .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+ .endExpression( *this );
+ }
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string lhs = Catch::toString( m_lhs );
+ std::string rhs = Catch::toString( m_rhs );
+ char delim = lhs.size() + rhs.size() < 40 &&
+ lhs.find('\n') == std::string::npos &&
+ rhs.find('\n') == std::string::npos ? ' ' : '\n';
+ dest.reserve( 7 + lhs.size() + rhs.size() );
+ // 2 for spaces around operator
+ // 2 for operator
+ // 2 for parentheses (conditionally added later)
+ // 1 for negation (conditionally added later)
+ dest = lhs;
+ dest += delim;
+ dest += Internal::OperatorTraits<Op>::getName();
+ dest += delim;
+ dest += rhs;
+ }
+
+private:
+ ResultBuilder& m_rb;
+ LhsT m_lhs;
+ RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+ MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+ : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+ virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+ return true;
+ }
+
+ virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+ std::string matcherAsString = m_matcher.toString();
+ dest = Catch::toString( m_arg );
+ dest += ' ';
+ if( matcherAsString == Detail::unprintableString )
+ dest += m_matcherString;
+ else
+ dest += matcherAsString;
+ }
+
+private:
+ ArgT m_arg;
+ MatcherT m_matcher;
+ char const* m_matcherString;
};
} // end namespace Catch
@@ -1903,6 +2026,14 @@
return ExpressionLhs<bool>( *this, value );
}
+ template<typename ArgT, typename MatcherT>
+ inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+ char const* matcherString ) {
+ MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+ setResultType( matcher.match( arg ) );
+ endExpression( expr );
+ }
+
} // namespace Catch
// #included from: catch_message.h
@@ -2005,11 +2136,19 @@
#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
-#define CATCH_PLATFORM_MAC
+# define CATCH_PLATFORM_MAC
#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
-#define CATCH_PLATFORM_IPHONE
+# define CATCH_PLATFORM_IPHONE
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
-#define CATCH_PLATFORM_WINDOWS
+# define CATCH_PLATFORM_WINDOWS
+# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+# define CATCH_DEFINES_NOMINMAX
+# endif
+# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# endif
#endif
#include <string>
@@ -2024,27 +2163,36 @@
// The following code snippet based on:
// http://cocoawithlove.com/2008/03/break-into-debugger.html
- #ifdef DEBUG
- #if defined(__ppc64__) || defined(__ppc__)
- #define CATCH_BREAK_INTO_DEBUGGER() \
- if( Catch::isDebuggerActive() ) { \
- __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
- : : : "memory","r0","r3","r4" ); \
- }
- #else
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
- #endif
+ #if defined(__ppc64__) || defined(__ppc__)
+ #define CATCH_TRAP() \
+ __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" )
+ #else
+ #define CATCH_TRAP() __asm__("int $3\n" : : )
#endif
+#elif defined(CATCH_PLATFORM_LINUX)
+ // If we can use inline assembler, do it because this allows us to break
+ // directly at the location of the failing check instead of breaking inside
+ // raise() called from it, i.e. one stack frame below.
+ #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+ #define CATCH_TRAP() asm volatile ("int $3")
+ #else // Fall back to the generic way.
+ #include <signal.h>
+
+ #define CATCH_TRAP() raise(SIGTRAP)
+ #endif
#elif defined(_MSC_VER)
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+ #define CATCH_TRAP() __debugbreak()
#elif defined(__MINGW32__)
extern "C" __declspec(dllimport) void __stdcall DebugBreak();
- #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+ #define CATCH_TRAP() DebugBreak()
#endif
-#ifndef CATCH_BREAK_INTO_DEBUGGER
-#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#ifdef CATCH_TRAP
+ #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+ #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
#endif
// #included from: catch_interfaces_runner.h
@@ -2059,6 +2207,45 @@
};
}
+// #included from: catch_type_traits.hpp
+#define TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+
+ template <typename T>
+ using add_lvalue_reference = std::add_lvalue_reference<T>;
+
+ template <typename T>
+ using add_const = std::add_const<T>;
+
+#else
+
+ template <typename T>
+ struct add_const {
+ typedef const T type;
+ };
+
+ template <typename T>
+ struct add_lvalue_reference {
+ typedef T& type;
+ };
+ template <typename T>
+ struct add_lvalue_reference<T&> {
+ typedef T& type;
+ };
+ // No && overload, because that is C++11, in which case we have
+ // proper type_traits implementation from the standard library
+
+#endif
+
+}
+
///////////////////////////////////////////////////////////////////////////////
// In the event of a failure works out if the debugger needs to be invoked
// and/or an exception thrown and takes appropriate action.
@@ -2077,10 +2264,11 @@
( __catchResult <= expr ).endExpression(); \
} \
catch( ... ) { \
- __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+ __catchResult.useActiveException( resultDisposition ); \
} \
INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
@@ -2097,7 +2285,7 @@
do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
try { \
- expr; \
+ static_cast<void>(expr); \
__catchResult.captureResult( Catch::ResultWas::Ok ); \
} \
catch( ... ) { \
@@ -2112,7 +2300,7 @@
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
if( __catchResult.allowThrows() ) \
try { \
- expr; \
+ static_cast<void>(expr); \
__catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
} \
catch( ... ) { \
@@ -2126,13 +2314,13 @@
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
do { \
- Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \
if( __catchResult.allowThrows() ) \
try { \
- expr; \
+ static_cast<void>(expr); \
__catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
} \
- catch( exceptionType ) { \
+ catch( Catch::add_const<Catch::add_lvalue_reference<exceptionType>::type>::type ) { \
__catchResult.captureResult( Catch::ResultWas::Ok ); \
} \
catch( ... ) { \
@@ -2171,13 +2359,7 @@
do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
try { \
- std::string matcherAsString = (matcher).toString(); \
- __catchResult \
- .setLhs( Catch::toString( arg ) ) \
- .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
- .setOp( "matches" ) \
- .setResultType( (matcher).match( arg ) ); \
- __catchResult.captureExpression(); \
+ __catchResult.captureMatch( arg, matcher, #matcher ); \
} catch( ... ) { \
__catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
} \
@@ -2260,6 +2442,8 @@
};
}
+#include <string>
+
namespace Catch {
struct SectionInfo {
@@ -2630,6 +2814,10 @@
#include <cmath>
#include <limits>
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
namespace Catch {
namespace Detail {
@@ -2658,9 +2846,56 @@
return approx;
}
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( const T& lhs, Approx const& rhs ) {
+ // Thanks to Richard Harris for his help refining this formula
+ auto lhs_v = double(lhs);
+ return std::fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs_v), std::fabs(rhs.m_value) ) );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator == ( Approx const& lhs, const T& rhs ) {
+ return operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( T lhs, Approx const& rhs ) {
+ return !operator==( lhs, rhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator != ( Approx const& lhs, T rhs ) {
+ return !operator==( rhs, lhs );
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( T lhs, Approx const& rhs )
+ {
+ return double(lhs) < rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator <= ( Approx const& lhs, T rhs )
+ {
+ return lhs.m_value < double(rhs) || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( T lhs, Approx const& rhs )
+ {
+ return double(lhs) > rhs.m_value || lhs == rhs;
+ }
+
+ template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+ friend bool operator >= ( Approx const& lhs, T rhs )
+ {
+ return lhs.m_value > double(rhs) || lhs == rhs;
+ }
+#else
friend bool operator == ( double lhs, Approx const& rhs ) {
// Thanks to Richard Harris for his help refining this formula
- return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+ return std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
}
friend bool operator == ( Approx const& lhs, double rhs ) {
@@ -2694,6 +2929,7 @@
{
return lhs.m_value > rhs || lhs == rhs;
}
+#endif
Approx& epsilon( double newEpsilon ) {
m_epsilon = newEpsilon;
@@ -2849,7 +3085,8 @@
IsHidden = 1 << 1,
ShouldFail = 1 << 2,
MayFail = 1 << 3,
- Throws = 1 << 4
+ Throws = 1 << 4,
+ NonPortable = 1 << 5
};
TestCaseInfo( std::string const& _name,
@@ -3144,6 +3381,8 @@
// #included from: catch_wildcard_pattern.hpp
#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+#include <stdexcept>
+
namespace Catch
{
class WildcardPattern {
@@ -3161,11 +3400,11 @@
m_wildcard( NoWildcard ),
m_pattern( adjustCase( pattern ) )
{
- if( startsWith( m_pattern, "*" ) ) {
+ if( startsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 1 );
m_wildcard = WildcardAtStart;
}
- if( endsWith( m_pattern, "*" ) ) {
+ if( endsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
}
@@ -3352,6 +3591,8 @@
m_start = start;
}
void escape() {
+ if( m_mode == None )
+ m_start = m_pos;
m_mode = EscapedName;
m_escapeChars.push_back( m_pos );
}
@@ -3360,7 +3601,7 @@
void addPattern() {
std::string token = subString();
for( size_t i = 0; i < m_escapeChars.size(); ++i )
- token = token.substr( 0, m_escapeChars[i] ) + token.substr( m_escapeChars[i]+1 );
+ token = token.substr( 0, m_escapeChars[i]-i ) + token.substr( m_escapeChars[i]+1-i );
m_escapeChars.clear();
if( startsWith( token, "exclude:" ) ) {
m_exclusion = true;
@@ -3395,7 +3636,7 @@
// #included from: catch_interfaces_config.h
#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
-#include <iostream>
+#include <iosfwd>
#include <string>
#include <vector>
@@ -3447,6 +3688,8 @@
virtual RunTests::InWhatOrder runOrder() const = 0;
virtual unsigned int rngSeed() const = 0;
virtual UseColour::YesOrNo useColour() const = 0;
+ virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+
};
}
@@ -3515,8 +3758,7 @@
#include <memory>
#include <vector>
#include <string>
-#include <iostream>
-#include <ctime>
+#include <stdexcept>
#ifndef CATCH_CONFIG_CONSOLE_WIDTH
#define CATCH_CONFIG_CONSOLE_WIDTH 80
@@ -3573,6 +3815,7 @@
std::vector<std::string> reporterNames;
std::vector<std::string> testsOrTags;
+ std::vector<std::string> sectionsToRun;
};
class Config : public SharedImpl<IConfig> {
@@ -3597,8 +3840,7 @@
}
}
- virtual ~Config() {
- }
+ virtual ~Config() {}
std::string const& getFilename() const {
return m_data.outputFilename ;
@@ -3611,27 +3853,26 @@
std::string getProcessName() const { return m_data.processName; }
- bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+ std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
+ std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
- std::vector<std::string> getReporterNames() const { return m_data.reporterNames; }
-
- int abortAfter() const { return m_data.abortAfter; }
-
- TestSpec const& testSpec() const { return m_testSpec; }
+ virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
bool showHelp() const { return m_data.showHelp; }
- bool showInvisibles() const { return m_data.showInvisibles; }
// IConfig interface
- virtual bool allowThrows() const { return !m_data.noThrow; }
- virtual std::ostream& stream() const { return m_stream->stream(); }
- virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
- virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; }
- virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
- virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
- virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; }
- virtual unsigned int rngSeed() const { return m_data.rngSeed; }
- virtual UseColour::YesOrNo useColour() const { return m_data.useColour; }
+ virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; }
+ virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); }
+ virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; }
+ virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; }
+ virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; }
+ virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
+ virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; }
+ virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; }
+ virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; }
+ virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
+ virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
+ virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
private:
@@ -4693,6 +4934,7 @@
#endif
#include <fstream>
+#include <ctime>
namespace Catch {
@@ -4703,13 +4945,14 @@
config.abortAfter = x;
}
inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+ inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
inline void addWarning( ConfigData& config, std::string const& _warning ) {
if( _warning == "NoAssertions" )
config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
else
- throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" );
+ throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
}
inline void setOrder( ConfigData& config, std::string const& order ) {
if( startsWith( "declared", order ) )
@@ -4719,7 +4962,7 @@
else if( startsWith( "random", order ) )
config.runOrder = RunTests::InRandomOrder;
else
- throw std::runtime_error( "Unrecognised ordering: '" + order + "'" );
+ throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' );
}
inline void setRngSeed( ConfigData& config, std::string const& seed ) {
if( seed == "time" ) {
@@ -4730,7 +4973,7 @@
ss << seed;
ss >> config.rngSeed;
if( ss.fail() )
- throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" );
+ throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
}
}
inline void setVerbosity( ConfigData& config, int level ) {
@@ -4765,10 +5008,10 @@
std::string line;
while( std::getline( f, line ) ) {
line = trim(line);
- if( !line.empty() && !startsWith( line, "#" ) ) {
- if( !startsWith( line, "\"" ) )
- line = "\"" + line + "\"";
- addTestOrTags( config, line + "," );
+ if( !line.empty() && !startsWith( line, '#' ) ) {
+ if( !startsWith( line, '"' ) )
+ line = '"' + line + '"';
+ addTestOrTags( config, line + ',' );
}
}
}
@@ -4856,6 +5099,10 @@
.describe( "adds a tag for the filename" )
.bind( &ConfigData::filenamesAsTags );
+ cli["-c"]["--section"]
+ .describe( "specify section to run" )
+ .bind( &addSectionToRun, "section name" );
+
// Less common commands which don't have a short form
cli["--list-test-names-only"]
.describe( "list all/matching test cases names only" )
@@ -4928,19 +5175,16 @@
TextAttributes()
: initialIndent( std::string::npos ),
indent( 0 ),
- width( consoleWidth-1 ),
- tabChar( '\t' )
+ width( consoleWidth-1 )
{}
TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; }
TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; }
TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; }
- TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; }
std::size_t initialIndent; // indent of first line, or npos
std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos
std::size_t width; // maximum width of text, including indent. Longer text will wrap
- char tabChar; // If this char is seen the indent is changed to current pos
};
class Text {
@@ -4948,62 +5192,76 @@
Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
: attr( _attr )
{
- std::string wrappableChars = " [({.,/|\\-";
- std::size_t indent = _attr.initialIndent != std::string::npos
- ? _attr.initialIndent
- : _attr.indent;
- std::string remainder = _str;
+ const std::string wrappableBeforeChars = "[({<\t";
+ const std::string wrappableAfterChars = "])}>-,./|\\";
+ const std::string wrappableInsteadOfChars = " \n\r";
+ std::string indent = _attr.initialIndent != std::string::npos
+ ? std::string( _attr.initialIndent, ' ' )
+ : std::string( _attr.indent, ' ' );
- while( !remainder.empty() ) {
+ typedef std::string::const_iterator iterator;
+ iterator it = _str.begin();
+ const iterator strEnd = _str.end();
+
+ while( it != strEnd ) {
+
if( lines.size() >= 1000 ) {
lines.push_back( "... message truncated due to excessive size" );
return;
}
- std::size_t tabPos = std::string::npos;
- std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
- std::size_t pos = remainder.find_first_of( '\n' );
- if( pos <= width ) {
- width = pos;
- }
- pos = remainder.find_last_of( _attr.tabChar, width );
- if( pos != std::string::npos ) {
- tabPos = pos;
- if( remainder[width] == '\n' )
- width--;
- remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
- }
- if( width == remainder.size() ) {
- spliceLine( indent, remainder, width );
- }
- else if( remainder[width] == '\n' ) {
- spliceLine( indent, remainder, width );
- if( width <= 1 || remainder.size() != 1 )
- remainder = remainder.substr( 1 );
- indent = _attr.indent;
- }
- else {
- pos = remainder.find_last_of( wrappableChars, width );
- if( pos != std::string::npos && pos > 0 ) {
- spliceLine( indent, remainder, pos );
- if( remainder[0] == ' ' )
- remainder = remainder.substr( 1 );
+ std::string suffix;
+ std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
+ iterator itEnd = it+width;
+ iterator itNext = _str.end();
+
+ iterator itNewLine = std::find( it, itEnd, '\n' );
+ if( itNewLine != itEnd )
+ itEnd = itNewLine;
+
+ if( itEnd != strEnd ) {
+ bool foundWrapPoint = false;
+ iterator findIt = itEnd;
+ do {
+ if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
+ itEnd = findIt+1;
+ itNext = findIt+1;
+ foundWrapPoint = true;
+ }
+ else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
+ itEnd = findIt;
+ itNext = findIt;
+ foundWrapPoint = true;
+ }
+ else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
+ itNext = findIt+1;
+ itEnd = findIt;
+ foundWrapPoint = true;
+ }
+ if( findIt == it )
+ break;
+ else
+ --findIt;
+ }
+ while( !foundWrapPoint );
+
+ if( !foundWrapPoint ) {
+ // No good wrap char, so we'll break mid word and add a hyphen
+ --itEnd;
+ itNext = itEnd;
+ suffix = "-";
}
else {
- spliceLine( indent, remainder, width-1 );
- lines.back() += "-";
+ while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
+ --itEnd;
}
- if( lines.size() == 1 )
- indent = _attr.indent;
- if( tabPos != std::string::npos )
- indent += tabPos;
}
- }
- }
+ lines.push_back( indent + std::string( it, itEnd ) + suffix );
- void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
- lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
- _remainder = _remainder.substr( _pos );
+ if( indent.size() != _attr.indent )
+ indent = std::string( _attr.indent, ' ' );
+ it = itNext;
+ }
}
typedef std::vector<std::string>::const_iterator const_iterator;
@@ -5112,7 +5370,6 @@
#include <string>
#include <ostream>
#include <map>
-#include <assert.h>
namespace Catch
{
@@ -5400,9 +5657,9 @@
}
if( !config.testSpec().hasFilters() )
- Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl;
+ Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
else
- Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl;
+ Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
return matchedTests;
}
@@ -5417,8 +5674,8 @@
++it ) {
matchedTests++;
TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
- if( startsWith( testCaseInfo.name, "#" ) )
- Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl;
+ if( startsWith( testCaseInfo.name, '#' ) )
+ Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl;
else
Catch::cout() << testCaseInfo.name << std::endl;
}
@@ -5481,9 +5738,9 @@
.setInitialIndent( 0 )
.setIndent( oss.str().size() )
.setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
- Catch::cout() << oss.str() << wrapper << "\n";
+ Catch::cout() << oss.str() << wrapper << '\n';
}
- Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl;
+ Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
return tagCounts.size();
}
@@ -5502,9 +5759,9 @@
.setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
Catch::cout() << " "
<< it->first
- << ":"
+ << ':'
<< std::string( maxNameLen - it->first.size() + 2, ' ' )
- << wrapper << "\n";
+ << wrapper << '\n';
}
Catch::cout() << std::endl;
return factories.size();
@@ -5535,15 +5792,27 @@
#include <string>
#include <assert.h>
#include <vector>
+#include <iterator>
+#include <stdexcept>
namespace Catch {
namespace TestCaseTracking {
+ struct NameAndLocation {
+ std::string name;
+ SourceLineInfo location;
+
+ NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+ : name( _name ),
+ location( _location )
+ {}
+ };
+
struct ITracker : SharedImpl<> {
virtual ~ITracker();
// static queries
- virtual std::string name() const = 0;
+ virtual NameAndLocation const& nameAndLocation() const = 0;
// dynamic queries
virtual bool isComplete() const = 0; // Successfully completed or failed
@@ -5559,7 +5828,7 @@
virtual void markAsNeedingAnotherRun() = 0;
virtual void addChild( Ptr<ITracker> const& child ) = 0;
- virtual ITracker* findChild( std::string const& name ) = 0;
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
virtual void openChild() = 0;
// Debug/ checking
@@ -5567,7 +5836,7 @@
virtual bool isIndexTracker() const = 0;
};
- class TrackerContext {
+ class TrackerContext {
enum RunState {
NotStarted,
@@ -5629,30 +5898,32 @@
Failed
};
class TrackerHasName {
- std::string m_name;
+ NameAndLocation m_nameAndLocation;
public:
- TrackerHasName( std::string const& name ) : m_name( name ) {}
+ TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
bool operator ()( Ptr<ITracker> const& tracker ) {
- return tracker->name() == m_name;
+ return
+ tracker->nameAndLocation().name == m_nameAndLocation.name &&
+ tracker->nameAndLocation().location == m_nameAndLocation.location;
}
};
typedef std::vector<Ptr<ITracker> > Children;
- std::string m_name;
+ NameAndLocation m_nameAndLocation;
TrackerContext& m_ctx;
ITracker* m_parent;
Children m_children;
CycleState m_runState;
public:
- TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent )
- : m_name( name ),
+ TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : m_nameAndLocation( nameAndLocation ),
m_ctx( ctx ),
m_parent( parent ),
m_runState( NotStarted )
{}
virtual ~TrackerBase();
- virtual std::string name() const CATCH_OVERRIDE {
- return m_name;
+ virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
+ return m_nameAndLocation;
}
virtual bool isComplete() const CATCH_OVERRIDE {
return m_runState == CompletedSuccessfully || m_runState == Failed;
@@ -5671,8 +5942,8 @@
m_children.push_back( child );
}
- virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE {
- Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) );
+ virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
+ Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
return( it != m_children.end() )
? it->get()
: CATCH_NULL;
@@ -5750,41 +6021,65 @@
};
class SectionTracker : public TrackerBase {
+ std::vector<std::string> m_filters;
public:
- SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent )
- : TrackerBase( name, ctx, parent )
- {}
+ SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( nameAndLocation, ctx, parent )
+ {
+ if( parent ) {
+ while( !parent->isSectionTracker() )
+ parent = &parent->parent();
+
+ SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+ addNextFilters( parentSection.m_filters );
+ }
+ }
virtual ~SectionTracker();
virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
- static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) {
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
SectionTracker* section = CATCH_NULL;
ITracker& currentTracker = ctx.currentTracker();
- if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isSectionTracker() );
section = static_cast<SectionTracker*>( childTracker );
}
else {
- section = new SectionTracker( name, ctx, ¤tTracker );
+ section = new SectionTracker( nameAndLocation, ctx, ¤tTracker );
currentTracker.addChild( section );
}
- if( !ctx.completedCycle() && !section->isComplete() ) {
-
- section->open();
- }
+ if( !ctx.completedCycle() )
+ section->tryOpen();
return *section;
}
+
+ void tryOpen() {
+ if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
+ open();
+ }
+
+ void addInitialFilters( std::vector<std::string> const& filters ) {
+ if( !filters.empty() ) {
+ m_filters.push_back(""); // Root - should never be consulted
+ m_filters.push_back(""); // Test Case - not a section filter
+ std::copy( filters.begin(), filters.end(), std::back_inserter( m_filters ) );
+ }
+ }
+ void addNextFilters( std::vector<std::string> const& filters ) {
+ if( filters.size() > 1 )
+ std::copy( filters.begin()+1, filters.end(), std::back_inserter( m_filters ) );
+ }
};
class IndexTracker : public TrackerBase {
int m_size;
int m_index;
public:
- IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size )
- : TrackerBase( name, ctx, parent ),
+ IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+ : TrackerBase( nameAndLocation, ctx, parent ),
m_size( size ),
m_index( -1 )
{}
@@ -5792,17 +6087,17 @@
virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
- static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
+ static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
IndexTracker* tracker = CATCH_NULL;
ITracker& currentTracker = ctx.currentTracker();
- if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+ if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isIndexTracker() );
tracker = static_cast<IndexTracker*>( childTracker );
}
else {
- tracker = new IndexTracker( name, ctx, ¤tTracker, size );
+ tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size );
currentTracker.addChild( tracker );
}
@@ -5830,7 +6125,7 @@
};
inline ITracker& TrackerContext::startRun() {
- m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL );
+ m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
m_currentTracker = CATCH_NULL;
m_runState = Executing;
return *m_rootTracker;
@@ -5850,35 +6145,128 @@
namespace Catch {
- // Report the error condition then exit the process
- inline void fatal( std::string const& message, int exitCode ) {
+ // Report the error condition
+ inline void reportFatal( std::string const& message ) {
IContext& context = Catch::getCurrentContext();
IResultCapture* resultCapture = context.getResultCapture();
resultCapture->handleFatalErrorCondition( message );
-
- if( Catch::alwaysTrue() ) // avoids "no return" warnings
- exit( exitCode );
}
} // namespace Catch
#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+// #included from: catch_windows_h_proxy.h
+
+#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# define NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINES_NOMINMAX
+# undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+# undef WIN32_LEAN_AND_MEAN
+#endif
+
+
+# if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+ struct FatalConditionHandler {
+ void reset() {}
+ };
+}
+
+# else // CATCH_CONFIG_WINDOWS_SEH is defined
namespace Catch {
+ struct SignalDefs { DWORD id; const char* name; };
+ extern SignalDefs signalDefs[];
+ // There is no 1-1 mapping between signals and windows exceptions.
+ // Windows can easily distinguish between SO and SigSegV,
+ // but SigInt, SigTerm, etc are handled differently.
+ SignalDefs signalDefs[] = {
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" },
+ { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+ { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
+ };
+
struct FatalConditionHandler {
- void reset() {}
- };
+
+ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+ for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+ reset();
+ reportFatal(signalDefs[i].name);
+ }
+ }
+ // If its not an exception we care about, pass it along.
+ // This stops us from eating debugger breaks etc.
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ FatalConditionHandler() {
+ isSet = true;
+ // 32k seems enough for Catch to handle stack overflow,
+ // but the value was found experimentally, so there is no strong guarantee
+ guaranteeSize = 32 * 1024;
+ exceptionHandlerHandle = CATCH_NULL;
+ // Register as first handler in current chain
+ exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+ // Pass in guarantee size to be filled
+ SetThreadStackGuarantee(&guaranteeSize);
+ }
+
+ static void reset() {
+ if (isSet) {
+ // Unregister handler and restore the old guarantee
+ RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+ SetThreadStackGuarantee(&guaranteeSize);
+ exceptionHandlerHandle = CATCH_NULL;
+ isSet = false;
+ }
+ }
+
+ ~FatalConditionHandler() {
+ reset();
+ }
+ private:
+ static bool isSet;
+ static ULONG guaranteeSize;
+ static PVOID exceptionHandlerHandle;
+ };
+
+ bool FatalConditionHandler::isSet = false;
+ ULONG FatalConditionHandler::guaranteeSize = 0;
+ PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
} // namespace Catch
+# endif // CATCH_CONFIG_WINDOWS_SEH
+
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
#include <signal.h>
namespace Catch {
- struct SignalDefs { int id; const char* name; };
+ struct SignalDefs {
+ int id;
+ const char* name;
+ };
extern SignalDefs signalDefs[];
SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" },
@@ -5887,35 +6275,66 @@
{ SIGSEGV, "SIGSEGV - Segmentation violation signal" },
{ SIGTERM, "SIGTERM - Termination request signal" },
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
- };
+ };
struct FatalConditionHandler {
+ static bool isSet;
+ static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
+ static stack_t oldSigStack;
+ static char altStackMem[SIGSTKSZ];
+
static void handleSignal( int sig ) {
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
- if( sig == signalDefs[i].id )
- fatal( signalDefs[i].name, -sig );
- fatal( "<unknown signal>", -sig );
+ std::string name = "<unknown signal>";
+ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+ SignalDefs &def = signalDefs[i];
+ if (sig == def.id) {
+ name = def.name;
+ break;
+ }
+ }
+ reset();
+ reportFatal(name);
+ raise( sig );
}
- FatalConditionHandler() : m_isSet( true ) {
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
- signal( signalDefs[i].id, handleSignal );
- }
- ~FatalConditionHandler() {
- reset();
- }
- void reset() {
- if( m_isSet ) {
- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
- signal( signalDefs[i].id, SIG_DFL );
- m_isSet = false;
+ FatalConditionHandler() {
+ isSet = true;
+ stack_t sigStack;
+ sigStack.ss_sp = altStackMem;
+ sigStack.ss_size = SIGSTKSZ;
+ sigStack.ss_flags = 0;
+ sigaltstack(&sigStack, &oldSigStack);
+ struct sigaction sa = { 0 };
+
+ sa.sa_handler = handleSignal;
+ sa.sa_flags = SA_ONSTACK;
+ for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+ sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
}
}
- bool m_isSet;
+ ~FatalConditionHandler() {
+ reset();
+ }
+ static void reset() {
+ if( isSet ) {
+ // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+ for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
+ sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
+ }
+ // Return the old stack
+ sigaltstack(&oldSigStack, CATCH_NULL);
+ isSet = false;
+ }
+ }
};
+ bool FatalConditionHandler::isSet = false;
+ struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+ stack_t FatalConditionHandler::oldSigStack = {};
+ char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
} // namespace Catch
#endif // not Windows
@@ -5994,10 +6413,12 @@
m_activeTestCase = &testCase;
do {
- m_trackerContext.startRun();
+ ITracker& rootTracker = m_trackerContext.startRun();
+ assert( rootTracker.isSectionTracker() );
+ static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
do {
m_trackerContext.startCycle();
- m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name );
+ m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
runCurrentTest( redirectedCout, redirectedCerr );
}
while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
@@ -6042,7 +6463,7 @@
m_messages.clear();
// Reset working state
- m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+ m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
m_lastResult = result;
}
@@ -6051,10 +6472,7 @@
Counts& assertions
)
{
- std::ostringstream oss;
- oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
-
- ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() );
+ ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
if( !sectionTracker.isOpen() )
return false;
m_activeSections.push_back( §ionTracker );
@@ -6113,7 +6531,7 @@
virtual std::string getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
- : "";
+ : std::string();
}
virtual const AssertionResult* getLastResult() const {
@@ -6143,11 +6561,11 @@
deltaTotals.testCases.failed = 1;
m_reporter->testCaseEnded( TestCaseStats( testInfo,
deltaTotals,
- "",
- "",
+ std::string(),
+ std::string(),
false ) );
m_totals.testCases.failed++;
- testGroupEnded( "", m_totals, 1, 1 );
+ testGroupEnded( std::string(), m_totals, 1, 1 );
m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
}
@@ -6166,7 +6584,7 @@
Counts prevAssertions = m_totals.assertions;
double duration = 0;
try {
- m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+ m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal );
seedRng( *m_config );
@@ -6496,7 +6914,6 @@
#include <vector>
#include <set>
#include <sstream>
-#include <iostream>
#include <algorithm>
namespace Catch {
@@ -6557,7 +6974,7 @@
ss << Colour( Colour::Red )
<< "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
- << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+ << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
<< "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
throw std::runtime_error(ss.str());
@@ -6589,7 +7006,7 @@
virtual void registerTest( TestCase const& testCase ) {
std::string name = testCase.getTestCaseInfo().name;
- if( name == "" ) {
+ if( name.empty() ) {
std::ostringstream oss;
oss << "Anonymous test case " << ++m_unnamedCount;
return registerTest( testCase.withName( oss.str() ) );
@@ -6638,7 +7055,7 @@
inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
std::string className = classOrQualifiedMethodName;
- if( startsWith( className, "&" ) )
+ if( startsWith( className, '&' ) )
{
std::size_t lastColons = className.rfind( "::" );
std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
@@ -6856,7 +7273,7 @@
// #included from: catch_notimplemented_exception.hpp
#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
-#include <ostream>
+#include <sstream>
namespace Catch {
@@ -6928,7 +7345,7 @@
m_ofs.open( filename.c_str() );
if( m_ofs.fail() ) {
std::ostringstream oss;
- oss << "Unable to open file: '" << filename << "'";
+ oss << "Unable to open file: '" << filename << '\'';
throw std::domain_error( oss.str() );
}
}
@@ -6981,6 +7398,11 @@
Context( Context const& );
void operator=( Context const& );
+ public:
+ virtual ~Context() {
+ deleteAllValues( m_generatorsByTestName );
+ }
+
public: // IContext
virtual IResultCapture* getResultCapture() {
return m_resultCapture;
@@ -7094,16 +7516,6 @@
#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
-
namespace Catch {
namespace {
@@ -7370,7 +7782,7 @@
std::string AssertionResult::getExpression() const {
if( isFalseTest( m_info.resultDisposition ) )
- return "!" + m_info.capturedExpression;
+ return '!' + m_info.capturedExpression;
else
return m_info.capturedExpression;
}
@@ -7386,7 +7798,7 @@
}
std::string AssertionResult::getExpandedExpression() const {
- return m_resultData.reconstructedExpression;
+ return m_resultData.reconstructExpression();
}
std::string AssertionResult::getMessage() const {
@@ -7400,15 +7812,25 @@
return m_info.macroName;
}
+ void AssertionResult::discardDecomposedExpression() const {
+ m_resultData.decomposedExpression = CATCH_NULL;
+ }
+
+ void AssertionResult::expandDecomposedExpression() const {
+ m_resultData.reconstructExpression();
+ }
+
} // end namespace Catch
// #included from: catch_test_case_info.hpp
#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+#include <cctype>
+
namespace Catch {
inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
- if( startsWith( tag, "." ) ||
+ if( startsWith( tag, '.' ) ||
tag == "hide" ||
tag == "!hide" )
return TestCaseInfo::IsHidden;
@@ -7418,11 +7840,13 @@
return TestCaseInfo::ShouldFail;
else if( tag == "!mayfail" )
return TestCaseInfo::MayFail;
+ else if( tag == "!nonportable" )
+ return TestCaseInfo::NonPortable;
else
return TestCaseInfo::None;
}
inline bool isReservedTag( std::string const& tag ) {
- return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
+ return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
}
inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
if( isReservedTag( tag ) ) {
@@ -7492,7 +7916,7 @@
std::ostringstream oss;
for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
- oss << "[" << *it << "]";
+ oss << '[' << *it << ']';
std::string lcaseTag = toLower( *it );
testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
testCaseInfo.lcaseTags.insert( lcaseTag );
@@ -7608,18 +8032,18 @@
{}
std::ostream& operator << ( std::ostream& os, Version const& version ) {
- os << version.majorVersion << "."
- << version.minorVersion << "."
+ os << version.majorVersion << '.'
+ << version.minorVersion << '.'
<< version.patchNumber;
if( !version.branchName.empty() ) {
- os << "-" << version.branchName
- << "." << version.buildNumber;
+ os << '-' << version.branchName
+ << '.' << version.buildNumber;
}
return os;
}
- Version libraryVersion( 1, 6, 0, "", 0 );
+ Version libraryVersion( 1, 7, 2, "", 0 );
}
@@ -7790,7 +8214,6 @@
#endif
#ifdef CATCH_PLATFORM_WINDOWS
-#include <windows.h>
#else
#include <sys/time.h>
#endif
@@ -7839,19 +8262,28 @@
// #included from: catch_common.hpp
#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+#include <cstring>
+#include <cctype>
+
namespace Catch {
bool startsWith( std::string const& s, std::string const& prefix ) {
- return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
+ return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+ }
+ bool startsWith( std::string const& s, char prefix ) {
+ return !s.empty() && s[0] == prefix;
}
bool endsWith( std::string const& s, std::string const& suffix ) {
- return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix;
+ return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+ }
+ bool endsWith( std::string const& s, char suffix ) {
+ return !s.empty() && s[s.size()-1] == suffix;
}
bool contains( std::string const& s, std::string const& infix ) {
return s.find( infix ) != std::string::npos;
}
char toLowerCh(char c) {
- return static_cast<char>( ::tolower( c ) );
+ return static_cast<char>( std::tolower( c ) );
}
void toLowerInPlace( std::string& s ) {
std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
@@ -7866,7 +8298,7 @@
std::string::size_type start = str.find_first_not_of( whitespaceChars );
std::string::size_type end = str.find_last_not_of( whitespaceChars );
- return start != std::string::npos ? str.substr( start, 1+end-start ) : "";
+ return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
}
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
@@ -7889,29 +8321,25 @@
{}
std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
- os << pluraliser.m_count << " " << pluraliser.m_label;
+ os << pluraliser.m_count << ' ' << pluraliser.m_label;
if( pluraliser.m_count != 1 )
- os << "s";
+ os << 's';
return os;
}
- SourceLineInfo::SourceLineInfo() : line( 0 ){}
+ SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){}
SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
: file( _file ),
line( _line )
{}
- SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
- : file( other.file ),
- line( other.line )
- {}
bool SourceLineInfo::empty() const {
- return file.empty();
+ return file[0] == '\0';
}
bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
- return line == other.line && file == other.file;
+ return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
}
bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
- return line < other.line || ( line == other.line && file < other.file );
+ return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
}
void seedRng( IConfig const& config ) {
@@ -7924,16 +8352,16 @@
std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
#ifndef __GNUG__
- os << info.file << "(" << info.line << ")";
+ os << info.file << '(' << info.line << ')';
#else
- os << info.file << ":" << info.line;
+ os << info.file << ':' << info.line;
#endif
return os;
}
void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
std::ostringstream oss;
- oss << locationInfo << ": Internal Catch error: '" << message << "'";
+ oss << locationInfo << ": Internal Catch error: '" << message << '\'';
if( alwaysTrue() )
throw std::logic_error( oss.str() );
}
@@ -7980,8 +8408,6 @@
// #included from: catch_debugger.hpp
#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
-#include <iostream>
-
#ifdef CATCH_PLATFORM_MAC
#include <assert.h>
@@ -8030,6 +8456,33 @@
}
} // namespace Catch
+#elif defined(CATCH_PLATFORM_LINUX)
+ #include <fstream>
+ #include <string>
+
+ namespace Catch{
+ // The standard POSIX way of detecting a debugger is to attempt to
+ // ptrace() the process, but this needs to be done from a child and not
+ // this process itself to still allow attaching to this process later
+ // if wanted, so is rather heavy. Under Linux we have the PID of the
+ // "debugger" (which doesn't need to be gdb, of course, it could also
+ // be strace, for example) in /proc/$PID/status, so just get it from
+ // there instead.
+ bool isDebuggerActive(){
+ std::ifstream in("/proc/self/status");
+ for( std::string line; std::getline(in, line); ) {
+ static const int PREFIX_LEN = 11;
+ if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+ // We're traced if the PID is not 0 and no other PID starts
+ // with 0 digit, so it's enough to check for just a single
+ // character.
+ return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+ }
+ }
+
+ return false;
+ }
+ } // namespace Catch
#elif defined(_MSC_VER)
extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
namespace Catch {
@@ -8051,7 +8504,7 @@
#endif // Platform
#ifdef CATCH_PLATFORM_WINDOWS
- extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
+
namespace Catch {
void writeToDebugConsole( std::string const& text ) {
::OutputDebugStringA( text.c_str() );
@@ -8127,7 +8580,7 @@
}
}
}
- return "\"" + s + "\"";
+ return '"' + s + '"';
}
std::string toString( std::wstring const& value ) {
@@ -8148,19 +8601,19 @@
std::string toString( const wchar_t* const value )
{
- return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+ return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
}
std::string toString( wchar_t* const value )
{
- return Catch::toString( static_cast<const wchar_t*>( value ) );
+ return Catch::toString( static_cast<const wchar_t*>( value ) );
}
std::string toString( int value ) {
std::ostringstream oss;
oss << value;
if( value > Detail::hexThreshold )
- oss << " (0x" << std::hex << value << ")";
+ oss << " (0x" << std::hex << value << ')';
return oss.str();
}
@@ -8168,7 +8621,7 @@
std::ostringstream oss;
oss << value;
if( value > Detail::hexThreshold )
- oss << " (0x" << std::hex << value << ")";
+ oss << " (0x" << std::hex << value << ')';
return oss.str();
}
@@ -8196,7 +8649,7 @@
return fpToString( value, 10 );
}
std::string toString( const float value ) {
- return fpToString( value, 5 ) + "f";
+ return fpToString( value, 5 ) + 'f';
}
std::string toString( bool value ) {
@@ -8204,9 +8657,19 @@
}
std::string toString( char value ) {
- return value < ' '
- ? toString( static_cast<unsigned int>( value ) )
- : Detail::makeString( value );
+ if ( value == '\r' )
+ return "'\\r'";
+ if ( value == '\f' )
+ return "'\\f'";
+ if ( value == '\n' )
+ return "'\\n'";
+ if ( value == '\t' )
+ return "'\\t'";
+ if ( '\0' <= value && value < ' ' )
+ return toString( static_cast<unsigned int>( value ) );
+ char chstr[] = "' '";
+ chstr[1] = value;
+ return chstr;
}
std::string toString( signed char value ) {
@@ -8222,14 +8685,14 @@
std::ostringstream oss;
oss << value;
if( value > Detail::hexThreshold )
- oss << " (0x" << std::hex << value << ")";
+ oss << " (0x" << std::hex << value << ')';
return oss.str();
}
std::string toString( unsigned long long value ) {
std::ostringstream oss;
oss << value;
if( value > Detail::hexThreshold )
- oss << " (0x" << std::hex << value << ")";
+ oss << " (0x" << std::hex << value << ')';
return oss.str();
}
#endif
@@ -8286,22 +8749,10 @@
m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
return *this;
}
- ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
- m_exprComponents.lhs = lhs;
- return *this;
- }
- ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
- m_exprComponents.rhs = rhs;
- return *this;
- }
- ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
- m_exprComponents.op = op;
- return *this;
- }
- void ResultBuilder::endExpression() {
- m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
- captureExpression();
+ void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
+ AssertionResult result = build( expr );
+ handleResult( result );
}
void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
@@ -8314,6 +8765,7 @@
setResultType( resultType );
captureExpression();
}
+
void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
if( expectedMessage.empty() )
captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() );
@@ -8323,7 +8775,7 @@
void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) {
- assert( m_exprComponents.testFalse == false );
+ assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
AssertionResultData data = m_data;
data.resultType = ResultWas::Ok;
data.reconstructedExpression = m_assertionInfo.capturedExpression;
@@ -8341,6 +8793,7 @@
AssertionResult result = build();
handleResult( result );
}
+
void ResultBuilder::handleResult( AssertionResult const& result )
{
getResultCapture().assertionEnded( result );
@@ -8352,6 +8805,7 @@
m_shouldThrow = true;
}
}
+
void ResultBuilder::react() {
if( m_shouldThrow )
throw Catch::TestFailureException();
@@ -8362,43 +8816,32 @@
AssertionResult ResultBuilder::build() const
{
- assert( m_data.resultType != ResultWas::Unknown );
+ return build( *this );
+ }
+ // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
+ // a temporary DecomposedExpression, which in turn holds references to
+ // operands, possibly temporary as well.
+ // It should immediately be passed to handleResult; if the expression
+ // needs to be reported, its string expansion must be composed before
+ // the temporaries are destroyed.
+ AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
+ {
+ assert( m_data.resultType != ResultWas::Unknown );
AssertionResultData data = m_data;
- // Flip bool results if testFalse is set
- if( m_exprComponents.testFalse ) {
- if( data.resultType == ResultWas::Ok )
- data.resultType = ResultWas::ExpressionFailed;
- else if( data.resultType == ResultWas::ExpressionFailed )
- data.resultType = ResultWas::Ok;
+ // Flip bool results if FalseTest flag is set
+ if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
+ data.negate( expr.isBinaryExpression() );
}
data.message = m_stream.oss.str();
- data.reconstructedExpression = reconstructExpression();
- if( m_exprComponents.testFalse ) {
- if( m_exprComponents.op == "" )
- data.reconstructedExpression = "!" + data.reconstructedExpression;
- else
- data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
- }
+ data.decomposedExpression = &expr; // for lazy reconstruction
return AssertionResult( m_assertionInfo, data );
}
- std::string ResultBuilder::reconstructExpression() const {
- if( m_exprComponents.op == "" )
- return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs;
- else if( m_exprComponents.op == "matches" )
- return m_exprComponents.lhs + " " + m_exprComponents.rhs;
- else if( m_exprComponents.op != "!" ) {
- if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
- m_exprComponents.lhs.find("\n") == std::string::npos &&
- m_exprComponents.rhs.find("\n") == std::string::npos )
- return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
- else
- return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
- }
- else
- return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
+
+ void ResultBuilder::reconstructExpression( std::string& dest ) const {
+ dest = m_assertionInfo.capturedExpression;
}
} // end namespace Catch
@@ -8427,9 +8870,6 @@
} // end namespace Catch
-#include <map>
-#include <iostream>
-
namespace Catch {
TagAliasRegistry::~TagAliasRegistry() {}
@@ -8459,7 +8899,7 @@
void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
- if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
+ if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
std::ostringstream oss;
oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
throw std::domain_error( oss.str().c_str() );
@@ -8467,7 +8907,7 @@
if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
std::ostringstream oss;
oss << "error: tag alias, \"" << alias << "\" already registered.\n"
- << "\tFirst seen at " << find(alias)->lineInfo << "\n"
+ << "\tFirst seen at " << find(alias)->lineInfo << '\n'
<< "\tRedefined at " << lineInfo;
throw std::domain_error( oss.str().c_str() );
}
@@ -8735,12 +9175,12 @@
struct BySectionInfo {
BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
- BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+ BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
bool operator() ( Ptr<SectionNode> const& node ) const {
return node->stats.sectionInfo.lineInfo == m_other.lineInfo;
}
private:
- void operator=( BySectionInfo const& );
+ void operator=( BySectionInfo const& );
SectionInfo const& m_other;
};
@@ -8796,6 +9236,12 @@
assert( !m_sectionStack.empty() );
SectionNode& sectionNode = *m_sectionStack.back();
sectionNode.assertions.push_back( assertionStats );
+ // AssertionResult holds a pointer to a temporary DecomposedExpression,
+ // which getExpandedExpression() calls to build the expression string.
+ // Our section stack copy of the assertionResult will likely outlive the
+ // temporary, so it must be expanded or discarded now to avoid calling
+ // a destroyed object later.
+ prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
return true;
}
virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
@@ -8830,6 +9276,13 @@
virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
+ virtual void prepareExpandedExpression( AssertionResult& result ) const {
+ if( result.isOk() )
+ result.discardDecomposedExpression();
+ else
+ result.expandDecomposedExpression();
+ }
+
Ptr<IConfig const> m_config;
std::ostream& stream;
std::vector<AssertionStats> m_assertions;
@@ -8850,7 +9303,7 @@
char const* getLineOfChars() {
static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
if( !*line ) {
- memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+ std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
}
return line;
@@ -8935,7 +9388,7 @@
return new T( config );
}
virtual std::string getDescription() const {
- return "";
+ return std::string();
}
};
@@ -9004,8 +9457,11 @@
default:
// Escape control chars - based on contribution by @espenalb in PR #465 and
// by @mrpi PR #588
- if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
- os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>( c ) << ';';
+ if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
+ // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+ os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>( c );
+ }
else
os << c;
}
@@ -9059,20 +9515,17 @@
XmlWriter()
: m_tagIsOpen( false ),
m_needsNewline( false ),
- m_os( &Catch::cout() )
+ m_os( Catch::cout() )
{
- // We encode control characters, which requires
- // XML 1.1
- // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
- *m_os << "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n";
+ writeDeclaration();
}
XmlWriter( std::ostream& os )
: m_tagIsOpen( false ),
m_needsNewline( false ),
- m_os( &os )
+ m_os( os )
{
- *m_os << "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n";
+ writeDeclaration();
}
~XmlWriter() {
@@ -9083,7 +9536,7 @@
XmlWriter& startElement( std::string const& name ) {
ensureTagClosed();
newlineIfNecessary();
- stream() << m_indent << "<" << name;
+ m_os << m_indent << '<' << name;
m_tags.push_back( name );
m_indent += " ";
m_tagIsOpen = true;
@@ -9100,24 +9553,25 @@
newlineIfNecessary();
m_indent = m_indent.substr( 0, m_indent.size()-2 );
if( m_tagIsOpen ) {
- stream() << "/>\n";
+ m_os << "/>";
m_tagIsOpen = false;
}
else {
- stream() << m_indent << "</" << m_tags.back() << ">\n";
+ m_os << m_indent << "</" << m_tags.back() << ">";
}
+ m_os << std::endl;
m_tags.pop_back();
return *this;
}
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
if( !name.empty() && !attribute.empty() )
- stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\"";
+ m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
return *this;
}
XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
- stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
+ m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
return *this;
}
@@ -9133,8 +9587,8 @@
bool tagWasOpen = m_tagIsOpen;
ensureTagClosed();
if( tagWasOpen && indent )
- stream() << m_indent;
- stream() << XmlEncode( text );
+ m_os << m_indent;
+ m_os << XmlEncode( text );
m_needsNewline = true;
}
return *this;
@@ -9142,39 +9596,39 @@
XmlWriter& writeComment( std::string const& text ) {
ensureTagClosed();
- stream() << m_indent << "<!--" << text << "-->";
+ m_os << m_indent << "<!--" << text << "-->";
m_needsNewline = true;
return *this;
}
+ void writeStylesheetRef( std::string const& url ) {
+ m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+ }
+
XmlWriter& writeBlankLine() {
ensureTagClosed();
- stream() << "\n";
+ m_os << '\n';
return *this;
}
- void setStream( std::ostream& os ) {
- m_os = &os;
+ void ensureTagClosed() {
+ if( m_tagIsOpen ) {
+ m_os << ">" << std::endl;
+ m_tagIsOpen = false;
+ }
}
private:
XmlWriter( XmlWriter const& );
void operator=( XmlWriter const& );
- std::ostream& stream() {
- return *m_os;
- }
-
- void ensureTagClosed() {
- if( m_tagIsOpen ) {
- stream() << ">\n";
- m_tagIsOpen = false;
- }
+ void writeDeclaration() {
+ m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
}
void newlineIfNecessary() {
if( m_needsNewline ) {
- stream() << "\n";
+ m_os << std::endl;
m_needsNewline = false;
}
}
@@ -9183,7 +9637,7 @@
bool m_needsNewline;
std::vector<std::string> m_tags;
std::string m_indent;
- std::ostream* m_os;
+ std::ostream& m_os;
};
}
@@ -9219,6 +9673,10 @@
return "Reports test results as an XML document";
}
+ virtual std::string getStylesheetRef() const {
+ return std::string();
+ }
+
public: // StreamingReporterBase
virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
@@ -9227,6 +9685,9 @@
virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
StreamingReporterBase::testRunStarting( testInfo );
+ std::string stylesheetRef = getStylesheetRef();
+ if( !stylesheetRef.empty() )
+ m_xml.writeStylesheetRef( stylesheetRef );
m_xml.startElement( "Catch" );
if( !m_config->name().empty() )
m_xml.writeAttribute( "name", m_config->name() );
@@ -9240,10 +9701,14 @@
virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
StreamingReporterBase::testCaseStarting(testInfo);
- m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name );
+ m_xml.startElement( "TestCase" )
+ .writeAttribute( "name", trim( testInfo.name ) )
+ .writeAttribute( "description", testInfo.description )
+ .writeAttribute( "tags", testInfo.tagsAsString );
if ( m_config->showDurations() == ShowDurations::Always )
m_testCaseTimer.start();
+ m_xml.ensureTagClosed();
}
virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
@@ -9252,6 +9717,7 @@
m_xml.startElement( "Section" )
.writeAttribute( "name", trim( sectionInfo.name ) )
.writeAttribute( "description", sectionInfo.description );
+ m_xml.ensureTagClosed();
}
}
@@ -9283,7 +9749,7 @@
if( assertionResult.hasExpression() ) {
m_xml.startElement( "Expression" )
.writeAttribute( "success", assertionResult.succeeded() )
- .writeAttribute( "type", assertionResult.getTestMacroName() )
+ .writeAttribute( "type", assertionResult.getTestMacroName() )
.writeAttribute( "filename", assertionResult.getSourceInfo().file )
.writeAttribute( "line", assertionResult.getSourceInfo().line );
@@ -9351,6 +9817,11 @@
if ( m_config->showDurations() == ShowDurations::Always )
e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+ if( !testCaseStats.stdOut.empty() )
+ m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+ if( !testCaseStats.stdErr.empty() )
+ m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+
m_xml.endElement();
}
@@ -9390,6 +9861,35 @@
namespace Catch {
+ namespace {
+ std::string getCurrentTimestamp() {
+ // Beware, this is not reentrant because of backward compatibility issues
+ // Also, UTC only, again because of backward compatibility (%z is C++11)
+ time_t rawtime;
+ std::time(&rawtime);
+ const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+ std::tm timeInfo = {};
+ gmtime_s(&timeInfo, &rawtime);
+#else
+ std::tm* timeInfo;
+ timeInfo = std::gmtime(&rawtime);
+#endif
+
+ char timeStamp[timeStampSize];
+ const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+ std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+ std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+ return std::string(timeStamp);
+ }
+
+ }
+
class JunitReporter : public CumulativeReporterBase {
public:
JunitReporter( ReporterConfig const& _config )
@@ -9454,7 +9954,7 @@
xml.writeAttribute( "time", "" );
else
xml.writeAttribute( "time", suiteTime );
- xml.writeAttribute( "timestamp", "tbd" ); // !TBD
+ xml.writeAttribute( "timestamp", getCurrentTimestamp() );
// Write test cases
for( TestGroupNode::ChildNodes::const_iterator
@@ -9489,7 +9989,7 @@
SectionNode const& sectionNode ) {
std::string name = trim( sectionNode.stats.sectionInfo.name );
if( !rootName.empty() )
- name = rootName + "/" + name;
+ name = rootName + '/' + name;
if( !sectionNode.assertions.empty() ||
!sectionNode.stdOut.empty() ||
@@ -9567,14 +10067,14 @@
std::ostringstream oss;
if( !result.getMessage().empty() )
- oss << result.getMessage() << "\n";
+ oss << result.getMessage() << '\n';
for( std::vector<MessageInfo>::const_iterator
it = stats.infoMessages.begin(),
itEnd = stats.infoMessages.end();
it != itEnd;
++it )
if( it->type == ResultWas::Info )
- oss << it->message << "\n";
+ oss << it->message << '\n';
oss << "at " << result.getSourceInfo();
xml.writeText( oss.str(), false );
@@ -9609,7 +10109,7 @@
}
virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
- stream << "No test cases matched '" << spec << "'" << std::endl;
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
}
virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
@@ -9651,12 +10151,12 @@
}
if( m_headerPrinted ) {
if( m_config->showDurations() == ShowDurations::Always )
- stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
m_headerPrinted = false;
}
else {
if( m_config->showDurations() == ShowDurations::Always )
- stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+ stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl;
}
StreamingReporterBase::sectionEnded( _sectionStats );
}
@@ -9670,7 +10170,7 @@
printSummaryDivider();
stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
printTotals( _testGroupStats.totals );
- stream << "\n" << std::endl;
+ stream << '\n' << std::endl;
}
StreamingReporterBase::testGroupEnded( _testGroupStats );
}
@@ -9762,13 +10262,13 @@
printSourceInfo();
if( stats.totals.assertions.total() > 0 ) {
if( result.isOk() )
- stream << "\n";
+ stream << '\n';
printResultType();
printOriginalExpression();
printReconstructedExpression();
}
else {
- stream << "\n";
+ stream << '\n';
}
printMessage();
}
@@ -9785,25 +10285,25 @@
Colour colourGuard( Colour::OriginalExpression );
stream << " ";
stream << result.getExpressionInMacro();
- stream << "\n";
+ stream << '\n';
}
}
void printReconstructedExpression() const {
if( result.hasExpandedExpression() ) {
stream << "with expansion:\n";
Colour colourGuard( Colour::ReconstructedExpression );
- stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n";
+ stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n';
}
}
void printMessage() const {
if( !messageLabel.empty() )
- stream << messageLabel << ":" << "\n";
+ stream << messageLabel << ':' << '\n';
for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
it != itEnd;
++it ) {
// If this assertion is a warning ignore any INFO messages
if( printInfoMessages || it->type != ResultWas::Info )
- stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n";
+ stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n';
}
}
void printSourceInfo() const {
@@ -9835,7 +10335,7 @@
}
}
void lazyPrintRunInfo() {
- stream << "\n" << getLineOfChars<'~'>() << "\n";
+ stream << '\n' << getLineOfChars<'~'>() << '\n';
Colour colour( Colour::SecondaryText );
stream << currentTestRunInfo->name
<< " is a Catch v" << libraryVersion << " host application.\n"
@@ -9866,22 +10366,22 @@
printHeaderString( it->name, 2 );
}
- SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+ SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
if( !lineInfo.empty() ){
- stream << getLineOfChars<'-'>() << "\n";
+ stream << getLineOfChars<'-'>() << '\n';
Colour colourGuard( Colour::FileName );
- stream << lineInfo << "\n";
+ stream << lineInfo << '\n';
}
- stream << getLineOfChars<'.'>() << "\n" << std::endl;
+ stream << getLineOfChars<'.'>() << '\n' << std::endl;
}
void printClosedHeader( std::string const& _name ) {
printOpenHeader( _name );
- stream << getLineOfChars<'.'>() << "\n";
+ stream << getLineOfChars<'.'>() << '\n';
}
void printOpenHeader( std::string const& _name ) {
- stream << getLineOfChars<'-'>() << "\n";
+ stream << getLineOfChars<'-'>() << '\n';
{
Colour colourGuard( Colour::Headers );
printHeaderString( _name );
@@ -9898,7 +10398,7 @@
i = 0;
stream << Text( _string, TextAttributes()
.setIndent( indent+i)
- .setInitialIndent( indent ) ) << "\n";
+ .setInitialIndent( indent ) ) << '\n';
}
struct SummaryColumn {
@@ -9913,9 +10413,9 @@
std::string row = oss.str();
for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
while( it->size() < row.size() )
- *it = " " + *it;
+ *it = ' ' + *it;
while( it->size() > row.size() )
- row = " " + row;
+ row = ' ' + row;
}
rows.push_back( row );
return *this;
@@ -9935,8 +10435,8 @@
stream << Colour( Colour::ResultSuccess ) << "All tests passed";
stream << " ("
<< pluralise( totals.assertions.passed, "assertion" ) << " in "
- << pluralise( totals.testCases.passed, "test case" ) << ")"
- << "\n";
+ << pluralise( totals.testCases.passed, "test case" ) << ')'
+ << '\n';
}
else {
@@ -9971,10 +10471,10 @@
else if( value != "0" ) {
stream << Colour( Colour::LightGrey ) << " | ";
stream << Colour( it->colour )
- << value << " " << it->label;
+ << value << ' ' << it->label;
}
}
- stream << "\n";
+ stream << '\n';
}
static std::size_t makeRatio( std::size_t number, std::size_t total ) {
@@ -10010,10 +10510,10 @@
else {
stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
}
- stream << "\n";
+ stream << '\n';
}
void printSummaryDivider() {
- stream << getLineOfChars<'-'>() << "\n";
+ stream << getLineOfChars<'-'>() << '\n';
}
private:
@@ -10048,7 +10548,7 @@
}
virtual void noMatchingTestCases( std::string const& spec ) {
- stream << "No test cases matched '" << spec << "'" << std::endl;
+ stream << "No test cases matched '" << spec << '\'' << std::endl;
}
virtual void assertionStarting( AssertionInfo const& ) {
@@ -10075,7 +10575,7 @@
virtual void testRunEnded( TestRunStats const& _testRunStats ) {
printTotals( _testRunStats.totals );
- stream << "\n" << std::endl;
+ stream << '\n' << std::endl;
StreamingReporterBase::testRunEnded( _testRunStats );
}
@@ -10175,26 +10675,26 @@
void printSourceInfo() const {
Colour colourGuard( Colour::FileName );
- stream << result.getSourceInfo() << ":";
+ stream << result.getSourceInfo() << ':';
}
void printResultType( Colour::Code colour, std::string passOrFail ) const {
if( !passOrFail.empty() ) {
{
Colour colourGuard( colour );
- stream << " " << passOrFail;
+ stream << ' ' << passOrFail;
}
- stream << ":";
+ stream << ':';
}
}
void printIssue( std::string issue ) const {
- stream << " " << issue;
+ stream << ' ' << issue;
}
void printExpressionWas() {
if( result.hasExpression() ) {
- stream << ";";
+ stream << ';';
{
Colour colour( dimColour() );
stream << " expression was:";
@@ -10205,7 +10705,7 @@
void printOriginalExpression() const {
if( result.hasExpression() ) {
- stream << " " << result.getExpression();
+ stream << ' ' << result.getExpression();
}
}
@@ -10221,7 +10721,7 @@
void printMessage() {
if ( itMessage != messages.end() ) {
- stream << " '" << itMessage->message << "'";
+ stream << " '" << itMessage->message << '\'';
++itMessage;
}
}
@@ -10236,13 +10736,13 @@
{
Colour colourGuard( colour );
- stream << " with " << pluralise( N, "message" ) << ":";
+ stream << " with " << pluralise( N, "message" ) << ':';
}
for(; itMessage != itEnd; ) {
// If this assertion is a warning ignore any INFO messages
if( printInfoMessages || itMessage->type != ResultWas::Info ) {
- stream << " '" << itMessage->message << "'";
+ stream << " '" << itMessage->message << '\'';
if ( ++itMessage != itEnd ) {
Colour colourGuard( dimColour() );
stream << " and";
@@ -10268,7 +10768,7 @@
// - green: Passed [both/all] N tests cases with M assertions.
std::string bothOrAll( std::size_t count ) const {
- return count == 1 ? "" : count == 2 ? "both " : "all " ;
+ return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
}
void printTotals( const Totals& totals ) const {
@@ -10279,12 +10779,12 @@
Colour colour( Colour::ResultError );
const std::string qualify_assertions_failed =
totals.assertions.failed == totals.assertions.total() ?
- bothOrAll( totals.assertions.failed ) : "";
+ bothOrAll( totals.assertions.failed ) : std::string();
stream <<
"Failed " << bothOrAll( totals.testCases.failed )
<< pluralise( totals.testCases.failed, "test case" ) << ", "
"failed " << qualify_assertions_failed <<
- pluralise( totals.assertions.failed, "assertion" ) << ".";
+ pluralise( totals.assertions.failed, "assertion" ) << '.';
}
else if( totals.assertions.total() == 0 ) {
stream <<
@@ -10296,14 +10796,14 @@
Colour colour( Colour::ResultError );
stream <<
"Failed " << pluralise( totals.testCases.failed, "test case" ) << ", "
- "failed " << pluralise( totals.assertions.failed, "assertion" ) << ".";
+ "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
}
else {
Colour colour( Colour::ResultSuccess );
stream <<
"Passed " << bothOrAll( totals.testCases.passed )
<< pluralise( totals.testCases.passed, "test case" ) <<
- " with " << pluralise( totals.assertions.passed, "assertion" ) << ".";
+ " with " << pluralise( totals.assertions.passed, "assertion" ) << '.';
}
}
};
@@ -10389,7 +10889,8 @@
// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
- return Catch::Session().run( argc, argv );
+ int result = Catch::Session().run( argc, argv );
+ return ( result < 0xff ? result : 0xff );
}
#else // __OBJC__
@@ -10407,7 +10908,7 @@
[pool drain];
#endif
- return result;
+ return ( result < 0xff ? result : 0xff );
}
#endif // __OBJC__
@@ -10442,7 +10943,7 @@
#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" )
#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
-#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )