Merge branch 'develop' of https://github.com/cryfs/cryfs into develop
This commit is contained in:
commit
8373abf216
@ -55,7 +55,14 @@ references:
|
||||
sudo chmod o-w /etc/apt/sources.list.d/clang.list
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get update -qq
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git ccache $APT_COMPILER_PACKAGE cmake make libcurl4-openssl-dev libssl-dev libfuse-dev python
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y git ccache $APT_COMPILER_PACKAGE cmake make libcurl4-openssl-dev libssl-dev libfuse-dev
|
||||
|
||||
# install conan
|
||||
pyenv global 3.7.0
|
||||
python3 -m pip install -U pip
|
||||
python3 -m pip install conan
|
||||
source ~/.profile
|
||||
|
||||
# Use /dev/urandom when /dev/random is accessed to use less entropy
|
||||
sudo cp -a /dev/urandom /dev/random
|
||||
|
||||
@ -67,14 +74,25 @@ references:
|
||||
sudo ln -s /usr/bin/run-clang-tidy-8.py /usr/bin/run-clang-tidy.py
|
||||
fi
|
||||
|
||||
# Setup build cache
|
||||
sudo mkdir -p /build_cache/ccache
|
||||
sudo mkdir -p /build_cache/conan
|
||||
sudo chown -R circleci:circleci /build_cache
|
||||
|
||||
# Setup conan cache
|
||||
echo 'export CONAN_USER_HOME=/build_cache/conan' >> $BASH_ENV
|
||||
if [[ "${CXX}" == *"g++"* ]]; then
|
||||
# Conan uses old gcc ABI by default but we can only build with the cxx11 ABI.
|
||||
conan profile new default --detect
|
||||
conan profile update settings.compiler.libcxx=libstdc++11 default
|
||||
fi
|
||||
|
||||
# Setup ccache
|
||||
sudo ln -s /usr/bin/ccache /usr/local/bin/$CC
|
||||
sudo ln -s /usr/bin/ccache /usr/local/bin/$CXX
|
||||
sudo mkdir /ccache_data
|
||||
sudo chown circleci:circleci /ccache_data
|
||||
echo 'export CCACHE_COMPILERCHECK=content' >> $BASH_ENV
|
||||
echo 'export CCACHE_COMPRESS=1' >> $BASH_ENV
|
||||
echo 'export CCACHE_DIR=/ccache_data' >> $BASH_ENV
|
||||
echo 'export CCACHE_DIR=/build_cache/ccache' >> $BASH_ENV
|
||||
echo 'export CCACHE_SLOPPINESS=include_file_mtime' >> $BASH_ENV
|
||||
|
||||
sudo mkdir -p /tmp/aptcache
|
||||
@ -88,60 +106,20 @@ references:
|
||||
cmake --version
|
||||
/usr/local/bin/$CC --version
|
||||
/usr/local/bin/$CXX --version
|
||||
upgrade_boost_pre: &upgrade_boost_pre
|
||||
restore_cache:
|
||||
keys:
|
||||
# Find the most recent cache from any branch
|
||||
- v5_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
|
||||
upgrade_boost_post: &upgrade_boost_post
|
||||
save_cache:
|
||||
key: v5_upgrade_boost_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
|
||||
paths:
|
||||
- /tmp/boost_1_65_1
|
||||
upgrade_boost: &upgrade_boost
|
||||
run:
|
||||
name: Upgrade Boost
|
||||
command: |
|
||||
# Detect number of CPU cores
|
||||
export NUMCORES=`nproc`
|
||||
echo Using $NUMCORES cores
|
||||
# Download and prepare boost (only if not already present from cache)
|
||||
if [ ! -d "/tmp/boost_1_65_1" ]; then
|
||||
echo "Didn't find boost in cache. Downloading and building."
|
||||
wget -O /tmp/boost.tar.bz2 https://sourceforge.net/projects/boost/files/boost/1.65.1/boost_1_65_1.tar.bz2/download
|
||||
if [ $(sha512sum /tmp/boost.tar.bz2 | awk '{print $1;}') == "a9e6866d3bb3e7c198f442ff09f5322f58064dca79bc420f2f0168eb63964226dfbc4f034a5a5e5958281fdf7518a1b057c894fbda0b61fced59c1661bf30f1a" ]; then
|
||||
echo Correct sha512sum
|
||||
else
|
||||
echo Wrong sha512sum
|
||||
sha512sum boost.tar.bz2
|
||||
exit 1
|
||||
fi
|
||||
echo Extracting...
|
||||
tar -xf /tmp/boost.tar.bz2 -C /tmp
|
||||
rm -rf boost.tar.bz2
|
||||
cd /tmp/boost_1_65_1
|
||||
./bootstrap.sh --with-toolset=${BUILD_TOOLSET} --with-libraries=filesystem,thread,chrono,program_options
|
||||
cd ..
|
||||
else
|
||||
echo Found boost in cache. Use cache and build.
|
||||
fi
|
||||
# Compile and install boost (if cached, this should be fast)
|
||||
cd /tmp/boost_1_65_1
|
||||
sudo ./b2 toolset=${BUILD_TOOLSET} link=static cxxflags=-fPIC -d0 -j$NUMCORES install
|
||||
build_pre: &build_pre
|
||||
restore_cache:
|
||||
keys:
|
||||
# Find most recent cache from any revision on the same branch (cache keys are prefix matched)
|
||||
# CIRCLE_PR_NUMBER is only set if this is a pull request.
|
||||
- v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}
|
||||
- v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}
|
||||
# Fallback to less specific caches if the one above wasn't found
|
||||
- v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}
|
||||
- v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
|
||||
- v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}
|
||||
- v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}
|
||||
build_post: &build_post
|
||||
save_cache:
|
||||
key: v4_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}_{{ .Revision }}
|
||||
key: v6_build_cache_{{ checksum "/tmp/_build_env_vars" }}_{{ arch }}_{{ .Branch }}_{{ .Environment.CIRCLE_PR_NUMBER }}_{{ .Revision }}
|
||||
paths:
|
||||
- /ccache_data
|
||||
- /build_cache
|
||||
build: &build
|
||||
run:
|
||||
name: Build
|
||||
@ -164,6 +142,7 @@ references:
|
||||
# Build
|
||||
mkdir cmake
|
||||
cd cmake
|
||||
conan install .. -s build_type=${BUILD_TYPE} --build=missing
|
||||
cmake .. -DBUILD_TESTING=on -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${OPENMP_PARAMS} ${CMAKE_FLAGS}
|
||||
make -j$NUMCORES
|
||||
|
||||
@ -175,14 +154,14 @@ references:
|
||||
command: |
|
||||
if "${RUN_TESTS}"; then
|
||||
cd cmake
|
||||
./test/gitversion/gitversion-test ${GTEST_ARGS}
|
||||
./test/cpp-utils/cpp-utils-test ${GTEST_ARGS}
|
||||
if [ ! "$DISABLE_BROKEN_ASAN_TESTS" = true ] ; then ./test/fspp/fspp-test ${GTEST_ARGS} ; fi
|
||||
./test/parallelaccessstore/parallelaccessstore-test ${GTEST_ARGS}
|
||||
./test/blockstore/blockstore-test ${GTEST_ARGS}
|
||||
./test/blobstore/blobstore-test ${GTEST_ARGS}
|
||||
./test/cryfs/cryfs-test ${GTEST_ARGS}
|
||||
./test/cryfs-cli/cryfs-cli-test ${GTEST_ARGS}
|
||||
./bin/gitversion-test ${GTEST_ARGS}
|
||||
./bin/cpp-utils-test ${GTEST_ARGS}
|
||||
if [ ! "$DISABLE_BROKEN_ASAN_TESTS" = true ] ; then ./bin/fspp-test ${GTEST_ARGS} ; fi
|
||||
./bin/parallelaccessstore-test ${GTEST_ARGS}
|
||||
./bin/blockstore-test ${GTEST_ARGS}
|
||||
./bin/blobstore-test ${GTEST_ARGS}
|
||||
./bin/cryfs-test ${GTEST_ARGS}
|
||||
./bin/cryfs-cli-test ${GTEST_ARGS}
|
||||
fi
|
||||
job_definition: &job_definition
|
||||
<<: *container_config
|
||||
@ -191,9 +170,6 @@ references:
|
||||
- <<: *container_setup_pre
|
||||
- <<: *container_setup
|
||||
- <<: *container_setup_post
|
||||
- <<: *upgrade_boost_pre
|
||||
- <<: *upgrade_boost
|
||||
- <<: *upgrade_boost_post
|
||||
- checkout
|
||||
- <<: *build_pre
|
||||
- <<: *build
|
||||
@ -205,30 +181,6 @@ references:
|
||||
only: /.*/
|
||||
|
||||
jobs:
|
||||
gcc_5_debug:
|
||||
<<: *job_definition
|
||||
environment:
|
||||
CC: gcc-5
|
||||
CXX: g++-5
|
||||
BUILD_TOOLSET: gcc
|
||||
APT_COMPILER_PACKAGE: "g++-5"
|
||||
CXXFLAGS: ""
|
||||
BUILD_TYPE: "Debug"
|
||||
GTEST_ARGS: ""
|
||||
CMAKE_FLAGS: ""
|
||||
RUN_TESTS: true
|
||||
gcc_5_release:
|
||||
<<: *job_definition
|
||||
environment:
|
||||
CC: gcc-5
|
||||
CXX: g++-5
|
||||
BUILD_TOOLSET: gcc
|
||||
APT_COMPILER_PACKAGE: "g++-5"
|
||||
CXXFLAGS: ""
|
||||
BUILD_TYPE: "Release"
|
||||
GTEST_ARGS: ""
|
||||
CMAKE_FLAGS: ""
|
||||
RUN_TESTS: true
|
||||
gcc_6_debug:
|
||||
<<: *job_definition
|
||||
environment:
|
||||
@ -551,9 +503,6 @@ jobs:
|
||||
- <<: *container_setup_pre
|
||||
- <<: *container_setup
|
||||
- <<: *container_setup_post
|
||||
- <<: *upgrade_boost_pre
|
||||
- <<: *upgrade_boost
|
||||
- <<: *upgrade_boost_post
|
||||
- checkout
|
||||
- run:
|
||||
name: clang-tidy
|
||||
@ -580,10 +529,6 @@ workflows:
|
||||
|
||||
build_and_test:
|
||||
jobs:
|
||||
- gcc_5_debug:
|
||||
<<: *enable_for_tags
|
||||
- gcc_5_release:
|
||||
<<: *enable_for_tags
|
||||
- gcc_6_debug:
|
||||
<<: *enable_for_tags
|
||||
- gcc_6_release:
|
||||
|
@ -32,19 +32,20 @@ cmake --version
|
||||
|
||||
# Build
|
||||
echo Build target: ${BUILD_TARGET}
|
||||
conan install .. -s build_type=${BUILD_TARGET} --build=missing
|
||||
cmake .. -DBUILD_TESTING=on -DCMAKE_BUILD_TYPE=${BUILD_TARGET}
|
||||
make -j$NUMCORES
|
||||
|
||||
ccache --show-stats
|
||||
|
||||
# Test
|
||||
./test/gitversion/gitversion-test
|
||||
./test/cpp-utils/cpp-utils-test
|
||||
./test/parallelaccessstore/parallelaccessstore-test
|
||||
./test/blockstore/blockstore-test
|
||||
./test/blobstore/blobstore-test
|
||||
./test/cryfs/cryfs-test
|
||||
./bin/gitversion-test
|
||||
./bin/cpp-utils-test
|
||||
./bin/parallelaccessstore-test
|
||||
./bin/blockstore-test
|
||||
./bin/blobstore-test
|
||||
./bin/cryfs-test
|
||||
|
||||
# TODO Also run once fixed
|
||||
# ./test/fspp/fspp-test
|
||||
# ./test/cryfs-cli/cryfs-cli-test
|
||||
# ./bin/fspp-test
|
||||
# ./bin/cryfs-cli-test
|
||||
|
@ -18,5 +18,7 @@ brew install libomp
|
||||
# By default, travis only fetches the newest 50 commits. We need more in case we're further from the last version tag, so the build doesn't fail because it can't generate the version number.
|
||||
git fetch --unshallow --tags
|
||||
|
||||
pip install conan
|
||||
|
||||
# Setup ccache
|
||||
brew install ccache
|
||||
|
@ -26,6 +26,20 @@ option(USE_CLANG_TIDY "build with clang-tidy checks enabled" OFF)
|
||||
option(USE_IWYU "build with iwyu checks enabled" OFF)
|
||||
option(CLANG_TIDY_WARNINGS_AS_ERRORS "treat clang-tidy warnings as errors" OFF)
|
||||
|
||||
set(CONAN_BUILD_INFO_FILE ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||
|
||||
if(EXISTS ${CONAN_BUILD_INFO_FILE})
|
||||
include(${CONAN_BUILD_INFO_FILE})
|
||||
else()
|
||||
message(WARNING "The file ${CONAN_BUILD_INFO_FILE} doesn't exist, you have to run conan install first")
|
||||
endif()
|
||||
conan_basic_setup(TARGETS SKIP_STD)
|
||||
|
||||
if(CONAN_SETTINGS_COMPILER_LIBCXX STREQUAL "libstdc++")
|
||||
# TODO Test this warning works correctly and that the proposed solution in the warning message works.
|
||||
message(FATAL_ERROR "Conan is set up to build against libstdc++ (i.e. the legacy GCC ABI). We only support libstdc++11 (i.e. the new GCC ABI).\nPlease add the '-s compiler.libcxx=libstdc++11' argument when running 'conan install'.")
|
||||
endif()
|
||||
|
||||
if(USE_IWYU)
|
||||
# note: for iwyu, we need cmake 3.3
|
||||
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
|
||||
|
@ -7,7 +7,7 @@
|
||||
"inheritEnvironments": [ "msvc_x86" ],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
@ -18,7 +18,7 @@
|
||||
"inheritEnvironments": [ "msvc_x86" ],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
@ -29,7 +29,7 @@
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
@ -40,7 +40,7 @@
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
|
||||
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DBOOST_ROOT=C:\\local\\boost_1_68_0 -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"cmakeCommandArgs": "-DBUILD_TESTING=on -DDOKAN_PATH=\"C:\\Program Files\\Dokan\\Dokan Library-1.2.2\"",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": ""
|
||||
}
|
||||
|
@ -1,3 +1,12 @@
|
||||
Version 0.11.0 (unreleased)
|
||||
---------------
|
||||
Build changes:
|
||||
* Switch to Conan package manager
|
||||
|
||||
New features:
|
||||
* Add support for atime mount options (noatime, strictatime, relatime, atime, nodiratime). As before, relatime is the default.
|
||||
|
||||
|
||||
Version 0.10.2
|
||||
---------------
|
||||
Fixed bugs:
|
||||
|
43
README.md
43
README.md
@ -43,15 +43,10 @@ Building from source
|
||||
Requirements
|
||||
------------
|
||||
- Git (for getting the source code)
|
||||
- GCC version >= 5.0 or Clang >= 4.0
|
||||
- GCC version >= 6.5 or Clang >= 4.0
|
||||
- CMake version >= 3.1
|
||||
- Conan package manager
|
||||
- libcurl4 (including development headers)
|
||||
- Boost libraries version >= 1.65.1 (including development headers)
|
||||
- filesystem
|
||||
- system
|
||||
- chrono
|
||||
- program_options
|
||||
- thread
|
||||
- SSL development libraries (including development headers, e.g. libssl-dev)
|
||||
- libFUSE version >= 2.8.6 (including development headers), on Mac OS X instead install osxfuse from https://osxfuse.github.io/
|
||||
- Python >= 2.7
|
||||
@ -60,13 +55,16 @@ Requirements
|
||||
You can use the following commands to install these requirements
|
||||
|
||||
# Ubuntu
|
||||
$ sudo apt install git g++ cmake make libcurl4-openssl-dev libboost-filesystem-dev libboost-system-dev libboost-chrono-dev libboost-program-options-dev libboost-thread-dev libssl-dev libfuse-dev python
|
||||
$ sudo apt install git g++ cmake make libcurl4-openssl-dev libssl-dev libfuse-dev python
|
||||
$ sudo pip install conan
|
||||
|
||||
# Fedora
|
||||
sudo dnf install git gcc-c++ cmake make libcurl-devel boost-devel boost-static openssl-devel fuse-devel python
|
||||
$ sudo dnf install git gcc-c++ cmake make libcurl-devel openssl-devel fuse-devel python
|
||||
$ sudo pip install conan
|
||||
|
||||
# Macintosh
|
||||
brew install cmake boost openssl libomp
|
||||
$ brew install cmake openssl libomp
|
||||
$ sudo pip install conan
|
||||
|
||||
Build & Install
|
||||
---------------
|
||||
@ -79,6 +77,7 @@ Build & Install
|
||||
2. Build
|
||||
|
||||
$ mkdir cmake && cd cmake
|
||||
$ conan install .. --build=missing
|
||||
$ cmake ..
|
||||
$ make
|
||||
|
||||
@ -97,7 +96,6 @@ Building on Windows (experimental)
|
||||
Build with Visual Studio 2019 and pass in the following flags to CMake:
|
||||
|
||||
-DDOKAN_PATH=[dokan library location, e.g. "C:\Program Files\Dokan\DokanLibrary-1.2.1"]
|
||||
-DBOOST_ROOT=[path to root of boost installation]
|
||||
|
||||
If you set these variables correctly in the `CMakeSettings.json` file, you should be able to open the cryfs source folder with Visual Studio 2019.
|
||||
|
||||
@ -106,35 +104,25 @@ Troubleshooting
|
||||
|
||||
On most systems, CMake should find the libraries automatically. However, that doesn't always work.
|
||||
|
||||
1. **Boost headers not found**
|
||||
|
||||
Pass in the boost include path with
|
||||
|
||||
cmake .. -DBoost_INCLUDE_DIRS=/path/to/boost/headers
|
||||
|
||||
If you want to link boost dynamically (e.g. you don't have the static libraries), use the following:
|
||||
|
||||
cmake .. -DBoost_USE_STATIC_LIBS=off
|
||||
|
||||
2. **Fuse/Osxfuse library not found**
|
||||
1. **Fuse/Osxfuse library not found**
|
||||
|
||||
Pass in the library path with
|
||||
|
||||
cmake .. -DFUSE_LIB_PATH=/path/to/fuse/or/osxfuse
|
||||
|
||||
3. **Fuse/Osxfuse headers not found**
|
||||
2. **Fuse/Osxfuse headers not found**
|
||||
|
||||
Pass in the include path with
|
||||
|
||||
cmake .. -DCMAKE_CXX_FLAGS="-I/path/to/fuse/or/osxfuse/headers"
|
||||
|
||||
4. **Openssl headers not found**
|
||||
3. **Openssl headers not found**
|
||||
|
||||
Pass in the include path with
|
||||
|
||||
cmake .. -DCMAKE_C_FLAGS="-I/path/to/openssl/include"
|
||||
|
||||
5. **OpenMP not found (osx)**
|
||||
4. **OpenMP not found (osx)**
|
||||
|
||||
Either build it without OpenMP
|
||||
|
||||
@ -151,9 +139,11 @@ On most systems, CMake should find the libraries automatically. However, that do
|
||||
Creating .deb and .rpm packages
|
||||
-------------------------------
|
||||
|
||||
It is recommended to install CryFS using packages, because that allows for an easy way to uninstall it again once you don't need it anymore.
|
||||
|
||||
There are additional requirements if you want to create packages. They are:
|
||||
- CMake version >= 3.3
|
||||
- rpmbuild for creating .rpm package
|
||||
- rpmbuild if you're creating a .rpm package
|
||||
|
||||
1. Clone repository
|
||||
|
||||
@ -163,6 +153,7 @@ There are additional requirements if you want to create packages. They are:
|
||||
2. Build
|
||||
|
||||
$ mkdir cmake && cd cmake
|
||||
$ conan install .. --build=missing
|
||||
$ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=off
|
||||
$ make package
|
||||
|
||||
|
33
appveyor.yml
33
appveyor.yml
@ -1,6 +1,6 @@
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
#- Visual Studio 2017 Preview
|
||||
- Visual Studio 2019
|
||||
#- Visual Studio 2019 Preview
|
||||
|
||||
platform:
|
||||
- x64
|
||||
@ -20,29 +20,36 @@ init:
|
||||
- echo %APPVEYOR_BUILD_WORKER_IMAGE%
|
||||
- set arch=32
|
||||
- if "%PLATFORM%"=="x64" ( set arch=64)
|
||||
- set VisualStudioVersion=2017
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017 Preview" ( set VisualStudioVersion=Preview)
|
||||
- set VisualStudioVersion=2019
|
||||
- if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2019 Preview" ( set VisualStudioVersion=Preview)
|
||||
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio\%VisualStudioVersion%\Community\VC\Auxiliary\Build\vcvars%arch%.bat"
|
||||
|
||||
install:
|
||||
- choco install -y dokany --version 1.2.1.2000 --installargs INSTALLDEVFILES=1
|
||||
- pip install conan
|
||||
- conan --version
|
||||
- cmake --version
|
||||
- conan profile new default --detect
|
||||
# note: Conan misdetects our x86 CI platform as x64, fix that
|
||||
- if "%PLATFORM%"=="x86" ( conan profile update settings.arch=x86 default )
|
||||
- if "%PLATFORM%"=="x86" ( conan profile update settings.arch_build=x86 default )
|
||||
|
||||
build_script:
|
||||
- cmd: mkdir build
|
||||
- cmd: cd build
|
||||
# note: The cmake+ninja workflow requires us to set build type in both cmake commands ('cmake' and 'cmake --build'), otherwise the cryfs.exe will depend on debug versions of the visual studio c++ runtime (i.e. msvcp140d.dll)
|
||||
- cmd: cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_TESTING=on -DBOOST_ROOT="C:/Libraries/boost_1_67_0" -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.2.1"
|
||||
- cmd: conan install .. -s build_type=%CONFIGURATION% --build=missing
|
||||
- cmd: cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DBUILD_TESTING=on -DDOKAN_PATH="C:/Program Files/Dokan/DokanLibrary-1.2.1"
|
||||
- cmd: cmake --build . --config %CONFIGURATION%
|
||||
- cmd: .\test\gitversion\gitversion-test.exe
|
||||
- cmd: .\bin\gitversion-test.exe
|
||||
# cpp-utils-test disables ThreadDebuggingTest_ThreadName.*_thenIsCorrect because the appveyor image is too old to support the API needed for that
|
||||
- cmd: .\test\cpp-utils\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect
|
||||
#- cmd: .\test\fspp\fspp-test.exe
|
||||
- cmd: .\test\parallelaccessstore\parallelaccessstore-test.exe
|
||||
- cmd: .\test\blockstore\blockstore-test.exe
|
||||
- cmd: .\test\blobstore\blobstore-test.exe
|
||||
- cmd: .\test\cryfs\cryfs-test.exe
|
||||
#- cmd: .\test\cryfs-cli\cryfs-cli-test.exe
|
||||
- cmd: .\bin\cpp-utils-test.exe --gtest_filter=-ThreadDebuggingTest_ThreadName.*_thenIsCorrect
|
||||
#- cmd: .\bin\fspp-test.exe
|
||||
- cmd: .\bin\parallelaccessstore-test.exe
|
||||
- cmd: .\bin\blockstore-test.exe
|
||||
- cmd: .\bin\blobstore-test.exe
|
||||
- cmd: .\bin\cryfs-test.exe
|
||||
#- cmd: .\bin\cryfs-cli-test.exe
|
||||
|
||||
- cmd: cpack -C %CONFIGURATION% --verbose -G WIX
|
||||
|
||||
|
@ -6,22 +6,13 @@ include(CheckCXXCompilerFlag)
|
||||
# Uses: target_activate_cpp14(buildtarget)
|
||||
###################################################
|
||||
function(target_activate_cpp14 TARGET)
|
||||
if("${CMAKE_VERSION}" VERSION_GREATER "3.1")
|
||||
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 14)
|
||||
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
else("${CMAKE_VERSION}" VERSION_GREATER "3.1")
|
||||
check_cxx_compiler_flag("-std=c++14" COMPILER_HAS_CPP14_SUPPORT)
|
||||
if (COMPILER_HAS_CPP14_SUPPORT)
|
||||
target_compile_options(${TARGET} PRIVATE -std=c++14)
|
||||
else(COMPILER_HAS_CPP14_SUPPORT)
|
||||
check_cxx_compiler_flag("-std=c++1y" COMPILER_HAS_CPP14_PARTIAL_SUPPORT)
|
||||
if (COMPILER_HAS_CPP14_PARTIAL_SUPPORT)
|
||||
target_compile_options(${TARGET} PRIVATE -std=c++1y)
|
||||
if(MSVC)
|
||||
# Required by range-v3, see its README.md
|
||||
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 17)
|
||||
else()
|
||||
message(FATAL_ERROR "Compiler doesn't support C++14")
|
||||
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD 14)
|
||||
endif()
|
||||
endif(COMPILER_HAS_CPP14_SUPPORT)
|
||||
endif("${CMAKE_VERSION}" VERSION_GREATER "3.1")
|
||||
set_property(TARGET ${TARGET} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||
# Ideally, we'd like to use libc++ on linux as well, but:
|
||||
# - http://stackoverflow.com/questions/37096062/get-a-basic-c-program-to-compile-using-clang-on-ubuntu-16
|
||||
# - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=808086
|
||||
@ -106,26 +97,8 @@ endfunction(target_enable_style_warnings)
|
||||
# target_add_boost(buildtarget system filesystem) # list all libraries to link against in the dependencies
|
||||
##################################################
|
||||
function(target_add_boost TARGET)
|
||||
# Load boost libraries
|
||||
if(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
|
||||
# Many supported systems don't have boost >= 1.65.1. Better link it statically.
|
||||
message(STATUS "Boost will be statically linked")
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
else(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
|
||||
message(STATUS "Boost will be dynamically linked")
|
||||
set(Boost_USE_STATIC_LIBS OFF)
|
||||
endif(NOT DEFINED Boost_USE_STATIC_LIBS OR Boost_USE_STATIC_LIBS)
|
||||
set(BOOST_THREAD_VERSION 4)
|
||||
find_package(Boost 1.65.1
|
||||
REQUIRED
|
||||
COMPONENTS ${ARGN})
|
||||
target_include_directories(${TARGET} SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET} PUBLIC ${Boost_LIBRARIES})
|
||||
target_link_libraries(${TARGET} PUBLIC CONAN_PKG::boost)
|
||||
target_compile_definitions(${TARGET} PUBLIC BOOST_THREAD_VERSION=4)
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
# Also link to rt, because boost thread needs that.
|
||||
target_link_libraries(${TARGET} PUBLIC rt)
|
||||
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
endfunction(target_add_boost)
|
||||
|
||||
##################################################
|
||||
|
40
conanfile.py
Normal file
40
conanfile.py
Normal file
@ -0,0 +1,40 @@
|
||||
from conans import ConanFile, CMake
|
||||
|
||||
class CryFSConan(ConanFile):
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
requires = [
|
||||
"range-v3/0.9.1@ericniebler/stable",
|
||||
"spdlog/1.4.2",
|
||||
]
|
||||
generators = "cmake"
|
||||
default_options = {
|
||||
# Need to disable boost-math because that doesn't compile for some reason on CI
|
||||
"boost:without_math": True,
|
||||
"boost:without_wave": True,
|
||||
"boost:without_container": True,
|
||||
"boost:without_exception": True,
|
||||
"boost:without_graph": True,
|
||||
"boost:without_iostreams": True,
|
||||
"boost:without_locale": True,
|
||||
"boost:without_log": True,
|
||||
"boost:without_random": True,
|
||||
"boost:without_regex": True,
|
||||
"boost:without_mpi": True,
|
||||
"boost:without_serialization": True,
|
||||
"boost:without_coroutine": True,
|
||||
"boost:without_fiber": True,
|
||||
"boost:without_context": True,
|
||||
"boost:without_timer": True,
|
||||
"boost:without_date_time": True,
|
||||
"boost:without_atomic": True,
|
||||
"boost:without_graph_parallel": True,
|
||||
"boost:without_python": True,
|
||||
"boost:without_test": True,
|
||||
"boost:without_type_erasure": True,
|
||||
}
|
||||
|
||||
def requirements(self):
|
||||
if self.settings.os == "Windows":
|
||||
self.requires("boost/1.69.0@conan/stable")
|
||||
else:
|
||||
self.requires("boost/1.65.1@conan/stable")
|
@ -12,6 +12,7 @@ export NUMCORES=`nproc` && if [ ! -n "$NUMCORES" ]; then export NUMCORES=`sysctl
|
||||
echo Using ${NUMCORES} cores
|
||||
|
||||
# Run cmake in current working directory, but on source that is in the same directory as this script file
|
||||
conan install .. --build=missing
|
||||
cmake -DBUILD_TESTING=on -DCMAKE_EXPORT_COMPILE_COMMANDS=ON "${0%/*}"
|
||||
|
||||
# Filter all third party code from the compilation database
|
||||
|
@ -3,6 +3,7 @@
|
||||
#define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHE_QUEUEMAP_H_
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <cassert>
|
||||
#include <boost/optional.hpp>
|
||||
|
@ -89,8 +89,13 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_DL_LIBS})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC spdlog cryptopp)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC CONAN_PKG::spdlog cryptopp CONAN_PKG::range-v3)
|
||||
|
||||
target_add_boost(${PROJECT_NAME} filesystem system thread chrono)
|
||||
target_enable_style_warnings(${PROJECT_NAME})
|
||||
target_activate_cpp14(${PROJECT_NAME})
|
||||
|
||||
if(MSVC)
|
||||
# Required by range-v3, see its README.md
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC /experimental:preprocessor /permissive- /Zc:twoPhase-)
|
||||
endif()
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <vendor_cryptopp/hex.h>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include "../assert/assert.h"
|
||||
|
||||
|
@ -244,8 +244,7 @@ namespace cryfs_cli {
|
||||
}
|
||||
};
|
||||
const bool missingBlockIsIntegrityViolation = config.configFile->config()->missingBlockIsIntegrityViolation();
|
||||
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId,
|
||||
options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
|
||||
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId, options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
|
||||
_sanityCheckFilesystem(_device->get());
|
||||
|
||||
auto initFilesystem = [&] (fspp::fuse::Fuse *fs){
|
||||
|
@ -81,12 +81,10 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
|
||||
if (vm.count("missing-block-is-integrity-violation")) {
|
||||
missingBlockIsIntegrityViolation = vm["missing-block-is-integrity-violation"].as<bool>();
|
||||
}
|
||||
|
||||
if (vm.count("fuse-option")) {
|
||||
auto options = vm["fuse-option"].as<vector<string>>();
|
||||
for (const auto& option: options) {
|
||||
if (option == "noatime" || option == "atime") {
|
||||
LOG(WARN, "CryFS currently doesn't support noatime/atime flags. Using relatime behavior.");
|
||||
}
|
||||
fuseOptions.push_back("-o");
|
||||
fuseOptions.push_back(option);
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ ProgramOptions::ProgramOptions(bf::path baseDir, bf::path mountDir, optional<bf:
|
||||
_foreground(foreground),
|
||||
_allowFilesystemUpgrade(allowFilesystemUpgrade), _allowReplacedFilesystem(allowReplacedFilesystem), _allowIntegrityViolations(allowIntegrityViolations),
|
||||
_cipher(std::move(cipher)), _blocksizeBytes(std::move(blocksizeBytes)), _unmountAfterIdleMinutes(std::move(unmountAfterIdleMinutes)),
|
||||
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)), _fuseOptions(std::move(fuseOptions)) {
|
||||
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)),
|
||||
_fuseOptions(std::move(fuseOptions)) {
|
||||
if (!_mountDirIsDriveLetter) {
|
||||
_mountDir = bf::absolute(std::move(_mountDir));
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ add_library(${PROJECT_NAME} ${SOURCES})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils cryfs fspp-fuse)
|
||||
target_enable_style_warnings(${PROJECT_NAME})
|
||||
target_activate_cpp14(${PROJECT_NAME})
|
||||
target_add_boost(${PROJECT_NAME} program_options)
|
||||
|
||||
add_executable(${PROJECT_NAME}_bin main_unmount.cpp)
|
||||
set_target_properties(${PROJECT_NAME}_bin PROPERTIES OUTPUT_NAME cryfs-unmount)
|
||||
|
@ -51,7 +51,7 @@ set(LIB_SOURCES
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${LIB_SOURCES})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC cpp-utils fspp-interface blockstore blobstore gitversion)
|
||||
target_add_boost(${PROJECT_NAME} program_options chrono) # TODO Check that dependent projects don't get boost added (use PRIVATE here)
|
||||
target_add_boost(${PROJECT_NAME} chrono) # TODO Check that dependent projects don't get boost added (use PRIVATE here)
|
||||
target_enable_style_warnings(${PROJECT_NAME})
|
||||
target_activate_cpp14(${PROJECT_NAME})
|
||||
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include "cryfs/impl/localstate/LocalStateDir.h"
|
||||
#include <cryfs/impl/CryfsException.h>
|
||||
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
//TODO Get rid of this in favor of exception hierarchy
|
||||
using fspp::fuse::FuseErrnoException;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/FileBlobRef.h"
|
||||
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h"
|
||||
|
||||
|
||||
namespace cryfs {
|
||||
|
||||
class CryDevice final: public fspp::Device {
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "CryFile.h"
|
||||
#include "CryOpenFile.h"
|
||||
#include <cpp-utils/system/time.h>
|
||||
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
|
||||
|
||||
//TODO Get rid of this in favor of exception hierarchy
|
||||
using fspp::fuse::FuseErrnoException;
|
||||
@ -72,7 +71,7 @@ vector<fspp::Dir::Entry> CryDir::children() {
|
||||
device()->callFsActionCallbacks();
|
||||
if (!isRootDir()) { // NOLINT (workaround https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82481 )
|
||||
//TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function)
|
||||
parent()->updateAccessTimestampForChild(blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
|
||||
parent()->updateAccessTimestampForChild(blockId(), timestampUpdateBehavior());
|
||||
}
|
||||
vector<fspp::Dir::Entry> children;
|
||||
children.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));
|
||||
|
@ -70,6 +70,10 @@ optional<DirBlobRef*> CryNode::grandparent() {
|
||||
return _grandparent->get();
|
||||
}
|
||||
|
||||
fspp::TimestampUpdateBehavior CryNode::timestampUpdateBehavior() const {
|
||||
return _device->getContext().timestampUpdateBehavior();
|
||||
}
|
||||
|
||||
void CryNode::rename(const bf::path &to) {
|
||||
device()->callFsActionCallbacks();
|
||||
if (_parent == none) {
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
|
||||
// TODO grandparent is only needed to set the timestamps of the parent directory on rename and remove. Delete grandparent parameter once we store timestamps in the blob itself instead of in the directory listing.
|
||||
CryNode(CryDevice *device, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> grandparent, const blockstore::BlockId &blockId);
|
||||
|
||||
void access(int mask) const override;
|
||||
stat_info stat() const override;
|
||||
void chmod(fspp::mode_t mode) override;
|
||||
@ -37,6 +38,7 @@ protected:
|
||||
std::shared_ptr<const parallelaccessfsblobstore::DirBlobRef> parent() const;
|
||||
std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent();
|
||||
boost::optional<parallelaccessfsblobstore::DirBlobRef*> grandparent();
|
||||
fspp::TimestampUpdateBehavior timestampUpdateBehavior() const;
|
||||
|
||||
virtual fspp::Dir::EntryType getType() const = 0;
|
||||
|
||||
|
@ -43,7 +43,7 @@ void CryOpenFile::truncate(fspp::num_bytes_t size) const {
|
||||
|
||||
fspp::num_bytes_t CryOpenFile::read(void *buf, fspp::num_bytes_t count, fspp::num_bytes_t offset) const {
|
||||
_device->callFsActionCallbacks();
|
||||
_parent->updateAccessTimestampForChild(_fileBlob->blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
|
||||
_parent->updateAccessTimestampForChild(_fileBlob->blockId(), timestampUpdateBehavior());
|
||||
return _fileBlob->read(buf, offset, count);
|
||||
}
|
||||
|
||||
@ -64,4 +64,8 @@ void CryOpenFile::fdatasync() {
|
||||
_fileBlob->flush();
|
||||
}
|
||||
|
||||
fspp::TimestampUpdateBehavior CryOpenFile::timestampUpdateBehavior() const {
|
||||
return _device->getContext().timestampUpdateBehavior();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ public:
|
||||
void flush() override;
|
||||
void fsync() override;
|
||||
void fdatasync() override;
|
||||
fspp::TimestampUpdateBehavior timestampUpdateBehavior() const;
|
||||
|
||||
private:
|
||||
const CryDevice *_device;
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include "CryDevice.h"
|
||||
#include "CrySymlink.h"
|
||||
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h"
|
||||
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
|
||||
|
||||
//TODO Get rid of this in favor of exception hierarchy
|
||||
|
||||
@ -43,7 +42,7 @@ fspp::Dir::EntryType CrySymlink::getType() const {
|
||||
|
||||
bf::path CrySymlink::target() {
|
||||
device()->callFsActionCallbacks();
|
||||
parent()->updateAccessTimestampForChild(blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
|
||||
parent()->updateAccessTimestampForChild(blockId(), timestampUpdateBehavior());
|
||||
auto blob = LoadBlob(); // NOLINT (workaround https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82481 )
|
||||
return blob->target();
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
#include "FsBlobRef.h"
|
||||
#include "cryfs/impl/filesystem/fsblobstore/DirBlob.h"
|
||||
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
|
||||
#include <fspp/fs_interface/Node.h>
|
||||
#include <fspp/fs_interface/Context.h>
|
||||
|
||||
namespace cryfs {
|
||||
namespace cachingfsblobstore {
|
||||
@ -62,7 +62,7 @@ public:
|
||||
return _base->statChildWithKnownSize(blockId, size);
|
||||
}
|
||||
|
||||
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
return _base->updateAccessTimestampForChild(blockId, timestampUpdateBehavior);
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ fspp::Node::stat_info DirBlob::statChildWithKnownSize(const BlockId &blockId, fs
|
||||
return result;
|
||||
}
|
||||
|
||||
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
std::unique_lock<std::mutex> lock(_entriesAndChangedMutex);
|
||||
if (_entries.updateAccessTimestampForChild(blockId, timestampUpdateBehavior)) {
|
||||
_changed = true;
|
||||
|
@ -61,7 +61,7 @@ namespace cryfs {
|
||||
|
||||
fspp::Node::stat_info statChildWithKnownSize(const blockstore::BlockId &blockId, fspp::num_bytes_t size) const;
|
||||
|
||||
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);
|
||||
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior);
|
||||
|
||||
void updateModificationTimestampForChild(const blockstore::BlockId &blockId);
|
||||
|
||||
|
@ -228,22 +228,30 @@ void DirEntryList::setAccessTimes(const blockstore::BlockId &blockId, timespec l
|
||||
found->setLastModificationTime(lastModificationTime);
|
||||
}
|
||||
|
||||
bool DirEntryList::updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
ASSERT(timestampUpdateBehavior == TimestampUpdateBehavior::RELATIME, "Currently only relatime supported");
|
||||
bool DirEntryList::updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
auto found = _findById(blockId);
|
||||
|
||||
const timespec lastAccessTime = found->lastAccessTime();
|
||||
const timespec lastModificationTime = found->lastModificationTime();
|
||||
const timespec now = cpputils::time::now();
|
||||
const timespec yesterday {
|
||||
/*.tv_sec = */ now.tv_sec - 60*60*24,
|
||||
/*.tv_nsec = */ now.tv_nsec
|
||||
};
|
||||
bool changed = false;
|
||||
if (lastAccessTime < lastModificationTime || lastAccessTime < yesterday) {
|
||||
|
||||
switch (found->type()) {
|
||||
case fspp::Dir::EntryType::FILE:
|
||||
// fallthrough
|
||||
case fspp::Dir::EntryType::SYMLINK:
|
||||
if (timestampUpdateBehavior->shouldUpdateATimeOnFileRead(lastAccessTime, lastModificationTime, now)) {
|
||||
found->setLastAccessTime(now);
|
||||
changed = true;
|
||||
return true;
|
||||
}
|
||||
return changed;
|
||||
return false;
|
||||
case fspp::Dir::EntryType::DIR:
|
||||
if (timestampUpdateBehavior->shouldUpdateATimeOnDirectoryRead(lastAccessTime, lastModificationTime, now)) {
|
||||
found->setLastAccessTime(now);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
throw std::logic_error("Unhandled case");
|
||||
}
|
||||
|
||||
void DirEntryList::updateModificationTimestampForChild(const blockstore::BlockId &blockId) {
|
||||
|
@ -3,10 +3,10 @@
|
||||
#define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRYLIST_H
|
||||
|
||||
#include <cpp-utils/data/Data.h>
|
||||
#include <fspp/fs_interface/Context.h>
|
||||
#include "DirEntry.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "TimestampUpdateBehavior.h"
|
||||
|
||||
//TODO Address elements by name instead of by blockId when accessing them. Who knows whether there is two hard links for the same blob.
|
||||
|
||||
@ -40,7 +40,7 @@ namespace cryfs {
|
||||
void setMode(const blockstore::BlockId &blockId, fspp::mode_t mode);
|
||||
bool setUidGid(const blockstore::BlockId &blockId, fspp::uid_t uid, fspp::gid_t gid);
|
||||
void setAccessTimes(const blockstore::BlockId &blockId, timespec lastAccessTime, timespec lastModificationTime);
|
||||
bool updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);
|
||||
bool updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior);
|
||||
void updateModificationTimestampForChild(const blockstore::BlockId &blockId);
|
||||
|
||||
private:
|
||||
|
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef CRYFS_TIMESTAMPUPDATEBEHAVIOR_H
|
||||
#define CRYFS_TIMESTAMPUPDATEBEHAVIOR_H
|
||||
|
||||
namespace cryfs {
|
||||
namespace fsblobstore {
|
||||
|
||||
enum class TimestampUpdateBehavior : uint8_t {
|
||||
// currently only relatime supported
|
||||
RELATIME
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include "FsBlobRef.h"
|
||||
#include "cryfs/impl/filesystem/cachingfsblobstore/DirBlobRef.h"
|
||||
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
|
||||
#include <fspp/fs_interface/Node.h>
|
||||
|
||||
namespace cryfs {
|
||||
@ -58,7 +57,7 @@ public:
|
||||
return _base->statChildWithKnownSize(blockId, size);
|
||||
}
|
||||
|
||||
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
return _base->updateAccessTimestampForChild(blockId, timestampUpdateBehavior);
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,9 @@ set(SOURCES
|
||||
Node.cpp
|
||||
OpenFile.cpp
|
||||
Symlink.cpp
|
||||
Types.cpp)
|
||||
Types.cpp
|
||||
Context.cpp
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${SOURCES})
|
||||
|
||||
|
1
src/fspp/fs_interface/Context.cpp
Normal file
1
src/fspp/fs_interface/Context.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "Context.h"
|
128
src/fspp/fs_interface/Context.h
Normal file
128
src/fspp/fs_interface/Context.h
Normal file
@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_FSPP_FSINTERFACE_CONTEXT_H_
|
||||
#define MESSMER_FSPP_FSINTERFACE_CONTEXT_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <cpp-utils/system/time.h>
|
||||
|
||||
namespace fspp {
|
||||
|
||||
namespace detail {
|
||||
class TimestampUpdateBehaviorBase {
|
||||
public:
|
||||
virtual bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const = 0;
|
||||
virtual bool shouldUpdateATimeOnDirectoryRead(timespec oldATime, timespec oldMTime, timespec newATime) const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
// Defines how atime timestamps of files and directories are accessed on read accesses
|
||||
// (e.g. atime, strictatime, relatime, nodiratime)
|
||||
using TimestampUpdateBehavior = std::shared_ptr<detail::TimestampUpdateBehaviorBase>;
|
||||
|
||||
// atime attribute (of both files and directories) is updated only during write access.
|
||||
inline TimestampUpdateBehavior noatime() {
|
||||
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
|
||||
public:
|
||||
bool shouldUpdateATimeOnFileRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
|
||||
return false;
|
||||
}
|
||||
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
|
||||
return singleton;
|
||||
}
|
||||
|
||||
// This causes the atime attribute to update with every file access. (accessing file data, not just the metadata/attributes)
|
||||
inline TimestampUpdateBehavior strictatime() {
|
||||
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
|
||||
public:
|
||||
bool shouldUpdateATimeOnFileRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
|
||||
return true;
|
||||
}
|
||||
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
|
||||
return singleton;
|
||||
}
|
||||
|
||||
// This option causes the atime attribute to update only if the previous atime is older than mtime or ctime, or the previous atime is over 24 hours old.
|
||||
inline TimestampUpdateBehavior relatime() {
|
||||
// This option causes the atime attribute to update only if the previous atime is older than mtime or ctime, or the previous atime is over 24 hours old.
|
||||
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
|
||||
public:
|
||||
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
|
||||
const timespec yesterday {
|
||||
/*.tv_sec = */ newATime.tv_sec - 60*60*24,
|
||||
/*.tv_nsec = */ newATime.tv_nsec
|
||||
};
|
||||
|
||||
return oldATime < oldMTime || oldATime < yesterday;
|
||||
}
|
||||
bool shouldUpdateATimeOnDirectoryRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
|
||||
return shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
|
||||
return singleton;
|
||||
}
|
||||
|
||||
// atime of directories is updated only during write access, can be combined with relatime. atime of files follows the relatime rules.
|
||||
inline TimestampUpdateBehavior nodiratime_relatime() {
|
||||
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
|
||||
public:
|
||||
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
|
||||
return relatime()->shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
|
||||
}
|
||||
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
|
||||
return singleton;
|
||||
}
|
||||
|
||||
// atime of directories is updated only during write access, can be combined with relatime. atime of files follows the strictatime rules.
|
||||
inline TimestampUpdateBehavior nodiratime_strictatime() {
|
||||
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
|
||||
public:
|
||||
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
|
||||
return strictatime()->shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
|
||||
}
|
||||
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
|
||||
return singleton;
|
||||
}
|
||||
|
||||
class Context final {
|
||||
public:
|
||||
explicit Context(TimestampUpdateBehavior timestampUpdateBehavior)
|
||||
: _timestampUpdateBehavior(std::move(timestampUpdateBehavior)) {}
|
||||
|
||||
const TimestampUpdateBehavior& timestampUpdateBehavior() const {
|
||||
return _timestampUpdateBehavior;
|
||||
}
|
||||
|
||||
void setTimestampUpdateBehavior(TimestampUpdateBehavior value) {
|
||||
_timestampUpdateBehavior = std::move(value);
|
||||
}
|
||||
|
||||
private:
|
||||
TimestampUpdateBehavior _timestampUpdateBehavior;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -5,6 +5,8 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <cpp-utils/pointer/unique_ref.h>
|
||||
#include "Types.h"
|
||||
#include "Context.h"
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace fspp {
|
||||
class Node;
|
||||
@ -28,6 +30,19 @@ public:
|
||||
virtual boost::optional<cpputils::unique_ref<Dir>> LoadDir(const boost::filesystem::path &path) = 0;
|
||||
virtual boost::optional<cpputils::unique_ref<Symlink>> LoadSymlink(const boost::filesystem::path &path) = 0;
|
||||
|
||||
const Context& getContext() const {
|
||||
ASSERT(_context != boost::none, "Tried to call getContext() but file system isn't running yet.");
|
||||
return *_context;
|
||||
}
|
||||
|
||||
// called by fspp system on file system init. Don't call this manually.
|
||||
// TODO Is there a better way to do this?
|
||||
void setContext(Context&& context) {
|
||||
_context = std::move(context);
|
||||
}
|
||||
|
||||
private:
|
||||
boost::optional<Context> _context;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -8,14 +8,19 @@ template<class ConcreteFileSystemTestFixture>
|
||||
class FsppDeviceTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
|
||||
public:
|
||||
void Test_Load_While_Loaded() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/mynode");
|
||||
auto operation = [this] () {
|
||||
return [this] {
|
||||
this->device->Load("/mynode");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Load_While_Not_Loaded() {
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
fspp::Node::stat_info oldStat{};
|
||||
{
|
||||
auto node = this->CreateNode("/mynode");
|
||||
@ -32,6 +37,7 @@ public:
|
||||
EXPECT_EQ(oldStat.atime, newStat.atime);
|
||||
EXPECT_EQ(oldStat.mtime, newStat.mtime);
|
||||
EXPECT_EQ(oldStat.ctime, newStat.ctime);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,23 +11,32 @@ public:
|
||||
TYPED_TEST_SUITE_P(FsppDirTest_Timestamps);
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto operation = [&dir] () {
|
||||
return [dir = std::move(dir)] {
|
||||
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto operation = [&dir] () {
|
||||
return [dir = std::move(dir)] {
|
||||
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}*/
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile) {
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
timespec lowerBound = cpputils::time::now();
|
||||
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
@ -36,26 +45,37 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile)
|
||||
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
|
||||
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
|
||||
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto operation = [&dir] () {
|
||||
return [dir = std::move(dir)] {
|
||||
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createDir_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto operation = [&dir] () {
|
||||
return [dir = std::move(dir)] {
|
||||
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) {
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
timespec lowerBound = cpputils::time::now();
|
||||
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
@ -64,26 +84,37 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) {
|
||||
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
|
||||
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
|
||||
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto operation = [&dir] () {
|
||||
return [dir = std::move(dir)] {
|
||||
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto operation = [&dir] () {
|
||||
return [dir = std::move(dir)] {
|
||||
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) {
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
timespec lowerBound = cpputils::time::now();
|
||||
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
@ -92,144 +123,389 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) {
|
||||
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
|
||||
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
|
||||
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child);
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, children_empty) {
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_empty) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
this->setModificationTimestampLaterThanAccessTimestamp("/mydir"); // to make sure that even in relatime behavior, the read access below changes the access timestamp
|
||||
auto operation = [&dir] () {
|
||||
this->setAtimeOlderThanMtime("/mydir");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_empty) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
this->setAtimeNewerThanMtime("/mydir");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_empty) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/mydir");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, children_empty_inRootDir) {
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_empty_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto operation = [&dir] () {
|
||||
this->setAtimeOlderThanMtime("/");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty) {
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_empty_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
this->setAtimeNewerThanMtime("/");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_empty_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_nonempty) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
auto operation = [&dir] () {
|
||||
this->setAtimeOlderThanMtime("/mydir");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_nonempty) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
this->setAtimeNewerThanMtime("/mydir");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/mydir");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty_inRootDir) {
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_nonempty_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
auto operation = [&dir] () {
|
||||
this->setAtimeOlderThanMtime("/");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_nonempty_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
this->setAtimeNewerThanMtime("/");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty_inRootDir) {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/");
|
||||
return [dir = std::move(dir)] {
|
||||
dir->children();
|
||||
};
|
||||
};
|
||||
this->testBuilder().withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
class FsppDirTest_Timestamps_Entries: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
|
||||
public:
|
||||
|
||||
void Test_deleteChild() {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto child = this->CreateNode("/mydir/childname");
|
||||
auto operation = [&child]() {
|
||||
return [child = std::move(child)] {
|
||||
child->remove();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectUpdatesModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
void Test_deleteChild_inRootDir() {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto child = this->CreateNode("/childname");
|
||||
auto operation = [&child] () {
|
||||
auto child = this->CreateNode("/mydir/childname");
|
||||
return [child = std::move(child)] {
|
||||
child->remove();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
void Test_renameChild() {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto child = this->CreateNode("/mydir/childname");
|
||||
auto operation = [&child]() {
|
||||
return [child = std::move(child)] {
|
||||
child->rename("/mydir/mychild");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectUpdatesModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&]{
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
void Test_renameChild_inRootDir() {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto child = this->CreateNode("/childname");
|
||||
auto operation = [&child] () {
|
||||
child->rename("/mydir/mychild");
|
||||
return [child = std::move(child)] {
|
||||
child->rename("/mychild");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&]{
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
void Test_moveChildIn() {
|
||||
auto operation = [this] {
|
||||
auto sourceDir = this->CreateDir("/sourcedir");
|
||||
auto child = this->CreateNode("/sourcedir/childname");
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto operation = [&child]() {
|
||||
return [child = std::move(child)] {
|
||||
child->rename("/mydir/mychild");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectUpdatesModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
void Test_moveChildIn_inRootDir() {
|
||||
auto operation = [this] {
|
||||
auto sourceDir = this->CreateDir("/sourcedir");
|
||||
auto child = this->CreateNode("/sourcedir/childname");
|
||||
auto dir = this->LoadDir("/");
|
||||
auto operation = [&child] () {
|
||||
return [child = std::move(child)] {
|
||||
child->rename("/mychild");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
void Test_moveChildOut() {
|
||||
auto operation = [this] {
|
||||
auto dir = this->CreateDir("/mydir");
|
||||
auto child = this->CreateNode("/mydir/childname");
|
||||
this->CreateDir("/targetdir");
|
||||
auto operation = [&child]() {
|
||||
return [child = std::move(child)] {
|
||||
child->rename("/targetdir/mychild");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectUpdatesModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
/* TODO Re-enable this test once the root dir handles timestamps correctly
|
||||
void Test_moveChildOut_inRootDir() {
|
||||
auto operation = [this] {
|
||||
auto dir = this->LoadDir("/");
|
||||
auto child = this->CreateNode("/childname");
|
||||
this->CreateDir("/targetdir");
|
||||
auto operation = [&child] () {
|
||||
return [child = std::move(child)] {
|
||||
child->rename("/targetdir/mychild");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
}*/
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(FsppDirTest_Timestamps,
|
||||
@ -239,8 +515,12 @@ REGISTER_TYPED_TEST_SUITE_P(FsppDirTest_Timestamps,
|
||||
createDir_TimestampsOfCreatedDir,
|
||||
createSymlink,
|
||||
createSymlink_TimestampsOfCreatedSymlink,
|
||||
children_empty,
|
||||
children_nonempty
|
||||
givenAtimeNewerThanMtime_children_empty,
|
||||
givenAtimeOlderThanMtime_children_empty,
|
||||
givenAtimeNewerThanMtimeButBeforeYesterday_children_empty,
|
||||
givenAtimeNewerThanMtime_children_nonempty,
|
||||
givenAtimeOlderThanMtime_children_nonempty,
|
||||
givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty
|
||||
);
|
||||
|
||||
REGISTER_NODE_TEST_SUITE(FsppDirTest_Timestamps_Entries,
|
||||
|
@ -17,75 +17,111 @@ public:
|
||||
TYPED_TEST_SUITE_P(FsppFileTest_Timestamps);
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, open_nomode) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFile("/myfile");
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->open(fspp::openflags_t(0));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, open_rdonly) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFile("/myfile");
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->open(fspp::openflags_t::RDONLY());
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, open_wronly) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFile("/myfile");
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->open(fspp::openflags_t::WRONLY());
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, open_rdwr) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFile("/myfile");
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->open(fspp::openflags_t::RDWR());
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_empty_to_empty) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->truncate(fspp::num_bytes_t(0));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_empty_to_nonempty) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->truncate(fspp::num_bytes_t(10));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_empty) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->truncate(fspp::num_bytes_t(0));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_nonempty_shrink) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->truncate(fspp::num_bytes_t(5));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_nonempty_grow) {
|
||||
auto operation = [this] {
|
||||
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&file] () {
|
||||
return [file = std::move(file)] {
|
||||
file->truncate(fspp::num_bytes_t(20));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(FsppFileTest_Timestamps,
|
||||
|
@ -13,64 +13,71 @@ class FsppNodeTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture
|
||||
public:
|
||||
|
||||
void Test_Create() {
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
timespec lowerBound = cpputils::time::now();
|
||||
auto node = this->CreateNode("/mynode");
|
||||
timespec upperBound = cpputils::time::now();
|
||||
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
|
||||
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
|
||||
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Stat() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/mynode");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->stat();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Chmod() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/mynode");
|
||||
fspp::mode_t mode = this->stat(*node).mode;
|
||||
auto operation = [&node, mode] () {
|
||||
return [mode, node = std::move(node)] {
|
||||
node->chmod(mode);
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Chown() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/mynode");
|
||||
fspp::uid_t uid = this->stat(*node).uid;
|
||||
fspp::gid_t gid = this->stat(*node).gid;
|
||||
auto operation = [&node, uid, gid] () {
|
||||
return [uid, gid, node = std::move(node)] {
|
||||
node->chown(uid, gid);
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Access() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/mynode");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->access(0);
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Error_TargetParentDirDoesntExist() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
try {
|
||||
node->rename("/notexistingdir/newname");
|
||||
EXPECT_TRUE(false); // expect rename to fail
|
||||
@ -78,15 +85,17 @@ public:
|
||||
EXPECT_EQ(ENOENT, e.getErrno()); //Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Error_TargetParentDirIsFile() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/oldname");
|
||||
this->CreateFile("/somefile");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
try {
|
||||
node->rename("/somefile/newname");
|
||||
EXPECT_TRUE(false); // expect rename to fail
|
||||
@ -94,16 +103,18 @@ public:
|
||||
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Error_RootDir() {
|
||||
// TODO Re-enable this test once the root dir stores timestamps correctly
|
||||
/*
|
||||
auto operation = [this] {
|
||||
auto root = this->Load("/");
|
||||
auto operation = [&root] () {
|
||||
return [root = std::move(root)] {
|
||||
try {
|
||||
root->rename("/newname");
|
||||
EXPECT_TRUE(false); // expect throws
|
||||
@ -111,162 +122,164 @@ public:
|
||||
EXPECT_EQ(EBUSY, e.getErrno()); //Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
void Test_Rename_InRoot() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_InNested() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir");
|
||||
auto node = this->CreateNode("/mydir/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/mydir/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/mydir/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/mydir/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_RootToNested_SameName() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir");
|
||||
auto node = this->CreateNode("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/mydir/oldname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_RootToNested_NewName() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir");
|
||||
auto node = this->CreateNode("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/mydir/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_NestedToRoot_SameName() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir");
|
||||
auto node = this->CreateNode("/mydir/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/oldname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_NestedToRoot_NewName() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir");
|
||||
auto node = this->CreateNode("/mydir/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_NestedToNested_SameName() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir1");
|
||||
this->CreateDir("/mydir2");
|
||||
auto node = this->CreateNode("/mydir1/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/mydir2/oldname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_NestedToNested_NewName() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir1");
|
||||
this->CreateDir("/mydir2");
|
||||
auto node = this->CreateNode("/mydir1/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/mydir2/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_ToItself() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/oldname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Overwrite_InSameDir() {
|
||||
auto operation = [this] {
|
||||
auto node = this->CreateNode("/oldname");
|
||||
this->CreateNode("/newname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Overwrite_InDifferentDir() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir1");
|
||||
this->CreateDir("/mydir2");
|
||||
this->CreateNode("/mydir2/newname");
|
||||
auto node = this->CreateNode("/mydir1/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
node->rename("/mydir2/newname");
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation, {
|
||||
this->ExpectDoesntUpdateAccessTimestamp,
|
||||
this->ExpectDoesntUpdateModificationTimestamp,
|
||||
this->ExpectUpdatesMetadataTimestamp
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Overwrite_Error_DirWithFile_InSameDir() {
|
||||
auto operation = [this] {
|
||||
this->CreateFile("/oldname");
|
||||
this->CreateDir("/newname");
|
||||
auto node = this->Load("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
try {
|
||||
node->rename("/newname");
|
||||
EXPECT_TRUE(false); // expect rename to fail
|
||||
@ -274,18 +287,20 @@ public:
|
||||
EXPECT_EQ(EISDIR, e.getErrno()); //Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Overwrite_Error_DirWithFile_InDifferentDir() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir1");
|
||||
this->CreateDir("/mydir2");
|
||||
this->CreateFile("/mydir1/oldname");
|
||||
this->CreateDir("/mydir2/newname");
|
||||
auto node = this->Load("/mydir1/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
try {
|
||||
node->rename("/mydir2/newname");
|
||||
EXPECT_TRUE(false); // expect rename to fail
|
||||
@ -293,16 +308,18 @@ public:
|
||||
EXPECT_EQ(EISDIR, e.getErrno());//Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Overwrite_Error_FileWithDir_InSameDir() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/oldname");
|
||||
this->CreateFile("/newname");
|
||||
auto node = this->Load("/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
try {
|
||||
node->rename("/newname");
|
||||
EXPECT_TRUE(false); // expect rename to fail
|
||||
@ -310,18 +327,20 @@ public:
|
||||
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Rename_Overwrite_Error_FileWithDir_InDifferentDir() {
|
||||
auto operation = [this] {
|
||||
this->CreateDir("/mydir1");
|
||||
this->CreateDir("/mydir2");
|
||||
this->CreateDir("/mydir1/oldname");
|
||||
this->CreateFile("/mydir2/newname");
|
||||
auto node = this->Load("/mydir1/oldname");
|
||||
auto operation = [&node] () {
|
||||
return [node = std::move(node)] {
|
||||
try {
|
||||
node->rename("/mydir2/newname");
|
||||
EXPECT_TRUE(false); // expect rename to fail
|
||||
@ -329,16 +348,18 @@ public:
|
||||
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
|
||||
}
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation, {
|
||||
this->ExpectDoesntUpdateAnyTimestamps
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
void Test_Utimens() {
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto node = this->CreateNode("/mynode");
|
||||
timespec atime = this->xSecondsAgo(100);
|
||||
timespec mtime = this->xSecondsAgo(200);
|
||||
auto operation = [&node, atime, mtime] () {
|
||||
auto operation = [atime, mtime, &node] {
|
||||
node->utimens(atime, mtime);
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
|
||||
@ -346,6 +367,7 @@ public:
|
||||
});
|
||||
EXPECT_EQ(atime, this->stat(*node).atime);
|
||||
EXPECT_EQ(mtime, this->stat(*node).mtime);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,120 +14,343 @@ public:
|
||||
auto file = this->CreateFile(path);
|
||||
file->truncate(size);
|
||||
auto openFile = file->open(fspp::openflags_t::RDWR());
|
||||
assert(this->stat(*openFile).size == size);
|
||||
assert(this->stat(*this->Load(path)).size == size);
|
||||
ASSERT(this->stat(*openFile).size == size, "");
|
||||
ASSERT(this->stat(*this->Load(path)).size == size, "");
|
||||
return openFile;
|
||||
}
|
||||
void CreateFileWithSize(const boost::filesystem::path &path, fspp::num_bytes_t size) {
|
||||
auto file = this->CreateFile(path);
|
||||
file->truncate(size);
|
||||
}
|
||||
cpputils::unique_ref<fspp::OpenFile> OpenFile(const boost::filesystem::path &path, fspp::num_bytes_t size) {
|
||||
auto file = this->LoadFile(path);
|
||||
auto openFile = file->open(fspp::openflags_t::RDWR());
|
||||
ASSERT(this->stat(*openFile).size == size, "");
|
||||
ASSERT(this->stat(*this->Load(path)).size == size, "");
|
||||
return openFile;
|
||||
}
|
||||
};
|
||||
TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps);
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, stat) {
|
||||
auto openFile = this->CreateAndOpenFile("/mynode");
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->stat();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFile("/mynode");
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_empty_to_empty) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->truncate(fspp::num_bytes_t(0));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_empty_to_nonempty) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->truncate(fspp::num_bytes_t(10));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_empty) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->truncate(fspp::num_bytes_t(0));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_nonempty_shrink) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->truncate(fspp::num_bytes_t(5));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_nonempty_grow) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->truncate(fspp::num_bytes_t(20));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, read_inbounds) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&openFile] () {
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_read_inbounds) {
|
||||
auto operation = [this] () {
|
||||
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/myfile");
|
||||
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
|
||||
auto* openFilePtr = openFile.get();
|
||||
|
||||
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
|
||||
std::array<char, 5> buffer{};
|
||||
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
|
||||
});
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, read_outofbounds) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtime_read_inbounds) {
|
||||
auto operation = [this] () {
|
||||
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->setAtimeNewerThanMtime("/myfile");
|
||||
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
|
||||
auto* openFilePtr = openFile.get();
|
||||
|
||||
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
|
||||
std::array<char, 5> buffer{};
|
||||
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
|
||||
});
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeOlderThanMtime_read_inbounds) {
|
||||
auto operation = [this] () {
|
||||
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->setAtimeOlderThanMtime("/myfile");
|
||||
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
|
||||
auto* openFilePtr = openFile.get();
|
||||
|
||||
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
|
||||
std::array<char, 5> buffer{};
|
||||
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
|
||||
});
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_read_outofbounds) {
|
||||
auto operation = [this] () {
|
||||
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/myfile");
|
||||
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
|
||||
auto* openFilePtr = openFile.get();
|
||||
|
||||
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
|
||||
std::array<char, 5> buffer{};
|
||||
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
|
||||
});
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtime_read_outofbounds) {
|
||||
auto operation = [this] () {
|
||||
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->setAtimeNewerThanMtime("/myfile");
|
||||
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
|
||||
auto* openFilePtr = openFile.get();
|
||||
|
||||
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
|
||||
std::array<char, 5> buffer{};
|
||||
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
|
||||
});
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeOlderThanMtime_read_outofbounds) {
|
||||
auto operation = [this] () {
|
||||
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->setAtimeOlderThanMtime("/myfile");
|
||||
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
|
||||
auto* openFilePtr = openFile.get();
|
||||
|
||||
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
|
||||
std::array<char, 5> buffer{};
|
||||
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
|
||||
});
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
auto op = operation();
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, write_inbounds) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, write_outofbounds) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
return [openFile] {
|
||||
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(2));
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, flush) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
return [openFile] {
|
||||
openFile->flush();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, fsync) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
return [openFile] {
|
||||
openFile->fsync();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppOpenFileTest_Timestamps, fdatasync) {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
auto operation = [] (fspp::OpenFile* openFile){
|
||||
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
|
||||
auto operation = [&openFile] () {
|
||||
return [openFile] {
|
||||
openFile->fdatasync();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
};
|
||||
this->testBuilder().withAnyAtimeConfig([&] {
|
||||
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
});
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps,
|
||||
@ -137,8 +360,12 @@ REGISTER_TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps,
|
||||
truncate_nonempty_to_empty,
|
||||
truncate_nonempty_to_nonempty_shrink,
|
||||
truncate_nonempty_to_nonempty_grow,
|
||||
read_inbounds,
|
||||
read_outofbounds,
|
||||
givenAtimeNewerThanMtimeButBeforeYesterday_read_inbounds,
|
||||
givenAtimeNewerThanMtime_read_inbounds,
|
||||
givenAtimeOlderThanMtime_read_inbounds,
|
||||
givenAtimeNewerThanMtimeButBeforeYesterday_read_outofbounds,
|
||||
givenAtimeNewerThanMtime_read_outofbounds,
|
||||
givenAtimeOlderThanMtime_read_outofbounds,
|
||||
write_inbounds,
|
||||
write_outofbounds,
|
||||
flush,
|
||||
|
@ -10,17 +10,76 @@ public:
|
||||
};
|
||||
TYPED_TEST_SUITE_P(FsppSymlinkTest_Timestamps);
|
||||
|
||||
TYPED_TEST_P(FsppSymlinkTest_Timestamps, target) {
|
||||
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_target) {
|
||||
auto operation = [this] {
|
||||
auto symlink = this->CreateSymlink("/mysymlink");
|
||||
this->setModificationTimestampLaterThanAccessTimestamp("/mysymlink"); // to make sure that even in relatime behavior, the read access below changes the access timestamp
|
||||
auto operation = [&symlink] () {
|
||||
this->setAtimeNewerThanMtimeButBeforeYesterday("/mysymlink");
|
||||
return [symlink = std::move(symlink)] {
|
||||
symlink->target();
|
||||
};
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeOlderThanMtime_target) {
|
||||
auto operation = [this] {
|
||||
auto symlink = this->CreateSymlink("/mysymlink");
|
||||
this->setAtimeOlderThanMtime("/mysymlink");
|
||||
return [symlink = std::move(symlink)] {
|
||||
symlink->target();
|
||||
};
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeNewerThanMtime_target) {
|
||||
auto operation = [this] {
|
||||
auto symlink = this->CreateSymlink("/mysymlink");
|
||||
this->setAtimeNewerThanMtime("/mysymlink");
|
||||
return [symlink = std::move(symlink)] {
|
||||
symlink->target();
|
||||
};
|
||||
};
|
||||
this->testBuilder()
|
||||
.withNoatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
}).withRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeRelatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
|
||||
}).withNodiratimeStrictatime([&] {
|
||||
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
|
||||
});
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_SUITE_P(FsppSymlinkTest_Timestamps,
|
||||
target
|
||||
givenAtimeNewerThanMtimeButBeforeYesterday_target,
|
||||
givenAtimeNewerThanMtime_target,
|
||||
givenAtimeOlderThanMtime_target
|
||||
);
|
||||
|
||||
#endif
|
||||
|
@ -30,10 +30,20 @@ public:
|
||||
"Given test fixture for instantiating the (type parameterized) FileSystemTest must inherit from FileSystemTestFixture"
|
||||
);
|
||||
|
||||
FileSystemTest(): fixture(), device(fixture.createDevice()) {}
|
||||
FileSystemTest(): fixture(nullptr), device(nullptr) {
|
||||
resetFilesystem(fspp::Context{fspp::relatime()});
|
||||
}
|
||||
|
||||
ConcreteFileSystemTestFixture fixture;
|
||||
cpputils::unique_ref<fspp::Device> device;
|
||||
void resetFilesystem(fspp::Context&& context) {
|
||||
device = nullptr;
|
||||
fixture = nullptr;
|
||||
fixture = std::make_unique<ConcreteFileSystemTestFixture>();
|
||||
device = fixture->createDevice();
|
||||
device->setContext(std::move(context));
|
||||
}
|
||||
|
||||
std::unique_ptr<ConcreteFileSystemTestFixture> fixture;
|
||||
std::unique_ptr<fspp::Device> device;
|
||||
|
||||
static constexpr fspp::mode_t MODE_PUBLIC = fspp::mode_t()
|
||||
.addUserReadFlag().addUserWriteFlag().addUserExecFlag()
|
||||
@ -91,10 +101,36 @@ public:
|
||||
EXPECT_NE(nullptr, dynamic_cast<const fspp::Symlink*>(node.get()));
|
||||
}
|
||||
|
||||
void setModificationTimestampLaterThanAccessTimestamp(const boost::filesystem::path& path) {
|
||||
void setAtimeOlderThanMtime(const boost::filesystem::path& path) {
|
||||
auto node = device->Load(path).value();
|
||||
auto st = node->stat();
|
||||
st.mtime.tv_nsec = st.mtime.tv_nsec + 1;
|
||||
st.atime.tv_nsec = st.mtime.tv_nsec - 1;
|
||||
node->utimens(
|
||||
st.atime,
|
||||
st.mtime
|
||||
);
|
||||
}
|
||||
|
||||
void setAtimeNewerThanMtime(const boost::filesystem::path& path) {
|
||||
auto node = device->Load(path).value();
|
||||
auto st = node->stat();
|
||||
st.atime.tv_nsec = st.mtime.tv_nsec + 1;
|
||||
node->utimens(
|
||||
st.atime,
|
||||
st.mtime
|
||||
);
|
||||
}
|
||||
|
||||
void setAtimeNewerThanMtimeButBeforeYesterday(const boost::filesystem::path& path) {
|
||||
auto node = device->Load(path).value();
|
||||
auto st = node->stat();
|
||||
const timespec now = cpputils::time::now();
|
||||
const timespec before_yesterday {
|
||||
/*.tv_sec = */ now.tv_sec - 60*60*24 - 1,
|
||||
/*.tv_nsec = */ now.tv_nsec
|
||||
};
|
||||
st.atime = before_yesterday;
|
||||
st.mtime.tv_nsec = st.atime.tv_nsec - 1;
|
||||
node->utimens(
|
||||
st.atime,
|
||||
st.mtime
|
||||
|
@ -10,17 +10,22 @@
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
class TimestampTestUtils : public virtual FileSystemTest<ConcreteFileSystemTestFixture> {
|
||||
public:
|
||||
using TimestampUpdateBehavior = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
|
||||
using TimestampUpdateExpectation = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
|
||||
|
||||
static TimestampUpdateBehavior ExpectUpdatesAccessTimestamp;
|
||||
static TimestampUpdateBehavior ExpectDoesntUpdateAccessTimestamp;
|
||||
static TimestampUpdateBehavior ExpectUpdatesModificationTimestamp;
|
||||
static TimestampUpdateBehavior ExpectDoesntUpdateModificationTimestamp;
|
||||
static TimestampUpdateBehavior ExpectUpdatesMetadataTimestamp;
|
||||
static TimestampUpdateBehavior ExpectDoesntUpdateMetadataTimestamp;
|
||||
static TimestampUpdateBehavior ExpectDoesntUpdateAnyTimestamps;
|
||||
static TimestampUpdateExpectation ExpectUpdatesAccessTimestamp;
|
||||
static TimestampUpdateExpectation ExpectDoesntUpdateAccessTimestamp;
|
||||
static TimestampUpdateExpectation ExpectUpdatesModificationTimestamp;
|
||||
static TimestampUpdateExpectation ExpectDoesntUpdateModificationTimestamp;
|
||||
static TimestampUpdateExpectation ExpectUpdatesMetadataTimestamp;
|
||||
static TimestampUpdateExpectation ExpectDoesntUpdateMetadataTimestamp;
|
||||
static TimestampUpdateExpectation ExpectDoesntUpdateAnyTimestamps;
|
||||
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<fspp::Node::stat_info()> statOld, std::function<fspp::Node::stat_info()> statNew, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
|
||||
void setTimestampUpdateBehavior(fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
|
||||
FileSystemTest<ConcreteFileSystemTestFixture>::device->setContext(fspp::Context { timestampUpdateBehavior });
|
||||
}
|
||||
|
||||
template<class Operation>
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<fspp::Node::stat_info()> statOld, std::function<fspp::Node::stat_info()> statNew, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
|
||||
auto oldStat = statOld();
|
||||
ensureNodeTimestampsAreOld(oldStat);
|
||||
timespec timeBeforeOperation = cpputils::time::now();
|
||||
@ -32,26 +37,29 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const fspp::OpenFile &node, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
|
||||
template<class Operation>
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const fspp::OpenFile &node, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
|
||||
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(
|
||||
[this, &node](){return this->stat(node);},
|
||||
[this, &node](){return this->stat(node);},
|
||||
operation,
|
||||
std::forward<Operation>(operation),
|
||||
behaviorChecks
|
||||
);
|
||||
}
|
||||
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &oldPath, const boost::filesystem::path &newPath, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
|
||||
template<class Operation>
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &oldPath, const boost::filesystem::path &newPath, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
|
||||
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(
|
||||
[this, oldPath](){return this->stat(*this->Load(oldPath));},
|
||||
[this, newPath](){return this->stat(*this->Load(newPath));},
|
||||
operation,
|
||||
std::forward<Operation>(operation),
|
||||
behaviorChecks
|
||||
);
|
||||
}
|
||||
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &path, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
|
||||
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(path, path, operation, behaviorChecks);
|
||||
template<class Operation>
|
||||
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &path, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
|
||||
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(path, path, std::forward<Operation>(operation), behaviorChecks);
|
||||
}
|
||||
|
||||
void EXPECT_ACCESS_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) {
|
||||
@ -90,6 +98,55 @@ public:
|
||||
EXPECT_LT(nodeStat.ctime, cpputils::time::now());
|
||||
}
|
||||
|
||||
class TestBuilder final {
|
||||
public:
|
||||
explicit TestBuilder(TimestampTestUtils* fixture): _fixture(fixture) {}
|
||||
|
||||
const TestBuilder& withNoatime(std::function<void()> expectations) const {
|
||||
_fixture->resetFilesystem(fspp::Context {fspp::noatime()});
|
||||
expectations();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TestBuilder& withStrictatime(std::function<void()> expectations) const {
|
||||
_fixture->resetFilesystem(fspp::Context {fspp::strictatime()});
|
||||
expectations();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TestBuilder& withRelatime(std::function<void()> expectations) const {
|
||||
_fixture->resetFilesystem(fspp::Context {fspp::relatime()});
|
||||
expectations();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TestBuilder& withNodiratimeRelatime(std::function<void()> expectations) const {
|
||||
_fixture->resetFilesystem(fspp::Context {fspp::nodiratime_relatime()});
|
||||
expectations();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TestBuilder& withNodiratimeStrictatime(std::function<void()> expectations) const {
|
||||
_fixture->resetFilesystem(fspp::Context {fspp::nodiratime_strictatime()});
|
||||
expectations();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const TestBuilder& withAnyAtimeConfig(std::function<void()> expectations) const {
|
||||
return withNoatime(expectations)
|
||||
.withStrictatime(expectations)
|
||||
.withRelatime(expectations)
|
||||
.withNodiratimeRelatime(expectations)
|
||||
.withNodiratimeStrictatime(expectations);
|
||||
}
|
||||
|
||||
private:
|
||||
TimestampTestUtils* _fixture;
|
||||
};
|
||||
TestBuilder testBuilder() {
|
||||
return TestBuilder(this);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void waitUntilClockProgresses() {
|
||||
@ -101,7 +158,7 @@ private:
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
UNUSED(statBeforeOperation);
|
||||
UNUSED(timeBeforeOperation);
|
||||
@ -111,7 +168,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
UNUSED(timeBeforeOperation);
|
||||
UNUSED(timeAfterOperation);
|
||||
@ -119,7 +176,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
UNUSED(statBeforeOperation);
|
||||
EXPECT_LE(timeBeforeOperation, statAfterOperation.mtime);
|
||||
@ -127,7 +184,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
UNUSED(timeBeforeOperation);
|
||||
UNUSED(timeAfterOperation);
|
||||
@ -135,7 +192,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
UNUSED(statBeforeOperation);
|
||||
EXPECT_LE(timeBeforeOperation, statAfterOperation.ctime);
|
||||
@ -143,7 +200,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
UNUSED(timeBeforeOperation);
|
||||
UNUSED(timeAfterOperation);
|
||||
@ -151,7 +208,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
|
||||
};
|
||||
|
||||
template<class ConcreteFileSystemTestFixture>
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
|
||||
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
|
||||
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
|
||||
ExpectDoesntUpdateAccessTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);
|
||||
ExpectDoesntUpdateModificationTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);
|
||||
|
@ -39,6 +39,7 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||
find_library_with_path(FUSE "osxfuse" FUSE_LIB_PATH)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${FUSE})
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC /usr/local/include/osxfuse/)
|
||||
else() # Linux
|
||||
find_library_with_path(FUSE "fuse" FUSE_LIB_PATH)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC ${FUSE})
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <cpp-utils/pointer/unique_ref.h>
|
||||
#include <sys/stat.h>
|
||||
#include "../fs_interface/Dir.h"
|
||||
#include "../fs_interface/Context.h"
|
||||
#if defined(_MSC_VER)
|
||||
#include <fuse/fuse.h>
|
||||
#else
|
||||
@ -19,6 +20,8 @@ class Filesystem {
|
||||
public:
|
||||
virtual ~Filesystem() {}
|
||||
|
||||
virtual void setContext(Context&& context) = 0;
|
||||
|
||||
//TODO Test uid/gid parameters of createAndOpenFile
|
||||
virtual int createAndOpenFile(const boost::filesystem::path &path, ::mode_t mode, ::uid_t uid, ::gid_t gid) = 0;
|
||||
virtual int openFile(const boost::filesystem::path &path, int flags) = 0;
|
||||
|
@ -13,6 +13,10 @@
|
||||
#include "InvalidFilesystem.h"
|
||||
#include <codecvt>
|
||||
|
||||
#include <range/v3/view/split.hpp>
|
||||
#include <range/v3/view/join.hpp>
|
||||
#include <range/v3/view/filter.hpp>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <codecvt>
|
||||
#include <dokan/dokan.h>
|
||||
@ -265,19 +269,19 @@ void Fuse::_logUnknownException() {
|
||||
LOG(ERR, "Unknown exception thrown");
|
||||
}
|
||||
|
||||
void Fuse::runInForeground(const bf::path &mountdir, const vector<string> &fuseOptions) {
|
||||
vector<string> realFuseOptions = fuseOptions;
|
||||
void Fuse::runInForeground(const bf::path &mountdir, vector<string> fuseOptions) {
|
||||
vector<string> realFuseOptions = std::move(fuseOptions);
|
||||
if (std::find(realFuseOptions.begin(), realFuseOptions.end(), "-f") == realFuseOptions.end()) {
|
||||
realFuseOptions.push_back("-f");
|
||||
}
|
||||
_run(mountdir, realFuseOptions);
|
||||
_run(mountdir, std::move(realFuseOptions));
|
||||
}
|
||||
|
||||
void Fuse::runInBackground(const bf::path &mountdir, const vector<string> &fuseOptions) {
|
||||
vector<string> realFuseOptions = fuseOptions;
|
||||
void Fuse::runInBackground(const bf::path &mountdir, vector<string> fuseOptions) {
|
||||
vector<string> realFuseOptions = std::move(fuseOptions);
|
||||
_removeAndWarnIfExists(&realFuseOptions, "-f");
|
||||
_removeAndWarnIfExists(&realFuseOptions, "-d");
|
||||
_run(mountdir, realFuseOptions);
|
||||
_run(mountdir, std::move(realFuseOptions));
|
||||
}
|
||||
|
||||
void Fuse::_removeAndWarnIfExists(vector<string> *fuseOptions, const std::string &option) {
|
||||
@ -291,7 +295,52 @@ void Fuse::_removeAndWarnIfExists(vector<string> *fuseOptions, const std::string
|
||||
}
|
||||
}
|
||||
|
||||
void Fuse::_run(const bf::path &mountdir, const vector<string> &fuseOptions) {
|
||||
namespace {
|
||||
void extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(string* csv_options, vector<string>* result) {
|
||||
const auto is_fuse_supported_atime_flag = [] (const std::string& flag) {
|
||||
constexpr std::array<const char*, 2> flags = {"noatime", "atime"};
|
||||
return flags.end() != std::find(flags.begin(), flags.end(), flag);
|
||||
};
|
||||
const auto is_fuse_unsupported_atime_flag = [] (const std::string& flag) {
|
||||
constexpr std::array<const char*, 3> flags = {"strictatime", "relatime", "nodiratime"};
|
||||
return flags.end() != std::find(flags.begin(), flags.end(), flag);
|
||||
};
|
||||
*csv_options = *csv_options
|
||||
| ranges::view::split(',')
|
||||
| ranges::view::filter(
|
||||
[&] (const std::string& elem) {
|
||||
if (is_fuse_unsupported_atime_flag(elem)) {
|
||||
result->push_back(elem);
|
||||
return false;
|
||||
}
|
||||
if (is_fuse_supported_atime_flag(elem)) {
|
||||
result->push_back(elem);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
| ranges::view::join(',')
|
||||
| ranges::to<string>();
|
||||
}
|
||||
|
||||
// Return a list of all atime options (e.g. atime, noatime, relatime, strictatime, nodiratime) that occur in the
|
||||
// fuseOptions input. They must be preceded by a '-o', i.e. {..., '-o', 'noatime', ...} and multiple ones can be
|
||||
// csv-concatenated, i.e. {..., '-o', 'atime,nodiratime', ...}.
|
||||
// Also, this function removes all of these atime options that are unknown to libfuse (i.e. all except atime and noatime)
|
||||
// from the input fuseOptions so we can pass it on to libfuse without crashing.
|
||||
vector<string> extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(vector<string>* fuseOptions) {
|
||||
vector<string> result;
|
||||
bool lastOptionWasDashO = false;
|
||||
for (string& option : *fuseOptions) {
|
||||
if (lastOptionWasDashO) {
|
||||
extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(&option, &result);
|
||||
}
|
||||
lastOptionWasDashO = (option == "-o");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void Fuse::_run(const bf::path &mountdir, vector<string> fuseOptions) {
|
||||
#if defined(__GLIBC__)|| defined(__APPLE__) || defined(_MSC_VER)
|
||||
// Avoid encoding errors for non-utf8 characters, see https://github.com/cryfs/cryfs/issues/247
|
||||
// this is ifdef'd out for non-glibc linux, because musl doesn't handle this correctly.
|
||||
@ -302,11 +351,66 @@ void Fuse::_run(const bf::path &mountdir, const vector<string> &fuseOptions) {
|
||||
|
||||
ASSERT(_argv.size() == 0, "Filesystem already started");
|
||||
|
||||
vector<string> atimeOptions = extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(&fuseOptions);
|
||||
_createContext(atimeOptions);
|
||||
|
||||
_argv = _build_argv(mountdir, fuseOptions);
|
||||
|
||||
fuse_main(_argv.size(), _argv.data(), operations(), this);
|
||||
}
|
||||
|
||||
void Fuse::_createContext(const vector<string> &fuseOptions) {
|
||||
const bool has_atime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "atime");
|
||||
const bool has_noatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "noatime");
|
||||
const bool has_relatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "relatime");
|
||||
const bool has_strictatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "strictatime");
|
||||
const bool has_nodiratime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "nodiratime");
|
||||
|
||||
// Default is RELATIME
|
||||
_context = Context(relatime());
|
||||
|
||||
if (has_noatime_flag) {
|
||||
ASSERT(!has_atime_flag, "Cannot have both, noatime and atime flags set.");
|
||||
ASSERT(!has_relatime_flag, "Cannot have both, noatime and relatime flags set.");
|
||||
ASSERT(!has_strictatime_flag, "Cannot have both, noatime and strictatime flags set.");
|
||||
// note: can have nodiratime flag set but that is ignored because it is already included in the noatime policy.
|
||||
_context->setTimestampUpdateBehavior(noatime());
|
||||
} else if (has_relatime_flag) {
|
||||
// note: can have atime and relatime both set, they're identical
|
||||
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above.");
|
||||
ASSERT(!has_strictatime_flag, "Cannot have both, relatime and strictatime flags set.");
|
||||
if (has_nodiratime_flag) {
|
||||
_context->setTimestampUpdateBehavior(nodiratime_relatime());
|
||||
} else {
|
||||
_context->setTimestampUpdateBehavior(relatime());
|
||||
}
|
||||
} else if (has_atime_flag) {
|
||||
// note: can have atime and relatime both set, they're identical
|
||||
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
ASSERT(!has_strictatime_flag, "Cannot have both, atime and strictatime flags set.");
|
||||
if (has_nodiratime_flag) {
|
||||
_context->setTimestampUpdateBehavior(nodiratime_relatime());
|
||||
} else {
|
||||
_context->setTimestampUpdateBehavior(relatime());
|
||||
}
|
||||
} else if (has_strictatime_flag) {
|
||||
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
ASSERT(!has_atime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
ASSERT(!has_relatime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
if (has_nodiratime_flag) {
|
||||
_context->setTimestampUpdateBehavior(nodiratime_strictatime());
|
||||
} else {
|
||||
_context->setTimestampUpdateBehavior(strictatime());
|
||||
}
|
||||
} else if (has_nodiratime_flag) {
|
||||
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
ASSERT(!has_atime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
ASSERT(!has_relatime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
ASSERT(!has_strictatime_flag, "This shouldn't happen, or we would have hit a case above");
|
||||
_context->setTimestampUpdateBehavior(nodiratime_relatime()); // use relatime by default
|
||||
}
|
||||
}
|
||||
|
||||
vector<char *> Fuse::_build_argv(const bf::path &mountdir, const vector<string> &fuseOptions) {
|
||||
vector<char *> argv;
|
||||
argv.reserve(6 + fuseOptions.size()); // fuseOptions + executable name + mountdir + 2x fuse options (subtype, fsname), each taking 2 entries ("-o", "key=value").
|
||||
@ -1100,6 +1204,9 @@ void Fuse::init(fuse_conn_info *conn) {
|
||||
ThreadNameForDebugging _threadName("init");
|
||||
_fs = _init(this);
|
||||
|
||||
ASSERT(_context != boost::none, "Context should have been initialized in Fuse::run() but somehow didn't");
|
||||
_fs->setContext(fspp::Context { *_context });
|
||||
|
||||
LOG(INFO, "Filesystem started.");
|
||||
|
||||
_running = true;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <cpp-utils/macros.h>
|
||||
#include <atomic>
|
||||
#include "stat_compatibility.h"
|
||||
#include <fspp/fs_interface/Context.h>
|
||||
|
||||
namespace fspp {
|
||||
class Device;
|
||||
@ -24,8 +25,8 @@ public:
|
||||
explicit Fuse(std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname);
|
||||
~Fuse();
|
||||
|
||||
void runInBackground(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
|
||||
void runInForeground(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
|
||||
void runInBackground(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
|
||||
void runInForeground(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
|
||||
bool running() const;
|
||||
void stop();
|
||||
|
||||
@ -67,11 +68,12 @@ private:
|
||||
static void _logUnknownException();
|
||||
static char *_create_c_string(const std::string &str);
|
||||
static void _removeAndWarnIfExists(std::vector<std::string> *fuseOptions, const std::string &option);
|
||||
void _run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
|
||||
void _run(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
|
||||
static bool _has_option(const std::vector<char *> &vec, const std::string &key);
|
||||
static bool _has_entry_with_prefix(const std::string &prefix, const std::vector<char *> &vec);
|
||||
std::vector<char *> _build_argv(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
|
||||
void _add_fuse_option_if_not_exists(std::vector<char *> *argv, const std::string &key, const std::string &value);
|
||||
void _createContext(const std::vector<std::string> &fuseOptions);
|
||||
|
||||
std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> _init;
|
||||
std::function<void()> _onMounted;
|
||||
@ -81,6 +83,7 @@ private:
|
||||
std::atomic<bool> _running;
|
||||
std::string _fstype;
|
||||
boost::optional<std::string> _fsname;
|
||||
boost::optional<Context> _context;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Fuse);
|
||||
};
|
||||
|
@ -7,6 +7,10 @@
|
||||
namespace fspp {
|
||||
namespace fuse {
|
||||
class InvalidFilesystem final : public Filesystem {
|
||||
void setContext(Context&&) override {
|
||||
throw std::logic_error("Filesystem not initialized yet");
|
||||
}
|
||||
|
||||
int createAndOpenFile(const boost::filesystem::path &, ::mode_t , ::uid_t , ::gid_t ) override {
|
||||
throw std::logic_error("Filesystem not initialized yet");
|
||||
}
|
||||
|
@ -3,14 +3,6 @@
|
||||
#define MESSMER_FSPP_FUSE_PARAMS_H_
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#include <fuse.h>
|
||||
#elif __APPLE__
|
||||
#include <osxfuse/fuse.h>
|
||||
#elif defined(_MSC_VER)
|
||||
#include <fuse.h> // Dokany fuse
|
||||
#else
|
||||
#error System not supported
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -91,6 +91,10 @@ FilesystemImpl::~FilesystemImpl() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void FilesystemImpl::setContext(Context&& context) {
|
||||
_device->setContext(std::move(context));
|
||||
}
|
||||
|
||||
unique_ref<File> FilesystemImpl::LoadFile(const bf::path &path) {
|
||||
PROFILE(_loadFileNanosec);
|
||||
auto file = _device->LoadFile(path);
|
||||
|
@ -24,6 +24,8 @@ public:
|
||||
explicit FilesystemImpl(cpputils::unique_ref<Device> device);
|
||||
virtual ~FilesystemImpl();
|
||||
|
||||
void setContext(Context&& context) override;
|
||||
|
||||
int openFile(const boost::filesystem::path &path, int flags) override;
|
||||
void flush(int descriptor) override;
|
||||
void closeFile(int descriptor) override;
|
||||
|
@ -128,7 +128,7 @@ TEST(BacktraceTest, ShowBacktraceOnSigIll) {
|
||||
#else
|
||||
TEST(BacktraceTest, ContainsBacktrace) {
|
||||
string backtrace = cpputils::backtrace();
|
||||
EXPECT_THAT(backtrace, HasSubstr("#1"));
|
||||
EXPECT_THAT(backtrace, HasSubstr("#0"));
|
||||
}
|
||||
TEST(BacktraceTest, ShowBacktraceOnNullptrAccess) {
|
||||
auto output = call_process_exiting_with_nullptr_violation();
|
||||
|
@ -137,4 +137,3 @@ TEST_F(ProgramOptionsTest, SomeFuseOptions) {
|
||||
//Fuse should have the mount dir as first parameter
|
||||
EXPECT_VECTOR_EQ({"-f", "--longoption"}, testobj.fuseOptions());
|
||||
}
|
||||
|
||||
|
@ -65,8 +65,10 @@ auto failOnIntegrityViolation() {
|
||||
TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
|
||||
{
|
||||
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
|
||||
dev.setContext(fspp::Context {fspp::relatime()});
|
||||
}
|
||||
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
|
||||
dev.setContext(fspp::Context {fspp::relatime()});
|
||||
auto rootDir = dev.LoadDir(bf::path("/"));
|
||||
rootDir.value()->children();
|
||||
}
|
||||
@ -74,10 +76,12 @@ TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
|
||||
TEST_F(CryFsTest, LoadingFilesystemDoesntModifyConfigFile) {
|
||||
{
|
||||
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
|
||||
dev.setContext(fspp::Context {fspp::relatime()});
|
||||
}
|
||||
Data configAfterCreating = Data::LoadFromFile(config.path()).value();
|
||||
{
|
||||
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
|
||||
dev.setContext(fspp::Context {fspp::relatime()});
|
||||
}
|
||||
Data configAfterLoading = Data::LoadFromFile(config.path()).value();
|
||||
EXPECT_EQ(configAfterCreating, configAfterLoading);
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
CryTestBase(): _tempLocalStateDir(), _localStateDir(_tempLocalStateDir.path()), _configFile(false), _device(nullptr) {
|
||||
auto fakeBlockStore = cpputils::make_unique_ref<blockstore::inmemory::InMemoryBlockStore2>();
|
||||
_device = std::make_unique<cryfs::CryDevice>(configFile(), std::move(fakeBlockStore), _localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
|
||||
_device->setContext(fspp::Context { fspp::relatime() });
|
||||
}
|
||||
|
||||
std::shared_ptr<cryfs::CryConfigFile> configFile() {
|
||||
|
@ -69,6 +69,7 @@ set(SOURCES
|
||||
fuse/access/FuseAccessModeTest.cpp
|
||||
fuse/access/FuseAccessErrorTest.cpp
|
||||
fuse/BasicFuseTest.cpp
|
||||
fuse/TimestampTest.cpp
|
||||
fuse/rmdir/testutils/FuseRmdirTest.cpp
|
||||
fuse/rmdir/FuseRmdirErrorTest.cpp
|
||||
fuse/rmdir/FuseRmdirDirnameTest.cpp
|
||||
|
313
test/fspp/fuse/TimestampTest.cpp
Normal file
313
test/fspp/fuse/TimestampTest.cpp
Normal file
@ -0,0 +1,313 @@
|
||||
#include "../testutils/FuseTest.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace fspp::fuse;
|
||||
|
||||
typedef FuseTest FuseTimestampTest;
|
||||
|
||||
// Single flag
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithoutAnyAtimeFlag_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeFlag_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "noatime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeFlag_thenHasStrictatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "strictatime"});
|
||||
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeFlag_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeFlag_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeFlag_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Flag combinations
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeAtimeFlag_withCsv_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime,atime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeAtimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime", "-o", "atime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeNoatimeFlag_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "atime,noatime"}),
|
||||
"Cannot have both, noatime and atime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeNoatimeFlag_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "atime", "-o", "noatime"}),
|
||||
"Cannot have both, noatime and atime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeRelatimeFlag_withCsv_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime,relatime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeRelatimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime", "-o", "relatime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeStrictatimeFlag_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "atime,strictatime"}),
|
||||
"Cannot have both, atime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeStrictatimeFlag_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "atime", "-o", "strictatime"}),
|
||||
"Cannot have both, atime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime,nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithAtimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "atime", "-o", "nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeAtime_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "noatime,atime"}),
|
||||
"Cannot have both, noatime and atime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeAtime_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "noatime", "-o", "atime"}),
|
||||
"Cannot have both, noatime and atime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNoatimeFlag_withCsv_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "noatime,noatime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNoatimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "noatime", "-o", "noatime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeRelatime_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "noatime,relatime"}),
|
||||
"Cannot have both, noatime and relatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeRelatime_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "noatime", "-o", "relatime"}),
|
||||
"Cannot have both, noatime and relatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeStrictatime_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "noatime,strictatime"}),
|
||||
"Cannot have both, noatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeStrictatime_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "noatime", "-o", "strictatime"}),
|
||||
"Cannot have both, noatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNodiratimeFlag_withCsv_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "noatime,nodiratime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNodiratimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "noatime", "-o", "nodiratime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeAtimeFlag_withCsv_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime,atime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeAtimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime", "-o", "atime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNoatime_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "relatime,noatime"}),
|
||||
"Cannot have both, noatime and relatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNoatime_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "relatime", "-o", "noatime"}),
|
||||
"Cannot have both, noatime and relatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeRelatimeFlag_withCsv_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime,relatime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeRelatimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime", "-o", "relatime"});
|
||||
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeStrictatime_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "relatime,strictatime"}),
|
||||
"Cannot have both, relatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeStrictatime_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "relatime", "-o", "strictatime"}),
|
||||
"Cannot have both, relatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime,nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "relatime", "-o", "nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeAtimeFlag_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "strictatime,atime"}),
|
||||
"Cannot have both, atime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeAtimeFlag_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "strictatime", "-o", "atime"}),
|
||||
"Cannot have both, atime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNoatimeFlag_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "strictatime,noatime"}),
|
||||
"Cannot have both, noatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNoatimeFlag_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "strictatime", "-o", "noatime"}),
|
||||
"Cannot have both, noatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeRelatimeFlag_withCsv_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "strictatime,relatime"}),
|
||||
"Cannot have both, relatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeRelatimeFlag_withSeparateFlags_thenFails) {
|
||||
EXPECT_DEATH(
|
||||
TestFS({"-o", "strictatime", "-o", "relatime"}),
|
||||
"Cannot have both, relatime and strictatime flags set.");
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeStrictatimeFlag_withCsv_thenHasStrictatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "strictatime,strictatime"});
|
||||
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeStrictatimeFlag_withSeparateFlags_thenHasStrictatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "strictatime", "-o", "strictatime"});
|
||||
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "strictatime,nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "strictatime", "-o", "nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeAtimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime,atime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeAtimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime", "-o", "atime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNoatimeFlag_withCsv_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime,noatime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNoatimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime", "-o", "noatime"});
|
||||
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeRelatimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime,relatime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeRelatimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime", "-o", "relatime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeStrictatimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime,strictatime"});
|
||||
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeStrictatimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime", "-o", "strictatime"});
|
||||
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime,nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
||||
|
||||
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
|
||||
auto fs = TestFS({"-o", "nodiratime", "-o", "nodiratime"});
|
||||
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
|
||||
}
|
@ -19,7 +19,7 @@ using namespace fspp::fuse;
|
||||
MockFilesystem::MockFilesystem() {}
|
||||
MockFilesystem::~MockFilesystem() {}
|
||||
|
||||
FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()) {
|
||||
FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()), _context(boost::none) {
|
||||
auto defaultAction = Throw(FuseErrnoException(EIO));
|
||||
ON_CALL(*fsimpl, openFile(_,_)).WillByDefault(defaultAction);
|
||||
ON_CALL(*fsimpl, closeFile(_)).WillByDefault(defaultAction);
|
||||
@ -48,18 +48,23 @@ FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()) {
|
||||
ON_CALL(*fsimpl, readSymlink(_,_,_)).WillByDefault(defaultAction);
|
||||
|
||||
EXPECT_CALL(*fsimpl, access(_,_)).WillRepeatedly(Return());
|
||||
|
||||
ON_CALL(*fsimpl, setContext(_)).WillByDefault(Invoke([this] (fspp::Context context) {
|
||||
_context = std::move(context);
|
||||
}));
|
||||
|
||||
ReturnIsDirOnLstat("/");
|
||||
}
|
||||
|
||||
unique_ref<FuseTest::TempTestFS> FuseTest::TestFS() {
|
||||
return make_unique_ref<TempTestFS>(fsimpl);
|
||||
unique_ref<FuseTest::TempTestFS> FuseTest::TestFS(const std::vector<std::string>& fuseOptions) {
|
||||
return make_unique_ref<TempTestFS>(fsimpl, fuseOptions);
|
||||
}
|
||||
|
||||
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl)
|
||||
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl, const std::vector<std::string>& fuseOptions)
|
||||
:_mountDir(),
|
||||
_fuse([fsimpl] (Fuse*) {return fsimpl;}, []{}, "fusetest", boost::none), _fuse_thread(&_fuse) {
|
||||
|
||||
_fuse_thread.start(_mountDir.path(), {});
|
||||
_fuse_thread.start(_mountDir.path(), fuseOptions);
|
||||
}
|
||||
|
||||
FuseTest::TempTestFS::~TempTestFS() {
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
MockFilesystem();
|
||||
virtual ~MockFilesystem();
|
||||
|
||||
MOCK_METHOD(void, setContext, (fspp::Context&&), (override));
|
||||
MOCK_METHOD(int, openFile, (const boost::filesystem::path&, int), (override));
|
||||
MOCK_METHOD(void, closeFile, (int), (override));
|
||||
MOCK_METHOD(void, lstat, (const boost::filesystem::path&, fspp::fuse::STAT*), (override));
|
||||
@ -54,7 +55,7 @@ public:
|
||||
|
||||
class TempTestFS {
|
||||
public:
|
||||
TempTestFS(std::shared_ptr<MockFilesystem> fsimpl);
|
||||
TempTestFS(std::shared_ptr<MockFilesystem> fsimpl, const std::vector<std::string>& fuseOptions = {});
|
||||
virtual ~TempTestFS();
|
||||
public:
|
||||
const boost::filesystem::path &mountDir() const;
|
||||
@ -64,10 +65,18 @@ public:
|
||||
FuseThread _fuse_thread;
|
||||
};
|
||||
|
||||
cpputils::unique_ref<TempTestFS> TestFS();
|
||||
cpputils::unique_ref<TempTestFS> TestFS(const std::vector<std::string>& fuseOptions = {});
|
||||
|
||||
std::shared_ptr<MockFilesystem> fsimpl;
|
||||
|
||||
const fspp::Context& context() const {
|
||||
ASSERT(_context != boost::none, "Context wasn't correctly initialized");
|
||||
return *_context;
|
||||
}
|
||||
private:
|
||||
boost::optional<fspp::Context> _context;
|
||||
|
||||
public:
|
||||
|
||||
//TODO Combine ReturnIsFile and ReturnIsFileFstat. This should be possible in gmock by either (a) using ::testing::Undefined as parameter type or (b) using action macros
|
||||
static ::testing::Action<void(const boost::filesystem::path&, fspp::fuse::STAT*)> ReturnIsFile;
|
||||
|
2
vendor/CMakeLists.txt
vendored
2
vendor/CMakeLists.txt
vendored
@ -1,4 +1,2 @@
|
||||
add_subdirectory(googletest)
|
||||
add_subdirectory(spdlog)
|
||||
add_subdirectory(cryptopp)
|
||||
add_subdirectory(range-v3)
|
||||
|
3
vendor/README
vendored
3
vendor/README
vendored
@ -1,7 +1,6 @@
|
||||
This directory contains external projects, taken from the following locations:
|
||||
googletest: https://github.com/google/googletest/tree/release-1.10.0
|
||||
spdlog: https://github.com/gabime/spdlog/tree/v1.4.2/include/spdlog
|
||||
- changed: https://github.com/google/googletest/pull/2514
|
||||
cryptopp: https://github.com/weidai11/cryptopp/tree/CRYPTOPP_8_2_0
|
||||
- changed: added CMakeLists.txt and cryptopp-config.cmake from https://github.com/noloader/cryptopp-cmake/tree/CRYPTOPP_8_2_0
|
||||
- changed: commented out line including winapifamily.h in CMakeLists.txt
|
||||
range-v3: https://github.com/ericniebler/range-v3/tree/0.9.1
|
||||
|
@ -1,19 +1,6 @@
|
||||
#ifndef THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
|
||||
#define THIRD_PARTY_GOOGLETEST_GOOGLEMOCK_INCLUDE_GMOCK_PP_H_
|
||||
|
||||
#undef GMOCK_PP_INTERNAL_USE_MSVC
|
||||
#if defined(__clang__)
|
||||
#define GMOCK_PP_INTERNAL_USE_MSVC 0
|
||||
#elif defined(_MSC_VER)
|
||||
// TODO(iserna): Also verify tradional versus comformant preprocessor.
|
||||
static_assert(
|
||||
_MSC_VER >= 1900,
|
||||
"MSVC version not supported. There is support for MSVC 14.0 and above.");
|
||||
#define GMOCK_PP_INTERNAL_USE_MSVC 1
|
||||
#else
|
||||
#define GMOCK_PP_INTERNAL_USE_MSVC 0
|
||||
#endif
|
||||
|
||||
// Expands and concatenates the arguments. Constructed macros reevaluate.
|
||||
#define GMOCK_PP_CAT(_1, _2) GMOCK_PP_INTERNAL_CAT(_1, _2)
|
||||
|
||||
@ -29,10 +16,6 @@ static_assert(
|
||||
// Returns the only argument.
|
||||
#define GMOCK_PP_IDENTITY(_1) _1
|
||||
|
||||
// MSVC preprocessor collapses __VA_ARGS__ in a single argument, we use a
|
||||
// CAT-like directive to force correct evaluation. Each macro has its own.
|
||||
#if GMOCK_PP_INTERNAL_USE_MSVC
|
||||
|
||||
// Evaluates to the number of arguments after expansion.
|
||||
//
|
||||
// #define PAIR x, y
|
||||
@ -44,44 +27,28 @@ static_assert(
|
||||
//
|
||||
// Requires: the number of arguments after expansion is at most 15.
|
||||
#define GMOCK_PP_NARG(...) \
|
||||
GMOCK_PP_INTERNAL_NARG_CAT( \
|
||||
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, \
|
||||
8, 7, 6, 5, 4, 3, 2, 1), )
|
||||
GMOCK_PP_INTERNAL_16TH( \
|
||||
(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
|
||||
|
||||
// Returns 1 if the expansion of arguments has an unprotected comma. Otherwise
|
||||
// returns 0. Requires no more than 15 unprotected commas.
|
||||
#define GMOCK_PP_HAS_COMMA(...) \
|
||||
GMOCK_PP_INTERNAL_HAS_COMMA_CAT( \
|
||||
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
|
||||
1, 1, 1, 1, 1, 0), )
|
||||
GMOCK_PP_INTERNAL_16TH( \
|
||||
(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
|
||||
|
||||
// Returns the first argument.
|
||||
#define GMOCK_PP_HEAD(...) \
|
||||
GMOCK_PP_INTERNAL_HEAD_CAT(GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__), )
|
||||
GMOCK_PP_INTERNAL_HEAD((__VA_ARGS__))
|
||||
|
||||
// Returns the tail. A variadic list of all arguments minus the first. Requires
|
||||
// at least one argument.
|
||||
#define GMOCK_PP_TAIL(...) \
|
||||
GMOCK_PP_INTERNAL_TAIL_CAT(GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__), )
|
||||
GMOCK_PP_INTERNAL_TAIL((__VA_ARGS__))
|
||||
|
||||
// Calls CAT(_Macro, NARG(__VA_ARGS__))(__VA_ARGS__)
|
||||
#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
|
||||
GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT( \
|
||||
GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__), )
|
||||
|
||||
#else // GMOCK_PP_INTERNAL_USE_MSVC
|
||||
|
||||
#define GMOCK_PP_NARG(...) \
|
||||
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, \
|
||||
7, 6, 5, 4, 3, 2, 1)
|
||||
#define GMOCK_PP_HAS_COMMA(...) \
|
||||
GMOCK_PP_INTERNAL_INTERNAL_16TH(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
|
||||
1, 1, 1, 1, 0)
|
||||
#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD(__VA_ARGS__)
|
||||
#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL(__VA_ARGS__)
|
||||
#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
|
||||
GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#endif // GMOCK_PP_INTERNAL_USE_MSVC
|
||||
GMOCK_PP_IDENTITY( \
|
||||
GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__))
|
||||
|
||||
// If the arguments after expansion have no tokens, evaluates to `1`. Otherwise
|
||||
// evaluates to `0`.
|
||||
@ -140,7 +107,7 @@ static_assert(
|
||||
// Expands to 1 if the first argument starts with something in parentheses,
|
||||
// otherwise to 0.
|
||||
#define GMOCK_PP_IS_BEGIN_PARENS(...) \
|
||||
GMOCK_PP_INTERNAL_ALTERNATE_HEAD( \
|
||||
GMOCK_PP_HEAD( \
|
||||
GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \
|
||||
GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__))
|
||||
|
||||
@ -179,10 +146,6 @@ static_assert(
|
||||
#define GMOCK_PP_INTENRAL_EMPTY_TUPLE (, , , , , , , , , , , , , , , )
|
||||
#define GMOCK_PP_INTERNAL_CAT(_1, _2) _1##_2
|
||||
#define GMOCK_PP_INTERNAL_STRINGIZE(...) #__VA_ARGS__
|
||||
#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
|
||||
_10, _11, _12, _13, _14, _15, _16, \
|
||||
...) \
|
||||
_16
|
||||
#define GMOCK_PP_INTERNAL_CAT_5(_1, _2, _3, _4, _5) _1##_2##_3##_4##_5
|
||||
#define GMOCK_PP_INTERNAL_IS_EMPTY(_1, _2, _3, _4) \
|
||||
GMOCK_PP_HAS_COMMA(GMOCK_PP_INTERNAL_CAT_5(GMOCK_PP_INTERNAL_IS_EMPTY_CASE_, \
|
||||
@ -190,30 +153,24 @@ static_assert(
|
||||
#define GMOCK_PP_INTERNAL_IS_EMPTY_CASE_0001 ,
|
||||
#define GMOCK_PP_INTERNAL_IF_1(_Then, _Else) _Then
|
||||
#define GMOCK_PP_INTERNAL_IF_0(_Then, _Else) _Else
|
||||
#define GMOCK_PP_INTERNAL_HEAD(_1, ...) _1
|
||||
#define GMOCK_PP_INTERNAL_TAIL(_1, ...) __VA_ARGS__
|
||||
|
||||
#if GMOCK_PP_INTERNAL_USE_MSVC
|
||||
#define GMOCK_PP_INTERNAL_NARG_CAT(_1, _2) GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2)
|
||||
#define GMOCK_PP_INTERNAL_HEAD_CAT(_1, _2) GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2)
|
||||
#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT(_1, _2) \
|
||||
GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2)
|
||||
#define GMOCK_PP_INTERNAL_TAIL_CAT(_1, _2) GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2)
|
||||
#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT(_1, _2) \
|
||||
GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2)
|
||||
#define GMOCK_PP_INTERNAL_NARG_CAT_I(_1, _2) _1##_2
|
||||
#define GMOCK_PP_INTERNAL_HEAD_CAT_I(_1, _2) _1##_2
|
||||
#define GMOCK_PP_INTERNAL_HAS_COMMA_CAT_I(_1, _2) _1##_2
|
||||
#define GMOCK_PP_INTERNAL_TAIL_CAT_I(_1, _2) _1##_2
|
||||
#define GMOCK_PP_INTERNAL_VARIADIC_CALL_CAT_I(_1, _2) _1##_2
|
||||
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) \
|
||||
GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(GMOCK_PP_HEAD(__VA_ARGS__), )
|
||||
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT(_1, _2) \
|
||||
GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2)
|
||||
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD_CAT_I(_1, _2) _1##_2
|
||||
#else // GMOCK_PP_INTERNAL_USE_MSVC
|
||||
#define GMOCK_PP_INTERNAL_ALTERNATE_HEAD(...) GMOCK_PP_HEAD(__VA_ARGS__)
|
||||
#endif // GMOCK_PP_INTERNAL_USE_MSVC
|
||||
// Because of MSVC treating a token with a comma in it as a single token when passed
|
||||
// to another macro, we need to force it to evaluate it as multiple tokens. We do that
|
||||
// by using a "IDENTITY(MACRO PARENTHESIZED_ARGS)" macro.
|
||||
// We define one per possible macro that relies on this behavior.
|
||||
// Note "_Args" must be parenthesized.
|
||||
#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
|
||||
_10, _11, _12, _13, _14, _15, _16, \
|
||||
...) \
|
||||
_16
|
||||
#define GMOCK_PP_INTERNAL_16TH(_Args) \
|
||||
GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_16TH _Args)
|
||||
#define GMOCK_PP_INTERNAL_INTERNAL_HEAD(_1, ...) _1
|
||||
#define GMOCK_PP_INTERNAL_HEAD(_Args) \
|
||||
GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_HEAD _Args)
|
||||
#define GMOCK_PP_INTERNAL_INTERNAL_TAIL(_1, ...) __VA_ARGS__
|
||||
#define GMOCK_PP_INTERNAL_TAIL(_Args) \
|
||||
GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_TAIL _Args)
|
||||
|
||||
#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C(...) 1 _
|
||||
#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_1 1,
|
||||
|
@ -1,5 +1,10 @@
|
||||
#include "gmock/internal/gmock-pp.h"
|
||||
|
||||
// Used to test MSVC treating __VA_ARGS__ with a comma in it as one value
|
||||
#define GMOCK_TEST_REPLACE_comma_WITH_COMMA_I_comma ,
|
||||
#define GMOCK_TEST_REPLACE_comma_WITH_COMMA(x) \
|
||||
GMOCK_PP_CAT(GMOCK_TEST_REPLACE_comma_WITH_COMMA_I_, x)
|
||||
|
||||
// Static assertions.
|
||||
namespace testing {
|
||||
namespace internal {
|
||||
@ -17,6 +22,11 @@ static_assert(GMOCK_PP_NARG(x, y, z, w) == 4, "");
|
||||
static_assert(!GMOCK_PP_HAS_COMMA(), "");
|
||||
static_assert(GMOCK_PP_HAS_COMMA(b, ), "");
|
||||
static_assert(!GMOCK_PP_HAS_COMMA((, )), "");
|
||||
static_assert(
|
||||
GMOCK_PP_HAS_COMMA(GMOCK_TEST_REPLACE_comma_WITH_COMMA(comma)), "");
|
||||
static_assert(
|
||||
GMOCK_PP_HAS_COMMA(GMOCK_TEST_REPLACE_comma_WITH_COMMA(comma(unrelated))),
|
||||
"");
|
||||
static_assert(!GMOCK_PP_IS_EMPTY(, ), "");
|
||||
static_assert(!GMOCK_PP_IS_EMPTY(a), "");
|
||||
static_assert(!GMOCK_PP_IS_EMPTY(()), "");
|
||||
|
2
vendor/range-v3/CMakeLists.txt
vendored
2
vendor/range-v3/CMakeLists.txt
vendored
@ -1,2 +0,0 @@
|
||||
add_library(range-v3 INTERFACE)
|
||||
target_include_directories(range-v3 SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/range-v3/include)
|
0
vendor/range-v3/range-v3/.buckconfig
vendored
0
vendor/range-v3/range-v3/.buckconfig
vendored
92
vendor/range-v3/range-v3/.clang-format
vendored
92
vendor/range-v3/range-v3/.clang-format
vendored
@ -1,92 +0,0 @@
|
||||
{
|
||||
AccessModifierOffset: -4,
|
||||
AlignAfterOpenBracket: Align,
|
||||
AlignEscapedNewlinesLeft: true,
|
||||
AlignTrailingComments: true,
|
||||
AllowAllParametersOfDeclarationOnNextLine: false,
|
||||
AllowShortBlocksOnASingleLine: true,
|
||||
AllowShortCaseLabelsOnASingleLine: false,
|
||||
AllowShortFunctionsOnASingleLine: None,
|
||||
AllowShortIfStatementsOnASingleLine: false,
|
||||
AllowShortLoopsOnASingleLine: false,
|
||||
AlwaysBreakBeforeMultilineStrings: true,
|
||||
AlwaysBreakAfterReturnType: None,
|
||||
AlwaysBreakTemplateDeclarations: true,
|
||||
BinPackArguments: false,
|
||||
BinPackParameters: true,
|
||||
BraceWrapping: {
|
||||
AfterCaseLabel: true,
|
||||
AfterClass: true,
|
||||
AfterControlStatement: true,
|
||||
AfterEnum: true,
|
||||
AfterFunction: true,
|
||||
AfterNamespace: true,
|
||||
AfterStruct: true,
|
||||
AfterUnion: true,
|
||||
AfterExternBlock: true,
|
||||
BeforeCatch: true,
|
||||
BeforeElse: true,
|
||||
IndentBraces: false,
|
||||
SplitEmptyFunction: false,
|
||||
SplitEmptyRecord: false,
|
||||
SplitEmptyNamespace: true,
|
||||
},
|
||||
BreakBeforeBinaryOperators: false,
|
||||
BreakBeforeBraces: Custom,
|
||||
BreakBeforeTernaryOperators: true,
|
||||
BreakConstructorInitializers: BeforeComma,
|
||||
BreakInheritanceList: BeforeComma,
|
||||
ColumnLimit: 90,
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false,
|
||||
ConstructorInitializerIndentWidth: 2,
|
||||
ContinuationIndentWidth: 4,
|
||||
Cpp11BracedListStyle: true,
|
||||
DerivePointerAlignment: false,
|
||||
ExperimentalAutoDetectBinPacking: false,
|
||||
ForEachMacros: ['RANGES_FOR',],
|
||||
IncludeBlocks: Regroup,
|
||||
IncludeCategories: [
|
||||
{ Regex: '^<range/v3/range_fwd.hpp',
|
||||
Priority: 5},
|
||||
{ Regex: '^<range/v3',
|
||||
Priority: 6},
|
||||
{ Regex: '^<concepts/',
|
||||
Priority: 4},
|
||||
{ Regex: '^<meta/',
|
||||
Priority: 3},
|
||||
{ Regex: '^<std/.*>$',
|
||||
Priority: 2},
|
||||
{ Regex: '^<.*>$',
|
||||
Priority: 1},
|
||||
],
|
||||
IndentCaseLabels: false,
|
||||
IndentFunctionDeclarationAfterType: false,
|
||||
IndentWidth: 4,
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true,
|
||||
Language: Cpp,
|
||||
MaxEmptyLinesToKeep: 1,
|
||||
MacroBlockBegin: "^(RANGES|META)_BEGIN_(NAMESPACE_(STD|VERSION|CONTAINER)|NIEBLOID)$",
|
||||
MacroBlockEnd: "^(RANGES|META)_END_(NAMESPACE_(STD|VERSION|CONTAINER)|NIEBLOID)$",
|
||||
NamespaceIndentation: All,
|
||||
PenaltyBreakBeforeFirstCallParameter: 10,
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000,
|
||||
PointerAlignment: Middle,
|
||||
SpaceAfterControlStatementKeyword: false,
|
||||
SpaceAfterTemplateKeyword: false,
|
||||
SpaceBeforeAssignmentOperators: true,
|
||||
SpaceBeforeParens: Never,
|
||||
SpaceInEmptyParentheses: false,
|
||||
SpacesBeforeTrailingComments: 1,
|
||||
SpacesInAngles: false,
|
||||
SpacesInCStyleCastParentheses: false,
|
||||
SpacesInParentheses: false,
|
||||
Standard: Cpp11,
|
||||
StatementMacros: [
|
||||
'RANGES_INLINE_VARIABLE',
|
||||
'RANGES_DEFINE_CPO',
|
||||
'CPP_member',
|
||||
'CPP_broken_friend_member',
|
||||
],
|
||||
TabWidth: 4,
|
||||
UseTab: Never,
|
||||
}
|
237
vendor/range-v3/range-v3/.gitignore
vendored
237
vendor/range-v3/range-v3/.gitignore
vendored
@ -1,237 +0,0 @@
|
||||
## Copyright (c) 2013 GitHub, Inc.
|
||||
##
|
||||
## 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.
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Clion files
|
||||
.idea
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build*/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
bazel-*
|
||||
cmake-build-*/
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||
!packages/*/build/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
#NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding addin-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.azurePubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
## TODO: If the tool you use requires repositories.config, also uncomment the next line
|
||||
#!packages/repositories.config
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Buck build artefacts
|
||||
.buckd/
|
||||
buck-out/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Visual Studio stuff
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
.vscode/
|
||||
.vs/
|
||||
|
||||
\#*#
|
||||
.#*
|
303
vendor/range-v3/range-v3/.travis.yml
vendored
303
vendor/range-v3/range-v3/.travis.yml
vendored
@ -1,303 +0,0 @@
|
||||
# Copyright Louis Dionne 2013-2016
|
||||
# Copyright Gonzalo BG 2014-2017
|
||||
# Copyright Julian Becker 2015
|
||||
# Copyright Manu Sánchez 2015
|
||||
# Copyright Casey Carter 2015-2017
|
||||
# Copyright Eric Niebler 2015-2016
|
||||
# Copyright Paul Fultz II 2015-2016
|
||||
# Copyright Jakub Szuppe 2016.
|
||||
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.txt or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
# Adapted from various sources, including:
|
||||
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
||||
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
||||
language: cpp
|
||||
dist: xenial
|
||||
script: cmake
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
env:
|
||||
global:
|
||||
- DEPS_DIR=${TRAVIS_BUILD_DIR}/deps
|
||||
- CMAKE_VERSION="3.12.0"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- ${DEPS_DIR}/cmake-${CMAKE_VERSION}
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- env: BUILD_TYPE=Release CPP=1z SYSTEM_LIBCXX=On
|
||||
os: osx
|
||||
compiler: clang
|
||||
|
||||
# The ASAN build in install_libcxx.sh doesn't work for versions < 4
|
||||
|
||||
# clang 3.6 C++17/14 Release libc++
|
||||
- env: CLANG_VERSION=3.6 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
|
||||
os: linux
|
||||
addons: &clang36
|
||||
apt:
|
||||
packages:
|
||||
- clang-3.6
|
||||
- libstdc++-5-dev
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
# clang 3.7 C++17/14 Release libc++
|
||||
- env: CLANG_VERSION=3.7 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
|
||||
os: linux
|
||||
addons: &clang37
|
||||
apt:
|
||||
packages:
|
||||
- clang-3.7
|
||||
- libstdc++-5-dev
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-xenial-3.7
|
||||
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.7 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
# clang 3.8 C++17/14 Release libc++
|
||||
- env: CLANG_VERSION=3.8 BUILD_TYPE=Release CPP=1z LIBCXX=On
|
||||
os: linux
|
||||
addons: &clang38
|
||||
apt:
|
||||
packages:
|
||||
- util-linux
|
||||
- clang-3.8
|
||||
- libstdc++-5-dev
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: CLANG_VERSION=3.8 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
|
||||
os: linux
|
||||
addons: *clang38
|
||||
|
||||
# clang 3.9 C++17/14 Release libc++
|
||||
- env: CLANG_VERSION=3.9 BUILD_TYPE=Release CPP=1z LIBCXX=On
|
||||
os: linux
|
||||
addons: &clang39
|
||||
apt:
|
||||
packages:
|
||||
- util-linux
|
||||
- clang-3.9
|
||||
- libstdc++-6-dev
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: CLANG_VERSION=3.9 BUILD_TYPE=Release CPP=14 LIBCXX=On HEADERS=On
|
||||
os: linux
|
||||
addons: *clang39
|
||||
|
||||
# clang 5 C++17/14/1z Debug/Release-ASAN libc++, 17 Debug libstdc++
|
||||
- env: CLANG_VERSION=5.0 BUILD_TYPE=Debug CPP=1z LIBCXX=On
|
||||
os: linux
|
||||
addons: &clang5
|
||||
apt:
|
||||
packages:
|
||||
- clang-5.0
|
||||
- libstdc++-6-dev
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-xenial-5.0
|
||||
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main'
|
||||
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||
|
||||
- env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=1z ASAN=On LIBCXX=On HEADERS=On
|
||||
os: linux
|
||||
addons: *clang5
|
||||
|
||||
- env: CLANG_VERSION=5.0 BUILD_TYPE=Debug CPP=14 LIBCXX=On
|
||||
os: linux
|
||||
addons: *clang5
|
||||
|
||||
- env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=14 ASAN=On LIBCXX=On
|
||||
os: linux
|
||||
addons: *clang5
|
||||
|
||||
- env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=1z
|
||||
os: linux
|
||||
addons: *clang5
|
||||
|
||||
# Module build is on the floor
|
||||
# - env: CLANG_VERSION=5.0 BUILD_TYPE=Release CPP=1z MSAN=On LIBCXX=On CLANG_MODULES=On
|
||||
# os: linux
|
||||
# addons: *clang5
|
||||
|
||||
# gcc-5 C++17/C++14 Release
|
||||
- env: GCC_VERSION=5 BUILD_TYPE=Release CPP=1z
|
||||
os: linux
|
||||
addons: &gcc5
|
||||
apt:
|
||||
packages:
|
||||
- g++-5
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=5 BUILD_TYPE=Release CPP=14 HEADERS=On
|
||||
os: linux
|
||||
addons: *gcc5
|
||||
|
||||
# gcc-6 C++17/14/1z Debug/Release
|
||||
- env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=1z
|
||||
os: linux
|
||||
addons: &gcc6
|
||||
apt:
|
||||
packages:
|
||||
- g++-6
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=6 BUILD_TYPE=Release CPP=1z
|
||||
os: linux
|
||||
addons: *gcc6
|
||||
|
||||
- env: GCC_VERSION=6 BUILD_TYPE=Debug CPP=14 HEADERS=On
|
||||
os: linux
|
||||
addons: *gcc6
|
||||
|
||||
- env: GCC_VERSION=6 BUILD_TYPE=Release CPP=14
|
||||
os: linux
|
||||
addons: *gcc6
|
||||
|
||||
# gcc-7 C++17/14/1z Debug/Release
|
||||
- env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=1z
|
||||
os: linux
|
||||
addons: &gcc7
|
||||
apt:
|
||||
packages:
|
||||
- g++-7
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=1z
|
||||
os: linux
|
||||
addons: *gcc7
|
||||
|
||||
- env: GCC_VERSION=7 BUILD_TYPE=Debug CPP=14 HEADERS=On
|
||||
os: linux
|
||||
addons: *gcc7
|
||||
|
||||
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=14
|
||||
os: linux
|
||||
addons: *gcc7
|
||||
|
||||
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=1z CONCEPTS=On
|
||||
os: linux
|
||||
addons: *gcc7
|
||||
|
||||
# gcc-8 C++17/14/1z Debug/Release
|
||||
- env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=1z
|
||||
os: linux
|
||||
addons: &gcc8
|
||||
apt:
|
||||
packages:
|
||||
- g++-8
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=8 BUILD_TYPE=Release CPP=1z
|
||||
os: linux
|
||||
addons: *gcc8
|
||||
|
||||
- env: GCC_VERSION=8 BUILD_TYPE=Debug CPP=14 HEADERS=On
|
||||
os: linux
|
||||
addons: *gcc8
|
||||
|
||||
- env: GCC_VERSION=8 BUILD_TYPE=Release CPP=14
|
||||
os: linux
|
||||
addons: *gcc8
|
||||
|
||||
- env: GCC_VERSION=8 BUILD_TYPE=Release CPP=1z CONCEPTS=On
|
||||
os: linux
|
||||
addons: *gcc8
|
||||
|
||||
# Install dependencies
|
||||
before_install:
|
||||
- set -e
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
brew update
|
||||
brew install gnu-sed
|
||||
brew install gnu-which
|
||||
brew upgrade cmake
|
||||
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
|
||||
elif [ "$BUILD_TYPE" == "Release" -a "$ASAN" != "On" -a "$MSAN" != "On" ]; then
|
||||
USE_VALGRIND=On
|
||||
fi
|
||||
- |
|
||||
if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||
if [ -f ${DEPS_DIR}/cmake-${CMAKE_VERSION}/cached ]; then
|
||||
echo "Using cached cmake version ${CMAKE_VERSION}."
|
||||
else
|
||||
CMAKE_URL="https://cmake.org/files/v3.12/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz"
|
||||
mkdir -p ${DEPS_DIR}/cmake-${CMAKE_VERSION}
|
||||
travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C ${DEPS_DIR}/cmake-${CMAKE_VERSION}
|
||||
touch ${DEPS_DIR}/cmake-${CMAKE_VERSION}/cached
|
||||
fi
|
||||
export PATH="${DEPS_DIR}/cmake-${CMAKE_VERSION}/bin:${PATH}"
|
||||
fi
|
||||
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||
- which $CXX && $CXX --version
|
||||
- which $CC
|
||||
- if [ "$USE_VALGRIND" == "On" ]; then which valgrind; fi
|
||||
- if [ "$ASAN" == "On" ]; then export SANITIZER="Address;Undefined"; fi
|
||||
- if [ "$MSAN" == "On" ]; then export SANITIZER="MemoryWithOrigins"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then PATH="${PATH}" CXX="$CXX" CC="$CC" ./install_libcxx.sh; fi
|
||||
|
||||
install:
|
||||
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
|
||||
# It is fixed in valgrind 3.10 so this won't be necessary if someone
|
||||
# replaces the current valgrind (3.7) with valgrind-3.10
|
||||
- |
|
||||
if [ "$USE_VALGRIND" == "On" ]; then
|
||||
sed -i 's/march=native/msse4.2/' cmake/ranges_flags.cmake
|
||||
# We need to explicitly initialize std::random_device on libstdc++ to avoid using RDRAND
|
||||
# since valgrind doesn't understand the instruction.
|
||||
CXX_FLAGS="${CXX_FLAGS} -DRANGES_WORKAROUND_VALGRIND_RDRAND"
|
||||
fi
|
||||
- if [ "$GCC_VERSION" == "5" ]; then CXX_FLAGS="${CXX_FLAGS} -DRANGES_CXX_CONSTEXPR=RANGES_CXX_CONSTEXPR11"; fi
|
||||
- |
|
||||
if [ "$LIBCXX" == "On" ]; then
|
||||
CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -nostdinc++ -cxx-isystem ${TRAVIS_BUILD_DIR}/llvm/include/c++/v1/ -Wno-unused-command-line-argument"
|
||||
CXX_LINKER_FLAGS="${CXX_LINKER_FLAGS} -L ${TRAVIS_BUILD_DIR}/llvm/lib -Wl,-rpath,${TRAVIS_BUILD_DIR}/llvm/lib -lc++abi"
|
||||
if [ -n "$CLANG_VERSION" ]; then
|
||||
if [ "$ASAN" == "On" ]; then
|
||||
CXX_FLAGS="${CXX_FLAGS} -fsanitize=address"
|
||||
elif [ "$MSAN" == "On" ]; then
|
||||
CXX_FLAGS="${CXX_FLAGS} -fsanitize=memory"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
- mkdir -p build
|
||||
# This cd works, but causes the shell to exit on OSX with set -e. I don't even.
|
||||
- set +e; cd build; set -e; pwd
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_FLAGS="${CXX_FLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${CXX_LINKER_FLAGS}" -DRANGES_CXX_STD=$CPP -DRANGE_V3_HEADER_CHECKS=$HEADERS -DRANGES_PREFER_REAL_CONCEPTS=$CONCEPTS -DRANGES_VERBOSE_BUILD=On -DRANGES_ASAN=$ASAN -DRANGES_MSAN=$MSAN -DRANGES_DEEP_STL_INTEGRATION=On -Wdev
|
||||
- cat CMakeFiles/CMakeError.log || true
|
||||
- cat CMakeFiles/CMakeOutput.log || true
|
||||
- if [ "$CLANG_MODULES" == "On" -a "$LIBCXX" == "On" ]; then cmake .. -DRANGES_MODULES=On -DRANGES_LIBCXX_MODULE="${TRAVIS_BUILD_DIR}/llvm/include/c++/v1/module.modulemap"; fi
|
||||
|
||||
- make -j2 VERBOSE=1
|
||||
|
||||
script:
|
||||
- if [ "$USE_VALGRIND" == "On" ]; then CTEST_FLAGS="-D ExperimentalMemCheck"; fi
|
||||
- ctest -j2 -VV ${CTEST_FLAGS}
|
||||
|
||||
notifications:
|
||||
email: false
|
42
vendor/range-v3/range-v3/BUCK
vendored
42
vendor/range-v3/range-v3/BUCK
vendored
@ -1,42 +0,0 @@
|
||||
prebuilt_cxx_library(
|
||||
name = 'concepts',
|
||||
header_namespace = 'concepts',
|
||||
header_only = True,
|
||||
exported_headers = subdir_glob([
|
||||
('include/concepts', '**/*.hpp'),
|
||||
]),
|
||||
licenses = [
|
||||
'LICENSE.txt',
|
||||
],
|
||||
)
|
||||
|
||||
prebuilt_cxx_library(
|
||||
name = 'meta',
|
||||
header_namespace = 'meta',
|
||||
header_only = True,
|
||||
exported_headers = subdir_glob([
|
||||
('include/meta', '**/*.hpp'),
|
||||
]),
|
||||
licenses = [
|
||||
'LICENSE.txt',
|
||||
],
|
||||
)
|
||||
|
||||
prebuilt_cxx_library(
|
||||
name = 'range-v3',
|
||||
header_namespace = 'range/v3',
|
||||
header_only = True,
|
||||
exported_headers = subdir_glob([
|
||||
('include/range/v3', '**/*.hpp'),
|
||||
]),
|
||||
licenses = [
|
||||
'LICENSE.txt',
|
||||
],
|
||||
visibility = [
|
||||
'PUBLIC'
|
||||
],
|
||||
deps = [
|
||||
':concepts',
|
||||
':meta',
|
||||
],
|
||||
)
|
42
vendor/range-v3/range-v3/BUILD.bazel
vendored
42
vendor/range-v3/range-v3/BUILD.bazel
vendored
@ -1,42 +0,0 @@
|
||||
cc_library(
|
||||
name = 'concepts',
|
||||
hdrs = glob([
|
||||
'include/concepts/**/*.hpp',
|
||||
]),
|
||||
includes = [
|
||||
"include",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = 'meta',
|
||||
hdrs = glob([
|
||||
'include/meta/**/*.hpp',
|
||||
]),
|
||||
includes = [
|
||||
"include",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = 'std',
|
||||
hdrs = glob([
|
||||
'include/std/**/*.hpp',
|
||||
]),
|
||||
includes = [
|
||||
"include",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = 'range-v3',
|
||||
hdrs = glob([
|
||||
'include/range/v3/**/*.hpp',
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
':concepts',
|
||||
':meta',
|
||||
':std',
|
||||
],
|
||||
)
|
171
vendor/range-v3/range-v3/CMakeLists.txt
vendored
171
vendor/range-v3/range-v3/CMakeLists.txt
vendored
@ -1,171 +0,0 @@
|
||||
# Copyright Eric Niebler 2014
|
||||
# Copyright Gonzalo Brito Gadeschi 2014, 2017
|
||||
# Copyright Louis Dionne 2015
|
||||
# Copyright Casey Carter 2016
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
get_directory_property(is_subproject PARENT_DIRECTORY)
|
||||
|
||||
if(NOT is_subproject)
|
||||
set(is_standalone YES)
|
||||
else()
|
||||
set(is_standalone NO)
|
||||
endif()
|
||||
|
||||
project(Range-v3 CXX)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export compilation data-base
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
add_library(meta INTERFACE)
|
||||
target_include_directories(meta INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
|
||||
target_include_directories(meta SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
|
||||
target_compile_options(meta INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/permissive->)
|
||||
|
||||
add_library(concepts INTERFACE)
|
||||
target_include_directories(concepts INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
|
||||
target_include_directories(concepts SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
|
||||
target_compile_options(concepts INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/permissive- /experimental:preprocessor /wd5105>)
|
||||
target_link_libraries(concepts INTERFACE meta)
|
||||
|
||||
add_library(range-v3 INTERFACE)
|
||||
target_include_directories(range-v3 INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>)
|
||||
target_include_directories(range-v3 SYSTEM INTERFACE $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
|
||||
target_compile_options(range-v3 INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/permissive->)
|
||||
target_link_libraries(range-v3 INTERFACE concepts meta)
|
||||
|
||||
function(rv3_add_test TESTNAME EXENAME FIRSTSOURCE)
|
||||
add_executable(${EXENAME} ${FIRSTSOURCE} ${ARGN})
|
||||
target_link_libraries(${EXENAME} range-v3)
|
||||
add_test(${TESTNAME} ${EXENAME})
|
||||
endfunction(rv3_add_test)
|
||||
|
||||
include(ranges_options)
|
||||
include(ranges_env)
|
||||
include(ranges_flags)
|
||||
|
||||
if(RANGE_V3_DOCS)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
if(RANGE_V3_TESTS)
|
||||
include(CTest)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
if(RANGE_V3_EXAMPLES)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
||||
if(RANGE_V3_PERF)
|
||||
add_subdirectory(perf)
|
||||
endif()
|
||||
|
||||
# Add header files as sources to fix MSVS 2017 not finding source during debugging
|
||||
file(GLOB_RECURSE RANGE_V3_PUBLIC_HEADERS_ABSOLUTE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
|
||||
add_custom_target(headers SOURCES ${RANGE_V3_PUBLIC_HEADERS_ABSOLUTE})
|
||||
set_target_properties(headers PROPERTIES FOLDER "header")
|
||||
|
||||
# Test all headers
|
||||
if(RANGE_V3_HEADER_CHECKS)
|
||||
include(TestHeaders)
|
||||
|
||||
file(GLOB_RECURSE RANGE_V3_PUBLIC_HEADERS
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
|
||||
# This header is not meant to be included directly:
|
||||
list(REMOVE_ITEM RANGE_V3_PUBLIC_HEADERS std/detail/associated_types.hpp)
|
||||
# Deprecated headers
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
foreach(header ${RANGE_V3_PUBLIC_HEADERS})
|
||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/${header}" is_deprecated
|
||||
LIMIT_COUNT 1
|
||||
REGEX ".*RANGES_DEPRECATED_HEADER.*")
|
||||
if(is_deprecated)
|
||||
list(APPEND RANGE_V3_DEPRECATED_PUBLIC_HEADERS "${header}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
add_header_test(test.range.v3.headers
|
||||
EXCLUDE ${RANGE_V3_DEPRECATED_PUBLIC_HEADERS}
|
||||
HEADERS ${RANGE_V3_PUBLIC_HEADERS})
|
||||
target_link_libraries(test.range.v3.headers PRIVATE range-v3)
|
||||
endif()
|
||||
|
||||
# Grab the range-v3 version numbers:
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/Version.cmake)
|
||||
set(RANGE_V3_VERSION ${RANGE_V3_MAJOR}.${RANGE_V3_MINOR}.${RANGE_V3_PATCHLEVEL})
|
||||
|
||||
# Try to build a new version.hpp
|
||||
configure_file(version.hpp.in include/range/v3/version.hpp @ONLY)
|
||||
file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/include/range/v3/version.hpp RANGE_V3_OLD_VERSION_HPP)
|
||||
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/include/range/v3/version.hpp RANGE_V3_NEW_VERSION_HPP)
|
||||
|
||||
# If the new version.hpp is materially different from the one in the source
|
||||
# directory, update it, commit, and tag.
|
||||
if(NOT RANGE_V3_NEW_VERSION_HPP STREQUAL RANGE_V3_OLD_VERSION_HPP)
|
||||
# Check that README.md and Version.cmake are the only changed file:
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" status --porcelain -uno
|
||||
OUTPUT_VARIABLE RANGE_V3_GIT_STATUS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string(REPLACE "\n" ";" RANGE_V3_GIT_STATUS ${RANGE_V3_GIT_STATUS})
|
||||
if (NOT "x${RANGE_V3_GIT_STATUS}" STREQUAL "x M Version.cmake; M doc/release_notes.md")
|
||||
message(FATAL_ERROR "Cannot update version.hpp: range-v3 source directory has a dirty status")
|
||||
endif()
|
||||
file(
|
||||
COPY ${CMAKE_CURRENT_BINARY_DIR}/include/range/v3/version.hpp
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include/range/v3
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" add -u
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" commit -m "${RANGE_V3_VERSION}"
|
||||
)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" tag -f -a "${RANGE_V3_VERSION}" -m "${RANGE_V3_VERSION}"
|
||||
)
|
||||
find_program(CONAN_EXECUTABLE NAMES conan conan.exe)
|
||||
if (NOT "x${CONAN_EXECUTABLE}" STREQUAL "xCONAN_EXECUTABLE-NOTFOUND")
|
||||
message(STATUS "Exporting conanfile for new version")
|
||||
execute_process(
|
||||
COMMAND ${CONAN_EXECUTABLE} create . range-v3/${RANGE_V3_VERSION}@ericniebler/stable
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
endif()
|
||||
message(STATUS "Version updated to ${RANGE_V3_VERSION}. Don't forget to:")
|
||||
message(STATUS " git push origin <feature-branch>")
|
||||
message(STATUS "and (after that is merged to master) then:")
|
||||
message(STATUS " conan create ${CMAKE_CURRENT_SOURCE_DIR} range-v3/${RANGE_V3_VERSION}@ericniebler/stable")
|
||||
message(STATUS " conan upload --all range-v3/${RANGE_V3_VERSION}@ericniebler/stable")
|
||||
endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# write_basic_package_version_file(...) gained ARCH_INDEPENDENT in CMake 3.14.
|
||||
# For CMake 3.6, this workaround makes the version file ARCH_INDEPENDENT
|
||||
# by making CMAKE_SIZEOF_VOID_P empty.
|
||||
set(OLD_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
||||
set(CMAKE_SIZEOF_VOID_P "")
|
||||
write_basic_package_version_file(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/range-v3-config-version.cmake
|
||||
VERSION ${RANGE_V3_VERSION}
|
||||
COMPATIBILITY ExactVersion
|
||||
)
|
||||
set(CMAKE_SIZEOF_VOID_P ${OLD_CMAKE_SIZEOF_VOID_P})
|
||||
|
||||
install(TARGETS concepts meta range-v3 EXPORT range-v3-targets DESTINATION lib)
|
||||
install(EXPORT range-v3-targets FILE range-v3-config.cmake DESTINATION lib/cmake/range-v3)
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/range-v3-config-version.cmake
|
||||
DESTINATION lib/cmake/range-v3)
|
||||
install(DIRECTORY include/ DESTINATION include FILES_MATCHING PATTERN "*")
|
||||
|
||||
export(EXPORT range-v3-targets FILE range-v3-config.cmake)
|
48
vendor/range-v3/range-v3/CREDITS.md
vendored
48
vendor/range-v3/range-v3/CREDITS.md
vendored
@ -1,48 +0,0 @@
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
In range-v3, I have integrated many ideas that come from other people. I would be remiss to not mention them. Many others helped either directly or indirectly in a variety of ways. In no particular order...
|
||||
|
||||
| Contributor | Contribution |
|
||||
|-----------------------------|------|
|
||||
| Jeremy Siek | Container algorithms (in Boost pre-history), Boost.Iterators |
|
||||
| Thorston Ottoson | Boost.Range v1 |
|
||||
| Neil Groves | Boost.Range v2 |
|
||||
| David Abrahams, Thomas Witt | Boost.Iterators, Sentinels |
|
||||
| Sean Parent | ASL, Projections, View / Range distinction, much Generic Program wisdom besides |
|
||||
| Dietmar Kühl | Array Traits, Property Map |
|
||||
| Andrew Sutton | C++ Concepts "Lite", Origin Libraries, Palo Alto Report |
|
||||
| Doug Gregor | C++0x Concepts |
|
||||
| Casey Carter | Co-author and Editor, Ranges TS; major code contributions |
|
||||
| Gonzalo Brito Gadeschi | Many ideas, bug reports, and code contributions |
|
||||
| Alexander Stepanov | STL, Generic Programming, Iterators, Elements of Programming, etc. |
|
||||
| Bjarne Stroustrup | A tireless effort to add proper support for Generic Programming to C++, early support for my Ranges proposal |
|
||||
| Herb Sutter | Early support for my Ranges proposal |
|
||||
| The Standard C++ Foundation | A generous grant supporting my Ranges work |
|
||||
|
||||
An Abreviated History
|
||||
--------------------
|
||||
|
||||
**Range v1**
|
||||
|
||||
I came to Boost in the early 2000's. By that time, Boost already had a Range library (Thorston Ottoson's). At this time, Boost.Range was little more that the `begin` and `end` free functions, and range-based overloads of the STL algorithms that dispatched to the iterator-based overloads in namespace `std`.
|
||||
|
||||
Boost also already had the Iterators library by David Abrahams and Jeremy Siek. This library had iterator adaptors like `filter_iterator` and `transform_iterator`.
|
||||
|
||||
**Range v2**
|
||||
|
||||
It seemed natural to me that the Range library and the adaptors from the Iterators library should be combined. I wrote the `filter` and `transform` range adaptors, commandeered the pipe operator (`|`) from bash for chaining, and put a rough library together called Range_ex in the Boost File Vault (which would later become the Boost Sandbox). I saw problems with my design and never finished it.
|
||||
|
||||
A few years later, Neil Groves picked up some of the ideas in my Range\_ex, polished them a great deal, published his own Range\_ex library, and submitted it to Boost. It became Boost.Range v2. At the time of writing (March, 2017), it is the version still shipping with Boost.
|
||||
|
||||
**Range v3**
|
||||
|
||||
In 2013, I published a blog post called ["Out Parameters, Move Semantics, and Stateful Algorithms"](http://ericniebler.com/2013/10/13/out-parameters-vs-move-semantics/) that turned my mind to ranges once again. Following that, it became clear to me that the Boost.Range library, designed for C++98, needed a facelift for the post-C++11 world. I began what I believed at the time would be a quick hack to bring Boost.Range into the modern world. I called it "Range v3", thinking it would become the third major version of the Boost.Range library. Subsequent posts detailed the evolution of my thinking as range-v3 took shape.
|
||||
|
||||
**Standardization**
|
||||
|
||||
Around this time, some big thinkers in the C++ community were working to resurrect the effort to add Concepts to C++. They published a paper ([N3351](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf)) that would become known as the **"Palo Alto Report"** which detailed the necessary and sufficient language and library support for a concept-checked version of the Standard Template Library. The authors of the paper included Alexander Stepanov, Bjarne Stroustrup, Sean Parent, Andrew Sutton, and more. Andrew Sutton began working in earnest to realize the core language changes, an effort that became known as "Concepts Lite". (It is now the Concepts TS.)
|
||||
|
||||
I decided early on that Concepts Lite, or something like it, would become part of Standard C++. Recognizing that C++ would need a concept-ified Standard Library to go with the language feature, I began evolving range-v3 in that direction, eventually submitting ["Ranges for the Standard Library, Revision 1"](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4128.html) to the C++ Standardization Committee, together with Andrew Sutton and Sean Parent. The Committee approved the direction in late 2014, and so it goes...
|
||||
|
||||
Today (2017-03), we are very close to a final Ranges TS and are on target to integrate into Standard C++ in 2020, with *much* more to come. Stay tuned.
|
151
vendor/range-v3/range-v3/LICENSE.txt
vendored
151
vendor/range-v3/range-v3/LICENSE.txt
vendored
@ -1,151 +0,0 @@
|
||||
========================================================
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
========================================================
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
libc++ License
|
||||
==============================================================================
|
||||
|
||||
The libc++ library is dual licensed under both the University of Illinois
|
||||
"BSD-Like" license and the MIT license. As a user of this code you may choose
|
||||
to use it under either license. As a contributor, you agree to allow your code
|
||||
to be used under both.
|
||||
|
||||
Full text of the relevant licenses is included below.
|
||||
|
||||
==============================================================================
|
||||
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||
http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
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
|
||||
CONTRIBUTORS 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 WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||
http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
Stepanov and McJones, "Elements of Programming" license
|
||||
==============================================================================
|
||||
|
||||
// Copyright (c) 2009 Alexander Stepanov and Paul McJones
|
||||
//
|
||||
// Permission to use, copy, modify, distribute and sell this software
|
||||
// and its documentation for any purpose is hereby granted without
|
||||
// fee, provided that the above copyright notice appear in all copies
|
||||
// and that both that copyright notice and this permission notice
|
||||
// appear in supporting documentation. The authors make no
|
||||
// representations about the suitability of this software for any
|
||||
// purpose. It is provided "as is" without express or implied
|
||||
// warranty.
|
||||
//
|
||||
// Algorithms from
|
||||
// Elements of Programming
|
||||
// by Alexander Stepanov and Paul McJones
|
||||
// Addison-Wesley Professional, 2009
|
||||
|
||||
==============================================================================
|
||||
SGI C++ Standard Template Library license
|
||||
==============================================================================
|
||||
|
||||
// Copyright (c) 1994
|
||||
// Hewlett-Packard Company
|
||||
//
|
||||
// Permission to use, copy, modify, distribute and sell this software
|
||||
// and its documentation for any purpose is hereby granted without fee,
|
||||
// provided that the above copyright notice appear in all copies and
|
||||
// that both that copyright notice and this permission notice appear
|
||||
// in supporting documentation. Hewlett-Packard Company makes no
|
||||
// representations about the suitability of this software for any
|
||||
// purpose. It is provided "as is" without express or implied warranty.
|
||||
//
|
||||
// Copyright (c) 1996
|
||||
// Silicon Graphics Computer Systems, Inc.
|
||||
//
|
||||
// Permission to use, copy, modify, distribute and sell this software
|
||||
// and its documentation for any purpose is hereby granted without fee,
|
||||
// provided that the above copyright notice appear in all copies and
|
||||
// that both that copyright notice and this permission notice appear
|
||||
// in supporting documentation. Silicon Graphics makes no
|
||||
// representations about the suitability of this software for any
|
||||
// purpose. It is provided "as is" without express or implied warranty.
|
||||
//
|
77
vendor/range-v3/range-v3/README.md
vendored
77
vendor/range-v3/range-v3/README.md
vendored
@ -1,77 +0,0 @@
|
||||
range-v3
|
||||
========
|
||||
|
||||
Range library for C++14/17/20. This code was the basis of [a formal proposal](https://ericniebler.github.io/std/wg21/D4128.html) to add range support to the C++ standard library. That proposal evolved through a Technical Specification, and finally into [P0896R4 "The One Ranges Proposal"](https://wg21.link/p0896r4) which was merged into the C++20 working drafts in November 2018.
|
||||
|
||||
About:
|
||||
------
|
||||
|
||||
Ranges are an extension of the Standard Template Library that makes its iterators and algorithms more powerful by making them _composable_. Unlike other range-like solutions which seek to do away with iterators, in range-v3 ranges are an abstration layer _on top_ of iterators.
|
||||
|
||||
Range-v3 is built on three pillars: Views, Actions, and Algorithms. The algorithms are the same as those with which you are already familiar in the STL, except that in range-v3 all the algorithms have overloads that take ranges in addition to the overloads that take iterators. Views are composable adaptations of ranges where the adaptation happens lazily as the view is iterated. And an action is an eager application of an algorithm to a container that mutates the container in-place and returns it for further processing.
|
||||
|
||||
Views and actions use the pipe syntax (e.g., `rng | adapt1 | adapt2 | ...`) so your code is terse and readable from left to right.
|
||||
|
||||
Documentation:
|
||||
--------------
|
||||
|
||||
Check out the (woefully incomplete) documentation [here](https://ericniebler.github.io/range-v3/).
|
||||
|
||||
Other resources (mind the dates, the library probably has changed since then):
|
||||
|
||||
- Usage:
|
||||
- Talk: [CppCon 2015: Eric Niebler "Ranges for the Standard Library"](https://www.youtube.com/watch?v=mFUXNMfaciE), 2015.
|
||||
- [A slice of Python in C++](http://ericniebler.com/2014/12/07/a-slice-of-python-in-c/), 07.12.2014.
|
||||
- Actions (back then called [Container Algorithms](http://ericniebler.com/2014/11/23/container-algorithms/)), 23.11.2014.
|
||||
- [Range comprehensions](http://ericniebler.com/2014/04/27/range-comprehensions/), 27.04.2014.
|
||||
- [Input iterators vs input ranges](http://ericniebler.com/2013/11/07/input-iterators-vs-input-ranges/), 07.11.2013.
|
||||
|
||||
- Design / Implementation:
|
||||
- Rationale behind range-v3: [N4128: Ranges for the standard library Revision 1](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4128.html), 2014.
|
||||
- Ranges TS: [N4560: C++ Extensions for Ranges](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4560.pdf), 2015.
|
||||
- Implementation of customization points in range-v3:
|
||||
- [N4381: Suggested Design for Customization Points](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html), 2015.
|
||||
- [P0386: Inline variables](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r0.pdf), 2016.
|
||||
- [Customization Point Design in C++11 and Beyond](http://ericniebler.com/2014/10/21/customization-point-design-in-c11-and-beyond/), 2014.
|
||||
- Proxy iterators in range-v3:
|
||||
- [D0022: Proxy Iterators for the Ranges Extensions](https://ericniebler.github.io/std/wg21/D0022.html).
|
||||
- [To Be or Not to Be (an Iterator)](http://ericniebler.com/2015/01/28/to-be-or-not-to-be-an-iterator/), 2015.
|
||||
- [Iterators++: Part1](http://ericniebler.com/2015/02/03/iterators-plus-plus-part-1/), 2015.
|
||||
- [Iterators++: Part2](http://ericniebler.com/2015/02/13/iterators-plus-plus-part-2/), 2015.
|
||||
- [Iterators++: Part3](http://ericniebler.com/2015/03/03/iterators-plus-plus-part-3/), 2015.
|
||||
- Metaprogramming utilities:
|
||||
- See the [meta documentation](https://ericniebler.github.io/meta/index.html), the library has changed significantly since the [2014 blog post](http://ericniebler.com/2014/11/13/tiny-metaprogramming-library/).
|
||||
- Concept emulation layer: [Concept checking in C++11](http://ericniebler.com/2013/11/23/concept-checking-in-c11/), 2013.
|
||||
- [C++Now 2014: Eric Niebler "C++11 Library Design"](https://www.youtube.com/watch?v=zgOF4NrQllo), 2014.
|
||||
|
||||
License:
|
||||
--------
|
||||
|
||||
Most of the source code in this project are mine, and those are under the Boost Software License. Parts are taken from Alex Stepanov's Elements of Programming, Howard Hinnant's libc++, and from the SGI STL. Please see the attached LICENSE file and the CREDITS file for the licensing and acknowledgments.
|
||||
|
||||
Supported Compilers
|
||||
-------------------
|
||||
|
||||
The code is known to work on the following compilers:
|
||||
|
||||
- clang 3.6.2 (or later)
|
||||
- GCC 5.0.2 (or later) (C++14 "extended constexpr" support is poor before 6.1.)
|
||||
- Clang/LLVM 6 (or later) on Windows (older versions may work - we haven't tested.)
|
||||
- Visual Studio 2019 Preview 4 (or later) on Windows, with some caveats due to range-v3's strict conformance requirements:
|
||||
- range-v3 needs `/std:c++17 /permissive-`
|
||||
- range-v3 needs a fully conforming preprocessor, so `/experimental:preprocessor` is necessary. Note that the conforming preprocessor diagnoses `C5105` "macro expansion producing 'defined' has undefined behavior" in some of the Windows SDK headers, so you'll probably want to suppress that warning with `/wd5105`.
|
||||
|
||||
[ Note: We've "retired" support for Clang/C2 with the VS2015 toolset (i.e., the `v140_clang_c2` toolset) which Microsoft no longer supports for C++ use. We no longer have CI runs, but haven't gone out of our way to break anything, so it will likely continue to work. ]
|
||||
|
||||
**Development Status:** This code is fairly stable, well-tested, and suitable for casual use, although currently lacking documentation. _In general_, no promise is made about support or long-term stability. This code *will* evolve without regard to backwards compatibility.
|
||||
|
||||
A notable exception is anything found within the `ranges::cpp20` namespace. Those components will change rarely or (preferably) never at all.
|
||||
|
||||
**Build status**
|
||||
- on Travis-CI: [![Travis Build Status](https://travis-ci.org/ericniebler/range-v3.svg?branch=master)](https://travis-ci.org/ericniebler/range-v3)
|
||||
- on AppVeyor: [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/fwl9ymc2t6ukn9qj/branch/master?svg=true)](https://ci.appveyor.com/project/ericniebler/range-v3)
|
||||
|
||||
Say Thanks!
|
||||
-----------
|
||||
|
||||
I do this work because I love it and because I love C++ and want it to be as excellent as I know it can be. If you like my work and are looking for a way to say thank you, you can leave a supportive comment on [my blog](http://ericniebler.com). Or you could leave me some kudos on my Open Hub range-v3 contribution page. Just click the **Give Kudos** button [here](https://www.openhub.net/p/range-v3/contributors/3053743222308608).
|
26
vendor/range-v3/range-v3/TODO.md
vendored
26
vendor/range-v3/range-v3/TODO.md
vendored
@ -1,26 +0,0 @@
|
||||
* Add contiguous iterator utilities. How about `is_contiguous_iterator` and `as_contiguous_range`:
|
||||
|
||||
```
|
||||
CPP_template(typename I, typename S)(
|
||||
requires RandomAccessIterator<I> &&
|
||||
SizedSentinel<S, I> &&
|
||||
is_contiguous_iterator<I>())
|
||||
subrange<std::add_pointer_t<iter_reference_t<I>>>
|
||||
as_contiguous_range(I begin, S end)
|
||||
{
|
||||
if(begin == end)
|
||||
return {nullptr, nullptr};
|
||||
else
|
||||
return {addressof(*begin), addressof(*begin) + (end - begin)};
|
||||
}
|
||||
```
|
||||
* Longer-term goals:
|
||||
- Make `inplace_merge` work with forward iterators
|
||||
- Make the sorting algorithms work with forward iterators
|
||||
|
||||
* Maybe iterators are not necessarily countable. Is there a relation between
|
||||
the ability to be able to subtract two iterators to find the distance, and
|
||||
with the existence of a DistanceType associated type? Think of:
|
||||
- counted iterators (subtractable regardless of traversal category)
|
||||
- repeat_view iterators (*not* subtractable but could be random access otherwise)
|
||||
- infinite ranges (only countable with an infinite precision integer which we lack)
|
5
vendor/range-v3/range-v3/Version.cmake
vendored
5
vendor/range-v3/range-v3/Version.cmake
vendored
@ -1,5 +0,0 @@
|
||||
# To update the range-v3 version, from a *CLEAN* working directory, update the version numbers below.
|
||||
# This makefile will generate a new version.hpp, *AMEND THE MOST RECENT COMMIT*, and git-tag the commit.
|
||||
set(RANGE_V3_MAJOR 0)
|
||||
set(RANGE_V3_MINOR 9)
|
||||
set(RANGE_V3_PATCHLEVEL 1)
|
0
vendor/range-v3/range-v3/WORKSPACE
vendored
0
vendor/range-v3/range-v3/WORKSPACE
vendored
59
vendor/range-v3/range-v3/appveyor.yml
vendored
59
vendor/range-v3/range-v3/appveyor.yml
vendored
@ -1,59 +0,0 @@
|
||||
shallow_clone: true
|
||||
|
||||
image: Visual Studio 2019
|
||||
|
||||
platform:
|
||||
- x64_x86
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- CXX: clang-cl
|
||||
CPP: latest
|
||||
|
||||
- CXX: cl
|
||||
CPP: 17
|
||||
|
||||
- CXX: cl
|
||||
CPP: latest
|
||||
|
||||
cache:
|
||||
- C:\ninja-1.8.2
|
||||
|
||||
install:
|
||||
- ps: |
|
||||
if (![IO.File]::Exists("C:\ninja-1.8.2\ninja.exe")) {
|
||||
Start-FileDownload 'https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip'
|
||||
7z x -y ninja-win.zip -oC:\ninja-1.8.2
|
||||
}
|
||||
$env:PATH="C:\ninja-1.8.2;$env:PATH"
|
||||
- for /f "tokens=1* delims=" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath') do call "%%i\VC\Auxiliary\Build\vcvarsall.bat" %PLATFORM%
|
||||
- cmake --version
|
||||
- ninja --version
|
||||
- clang-cl --version
|
||||
- cl /Bv || exit 0
|
||||
|
||||
build_script:
|
||||
- mkdir build && cd build
|
||||
- ps: |
|
||||
$env:CC=$env:CXX
|
||||
$env:HEADER_CHECK=0
|
||||
if ($env:PLATFORM -eq "x64") {
|
||||
if ($env:CONFIGURATION -eq "Debug") {
|
||||
$env:HEADER_CHECK=1
|
||||
}
|
||||
} elseif ($env:CXX -eq "clang-cl") {
|
||||
$env:CXXFLAGS='-m32'
|
||||
$env:CFLAGS='-m32'
|
||||
}
|
||||
- cmake .. -G Ninja -Wdev -DRANGE_V3_HEADER_CHECKS=%HEADER_CHECK% -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DRANGES_CXX_STD=%CPP%
|
||||
- ninja
|
||||
|
||||
test_script:
|
||||
- ctest -j2 --output-on-failure
|
||||
|
||||
deploy: off
|
79
vendor/range-v3/range-v3/c_cpp_properties.json
vendored
79
vendor/range-v3/range-v3/c_cpp_properties.json
vendored
@ -1,79 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Mac",
|
||||
"includePath": [
|
||||
"/Users/eniebler/llvm-install/include/c++/v1",
|
||||
"/usr/local/include",
|
||||
"/Users/eniebler/llvm-install/lib/clang/6.0.0/include",
|
||||
"/usr/include",
|
||||
"${workspaceRoot}/include",
|
||||
"/usr/local/opt/boost@1.67/incluse"
|
||||
],
|
||||
"defines": [],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/Users/eniebler/llvm-install/include/c++/v1",
|
||||
"/usr/local/include",
|
||||
"/Users/eniebler/llvm-install/lib/clang/6.0.0/include",
|
||||
"/usr/include",
|
||||
"${workspaceRoot}/include",
|
||||
"/usr/local/opt/boost@1.67/incluse"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"macFrameworkPath": [
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Frameworks"
|
||||
],
|
||||
"compilerPath": "/Users/eniebler/llvm-install/bin/clang++",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++11",
|
||||
"configurationProvider": "vector-of-bool.cmake-tools"
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}/include"
|
||||
],
|
||||
"defines": [],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include",
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}/include"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++11"
|
||||
},
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceRoot}/include"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE"
|
||||
],
|
||||
"intelliSenseMode": "msvc-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"${workspaceRoot}/include"
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++11"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
128
vendor/range-v3/range-v3/cmake/TestHeaders.cmake
vendored
128
vendor/range-v3/range-v3/cmake/TestHeaders.cmake
vendored
@ -1,128 +0,0 @@
|
||||
# Copyright Louis Dionne 2013-2017
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
#
|
||||
# This CMake module provides a function generating a unit test to make sure
|
||||
# that every public header can be included on its own.
|
||||
#
|
||||
# When a C++ library or application has many header files, it can happen that
|
||||
# a header does not include all the other headers it depends on. When this is
|
||||
# the case, it can happen that including that header file on its own will
|
||||
# break the compilation. This CMake module generates a dummy executable
|
||||
# comprised of many .cpp files, each of which includes a header file that
|
||||
# is part of the public API. In other words, the executable is comprised
|
||||
# of .cpp files of the form:
|
||||
#
|
||||
# #include <the/public/header.hpp>
|
||||
#
|
||||
# and then exactly one `main` function. If this succeeds to compile, it means
|
||||
# that the header can be included on its own, which is what clients expect.
|
||||
# Otherwise, you have a problem. Since writing these dumb unit tests by hand
|
||||
# is tedious and repetitive, you can use this CMake module to automate this
|
||||
# task.
|
||||
|
||||
# add_header_test(<target> [EXCLUDE_FROM_ALL] [EXCLUDE excludes...] HEADERS headers...)
|
||||
#
|
||||
# Generates header-inclusion unit tests for all the specified headers.
|
||||
#
|
||||
# This function creates a target which builds a dummy executable including
|
||||
# each specified header file individually. If this target builds successfully,
|
||||
# it means that all the specified header files can be included individually.
|
||||
#
|
||||
# Parameters
|
||||
# ----------
|
||||
# <target>:
|
||||
# The name of the target to generate.
|
||||
#
|
||||
# HEADERS headers:
|
||||
# A list of header files to generate the inclusion tests for. All headers
|
||||
# in this list must be represented as relative paths from the root of the
|
||||
# include directory added to the compiler's header search path. In other
|
||||
# words, it should be possible to include all headers in this list as
|
||||
#
|
||||
# #include <${header}>
|
||||
#
|
||||
# For example, for a library with the following structure:
|
||||
#
|
||||
# project/
|
||||
# doc/
|
||||
# test/
|
||||
# ...
|
||||
# include/
|
||||
# boost/
|
||||
# hana.hpp
|
||||
# hana/
|
||||
# transform.hpp
|
||||
# tuple.hpp
|
||||
# pair.hpp
|
||||
# ...
|
||||
#
|
||||
# When building the unit tests for that library, we'll add `-I project/include'
|
||||
# to the compiler's arguments. The list of public headers should therefore contain
|
||||
#
|
||||
# boost/hana.hpp
|
||||
# boost/hana/transform.hpp
|
||||
# boost/hana/tuple.hpp
|
||||
# boost/hana/pair.hpp
|
||||
# ...
|
||||
#
|
||||
# Usually, all the 'public' header files of a library should be tested for
|
||||
# standalone inclusion. A header is considered 'public' if a client should
|
||||
# be able to include that header on its own.
|
||||
#
|
||||
# [EXCLUDE excludes]:
|
||||
# An optional list of headers or regexes for which no unit test should be
|
||||
# generated. Basically, any header in the list specified by the `HEADERS`
|
||||
# argument that matches anything in `EXCLUDE` will be skipped.
|
||||
#
|
||||
# [EXCLUDE_FROM_ALL]:
|
||||
# If provided, the generated target is excluded from the 'all' target.
|
||||
#
|
||||
function(add_header_test target)
|
||||
cmake_parse_arguments(ARGS "EXCLUDE_FROM_ALL" # options
|
||||
"" # 1 value args
|
||||
"HEADERS;EXCLUDE" # multivalued args
|
||||
${ARGN})
|
||||
if (NOT ARGS_HEADERS)
|
||||
message(FATAL_ERROR "The `HEADERS` argument must be provided.")
|
||||
endif()
|
||||
|
||||
if (ARGS_EXCLUDE_FROM_ALL)
|
||||
set(ARGS_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL")
|
||||
else()
|
||||
set(ARGS_EXCLUDE_FROM_ALL "")
|
||||
endif()
|
||||
|
||||
foreach(header ${ARGS_HEADERS})
|
||||
set(skip FALSE)
|
||||
foreach(exclude ${ARGS_EXCLUDE})
|
||||
if (${header} MATCHES ${exclude})
|
||||
set(skip TRUE)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
if (skip)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
get_filename_component(filename "${header}" NAME_WE)
|
||||
get_filename_component(directory "${header}" DIRECTORY)
|
||||
|
||||
set(source "${CMAKE_CURRENT_BINARY_DIR}/headers/${directory}/${filename}.cpp")
|
||||
if (NOT EXISTS "${source}")
|
||||
file(WRITE "${source}" "#include <${header}>")
|
||||
endif()
|
||||
list(APPEND sources "${source}")
|
||||
endforeach()
|
||||
|
||||
set(standalone_main "${CMAKE_CURRENT_BINARY_DIR}/headers/_standalone_main.cpp")
|
||||
if (NOT EXISTS "${standalone_main}")
|
||||
file(WRITE "${standalone_main}" "int main() { }")
|
||||
endif()
|
||||
add_executable(${target}
|
||||
${ARGS_EXCLUDE_FROM_ALL}
|
||||
${sources}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/headers/_standalone_main.cpp"
|
||||
)
|
||||
endfunction()
|
@ -1,6 +0,0 @@
|
||||
#include <new>
|
||||
|
||||
int main() {
|
||||
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__ * 4) S {};
|
||||
(void) ::operator new(sizeof(S), static_cast<std::align_val_t>(alignof(S)));
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
template<class>
|
||||
concept bool True = true;
|
||||
|
||||
template<class T>
|
||||
constexpr bool test(T)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<True T>
|
||||
constexpr bool test(T)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
static_assert(::test(42), "");
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#include <experimental/coroutine>
|
||||
|
||||
struct present
|
||||
{
|
||||
struct promise_type
|
||||
{
|
||||
int result;
|
||||
|
||||
present get_return_object() { return {*this}; }
|
||||
std::experimental::suspend_never initial_suspend() { return {}; }
|
||||
std::experimental::suspend_never final_suspend() { return {}; }
|
||||
void return_value(int i) { result = i; }
|
||||
void unhandled_exception() {}
|
||||
};
|
||||
|
||||
promise_type& promise;
|
||||
|
||||
bool await_ready() const { return true; }
|
||||
void await_suspend(std::experimental::coroutine_handle<>) const {}
|
||||
int await_resume() const { return promise.result; }
|
||||
};
|
||||
|
||||
present f(int n)
|
||||
{
|
||||
if (n < 2)
|
||||
co_return 1;
|
||||
else
|
||||
co_return n * co_await f(n - 1);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
return f(5).promise.result != 120;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
# Copyright Louis Dionne 2015
|
||||
# Copyright Gonzalo Brito Gadeschi 2015
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# Setup compiler flags (more can be set on a per-target basis or in
|
||||
# subdirectories)
|
||||
|
||||
# Enable all warnings:
|
||||
ranges_append_flag(RANGES_HAS_WEVERYTHING -Weverything)
|
||||
ranges_append_flag(RANGES_HAS_PEDANTIC -pedantic)
|
||||
ranges_append_flag(RANGES_HAS_PEDANTIC_ERRORS -pedantic-errors)
|
||||
|
||||
# Selectively disable those warnings that are not worth it:
|
||||
ranges_append_flag(RANGES_HAS_WNO_CXX98_COMPAT -Wno-c++98-compat)
|
||||
ranges_append_flag(RANGES_HAS_WNO_CXX98_COMPAT_PEDANTIC -Wno-c++98-compat-pedantic)
|
||||
ranges_append_flag(RANGES_HAS_WNO_WEAK_VTABLES -Wno-weak-vtables)
|
||||
ranges_append_flag(RANGES_HAS_WNO_PADDED -Wno-padded)
|
||||
ranges_append_flag(RANGES_HAS_WNO_MISSING_PROTOTYPES -Wno-missing-prototypes)
|
||||
ranges_append_flag(RANGES_HAS_WNO_MISSING_VARIABLE_DECLARATIONS -Wno-missing-variable-declarations)
|
||||
ranges_append_flag(RANGES_HAS_WNO_DOCUMENTATION -Wno-documentation)
|
||||
ranges_append_flag(RANGES_HAS_WNO_DOCUMENTATION_UNKNOWN_COMMAND -Wno-documentation-unknown-command)
|
||||
ranges_append_flag(RANGES_HAS_WNO_OLD_STYLE_CAST -Wno-old-style-cast)
|
||||
|
||||
if (RANGES_ENV_MACOSX)
|
||||
ranges_append_flag(RANGES_HAS_WNO_GLOBAL_CONSTRUCTORS -Wno-global-constructors)
|
||||
ranges_append_flag(RANGES_HAS_WNO_EXIT_TIME_DESTRUCTORS -Wno-exit-time-destructors)
|
||||
endif()
|
||||
|
||||
if (RANGES_CXX_COMPILER_GCC)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.0")
|
||||
ranges_append_flag(RANGES_HAS_WNO_STRICT_OVERFLOW -Wno-strict-overflow)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0")
|
||||
ranges_append_flag(RANGES_HAS_WNO_MISSING_FIELD_INITIALIZERS -Wno-missing-field-initializers)
|
||||
endif()
|
||||
elseif ((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0") OR (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "7.0"))
|
||||
ranges_append_flag(RANGES_HAS_WNO_NOEXCEPT_TYPE -Wno-noexcept-type)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: test C++ flags: ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
94
vendor/range-v3/range-v3/cmake/ranges_env.cmake
vendored
94
vendor/range-v3/range-v3/cmake/ranges_env.cmake
vendored
@ -1,94 +0,0 @@
|
||||
# Copyright Gonzalo Brito Gadeschi 2015
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# Detects the C++ compiler, system, build-type, etc.
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
if("x${CMAKE_CXX_COMPILER_ID}" MATCHES "x.*Clang")
|
||||
if("x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
|
||||
set (RANGES_CXX_COMPILER_CLANGCL TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: compiler is clang-cl.")
|
||||
endif()
|
||||
else()
|
||||
set (RANGES_CXX_COMPILER_CLANG TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: compiler is clang.")
|
||||
endif()
|
||||
endif()
|
||||
elseif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set (RANGES_CXX_COMPILER_GCC TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: compiler is gcc.")
|
||||
endif()
|
||||
elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
|
||||
set (RANGES_CXX_COMPILER_MSVC TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: compiler is msvc.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "[range-v3 warning]: unknown compiler ${CMAKE_CXX_COMPILER_ID} !")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
set (RANGES_ENV_MACOSX TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: system is MacOSX.")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
set (RANGES_ENV_LINUX TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: system is Linux.")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
set (RANGES_ENV_WINDOWS TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: system is Windows.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "[range-v3 warning]: unknown system ${CMAKE_SYSTEM_NAME} !")
|
||||
endif()
|
||||
|
||||
if (RANGES_CXX_COMPILER_MSVC AND RANGES_CXX_STD LESS 17)
|
||||
# MSVC is currently supported only in 17+ mode
|
||||
set(RANGES_CXX_STD 17)
|
||||
elseif(RANGES_CXX_STD LESS 14)
|
||||
set(RANGES_CXX_STD 14)
|
||||
endif()
|
||||
|
||||
# Build type
|
||||
set(RANGES_DEBUG_BUILD FALSE)
|
||||
set(RANGES_RELEASE_BUILD FALSE)
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(RANGES_DEBUG_BUILD TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: build type is debug.")
|
||||
endif()
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(RANGES_RELEASE_BUILD TRUE)
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: build type is release.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "[range-v3 warning]: unknown build type, defaults to release!")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
set(RANGES_RELEASE_BUILD TRUE)
|
||||
endif()
|
||||
|
||||
# Find Valgrind
|
||||
find_program(MEMORYCHECK_COMMAND valgrind)
|
||||
if(MEMORYCHECK_COMMAND)
|
||||
set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full")
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: valgrind found at path: ${MEMORYCHECK_COMMAND}")
|
||||
endif()
|
||||
else()
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(WARNING "[range-v3 warning]: valgrind not found!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Doxygen)
|
||||
find_package(Git)
|
247
vendor/range-v3/range-v3/cmake/ranges_flags.cmake
vendored
247
vendor/range-v3/range-v3/cmake/ranges_flags.cmake
vendored
@ -1,247 +0,0 @@
|
||||
# Copyright Louis Dionne 2015
|
||||
# Copyright Gonzalo Brito Gadeschi 2015
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# Setup compiler flags (more can be set on a per-target basis or in
|
||||
# subdirectories)
|
||||
|
||||
# Compilation flags
|
||||
include(CheckCXXCompilerFlag)
|
||||
macro(ranges_append_flag testname flag)
|
||||
# As -Wno-* flags do not lead to build failure when there are no other
|
||||
# diagnostics, we check positive option to determine their applicability.
|
||||
# Of course, we set the original flag that is requested in the parameters.
|
||||
string(REGEX REPLACE "^-Wno-" "-W" alt ${flag})
|
||||
check_cxx_compiler_flag(${alt} ${testname})
|
||||
if (${testname})
|
||||
add_compile_options(${flag})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# All compilation flags
|
||||
# Language flag: version of the C++ standard to use
|
||||
message(STATUS "[range-v3]: C++ std=${RANGES_CXX_STD}")
|
||||
if (RANGES_CXX_COMPILER_CLANGCL OR RANGES_CXX_COMPILER_MSVC)
|
||||
ranges_append_flag(RANGES_HAS_CXXSTDCOLON "/std:c++${RANGES_CXX_STD}")
|
||||
set(RANGES_STD_FLAG "/std:c++${RANGES_CXX_STD}")
|
||||
if (RANGES_CXX_COMPILER_CLANGCL)
|
||||
ranges_append_flag(RANGES_HAS_FNO_MS_COMPATIBIILITY "-fno-ms-compatibility")
|
||||
ranges_append_flag(RANGES_HAS_FNO_DELAYED_TEMPLATE_PARSING "-fno-delayed-template-parsing")
|
||||
endif()
|
||||
# Enable "normal" warnings and make them errors:
|
||||
ranges_append_flag(RANGES_HAS_W3 /W3)
|
||||
ranges_append_flag(RANGES_HAS_WX /WX)
|
||||
else()
|
||||
ranges_append_flag(RANGES_HAS_CXXSTD "-std=c++${RANGES_CXX_STD}")
|
||||
set(RANGES_STD_FLAG "-std=c++${RANGES_CXX_STD}")
|
||||
# Enable "normal" warnings and make them errors:
|
||||
ranges_append_flag(RANGES_HAS_WALL -Wall)
|
||||
ranges_append_flag(RANGES_HAS_WEXTRA -Wextra)
|
||||
ranges_append_flag(RANGES_HAS_WERROR -Werror)
|
||||
endif()
|
||||
|
||||
if (RANGES_ENV_LINUX AND RANGES_CXX_COMPILER_CLANG)
|
||||
# On linux libc++ re-exports the system math headers. The ones from libstdc++
|
||||
# use the GCC __extern_always_inline intrinsic which is not supported by clang
|
||||
# versions 3.6, 3.7, 3.8, 3.9, 4.0, and current trunk 5.0 (as of 2017.04.13).
|
||||
#
|
||||
# This works around it by replacing __extern_always_inline with inline using a
|
||||
# macro:
|
||||
ranges_append_flag(RANGES_HAS_D__EXTERN_ALWAYS_INLINE -D__extern_always_inline=inline)
|
||||
endif()
|
||||
|
||||
# Deep integration support
|
||||
if (RANGES_DEEP_STL_INTEGRATION)
|
||||
if (RANGES_CXX_COMPILER_MSVC)
|
||||
add_compile_options(/I "${PROJECT_SOURCE_DIR}/include/std")
|
||||
else()
|
||||
add_compile_options(-isystem "${PROJECT_SOURCE_DIR}/include/std")
|
||||
endif()
|
||||
add_compile_options(-DRANGES_DEEP_STL_INTEGRATION=1)
|
||||
endif()
|
||||
|
||||
# Template diagnostic flags
|
||||
ranges_append_flag(RANGES_HAS_FDIAGNOSTIC_SHOW_TEMPLATE_TREE -fdiagnostics-show-template-tree)
|
||||
ranges_append_flag(RANGES_HAS_FTEMPLATE_BACKTRACE_LIMIT "-ftemplate-backtrace-limit=0")
|
||||
ranges_append_flag(RANGES_HAS_FMACRO_BACKTRACE_LIMIT "-fmacro-backtrace-limit=1")
|
||||
|
||||
# Clang modules support
|
||||
if (RANGES_MODULES)
|
||||
ranges_append_flag(RANGES_HAS_MODULES -fmodules)
|
||||
ranges_append_flag(RANGES_HAS_MODULE_MAP_FILE "-fmodule-map-file=${PROJECT_SOURCE_DIR}/include/module.modulemap")
|
||||
ranges_append_flag(RANGES_HAS_MODULE_CACHE_PATH "-fmodules-cache-path=${PROJECT_BINARY_DIR}/module.cache")
|
||||
if (RANGES_LIBCXX_MODULE)
|
||||
ranges_append_flag(RANGES_HAS_LIBCXX_MODULE_MAP_FILE "-fmodule-map-file=${RANGES_LIBCXX_MODULE}")
|
||||
endif()
|
||||
if (RANGES_ENV_MACOSX)
|
||||
ranges_append_flag(RANGES_HAS_NO_IMPLICIT_MODULE_MAPS -fno-implicit-module-maps)
|
||||
endif()
|
||||
if (RANGES_DEBUG_BUILD)
|
||||
ranges_append_flag(RANGES_HAS_GMODULES -gmodules)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Sanitizer support: detect incompatible sanitizer combinations
|
||||
if (RANGES_ASAN AND RANGES_MSAN)
|
||||
message(FATAL_ERROR "[range-v3 error]: AddressSanitizer and MemorySanitizer are both enabled at the same time!")
|
||||
endif()
|
||||
|
||||
if (RANGES_MSAN AND RANGES_ENV_MACOSX)
|
||||
message(FATAL_ERROR "[range-v3 error]: MemorySanitizer is not supported on MacOSX!")
|
||||
endif()
|
||||
|
||||
# AddressSanitizer support
|
||||
if (RANGES_ASAN)
|
||||
# This policy enables passing the linker flags to the linker when trying to
|
||||
# test the features, which is required to successfully link ASan binaries
|
||||
cmake_policy(SET CMP0056 NEW)
|
||||
set (ASAN_FLAGS "")
|
||||
if (RANGES_ENV_MACOSX) # LeakSanitizer not supported on MacOSX
|
||||
set (ASAN_FLAGS "-fsanitize=address,signed-integer-overflow,shift,integer-divide-by-zero,implicit-signed-integer-truncation,implicit-integer-sign-change,undefined,nullability")
|
||||
else()
|
||||
if (RANGES_CXX_COMPILER_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.0")
|
||||
set (ASAN_FLAGS "-fsanitize=address")
|
||||
else()
|
||||
set (ASAN_FLAGS "-fsanitize=address,signed-integer-overflow,shift,integer-divide-by-zero,implicit-signed-integer-truncation,implicit-integer-sign-change,leak,nullability")
|
||||
endif()
|
||||
endif()
|
||||
ranges_append_flag(RANGES_HAS_ASAN "${ASAN_FLAGS}")
|
||||
if (RANGES_HAS_ASAN) #ASAN flags must be passed to the linker:
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${ASAN_FLAGS}")
|
||||
endif()
|
||||
ranges_append_flag(RANGES_HAS_SANITIZE_NO_RECOVER "-fno-sanitize-recover=all")
|
||||
ranges_append_flag(RANGES_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer)
|
||||
endif()
|
||||
|
||||
# MemorySanitizer support
|
||||
if (RANGES_MSAN)
|
||||
# This policy enables passing the linker flags to the linker when trying to
|
||||
# compile the examples, which is required to successfully link MSan binaries
|
||||
cmake_policy(SET CMP0056 NEW)
|
||||
ranges_append_flag(RANGES_HAS_MSAN "-fsanitize=memory")
|
||||
ranges_append_flag(RANGES_HAS_MSAN_TRACK_ORIGINS -fsanitize-memory-track-origins)
|
||||
ranges_append_flag(RANGES_HAS_SANITIZE_RECOVER_ALL "-fno-sanitize-recover=all")
|
||||
ranges_append_flag(RANGES_HAS_NO_OMIT_FRAME_POINTER -fno-omit-frame-pointer)
|
||||
endif()
|
||||
|
||||
# Build types:
|
||||
if (RANGES_DEBUG_BUILD AND RANGES_RELEASE_BUILD)
|
||||
message(FATAL_ERROR "[range-v3 error] Cannot simultaneously generate debug and release builds!")
|
||||
endif()
|
||||
|
||||
if (RANGES_DEBUG_BUILD)
|
||||
ranges_append_flag(RANGES_HAS_O0 -O0)
|
||||
ranges_append_flag(RANGES_HAS_NO_INLINE -fno-inline)
|
||||
ranges_append_flag(RANGES_HAS_STACK_PROTECTOR_ALL -fstack-protector-all)
|
||||
ranges_append_flag(RANGES_HAS_G3 -g3)
|
||||
# Clang can generate debug info tuned for LLDB or GDB
|
||||
if (RANGES_CXX_COMPILER_CLANG)
|
||||
if (RANGES_ENV_MACOSX)
|
||||
ranges_append_flag(RANGES_HAS_GLLDB -glldb)
|
||||
elseif(RANGES_ENV_LINUX)
|
||||
ranges_append_flag(RANGES_HAS_GGDB -ggdb)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (RANGES_RELEASE_BUILD)
|
||||
if (NOT RANGES_ASSERTIONS)
|
||||
ranges_append_flag(RANGES_HAS_DNDEBUG -DNDEBUG)
|
||||
endif()
|
||||
if (NOT RANGES_ASAN AND NOT RANGES_MSAN)
|
||||
# The quality of ASan and MSan error messages suffers if we disable the
|
||||
# frame pointer, so leave it enabled when compiling with either of them:
|
||||
ranges_append_flag(RANGES_HAS_OMIT_FRAME_POINTER -fomit-frame-pointer)
|
||||
endif()
|
||||
|
||||
ranges_append_flag(RANGES_HAS_OFAST -Ofast)
|
||||
if (NOT RANGES_HAS_OFAST)
|
||||
ranges_append_flag(RANGES_HAS_O2 -O2)
|
||||
endif()
|
||||
ranges_append_flag(RANGES_HAS_STRICT_ALIASING -fstrict-aliasing)
|
||||
ranges_append_flag(RANGES_HAS_STRICT_VTABLE_POINTERS -fstrict-vtable-pointers)
|
||||
ranges_append_flag(RANGES_HAS_FAST_MATH -ffast-math)
|
||||
ranges_append_flag(RANGES_HAS_VECTORIZE -fvectorize)
|
||||
|
||||
if (NOT RANGES_ENV_MACOSX)
|
||||
# Sized deallocation is not available in MacOSX:
|
||||
ranges_append_flag(RANGES_HAS_SIZED_DEALLOCATION -fsized-deallocation)
|
||||
endif()
|
||||
|
||||
if (RANGES_LLVM_POLLY)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly -mllvm -polly-vectorizer=stripmine")
|
||||
endif()
|
||||
|
||||
if (RANGES_CXX_COMPILER_CLANG AND (NOT (RANGES_INLINE_THRESHOLD EQUAL -1)))
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -inline-threshold=${RANGES_INLINE_THRESHOLD}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (RANGES_NATIVE)
|
||||
ranges_append_flag(RANGES_HAS_MARCH_NATIVE "-march=native")
|
||||
ranges_append_flag(RANGES_HAS_MTUNE_NATIVE "-mtune=native")
|
||||
endif()
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS ${RANGES_STD_FLAG})
|
||||
# Probe for library and compiler support for aligned new
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/aligned_new_probe.cpp" RANGE_V3_PROBE_CODE)
|
||||
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGE_V3_ALIGNED_NEW_PROBE)
|
||||
unset(RANGE_V3_PROBE_CODE)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if (NOT RANGE_V3_ALIGNED_NEW_PROBE)
|
||||
add_compile_options("-DRANGES_CXX_ALIGNED_NEW=0")
|
||||
endif()
|
||||
|
||||
# Probe for coroutine TS support
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/coro_test_code.cpp" RANGE_V3_PROBE_CODE)
|
||||
if(RANGES_CXX_COMPILER_MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "/await")
|
||||
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGES_HAS_AWAIT)
|
||||
if(RANGES_HAS_AWAIT)
|
||||
set(RANGE_V3_COROUTINE_FLAGS "/await")
|
||||
endif()
|
||||
elseif(RANGES_CXX_COMPILER_CLANG)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fcoroutines-ts ${RANGES_STD_FLAG}")
|
||||
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGES_HAS_FCOROUTINES_TS)
|
||||
if(RANGES_HAS_FCOROUTINES_TS)
|
||||
set(RANGE_V3_COROUTINE_FLAGS "-fcoroutines-ts")
|
||||
endif()
|
||||
endif()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
unset(RANGE_V3_PROBE_CODE)
|
||||
if (RANGE_V3_COROUTINE_FLAGS)
|
||||
add_compile_options(${RANGE_V3_COROUTINE_FLAGS})
|
||||
endif()
|
||||
|
||||
# Test for concepts support
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/concepts_test_code.cpp" RANGE_V3_PROBE_CODE)
|
||||
if(RANGES_CXX_COMPILER_GCC OR RANGES_CXX_COMPILER_CLANG)
|
||||
set(CMAKE_REQUIRED_FLAGS "-fconcepts ${RANGES_STD_FLAG}")
|
||||
check_cxx_source_compiles("${RANGE_V3_PROBE_CODE}" RANGE_V3_HAS_FCONCEPTS)
|
||||
if(RANGE_V3_HAS_FCONCEPTS)
|
||||
set(RANGE_V3_CONCEPTS_FLAGS "-fconcepts")
|
||||
endif()
|
||||
endif()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
unset(RANGE_V3_PROBE_CODE)
|
||||
if (RANGE_V3_CONCEPTS_FLAGS AND RANGES_PREFER_REAL_CONCEPTS)
|
||||
add_compile_options(${RANGE_V3_CONCEPTS_FLAGS})
|
||||
endif()
|
||||
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: C++ flags: ${CMAKE_CXX_FLAGS}")
|
||||
message(STATUS "[range-v3]: C++ debug flags: ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
message(STATUS "[range-v3]: C++ Release Flags: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
message(STATUS "[range-v3]: C++ Compile Flags: ${CMAKE_CXX_COMPILE_FLAGS}")
|
||||
message(STATUS "[range-v3]: Compile options: ${COMPILE_OPTIONS_}")
|
||||
message(STATUS "[range-v3]: C Flags: ${CMAKE_C_FLAGS}")
|
||||
message(STATUS "[range-v3]: C Compile Flags: ${CMAKE_C_COMPILE_FLAGS}")
|
||||
message(STATUS "[range-v3]: EXE Linker flags: ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
message(STATUS "[range-v3]: C++ Linker flags: ${CMAKE_CXX_LINK_FLAGS}")
|
||||
message(STATUS "[range-v3]: MODULE Linker flags: ${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
get_directory_property(CMakeCompDirDefs COMPILE_DEFINITIONS)
|
||||
message(STATUS "[range-v3]: Compile Definitions: ${CmakeCompDirDefs}")
|
||||
endif()
|
@ -1,56 +0,0 @@
|
||||
# Copyright Gonzalo Brito Gadeschi 2015
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# CMake options
|
||||
|
||||
include(CMakeDependentOption)
|
||||
|
||||
set(RANGES_CXX_STD 14 CACHE STRING "C++ standard version.")
|
||||
option(RANGES_BUILD_CALENDAR_EXAMPLE "Builds the calendar example." ON)
|
||||
option(RANGES_ASAN "Run the tests using AddressSanitizer." OFF)
|
||||
option(RANGES_MSAN "Run the tests using MemorySanitizer." OFF)
|
||||
option(RANGES_ASSERTIONS "Enable assertions." ON)
|
||||
option(RANGES_DEBUG_INFO "Include debug information in the binaries." ON)
|
||||
option(RANGES_MODULES "Enables use of Clang modules (experimental)." OFF)
|
||||
option(RANGES_NATIVE "Enables -march/-mtune=native." ON)
|
||||
option(RANGES_VERBOSE_BUILD "Enables debug output from CMake." OFF)
|
||||
option(RANGES_LLVM_POLLY "Enables LLVM Polly." OFF)
|
||||
option(RANGES_PREFER_REAL_CONCEPTS
|
||||
"Use real concepts instead of emulation if the compiler supports it"
|
||||
ON)
|
||||
option(RANGES_DEEP_STL_INTEGRATION
|
||||
"Hijacks the primary std::iterator_traits template to emulate the C++20 std::ranges:: behavior."
|
||||
OFF)
|
||||
option(RANGE_V3_HEADER_CHECKS
|
||||
"Build the Range-v3 header checks and integrate with ctest"
|
||||
OFF)
|
||||
set(RANGES_INLINE_THRESHOLD -1 CACHE STRING "Force a specific inlining threshold.")
|
||||
|
||||
# Enable verbose configure when passing -Wdev to CMake
|
||||
if (DEFINED CMAKE_SUPPRESS_DEVELOPER_WARNINGS AND
|
||||
NOT CMAKE_SUPPRESS_DEVELOPER_WARNINGS)
|
||||
set(RANGES_VERBOSE_BUILD ON)
|
||||
endif()
|
||||
|
||||
if (RANGES_VERBOSE_BUILD)
|
||||
message(STATUS "[range-v3]: verbose build enabled.")
|
||||
endif()
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(RANGE_V3_TESTS
|
||||
"Build the Range-v3 tests and integrate with ctest"
|
||||
ON "${is_standalone}" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(RANGE_V3_EXAMPLES
|
||||
"Build the Range-v3 examples and integrate with ctest"
|
||||
ON "${is_standalone}" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(RANGE_V3_PERF
|
||||
"Build the Range-v3 performance benchmarks"
|
||||
ON "${is_standalone}" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(RANGE_V3_DOCS
|
||||
"Build the Range-v3 documentation"
|
||||
ON "${is_standalone}" OFF)
|
||||
|
||||
mark_as_advanced(RANGE_V3_PERF)
|
5
vendor/range-v3/range-v3/cmake/readme.md
vendored
5
vendor/range-v3/range-v3/cmake/readme.md
vendored
@ -1,5 +0,0 @@
|
||||
# CMake files overview:
|
||||
|
||||
- `ranges_options.cmake`: All options to configure the library.
|
||||
- `ranges_env.cmake`: Detects the environment: operating system, compiler, build-type, ...
|
||||
- `ranges_flags.cmake`: Sets up all compiler flags.
|
35
vendor/range-v3/range-v3/conanfile.py
vendored
35
vendor/range-v3/range-v3/conanfile.py
vendored
@ -1,35 +0,0 @@
|
||||
# Range v3 library
|
||||
#
|
||||
# Copyright Luis Martinez de Bartolome Izquierdo 2016
|
||||
#
|
||||
# Use, modification and distribution is subject to the
|
||||
# Boost Software License, Version 1.0. (See accompanying
|
||||
# file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
#
|
||||
# Project home: https://github.com/ericniebler/range-v3
|
||||
#
|
||||
|
||||
from conans import ConanFile, CMake
|
||||
|
||||
class Rangev3Conan(ConanFile):
|
||||
name = "range-v3"
|
||||
version = "0.9.0"
|
||||
license = "Boost Software License - Version 1.0 - August 17th, 2003"
|
||||
url = "https://github.com/ericniebler/range-v3"
|
||||
description = """Experimental range library for C++11/14/17"""
|
||||
# No settings/options are necessary, this is header only
|
||||
exports_sources = "include*", "LICENSE.txt", "CMakeLists.txt", "cmake/*", "Version.cmake", "version.hpp.in"
|
||||
no_copy_source = True
|
||||
|
||||
def package(self):
|
||||
cmake = CMake(self)
|
||||
cmake.definitions["RANGE_V3_TESTS"] = "OFF"
|
||||
cmake.definitions["RANGE_V3_EXAMPLES"] = "OFF"
|
||||
cmake.definitions["RANGE_V3_PERF"] = "OFF"
|
||||
cmake.definitions["RANGE_V3_DOCS"] = "OFF"
|
||||
cmake.definitions["RANGE_V3_HEADER_CHECKS"] = "OFF"
|
||||
cmake.configure()
|
||||
cmake.install()
|
||||
|
||||
self.copy("LICENSE.txt", dst="licenses", ignore_case=True, keep_path=False)
|
87
vendor/range-v3/range-v3/doc/CMakeLists.txt
vendored
87
vendor/range-v3/range-v3/doc/CMakeLists.txt
vendored
@ -1,87 +0,0 @@
|
||||
#=============================================================================
|
||||
# Setup the documentation
|
||||
#=============================================================================
|
||||
if (NOT DOXYGEN_FOUND)
|
||||
message(STATUS
|
||||
"Doxygen not found; the 'doc' and 'gh-pages.{clean,copy,update}' targets "
|
||||
"will be unavailable.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(CMAKE_FOLDER "doc")
|
||||
|
||||
configure_file(Doxyfile.in Doxyfile @ONLY)
|
||||
add_custom_target(doc.check
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile
|
||||
COMMENT "Running Doxygen to validate the documentation"
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(doc.check
|
||||
PROPERTIES FOLDER ${CMAKE_FOLDER}
|
||||
)
|
||||
|
||||
# if (NOT TARGET benchmarks)
|
||||
# message(STATUS
|
||||
# "The 'benchmarks' target is not available; the 'doc' and "
|
||||
# "'gh-pages.{clean,copy,update}' targets will be unavailable. "
|
||||
# "The 'doc.check' target can still be used to generate the "
|
||||
# "documentation to check for errors/warnings.")
|
||||
# return()
|
||||
# endif()
|
||||
|
||||
add_custom_target(doc
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
# DEPENDS benchmarks
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(doc
|
||||
PROPERTIES FOLDER ${CMAKE_FOLDER}
|
||||
)
|
||||
|
||||
if (NOT GIT_FOUND)
|
||||
message(STATUS
|
||||
"Git was not found; the 'gh-pages.{clean,copy,update}' targets "
|
||||
"will be unavailable.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_custom_target(gh-pages.clean
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/clean-gh-pages.cmake
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory search
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/gh-pages
|
||||
COMMENT "Cleaning up doc/gh-pages"
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(gh-pages.clean
|
||||
PROPERTIES FOLDER ${CMAKE_FOLDER}
|
||||
)
|
||||
|
||||
add_custom_target(gh-pages.copy
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/html ${CMAKE_CURRENT_LIST_DIR}/gh-pages
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/gh-pages
|
||||
COMMENT "Copying the documentation from ${CMAKE_CURRENT_BINARY_DIR}/html to doc/gh-pages"
|
||||
DEPENDS doc gh-pages.clean
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(gh-pages.copy
|
||||
PROPERTIES FOLDER ${CMAKE_FOLDER}
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} rev-parse --short HEAD
|
||||
OUTPUT_VARIABLE RANGE_V3_GIT_SHORT_SHA
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
add_custom_target(gh-pages.update
|
||||
COMMAND ${GIT_EXECUTABLE} add --all .
|
||||
COMMAND ${GIT_EXECUTABLE} commit -m "Update to ${RANGE_V3_GIT_SHORT_SHA}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/gh-pages
|
||||
COMMENT "Updating the gh-pages branch with freshly built documentation"
|
||||
DEPENDS gh-pages.copy
|
||||
VERBATIM
|
||||
)
|
||||
set_target_properties(gh-pages.update
|
||||
PROPERTIES FOLDER ${CMAKE_FOLDER}
|
||||
)
|
174
vendor/range-v3/range-v3/doc/Doxyfile.in
vendored
174
vendor/range-v3/range-v3/doc/Doxyfile.in
vendored
@ -1,174 +0,0 @@
|
||||
PROJECT_NAME = "Range-v3"
|
||||
PROJECT_BRIEF = "Range algorithms, views, and actions for the Standard Library"
|
||||
PROJECT_LOGO =
|
||||
PROJECT_NUMBER =
|
||||
|
||||
STRIP_FROM_PATH = @Range-v3_SOURCE_DIR@/include
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
STRIP_FROM_INC_PATH = @Range-v3_SOURCE_DIR@/include
|
||||
ALIASES =
|
||||
ENABLED_SECTIONS =
|
||||
|
||||
|
||||
# Resources
|
||||
OUTPUT_DIRECTORY =
|
||||
INPUT = @Range-v3_SOURCE_DIR@/include \
|
||||
@Range-v3_SOURCE_DIR@/doc/index.md \
|
||||
@Range-v3_SOURCE_DIR@/doc/examples.md \
|
||||
@Range-v3_SOURCE_DIR@/doc/release_notes.md
|
||||
FILE_PATTERNS = *.hpp *.md
|
||||
RECURSIVE = YES
|
||||
EXCLUDE = @Range-v3_SOURCE_DIR@/include/range/v3/detail \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/algorithm/aux_ \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/algorithm/tagspec.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/at.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/back.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/begin_end.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/data.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/distance.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/empty.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/front.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/getlines.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/index.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/istream_range.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/iterator_range.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/range_access.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/range_concepts.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/range_traits.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/size.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/span.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/to_container.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/to_container.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/associated_types.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/basic_iterator.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/common_iterator.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/concepts.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/counted_iterator.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/dangling.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/functional.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/infinity.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/invoke.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/iterator_concepts.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/iterator_traits.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/iterator.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/nullptr_v.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/semiregular.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/tagged_pair.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/tagged_tuple.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/unreachable.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/utility/view_adaptor.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/view_adaptor.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/view_facade.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/view_interface.hpp \
|
||||
@Range-v3_SOURCE_DIR@/include/range/v3/view/bounded.hpp
|
||||
EXAMPLE_PATH = @Range-v3_SOURCE_DIR@/example \
|
||||
@Range-v3_SOURCE_DIR@/test
|
||||
EXAMPLE_RECURSIVE = YES
|
||||
IMAGE_PATH = @Range-v3_BINARY_DIR@/image
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
||||
SHOW_GROUPED_MEMB_INC = YES
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
QT_AUTOBRIEF = YES
|
||||
MULTILINE_CPP_IS_BRIEF = YES
|
||||
INHERIT_DOCS = NO
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
SUBGROUPING = NO
|
||||
INLINE_GROUPED_CLASSES = NO
|
||||
INLINE_SIMPLE_STRUCTS = NO
|
||||
|
||||
# Generated formats
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
|
||||
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST = YES
|
||||
SHOW_USED_FILES = NO
|
||||
SHOW_FILES = YES
|
||||
SHOW_NAMESPACES = YES
|
||||
LAYOUT_FILE = @Range-v3_SOURCE_DIR@/doc/layout.xml
|
||||
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
HAVE_DOT = NO
|
||||
|
||||
HIDE_UNDOC_RELATIONS = NO
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = NO
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = NO
|
||||
FORCE_LOCAL_INCLUDES = NO
|
||||
INLINE_INFO = NO
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = YES
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_GROUP_NAMES = NO
|
||||
SORT_BY_SCOPE_NAME = YES
|
||||
|
||||
|
||||
ALPHABETICAL_INDEX = NO
|
||||
COLS_IN_ALPHA_INDEX = 1
|
||||
|
||||
# Preprocessing
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = YES
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH = @Range-v3_SOURCE_DIR@/include
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = RANGES_DOXYGEN_INVOKED=1 \
|
||||
META_DOXYGEN_INVOKED=1 \
|
||||
CPP_DOXYGEN_INVOKED=1 \
|
||||
"RANGES_INLINE_VARIABLE(T,N)=inline constexpr T N{};"
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
# Source browsing
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = NO
|
||||
# CLANG_ASSISTED_PARSING = NO
|
||||
# CLANG_OPTIONS =
|
||||
|
||||
# HTML output
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER =
|
||||
HTML_FOOTER =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 75 # 0 - 359
|
||||
HTML_COLORSTYLE_SAT = 100 # 0 - 255
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
HTML_TIMESTAMP = NO
|
||||
HTML_DYNAMIC_SECTIONS = YES
|
||||
HTML_INDEX_NUM_ENTRIES = 0 # Fully expand trees in the Indexes by default
|
||||
DISABLE_INDEX = YES
|
||||
GENERATE_TREEVIEW = YES
|
||||
TREEVIEW_WIDTH = 270
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# Mathjax (HTML only)
|
||||
USE_MATHJAX = NO
|
||||
MATHJAX_FORMAT = HTML-CSS
|
||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_CODEFILE =
|
@ -1,4 +0,0 @@
|
||||
FILE(GLOB gh_files "*.html" "*.js" "*.css" "*.png")
|
||||
IF( gh_files )
|
||||
execute_process( COMMAND ${CMAKE_COMMAND} -E remove ${gh_files} )
|
||||
ENDIF()
|
62
vendor/range-v3/range-v3/doc/examples.md
vendored
62
vendor/range-v3/range-v3/doc/examples.md
vendored
@ -1,62 +0,0 @@
|
||||
Examples
|
||||
========
|
||||
|
||||
\section example-algorithms Examples: Algorithms
|
||||
|
||||
\subsection example-hello Hello, Ranges!
|
||||
|
||||
\snippet hello.cpp hello
|
||||
|
||||
\subsection example-any-all-none any_of, all_of, none_of
|
||||
|
||||
\snippet any_all_none_of.cpp any_all_none_of
|
||||
|
||||
\subsection example-count count
|
||||
|
||||
\snippet count.cpp count
|
||||
|
||||
\subsection example-count_if count_if
|
||||
|
||||
\snippet count_if.cpp count_if
|
||||
|
||||
\subsection example-find find, find_if, find_if_not on sequence containers
|
||||
|
||||
\snippet find.cpp find
|
||||
|
||||
\subsection example-for_each-seq for_each on sequence containers
|
||||
|
||||
\snippet for_each_sequence.cpp for_each_sequence
|
||||
|
||||
\subsection example-for_each-assoc for_each on associative containers
|
||||
|
||||
\snippet for_each_assoc.cpp for_each_assoc
|
||||
|
||||
\subsection example-is_sorted is_sorted
|
||||
|
||||
\snippet is_sorted.cpp is_sorted
|
||||
|
||||
\section example-views Examples: Views
|
||||
|
||||
\subsection example-filter-transform Filter and transform
|
||||
|
||||
\snippet filter_transform.cpp filter_transform
|
||||
|
||||
\subsection example-accumulate-ints Generate ints and accumulate
|
||||
|
||||
\snippet accumulate_ints.cpp accumulate_ints
|
||||
|
||||
\subsection example-comprehension-conversion Convert a range comprehension to a vector
|
||||
|
||||
\snippet comprehension_conversion.cpp comprehension_conversion
|
||||
|
||||
\section example-actions Examples: Actions
|
||||
|
||||
\subsection example-sort-unique Remove non-unique elements from a container
|
||||
|
||||
\snippet sort_unique.cpp sort_unique
|
||||
|
||||
\section example-gestalt Examples: Putting it all together
|
||||
|
||||
\subsection example-calendar Calendar
|
||||
|
||||
\snippet calendar.cpp calendar
|
950
vendor/range-v3/range-v3/doc/index.md
vendored
950
vendor/range-v3/range-v3/doc/index.md
vendored
@ -1,950 +0,0 @@
|
||||
User Manual {#mainpage}
|
||||
===========
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section tutorial-preface Preface
|
||||
|
||||
--------------------------------------------
|
||||
Range library for C++14/17/20. This code is the basis of [the range support in C++20](http://eel.is/c++draft/#ranges).
|
||||
|
||||
**Development Status:**
|
||||
|
||||
This code is fairly stable, well-tested, and suitable for casual use, although
|
||||
currently lacking documentation. No promise is made about support or long-term
|
||||
stability. This code *will* evolve without regard to backwards compatibility.
|
||||
|
||||
A notable exception is anything found within the `ranges::cpp20` namespace.
|
||||
Those components will change rarely or (preferably) never at all.
|
||||
|
||||
\subsection tutorial-installation Installation
|
||||
|
||||
--------------------------------------------
|
||||
This library is header-only. You can get the source code from the
|
||||
[range-v3 repository](https://github.com/ericniebler/range-v3) on github. To
|
||||
compile with Range-v3, just `#include` the individual headers you want.
|
||||
|
||||
This distribution actually contains three separate header-only libraries:
|
||||
|
||||
* <strong><tt>include/concepts/...</tt></strong> contains the Concepts Portability Preprocessor, or
|
||||
CPP, which is a set of macros for defining and using concept checks,
|
||||
regardless of whether your compiler happens to support the C++20 concepts
|
||||
language feature or not.
|
||||
* <strong><tt>include/meta/...</tt></strong> contains the Meta Library, which is a set of
|
||||
meta-programming utilities for processing types and lists of types at compile
|
||||
time.
|
||||
* <strong><tt>include/range/...</tt></strong> contains the Range-v3 library, as described below.
|
||||
|
||||
The Range-v3 library is physically structured in directories by feature group:
|
||||
|
||||
* <strong><tt>include/range/v3/actions/...</tt></strong> contains _actions_, or composable
|
||||
components that operate eagerly on containers and return the mutated container
|
||||
for further actions.
|
||||
* <strong><tt>include/range/v3/algorithms/...</tt></strong> contains all the STL _algorithms_ with
|
||||
overloads that accept ranges, in addition to the familiar overloads that take iterators.
|
||||
* <strong><tt>include/range/v3/functional/...</tt></strong> contains many generally useful
|
||||
components that would be familiar to functional programmers.
|
||||
* <strong><tt>include/range/v3/iterator/...</tt></strong> contains the definitions of many useful
|
||||
iterators and iterator-related concepts and utilities.
|
||||
* <strong><tt>include/range/v3/numeric/...</tt></strong> contains numeric algorithms corresponding
|
||||
to those found in the standard `<numeric>` header.
|
||||
* <strong><tt>include/range/v3/range/...</tt></strong> contains range-related utilities, such as
|
||||
`begin`, `end`, and `size`, range traits and concepts, and conversions to
|
||||
containers.
|
||||
* <strong><tt>include/range/v3/utility/...</tt></strong> contains a miscellaneous assortment of
|
||||
reusable code.
|
||||
* <strong><tt>include/range/v3/view/...</tt></strong> contains _views_, or composable
|
||||
components that operate lazily on ranges and that themselves return ranges
|
||||
that can be operated upon with additional view adaptors.
|
||||
|
||||
\subsection tutorial-license License
|
||||
|
||||
--------------------------------------------
|
||||
Most of the source code in this project are mine, and those are under the Boost
|
||||
Software License. Parts are taken from Alex Stepanov's Elements of Programming,
|
||||
Howard Hinnant's libc++, and from the SGI STL. Please see the attached LICENSE
|
||||
file and the CREDITS file for the licensing and acknowledgements.
|
||||
|
||||
\subsection tutorial-compilers Supported Compilers
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
The code is known to work on the following compilers:
|
||||
|
||||
- clang 3.6.2
|
||||
- GCC 5.0.2
|
||||
- MSVC VS2017 15.9 (`_MSC_VER >= 1916`), with `/std:c++17 /permissive-`
|
||||
|
||||
\section tutorial-quick-start Quick Start
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Range-v3 is a generic library that augments the existing standard library with
|
||||
facilities for working with *ranges*. A range can be loosely thought of a pair
|
||||
of iterators, although they need not be implemented that way. Bundling begin/end
|
||||
iterators into a single object brings several benefits: convenience,
|
||||
composability, and correctness.
|
||||
|
||||
**Convenience**
|
||||
|
||||
It's more convenient to pass a single range object to an algorithm than separate
|
||||
begin/end iterators. Compare:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
std::vector<int> v{/*...*/};
|
||||
std::sort( v.begin(), v.end() );
|
||||
~~~~~~~
|
||||
|
||||
with
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
std::vector<int> v{/*...*/};
|
||||
ranges::sort( v );
|
||||
~~~~~~~
|
||||
|
||||
Range-v3 contains full implementations of all the standard algorithms with
|
||||
range-based overloads for convenience.
|
||||
|
||||
**Composability**
|
||||
|
||||
Having a single range object permits *pipelines* of operations. In a pipeline, a
|
||||
range is lazily adapted or eagerly mutated in some way, with the result
|
||||
immediately available for further adaptation or mutation. Lazy adaption is
|
||||
handled by *views*, and eager mutation is handled by *actions*.
|
||||
|
||||
For instance, the below uses _views_ to filter a container using a predicate
|
||||
and transform the resulting range with a function. Note that the underlying
|
||||
data is `const` and is not mutated by the views.
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
using namespace ranges;
|
||||
auto rng = vi | views::remove_if([](int i){ return i % 2 == 1; })
|
||||
| views::transform([](int i){ return std::to_string(i); });
|
||||
// rng == {"2","4","6","8","10"};
|
||||
~~~~~~~
|
||||
|
||||
In the code above, `rng` simply stores a reference to the underlying data and
|
||||
the filter and transformation functions. No work is done until `rng` is
|
||||
iterated.
|
||||
|
||||
In contrast, _actions_ do their work eagerly, but they also compose. Consider
|
||||
the code below, which reads some data into a vector, sorts it, and makes it
|
||||
unique.
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
extern std::vector<int> read_data();
|
||||
using namespace ranges;
|
||||
std::vector<int> vi = read_data() | actions::sort | actions::unique;
|
||||
~~~~~~~
|
||||
|
||||
Unlike views, with actions each step in the pipeline (`actions::sort` and
|
||||
`actions::unique`) accepts a container _by value_, mutates it in place, and
|
||||
returns it.
|
||||
|
||||
**Correctness**
|
||||
|
||||
Whether you are using views or actions, you are operating on data in a pure
|
||||
functional, declarative style. You rarely need to trouble yourself with
|
||||
iterators, although they are there under the covers should you need them.
|
||||
|
||||
By operating declaratively and functionally instead of imperatively, we reduce
|
||||
the need for overt state manipulation and branches and loops. This brings down
|
||||
the number of states your program can be in, which brings down your bug counts.
|
||||
|
||||
In short, if you can find a way to express your solution as a composition of
|
||||
functional transformations on your data, you can make your code _correct by
|
||||
construction_.
|
||||
|
||||
\subsection tutorial-views Views
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
As described above, the big advantage of ranges over iterators is their
|
||||
composability. They permit a functional style of programming where data is
|
||||
manipulated by passing it through a series of combinators. In addition, the
|
||||
combinators can be *lazy*, only doing work when the answer is requested, and
|
||||
*purely functional*, without mutating the original data. This makes it easier to
|
||||
reason about your code.
|
||||
|
||||
A _view_ is a lightweight wrapper that presents a view of an underlying sequence
|
||||
of elements in some custom way without mutating or copying it. Views are cheap
|
||||
to create and copy and have non-owning reference semantics. Below are some
|
||||
examples that use views:
|
||||
|
||||
Filter a container using a predicate and transform it.
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
std::vector<int> const vi{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
using namespace ranges;
|
||||
auto rng = vi | views::remove_if([](int i){return i % 2 == 1;})
|
||||
| views::transform([](int i){return std::to_string(i);});
|
||||
// rng == {"2","4","6","8","10"};
|
||||
~~~~~~~
|
||||
|
||||
Generate an infinite list of integers starting at 1, square them, take the first
|
||||
10, and sum them:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
using namespace ranges;
|
||||
int sum = accumulate(views::ints(1)
|
||||
| views::transform([](int i){return i*i;})
|
||||
| views::take(10), 0);
|
||||
~~~~~~~
|
||||
|
||||
Generate a sequence on the fly with a range comprehension and initialize a
|
||||
vector with it:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
using namespace ranges;
|
||||
auto vi =
|
||||
views::for_each(views::ints(1, 10), [](int i) {
|
||||
return yield_from(views::repeat_n(i, i));
|
||||
})
|
||||
| to<std::vector>();
|
||||
// vi == {1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,...}
|
||||
~~~~~~~
|
||||
|
||||
### View const-ness
|
||||
|
||||
Logically, a view is a factory for iterators, but in practice a view is often
|
||||
implemented as a state machine, with the state stored within the view object
|
||||
itself (to keep iterators small) and mutated as the view is iterated. Because
|
||||
the view contains mutable state, many views lack a `const`-qualified
|
||||
`begin()`/`end()`. When `const` versions of `begin()`/`end()` are provided, they
|
||||
are truly `const`; that is, thread-safe.
|
||||
|
||||
Since views present the same interface as containers, the temptation is to think
|
||||
they behave like containers with regard to `const`-ness. This is not the case.
|
||||
Their behavior with regards to `const`-ness is similar to iterators and
|
||||
pointers.
|
||||
|
||||
The `const`-ness of a view is not related to the `const`-ness of the underlying
|
||||
data. A non-`const` view may refer to elements that are themselves `const`, and
|
||||
_vice versa_. This is analogous to pointers; an `int* const` is a `const`
|
||||
pointer to a mutable `int`, and a `int const*` is a non-`const` pointer to a
|
||||
`const` `int`.
|
||||
|
||||
Use non-`const` views whenever possible. If you need thread-safety, work with
|
||||
view copies in threads; don't share.
|
||||
|
||||
### View validity
|
||||
|
||||
Any operation on the underlying range that invalidates its iterators or
|
||||
sentinels will also invalidate any view that refers to any part of that range.
|
||||
Additionally, some views (_e.g._, `views::filter`), are invalidated when the
|
||||
underlying elements of the range are mutated. It is best to recreate a view
|
||||
after any operation that may have mutated the underlying range.
|
||||
|
||||
### List of range views
|
||||
|
||||
Below is a list of the lazy range combinators, or views, that Range-v3
|
||||
provides, and a blurb about how each is intended to be used.
|
||||
|
||||
<DL>
|
||||
<DT>\link ranges::views::addressof_fn `views::addressof`\endlink</DT>
|
||||
<DD>Given a source range of lvalue references, return a new view that is the result of taking `std::addressof` of each.</DD>
|
||||
<DT>\link ranges::views::adjacent_filter_fn `views::adjacent_filter`\endlink</DT>
|
||||
<DD>For each pair of adjacent elements in a source range, evaluate the specified binary predicate. If the predicate evaluates to false, the second element of the pair is removed from the result range; otherwise, it is included. The first element in the source range is always included. (For instance, `adjacent_filter` with `std::not_equal_to` filters out all the non-unique elements.)</DD>
|
||||
<DT>\link ranges::views::adjacent_remove_if_fn `views::adjacent_remove_if`\endlink</DT>
|
||||
<DD>For each pair of adjacent elements in a source range, evaluate the specified binary predicate. If the predicate evaluates to true, the first element of the pair is removed from the result range; otherwise, it is included. The last element in the source range is always included.</DD>
|
||||
<DT>\link ranges::views::all_fn `views::all`\endlink</DT>
|
||||
<DD>Return a range containing all the elements in the source. Useful for converting containers to ranges.</DD>
|
||||
<DT>\link ranges::any_view `any_view<T>(rng)`\endlink</DT>
|
||||
<DD>Type-erased range of elements with value type `T`; can store _any_ range with this value type.</DD>
|
||||
<DT>\link ranges::views::c_str_fn `views::c_str`\endlink</DT>
|
||||
<DD>View a `\0`-terminated C string (e.g. from a `const char*`) as a range.</DD>
|
||||
<DT>\link ranges::views::cartesian_product_fn `views::cartesian_product`\endlink</DT>
|
||||
<DD>Enumerates the n-ary cartesian product of `n` ranges, i.e., generates all `n`-tuples `(e1, e2, ... , en)` where `e1` is an element of the first range, `e2` is an element of the second range, etc.</DD>
|
||||
<DT>\link ranges::views::chunk_fn `views::chunk`\endlink</DT>
|
||||
<DD>Given a source range and an integer *N*, produce a range of contiguous ranges where each inner range has *N* contiguous elements. The final range may have fewer than *N* elements.</DD>
|
||||
<DT>\link ranges::views::common_fn `views::common`\endlink</DT>
|
||||
<DD>Convert the source range to a *common* range, where the type of the `end` is the same as the `begin`. Useful for calling algorithms in the `std::` namespace.</DD>
|
||||
<DT>\link ranges::views::concat_fn `views::concat`\endlink</DT>
|
||||
<DD>Given *N* source ranges, produce a result range that is the concatenation of all of them.</DD>
|
||||
<DT>\link ranges::views::const_fn `views::const_`\endlink</DT>
|
||||
<DD>Present a `const` view of a source range.</DD>
|
||||
<DT>\link ranges::views::counted_fn `views::counted`\endlink</DT>
|
||||
<DD>Given an iterator `it` and a count `n`, create a range that starts at `it` and includes the next `n` elements.</DD>
|
||||
<DT>\link ranges::views::cycle_fn `views::cycle`\endlink</DT>
|
||||
<DD>Returns an infinite range that endlessly repeats the source range.</DD>
|
||||
<DT>\link ranges::views::delimit_fn `views::delimit`\endlink</DT>
|
||||
<DD>Given a source range and a value, return a new range that ends either at the end of the source or at the first occurrence of the value, whichever comes first. Alternatively, `views::delimit` can be called with an iterator and a value, in which case it returns a range that starts at the specified position and ends at the first occurrence of the value.</DD>
|
||||
<DT>\link ranges::views::drop_fn `views::drop`\endlink</DT>
|
||||
<DD>Given a source range and an integral count, return a range consisting of all but the first *count* elements from the source range, or an empty range if it has fewer elements.</DD>
|
||||
<DT>\link ranges::views::drop_last_fn `views::drop_last`\endlink</DT>
|
||||
<DD>Given a source range and an integral count, return a range consisting of all but the last *count* elements from the source range, or an empty range if it has fewer elements.</DD>
|
||||
<DT>\link ranges::views::drop_exactly_fn `views::drop_exactly`\endlink</DT>
|
||||
<DD>Given a source range and an integral count, return a range consisting of all but the first *count* elements from the source range. The source range must have at least that many elements.</DD>
|
||||
<DT>\link ranges::views::drop_while_fn `views::drop_while`\endlink</DT>
|
||||
<DD>Remove elements from the front of a range that satisfy a unary predicate.</DD>
|
||||
<DT>\link ranges::views::empty() `views::empty`\endlink</DT>
|
||||
<DD>Create an empty range with a given value type.</DD>
|
||||
<DT>\link ranges::views::enumerate() `views::enumerate`\endlink</DT>
|
||||
<DD>Pair each element of a range with its index.</DD>
|
||||
<DT>\link ranges::views::filter_fn `views::filter`\endlink</DT>
|
||||
<DD>Given a source range and a unary predicate, filter the elements that satisfy the predicate. (For users of Boost.Range, this is like the `filter` adaptor.)</DD>
|
||||
<DT>\link ranges::views::for_each_fn `views::for_each`\endlink</DT>
|
||||
<DD>Lazily applies an unary function to each element in the source range that returns another range (possibly empty), flattening the result.</DD>
|
||||
<DT>\link ranges::views::generate_fn `views::generate`\endlink</DT>
|
||||
<DD>Given a nullary function, return an infinite range whose elements are generated with the function.</DD>
|
||||
<DT>\link ranges::views::generate_n_fn `views::generate_n`\endlink</DT>
|
||||
<DD>Given a nullary function and a count, return a range that generates the requested number of elements by calling the function.</DD>
|
||||
<DT>\link ranges::views::group_by_fn `views::group_by`\endlink</DT>
|
||||
<DD>Given a source range and a binary predicate, return a range of ranges where each range contains contiguous elements from the source range such that the following condition holds: for each element in the range apart from the first, when that element and the first element are passed to the binary predicate, the result is true. In essence, `views::group_by` *groups* contiguous elements together with a binary predicate.</DD>
|
||||
<DT>\link ranges::views::indirect_fn `views::indirect`\endlink</DT>
|
||||
<DD>Given a source range of readable values (e.g. pointers or iterators), return a new view that is the result of dereferencing each.</DD>
|
||||
<DT>\link ranges::views::intersperse_fn `views::intersperse`\endlink</DT>
|
||||
<DD>Given a source range and a value, return a new range where the value is inserted between contiguous elements from the source.</DD>
|
||||
<DT>\link ranges::views::ints_fn `views::ints`\endlink</DT>
|
||||
<DD>Generate a range of monotonically increasing `int`s. When used without arguments, it generates the quasi-infinite range [0,1,2,3...]. It can also be called with a lower bound, or with a lower and upper bound (exclusive). An inclusive version is provided by `closed_ints`.</DD>
|
||||
<DT>\link ranges::views::iota_fn `views::iota`\endlink</DT>
|
||||
<DD>A generalization of `views::ints` that generates a sequence of monotonically increasing values of any incrementable type. When specified with a single argument, the result is an infinite range beginning at the specified value. With two arguments, the values are assumed to denote a half-open range.</DD>
|
||||
<DT>\link ranges::views::join_fn `views::join`\endlink</DT>
|
||||
<DD>Given a range of ranges, join them into a flattened sequence of elements. Optionally, you can specify a value or a range to be inserted between each source range.</DD>
|
||||
<DT>\link ranges::views::keys_fn `views::keys`\endlink</DT>
|
||||
<DD>Given a range of `pair`s (like a `std::map`), return a new range consisting of just the first element of the `pair`.</DD>
|
||||
<DT>\link ranges::views::linear_distribute_fn `views::linear_distribute`\endlink</DT>
|
||||
<DD>Distributes `n` values linearly in the closed interval `[from, to]` (the end points are always included). If `from == to`, returns `n`-times `to`, and if `n == 1` it returns `to`.</DD>
|
||||
<DT>\link ranges::views::move_fn `views::move`\endlink</DT>
|
||||
<DD>Given a source range, return a new range where each element has been has been cast to an rvalue reference.</DD>
|
||||
<DT>\link ranges::views::partial_sum_fn `views::partial_sum`\endlink</DT>
|
||||
<DD>Given a range and a binary function, return a new range where the *N*<SUP>th</SUP> element is the result of applying the function to the *N*<SUP>th</SUP> element from the source range and the (N-1)th element from the result range.</DD>
|
||||
<DT>\link ranges::views::remove_fn `views::remove`\endlink</DT>
|
||||
<DD>Given a source range and a value, filter out those elements that do not equal value.</DD>
|
||||
<DT>\link ranges::views::remove_if_fn `views::remove_if`\endlink</DT>
|
||||
<DD>Given a source range and a unary predicate, filter out those elements that do not satisfy the predicate. (For users of Boost.Range, this is like the `filter` adaptor with the predicate negated.)</DD>
|
||||
<DT>\link ranges::views::repeat_fn `views::repeat`\endlink</DT>
|
||||
<DD>Given a value, create a range that is that value repeated infinitely.</DD>
|
||||
<DT>\link ranges::views::repeat_n_fn `views::repeat_n`\endlink</DT>
|
||||
<DD>Given a value and a count, create a range that is that value repeated *count* number of times.</DD>
|
||||
<DT>\link ranges::views::replace_fn `views::replace`\endlink</DT>
|
||||
<DD>Given a source range, a source value and a target value, create a new range where all elements equal to the source value are replaced with the target value.</DD>
|
||||
<DT>\link ranges::views::replace_if_fn `views::replace_if`\endlink</DT>
|
||||
<DD>Given a source range, a unary predicate and a target value, create a new range where all elements that satisfy the predicate are replaced with the target value.</DD>
|
||||
<DT>\link ranges::views::reverse_fn `views::reverse`\endlink</DT>
|
||||
<DD>Create a new range that traverses the source range in reverse order.</DD>
|
||||
<DT>\link ranges::views::sample_fn `views::sample`\endlink</DT>
|
||||
<DD>Returns a random sample of a range of length `size(range)`.</DD>
|
||||
<DT>\link ranges::views::single_fn `views::single`\endlink</DT>
|
||||
<DD>Given a value, create a range with exactly one element.</DD>
|
||||
<DT>\link ranges::views::slice_fn `views::slice`\endlink</DT>
|
||||
<DD>Give a source range a lower bound (inclusive) and an upper bound (exclusive), create a new range that begins and ends at the specified offsets. Both the begin and the end can be integers relative to the front, or relative to the end with "`end-2`" syntax.</DD>
|
||||
<DT>\link ranges::views::sliding_fn `views::sliding`\endlink</DT>
|
||||
<DD>Given a range and a count `n`, place a window over the first `n` elements of the underlying range. Return the contents of that window as the first element of the adapted range, then slide the window forward one element at a time until hitting the end of the underlying range.</DD>
|
||||
<DT>\link ranges::views::split_fn `views::split`\endlink</DT>
|
||||
<DD>Given a source range and a delimiter specifier, split the source range into a range of ranges using the delimiter specifier to find the boundaries. The delimiter specifier can be an element or a range of elements. The elements matching the delimiter are excluded from the resulting range of ranges.</DD>
|
||||
<DT>\link ranges::views::split_when_fn `views::split_when`\endlink</DT>
|
||||
<DD>Given a source range and a delimiter specifier, split the source range into a range of ranges using the delimiter specifier to find the boundaries. The delimiter specifier can be a predicate or a function. The predicate should take a single argument of the range's reference type and return `true` if and only if the element is part of a delimiter. The function should accept an iterator and sentinel indicating the current position and end of the source range and return `std::make_pair(true, iterator_past_the_delimiter)` if the current position is a boundary; otherwise `std::make_pair(false, ignored_iterator_value)`. The elements matching the delimiter are excluded from the resulting range of ranges.</DD>
|
||||
<DT>\link ranges::views::stride_fn `views::stride`\endlink</DT>
|
||||
<DD>Given a source range and an integral stride value, return a range consisting of every *N*<SUP>th</SUP> element, starting with the first.</DD>
|
||||
<DT>\link ranges::views::tail_fn `views::tail`\endlink</DT>
|
||||
<DD>Given a source range, return a new range without the first element. The range must have at least one element.</DD>
|
||||
<DT>\link ranges::views::take_fn `views::take`\endlink</DT>
|
||||
<DD>Given a source range and an integral count, return a range consisting of the first *count* elements from the source range, or the complete range if it has fewer elements. (The result of `views::take` is not a `sized_range`.)</DD>
|
||||
<DT>\link ranges::views::take_exactly_fn `views::take_exactly`\endlink</DT>
|
||||
<DD>Given a source range and an integral count, return a range consisting of the first *count* elements from the source range. The source range must have at least that many elements. (The result of `views::take_exactly` is a `sized_range`.)</DD>
|
||||
<DT>\link ranges::views::take_last_fn `views::take_last`\endlink</DT>
|
||||
<DD>Given a source range and an integral count, return a range consisting of the last *count* elements from the source range. The source range must be a `sized_range`. If the source range does not have at least *count* elements, the full range is returned.</DD>
|
||||
<DT>\link ranges::views::take_while_fn `views::take_while`\endlink</DT>
|
||||
<DD>Given a source range and a unary predicate, return a new range consisting of the elements from the front that satisfy the predicate.</DD>
|
||||
<DT>\link ranges::views::tokenize_fn `views::tokenize`\endlink</DT>
|
||||
<DD>Given a source range and optionally a submatch specifier and a `std::regex_constants::match_flag_type`, return a `std::regex_token_iterator` to step through the regex submatches of the source range. The submatch specifier may be either a plain `int`, a `std::vector<int>`, or a `std::initializer_list<int>`.</DD>
|
||||
<DT>\link ranges::views::transform_fn `views::transform`\endlink</DT>
|
||||
<DD>Given a source range and a unary function, return a new range where each result element is the result of applying the unary function to a source element.</DD>
|
||||
<DT>\link ranges::views::trim_fn `views::trim`\endlink</DT>
|
||||
<DD>Given a source bidirectional range and a unary predicate, return a new range without the front and back elements that satisfy the predicate.</DD>
|
||||
<DT>\link ranges::views::unbounded_fn `views::unbounded`\endlink</DT>
|
||||
<DD>Given an iterator, return an infinite range that begins at that position.</DD>
|
||||
<DT>\link ranges::views::unique_fn `views::unique`\endlink</DT>
|
||||
<DD>Given a range, return a new range where all consecutive elements that compare equal save the first have been filtered out.</DD>
|
||||
<DT>\link ranges::views::values_fn `views::values`\endlink</DT>
|
||||
<DD>Given a range of `pair`s (like a `std::map`), return a new range consisting of just the second element of the `pair`.</DD>
|
||||
<DT>\link ranges::views::zip_fn `views::zip`\endlink</DT>
|
||||
<DD>Given *N* ranges, return a new range where *M*<SUP>th</SUP> element is the result of calling `make_tuple` on the *M*<SUP>th</SUP> elements of all *N* ranges.</DD>
|
||||
<DT>\link ranges::views::zip_with_fn `views::zip_with`\endlink</DT>
|
||||
<DD>Given *N* ranges and a *N*-ary function, return a new range where *M*<SUP>th</SUP> element is the result of calling the function on the *M*<SUP>th</SUP> elements of all *N* ranges.</DD>
|
||||
</DL>
|
||||
|
||||
\subsection tutorial-actions Actions
|
||||
|
||||
--------------------------------------------------------------
|
||||
When you want to mutate a container in-place, or forward it through a chain of
|
||||
mutating operations, you can use actions. The following examples should make it
|
||||
clear.
|
||||
|
||||
Read data into a vector, sort it, and make it unique.
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
extern std::vector<int> read_data();
|
||||
using namespace ranges;
|
||||
std::vector<int> vi = read_data() | actions::sort | actions::unique;
|
||||
~~~~~~~
|
||||
|
||||
Do the same to a `vector` that already contains some data:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
vi = std::move(vi) | actions::sort | actions::unique;
|
||||
~~~~~~~
|
||||
|
||||
Mutate the container in-place:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
vi |= actions::sort | actions::unique;
|
||||
~~~~~~~
|
||||
|
||||
Same as above, but with function-call syntax instead of pipe syntax:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
actions::unique(actions::sort(vi));
|
||||
~~~~~~~
|
||||
|
||||
### List of range actions
|
||||
|
||||
Below is a list of the eager range combinators, or actions, that Range-v3
|
||||
provides, and a blurb about how each is intended to be used.
|
||||
|
||||
<DL>
|
||||
<DT>\link ranges::actions::drop_fn `actions::drop`\endlink</DT>
|
||||
<DD>Removes the first `N` elements of the source range.</DD>
|
||||
<DT>\link ranges::actions::drop_while_fn `actions::drop_while`\endlink</DT>
|
||||
<DD>Removes the first elements of the source range that satisfy the unary predicate.</DD>
|
||||
<DT>`actions::erase`</DT>
|
||||
<DD>Removes all elements in the sub-range of the source (range version) or all elements after position.</DD>
|
||||
<DT>`actions::insert`</DT>
|
||||
<DD>Inserts all elements of the range into the source at position.</DD>
|
||||
<DT>\link ranges::actions::join_fn `actions::join`\endlink</DT>
|
||||
<DD>Flattens a range of ranges.</DD>
|
||||
<DT> `actions::push_back`</DT>
|
||||
<DD>Appends elements to the tail of the source.</DD>
|
||||
<DT>`actions::push_front`</DT>
|
||||
<DD>Appends elements before the head of the source.</DD>
|
||||
<DT>\link ranges::actions::remove_if_fn `actions::remove_if`\endlink</DT>
|
||||
<DD>Removes all elements from the source that satisfy the predicate.</DD>
|
||||
<DT>\link ranges::actions::remove_fn `actions::remove`\endlink</DT>
|
||||
<DD>Removes all elements from the source that are equal to value.</DD>
|
||||
<DT>\link ranges::actions::reverse_fn `actions::reverse`\endlink</DT>
|
||||
<DD>Reverses all the elements in the container.</DD>
|
||||
<DT>\link ranges::actions::shuffle_fn `actions::shuffle`\endlink</DT>
|
||||
<DD>Shuffles the source range.</DD>
|
||||
<DT>\link ranges::actions::slice_fn `actions::slice`\endlink</DT>
|
||||
<DD>Removes all elements from the source that are not part of the sub-range.</DD>
|
||||
<DT>\link ranges::actions::sort_fn `actions::sort`\endlink</DT>
|
||||
<DD>Sorts the source range (unstable).</DD>
|
||||
<DT>\link ranges::actions::split_fn `actions::split`\endlink</DT>
|
||||
<DD>Split a range into a sequence of subranges using a delimiter (a value, a sequence of values, a predicate, or a binary function returning a `pair<bool, N>`).</DD>
|
||||
<DT>\link ranges::actions::stable_sort_fn `actions::stable_sort`\endlink</DT>
|
||||
<DD>Sorts the source range (stable).</DD>
|
||||
<DT>\link ranges::actions::stride_fn `actions::stride`\endlink</DT>
|
||||
<DD>Removes all elements whose position does not match the stride.</DD>
|
||||
<DT>\link ranges::actions::take_fn `actions::take`\endlink</DT>
|
||||
<DD>Keeps the first `N`-th elements of the range, removes the rest.</DD>
|
||||
<DT>\link ranges::actions::take_while_fn `actions::take_while`\endlink</DT>
|
||||
<DD>Keeps the first elements that satisfy the predicate, removes the rest.</DD>
|
||||
<DT>\link ranges::actions::transform_fn `actions::transform`\endlink</DT>
|
||||
<DD>Replaces elements of the source with the result of the unary function.</DD>
|
||||
<DT>\link ranges::actions::unique_fn `actions::unique`\endlink</DT>
|
||||
<DD>Removes adjacent elements of the source that compare equal. If the source is sorted, removes all duplicate elements.</DD>
|
||||
<DT>\link ranges::actions::unstable_remove_if_fn `actions::unstable_remove_if`\endlink</DT>
|
||||
<DD>Much faster (each element remove has constant time complexity), unordered version of `remove_if`. Requires bidirectional container.</DD>
|
||||
</DL>
|
||||
|
||||
|
||||
\subsection tutorial-utilities Utilities
|
||||
|
||||
----------------------------------------------
|
||||
Below we cover some utilities that range-v3 provides for creating your own
|
||||
view adaptors and iterators.
|
||||
|
||||
### Create Custom Views with view_facade
|
||||
|
||||
Range-v3 provides a utility for easily creating your own range types, called
|
||||
\link ranges::view_facade `ranges::view_facade`\endlink. The code below uses
|
||||
`view_facade` to create a range that traverses a null-terminated string:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
#include <range/v3/all.hpp>
|
||||
|
||||
// A range that iterates over all the characters in a
|
||||
// null-terminated string.
|
||||
class c_string_range
|
||||
: public ranges::view_facade<c_string_range>
|
||||
{
|
||||
friend ranges::range_access;
|
||||
char const * sz_ = "";
|
||||
char const & read() const { return *sz_; }
|
||||
bool equal(ranges::default_sentinel_t) const { return *sz_ == '\0'; }
|
||||
void next() { ++sz_; }
|
||||
public:
|
||||
c_string_range() = default;
|
||||
explicit c_string_range(char const *sz) : sz_(sz)
|
||||
{
|
||||
assert(sz != nullptr);
|
||||
}
|
||||
};
|
||||
~~~~~~~
|
||||
|
||||
The `view_facade` class generates an iterator and begin/end member functions
|
||||
from the minimal interface provided by `c_string_range`. This is an example of a
|
||||
very simple range for which it is not necessary to separate the range itself
|
||||
from the thing that iterates the range. Future examples will show examples of
|
||||
more sophisticated ranges.
|
||||
|
||||
With `c_string_range`, you can now use algorithms to operate on null-terminated
|
||||
strings, as below:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
c_string_range r("hello world");
|
||||
// Iterate over all the characters and print them out
|
||||
ranges::for_each(r, [](char ch){
|
||||
std::cout << ch << ' ';
|
||||
});
|
||||
// prints: h e l l o w o r l d
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
### Create Custom Views with view_adaptor
|
||||
|
||||
Often, a new range type is most easily expressed by adapting an existing range
|
||||
type. That's the case for many of the range views provided by the Range-v3
|
||||
library; for example, the `views::remove_if` and `views::transform` views. These
|
||||
are rich types with many moving parts, but thanks to a helper class called
|
||||
\link ranges::view_adaptor `ranges::view_adaptor`\endlink, they aren't hard
|
||||
to write.
|
||||
|
||||
Below in roughly 2 dozen lines of code is the `transform` view, which takes one
|
||||
range and transforms all the elements with a unary function.
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
#include <range/v3/all.hpp>
|
||||
|
||||
// A class that adapts an existing range with a function
|
||||
template<class Rng, class Fun>
|
||||
class transform_view
|
||||
: public ranges::view_adaptor<transform_view<Rng, Fun>, Rng>
|
||||
{
|
||||
friend ranges::range_access;
|
||||
ranges::semiregular_box_t<Fun> fun_; // Make Fun model semiregular if it doesn't
|
||||
class adaptor : public ranges::adaptor_base
|
||||
{
|
||||
ranges::semiregular_box_t<Fun> fun_;
|
||||
public:
|
||||
adaptor() = default;
|
||||
adaptor(ranges::semiregular_box_t<Fun> const &fun) : fun_(fun) {}
|
||||
// Here is where we apply Fun to the elements:
|
||||
auto read(ranges::iterator_t<Rng> it) const -> decltype(fun_(*it))
|
||||
{
|
||||
return fun_(*it);
|
||||
}
|
||||
};
|
||||
adaptor begin_adaptor() const { return {fun_}; }
|
||||
adaptor end_adaptor() const { return {fun_}; }
|
||||
public:
|
||||
transform_view() = default;
|
||||
transform_view(Rng && rng, Fun fun)
|
||||
: transform_view::view_adaptor{std::forward<Rng>(rng)}
|
||||
, fun_(std::move(fun))
|
||||
{}
|
||||
};
|
||||
|
||||
template<class Rng, class Fun>
|
||||
transform_view<Rng, Fun> transform(Rng && rng, Fun fun)
|
||||
{
|
||||
return {std::forward<Rng>(rng), std::move(fun)};
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
Range transformation is achieved by defining a nested `adaptor` class that
|
||||
handles the transformation, and then defining `begin_adaptor` and `end_adaptor`
|
||||
members that return adaptors for the begin iterator and the end sentinel,
|
||||
respectively. The `adaptor` class has a `read` member that performs the
|
||||
transformation. It is passed an iterator to the current element. Other members
|
||||
are available for customization: `equal`, `next`, `prev`, `advance`, and
|
||||
`distance_to`; but the transform adaptor accepts the defaults defined in
|
||||
\link ranges::adaptor_base `ranges::adaptor_base`\endlink.
|
||||
|
||||
With `transform_view`, we can print out the first 20 squares:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
int main()
|
||||
{
|
||||
auto squares = ::transform(views::ints(1), [](int i){return i*i;});
|
||||
for(int i : squares | views::take(20))
|
||||
std::cout << i << ' ';
|
||||
std::cout << '\n';
|
||||
// prints 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
The `transform_view` defined above is an input range when it is wrapping an
|
||||
input range, a forward range when it's wrapping a forward range, etc. That happens
|
||||
because of smart defaults defined in the `adaptor_base` class that frees you
|
||||
from having to deal with a host of niggly detail when implementing iterators.
|
||||
|
||||
*(Note: the above `transform_view` always stores a copy of the function in the
|
||||
sentinel. That is only necessary if the underlying range's sentinel type models
|
||||
bidirectional_iterator. That's a finer point that you shouldn't worry about right
|
||||
now.)*
|
||||
|
||||
#### view_adaptor in details
|
||||
|
||||
Each `view_adaptor` contains `base()` member in view and iterator.
|
||||
`base()` - allow to access "adapted" range/iterator:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
std::vector<int> vec;
|
||||
auto list = vec | views::transfom([](int i){ return i+1; });
|
||||
|
||||
assert( vec.begin() == list.begin().base() );
|
||||
assert( vec.begin() == list.base().begin() );
|
||||
~~~~~~~
|
||||
|
||||
Like `basic_iterator`'s `cursor`, `view_adaptor`'s `adaptor` can contain mixin class too,
|
||||
to inject things into the public interface of the iterator:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
class adaptor : public ranges::adaptor_base
|
||||
{
|
||||
template<class base_mixin>
|
||||
struct mixin : base_mixin
|
||||
{
|
||||
// everything inside this class will be accessible from iterator
|
||||
using base_mixin::base_mixin;
|
||||
|
||||
auto& base_value() const
|
||||
{
|
||||
return *this->base();
|
||||
}
|
||||
|
||||
int get_i() const
|
||||
{
|
||||
return this->get().i;
|
||||
}
|
||||
};
|
||||
|
||||
int i = 100;
|
||||
};
|
||||
~~~~~~~
|
||||
|
||||
From within mixin you can call:
|
||||
|
||||
* `get()` - to access adaptor internals
|
||||
* `base()` - to access adaptable iterator
|
||||
|
||||
Iterator/sentinel adaptor may "override" the following members:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
class adaptor : public ranges::adaptor_base
|
||||
{
|
||||
// !For begin_adaptor only!
|
||||
template<typename Rng>
|
||||
constexpr auto begin(Rng &rng)
|
||||
{
|
||||
return ranges::begin(rng.base());
|
||||
}
|
||||
|
||||
// !For end_adaptor only!
|
||||
template<typename Rng>
|
||||
constexpr auto end(Rng &rng)
|
||||
{
|
||||
return ranges::end(rng.base());
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
bool equal(I const &this_iter, I const &that_iter) const
|
||||
{
|
||||
return this_iter == that_iter;
|
||||
}
|
||||
// or
|
||||
template<typename I>
|
||||
bool equal(I const &this_iter, I const &that_iter, adaptor const &that_adapt) const
|
||||
{
|
||||
return
|
||||
*this.some_value == that_adapt.some_value
|
||||
&& this_iter == that_iter;
|
||||
}
|
||||
|
||||
// !For end_adaptor only!
|
||||
// Same as equal, but compare iterator with sentinel.
|
||||
// Not used, if iterator same as sentinel, and both have the same adaptor.
|
||||
template<typename I, typename S>
|
||||
constexpr bool empty(I const &it, S const &end) const
|
||||
{
|
||||
return it == end;
|
||||
}
|
||||
// or
|
||||
template<typename I, typename S, typename SA>
|
||||
constexpr bool empty(I const &it, S const &end, SA const &end_adapt) const
|
||||
{
|
||||
return
|
||||
*this.some_value == end_adapt.some_value
|
||||
&& it == end;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
reference_t<I> read(I const &it)
|
||||
{
|
||||
return *it;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
void next(I &it)
|
||||
{
|
||||
++it;
|
||||
}
|
||||
|
||||
// !For bidirectional iterator only!
|
||||
template<typename I>
|
||||
void prev(I &it)
|
||||
{
|
||||
--it;
|
||||
}
|
||||
|
||||
// !For random access iterator only!
|
||||
template<typename I>
|
||||
void advance(I &it, difference_type_t<I> n)
|
||||
{
|
||||
it += n;
|
||||
}
|
||||
|
||||
// !For "sized" iterators only!
|
||||
template<typename I>
|
||||
difference_type_t<I> distance_to(I const &this_iter, I const &that_iter)
|
||||
{
|
||||
return that_iter - this_iter;
|
||||
}
|
||||
// or
|
||||
template<typename I>
|
||||
difference_type_t<I> distance_to
|
||||
(I const &this_iter, I const &that_iter, adaptor const &that_adapt)
|
||||
{
|
||||
return that_iter - this_iter;
|
||||
}
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
As you can see, some "overrides" have effect only for `begin_adaptor` or
|
||||
`end_adaptor`. In order to use full potential of adaptor, you need to have
|
||||
separate adaptors for begin and end:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
struct adaptor : adaptor_base
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
void next(iterator_t<Rng>& it)
|
||||
{
|
||||
++n;
|
||||
++it;
|
||||
}
|
||||
};
|
||||
|
||||
struct sentinel_adaptor : adaptor_base
|
||||
{
|
||||
int stop_at;
|
||||
bool empty(const iterator_t<Rng>&, const adaptor& ia, const sentinel_t<Rng>& s) const
|
||||
{
|
||||
return ia.n == stop_at;
|
||||
}
|
||||
};
|
||||
|
||||
adaptor begin_adaptor() const { return {}; }
|
||||
sentinel_adaptor end_adaptor() const { return {100}; }
|
||||
~~~~~~~
|
||||
|
||||
Sometimes, you can use the same adaptor for both `begin_adaptor` and `end_adaptor`:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
struct adaptor : adaptor_base
|
||||
{
|
||||
int n = 0;
|
||||
void next(iterator_t<Rng>& it)
|
||||
{
|
||||
++n;
|
||||
++it;
|
||||
}
|
||||
|
||||
// pay attention, we use equal, not empty. empty() will never trigger.
|
||||
template<typename I>
|
||||
bool equal(I const &this_iter, I const &that_iter, adaptor const &that_adapt) const
|
||||
{
|
||||
return *this.n == that_adapt.n;
|
||||
}
|
||||
};
|
||||
|
||||
adaptor begin_adaptor() const { return {}; }
|
||||
adaptor end_adaptor() const { return {100}; }
|
||||
~~~~~~~
|
||||
|
||||
Note that all the data you store in the adaptor will become part of the iterator.
|
||||
|
||||
If you will not "override" `begin_adaptor()` or/and `end_adaptor()` in your view_adaptor, default ones will be used.
|
||||
|
||||
### Create Custom Iterators with basic_iterator
|
||||
|
||||
Here is an example of Range-v3 compatible random access proxy iterator.
|
||||
The iterator returns a key/value pair, like the `zip` view.
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
#include <range/v3/iterator/basic_iterator.hpp>
|
||||
#include <range/v3/utility/common_tuple.hpp>
|
||||
|
||||
using KeyIter = typename std::vector<Key>::iterator;
|
||||
using ValueIter = typename std::vector<Value>::iterator;
|
||||
|
||||
struct cursor
|
||||
{
|
||||
// basic_iterator derives from "mixin", if present, so it can be used
|
||||
// to inject things into the public interface of the iterator
|
||||
struct mixin;
|
||||
|
||||
// This is for dereference operator.
|
||||
using value_type = std::pair<Key, Value>;
|
||||
ranges::common_pair<Key&, Value&> read() const
|
||||
{
|
||||
return { *key_iterator, *value_iterator };
|
||||
}
|
||||
|
||||
bool equal(const cursor& other) const
|
||||
{
|
||||
return key_iterator == other.key_iterator;
|
||||
}
|
||||
|
||||
void next()
|
||||
{
|
||||
++key_iterator;
|
||||
++value_iterator;
|
||||
}
|
||||
|
||||
// prev optional. Required for Bidirectional iterator
|
||||
void prev()
|
||||
{
|
||||
--key_iterator;
|
||||
--value_iterator;
|
||||
}
|
||||
|
||||
// advance and distance_to are optional. Required for random access iterator
|
||||
void advance(std::ptrdiff_t n)
|
||||
{
|
||||
key_iterator += n;
|
||||
value_iterator += n;
|
||||
}
|
||||
std::ptrdiff_t distance_to(const cursor& other) const
|
||||
{
|
||||
return other.key_iterator - this->key_iterator;
|
||||
}
|
||||
|
||||
cursor() = default;
|
||||
cursor(KeyIter key_iterator, ValueIter value_iterator)
|
||||
: key_iterator(key_iterator)
|
||||
, value_iterator(value_iterator)
|
||||
{}
|
||||
|
||||
KeyIter key_iterator;
|
||||
ValueIter value_iterator;
|
||||
};
|
||||
|
||||
struct cursor::mixin : ranges::basic_mixin<cursor>
|
||||
{
|
||||
using ranges::basic_mixin<cursor>::basic_mixin;
|
||||
|
||||
// It is necessary to expose constructor in this way
|
||||
mixin(KeyIter key_iterator, ValueIter value_iterator)
|
||||
: mixin{ cursor(key_iterator, value_iterator) }
|
||||
{}
|
||||
|
||||
KeyIter key_iterator()
|
||||
{
|
||||
return this->get().key_iterator;
|
||||
}
|
||||
ValueIter value_iterator()
|
||||
{
|
||||
return this->get().value_iterator;
|
||||
}
|
||||
};
|
||||
|
||||
using iterator = ranges::basic_iterator<cursor>;
|
||||
|
||||
void test()
|
||||
{
|
||||
std::vector<Key> keys = {1};
|
||||
std::vector<Value> values = {10};
|
||||
|
||||
iterator iter(keys.begin(), values.begin());
|
||||
ranges::common_pair<Key&, Value&> pair = *iter;
|
||||
Key& key = pair.first;
|
||||
Value& value = pair.second;
|
||||
|
||||
assert(&key == &keys[0]);
|
||||
assert(&value == &values[0]);
|
||||
|
||||
auto key_iter = iter.key_iterator();
|
||||
assert(key_iter == keys.begin());
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
`read()` returns references. But the default for `value_type`, which is
|
||||
`decay_t<decltype(read())>`, is `common_pair<Key&, Value&>`. That is not correct
|
||||
in our case. It should be `pair<Key, Value>`, so we explicitly specify
|
||||
`value_type`.
|
||||
|
||||
`ranges::common_pair` has conversions:
|
||||
|
||||
> `ranges::common_pair<Key&, Value&>` ↔ `ranges::common_pair<Key, Value>`.
|
||||
|
||||
All `ranges::common_pair`s converts to their `std::pair` equivalents, also.
|
||||
|
||||
For more information, see [http://wg21.link/P0186#basic-iterators-iterators.basic](http://wg21.link/P0186#basic-iterators-iterators.basic)
|
||||
|
||||
\subsection tutorial-concepts Concept Checking
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
The Range-v3 library makes heavy use of concepts to constrain functions, control
|
||||
overloading, and check type constraints at compile-time. It achieves this with
|
||||
the help of a Concepts emulation layer that works on any standard-conforming
|
||||
C++14 compiler. The library provides many useful concepts, both for the core
|
||||
language and for iterators and ranges. You can use the concepts framework to
|
||||
constrain your own code.
|
||||
|
||||
For instance, if you would like to write a function that takes an
|
||||
iterator/sentinel pair, you can write it like this:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
CPP_template(class Iter, class Sent, class Comp = /*...some_default..*/)
|
||||
(requires sentinel_for<Sent, Iter>)
|
||||
void my_algorithm(Iter first, Sent last, Comp comp = Comp{})
|
||||
{
|
||||
// ...
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
You can then add an overload that take a Range:
|
||||
|
||||
~~~~~~~{.cpp}
|
||||
CPP_template(class Rng, class Comp = /*...some_default..*/)
|
||||
(requires range<Rng>)
|
||||
void my_algorithm(Rng && rng, Comp comp = Comp{})
|
||||
{
|
||||
return my_algorithm(ranges::begin(rng), ranges::end(rng));
|
||||
}
|
||||
~~~~~~~
|
||||
|
||||
With the type constraints expressed with the `CPP_template` macro, these
|
||||
two overloads are guaranteed to not be ambiguous. When compiling with C++20
|
||||
concepts support, this uses real concept checks. On legacy compilers, it falls
|
||||
back to using `std::enable_if`.
|
||||
|
||||
\subsection tutorial-future Range-v3 and the Future
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Range-v3 formed the basis for the
|
||||
[Technical Specification on Ranges](https://www.iso.org/standard/70910.html),
|
||||
which has since been merged into the working draft of C++20.
|
||||
|
||||
In addition, a subset of range-v3's views are also a part of the C++20 working
|
||||
draft, with more slated for eventual inclusion in future versions of C++.
|
||||
|
||||
The actions, as well as various utilities, have not yet been reviewed by the
|
||||
Committee, although the basic direction has already passed an initial review.
|
||||
|
225
vendor/range-v3/range-v3/doc/layout.xml
vendored
225
vendor/range-v3/range-v3/doc/layout.xml
vendored
@ -1,225 +0,0 @@
|
||||
<doxygenlayout version="1.0">
|
||||
<!-- Generated by doxygen 1.8.7 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title=""/>
|
||||
<tab type="pages" visible="yes" title="" intro=""/>
|
||||
<tab type="modules" visible="yes"
|
||||
title="Reference"
|
||||
intro="
|
||||
The reference documentation is split into logical modules, as documented in
|
||||
the user manual."/>
|
||||
|
||||
<tab type="usergroup" visible="yes" title="Indexes">
|
||||
<tab type="classmembers" visible="yes"
|
||||
title="Methods"
|
||||
intro="
|
||||
This is an index of all the methods with links to the
|
||||
corresponding documentation."/>
|
||||
|
||||
<tab type="classlist" visible="yes"
|
||||
title="Classes"
|
||||
intro="
|
||||
This is an index of all the classes in the library with links to the
|
||||
corresponding documentation."/>
|
||||
|
||||
<tab type="filelist" visible="yes"
|
||||
title="Files"
|
||||
intro=""/>
|
||||
|
||||
</tab>
|
||||
|
||||
<!-- We don't use what's below -->
|
||||
<tab type="namespaces" visible="no" title="">
|
||||
<tab type="namespacelist" visible="no" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
|
||||
<tab type="examples" visible="no" title="" intro=""/>
|
||||
|
||||
<tab type="classes" visible="no" title="">
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="no" title="" intro=""/>
|
||||
</tab>
|
||||
|
||||
<tab type="files" visible="no" title="Files">
|
||||
<tab type="globals" visible="no" title="Globals" intro=""/>
|
||||
</tab>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="no"/>
|
||||
<detaileddescription title="Description"/>
|
||||
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
|
||||
<memberdecl>
|
||||
<related title="Synopsis of methods" subtitle=" "/>
|
||||
<nestedclasses visible="yes" title="Instances and minimal complete definitions"/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
|
||||
<!-- We don't use what's below -->
|
||||
<publictypes title=""/>
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<publicslots title=""/>
|
||||
<signals title=""/>
|
||||
<publicmethods title=""/>
|
||||
<publicstaticmethods title=""/>
|
||||
<publicattributes title=""/>
|
||||
<publicstaticattributes title=""/>
|
||||
<protectedtypes title=""/>
|
||||
<protectedslots title=""/>
|
||||
<protectedmethods title=""/>
|
||||
<protectedstaticmethods title=""/>
|
||||
<protectedattributes title=""/>
|
||||
<protectedstaticattributes title=""/>
|
||||
<packagetypes title=""/>
|
||||
<packagemethods title=""/>
|
||||
<packagestaticmethods title=""/>
|
||||
<packageattributes title=""/>
|
||||
<packagestaticattributes title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
<privatetypes title=""/>
|
||||
<privateslots title=""/>
|
||||
<privatemethods title=""/>
|
||||
<privatestaticmethods title=""/>
|
||||
<privateattributes title=""/>
|
||||
<privatestaticattributes title=""/>
|
||||
</memberdecl>
|
||||
<memberdef>
|
||||
<related title="Methods"/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
|
||||
<!-- This is not for C++ -->
|
||||
<services title=""/>
|
||||
<interfaces title=""/>
|
||||
<constructors title=""/>
|
||||
<properties title=""/>
|
||||
<events title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="$INCLUDE_GRAPH"/>
|
||||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<classes visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="no"/>
|
||||
<detaileddescription title="Description"/>
|
||||
|
||||
<groupgraph visible="$GROUP_GRAPHS"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
<typedefs title=""/>
|
||||
<enums title=""/>
|
||||
<enumvalues title=""/>
|
||||
<functions title=""/>
|
||||
<variables title=""/>
|
||||
<signals title=""/>
|
||||
<publicslots title=""/>
|
||||
<protectedslots title=""/>
|
||||
<privateslots title=""/>
|
||||
<events title=""/>
|
||||
<properties title=""/>
|
||||
<friends title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
308
vendor/range-v3/range-v3/doc/release_notes.md
vendored
308
vendor/range-v3/range-v3/doc/release_notes.md
vendored
@ -1,308 +0,0 @@
|
||||
Release Notes {#release_notes}
|
||||
=============
|
||||
|
||||
\section v0-9-1 Version 0.9.1
|
||||
|
||||
_Released:_ Sept 1, 2019.
|
||||
|
||||
gcc-9.x portability fixes.
|
||||
|
||||
\section v0-9-0 Version 0.9.0 "Std::ranger Things"
|
||||
|
||||
_Released:_ Aug 26, 2019.
|
||||
|
||||
Bring many interfaces into sync with the C++20 draft.
|
||||
|
||||
* **NEW:** An improved concepts portability layer with macros that use C++20
|
||||
concepts when the compiler supports them.
|
||||
* **NEW:** An improved directory structure that keeps disjoint parts of the
|
||||
library -- iterators, ranges, algorithms, actions, views, functional
|
||||
programming support, and general utilities -- physically separate.
|
||||
* **NEW:** A `RANGES_DEEP_STL_INTEGRATION` configuration option that makes your
|
||||
STL implementation default to structural conformance to infer iterator
|
||||
category, as in C++20. Applies to libc++, libstdc++, and MSVC's Standard
|
||||
Library.
|
||||
* **NEW:** A `ranges::cpp20` namespace that contains all the functionality of
|
||||
C++20's `std::ranges` namespace.
|
||||
* All concept names have been given standard_case (renamed from PascalCase) and
|
||||
have been harmonized with the C++20 draft.
|
||||
* The following range access customization points no longer accept rvalue ranges
|
||||
by default:
|
||||
- `ranges::begin`
|
||||
- `ranges::end`
|
||||
- `ranges::rbegin`
|
||||
- `ranges::rend`
|
||||
- `ranges::cbegin`
|
||||
- `ranges::cend`
|
||||
- `ranges::crbegin`
|
||||
- `ranges::crend`
|
||||
- `ranges::data`
|
||||
- `ranges::cdata`
|
||||
* Iterators may specify an `iterator_concept` type alias in addition to
|
||||
`iterator_category` -- either as a nested type or as a member of a
|
||||
`std::iterator_traits` specialization -- to denote conformance to the C++20
|
||||
iterator concepts as distinct from the C++98 iterator requirements.
|
||||
(See [P1037 "Deep Integration of the Ranges TS"](http://wg21.link/p1037)
|
||||
for more information.)
|
||||
* The `ranges::value_type` trait has been renamed to `readable_traits`.
|
||||
* The `ranges::difference_type` trait has been renamed to `incrementable_traits`.
|
||||
* The `ranges::iterator_category` trait has been deprecated. Specialize
|
||||
`std::iterator_traits` to non-intrusively specify an iterator's category
|
||||
and (optionally) concept.
|
||||
* Rename the `ranges::view` namespace to `ranges::views` and `ranges::action` to
|
||||
`ranges::actions` (with deprecated namespace aliases for migration).
|
||||
* Rename `view::bounded` to `views::common`.
|
||||
* Rename `unreachable` to `unreachable_sentinel_t`.
|
||||
* Change `dangling` from a class template that wraps an iterator to a class that
|
||||
acts as a placeholder for an iterator that would otherwise dangle.
|
||||
* Implement C++20's `subrange` as a view that wraps an iterator/sentinel pair;
|
||||
deprecate `iterator_range`.
|
||||
* Deprecate implicit conversion from view types to containers; rename
|
||||
`ranges::to_` to `ranges::to` and extend it to support converting a
|
||||
range-of-ranges to a container-of-containers.
|
||||
* Deprecate the `ranges::v3` inline versioning namespace.
|
||||
* The following views have had minor changes to bring them into conformance with
|
||||
the C++20 working draft:
|
||||
- `join_view`
|
||||
- `single_view`
|
||||
- `empty_view`
|
||||
- `split_view`
|
||||
- `reverse_view`
|
||||
- `all_view`
|
||||
- `take_view`
|
||||
- `iota_view`
|
||||
<p/>`iota_view<std::[u]intmax_t>`, in particular, is given a user-defined
|
||||
`difference_type` that avoids integer overflow.
|
||||
* New names for the iterator and range type aliases:
|
||||
| Old Name | New Name |
|
||||
|-------------------------------|-----------------------------|
|
||||
| `value_type_t` | `iter_value_t` |
|
||||
| `reference_t` | `iter_reference_t` |
|
||||
| `difference_type_t` | `iter_difference_t` |
|
||||
| `size_type_t` | _deprecated_ |
|
||||
| `rvalue_reference_t` | `iter_rvalue_reference_t` |
|
||||
| `range_value_type_t` | `range_value_t` |
|
||||
| `range_difference_type_t` | `range_difference_t` |
|
||||
| `range_size_type_t` | `range_size_t` |
|
||||
|
||||
\section v0-5-0 Version 0.5.0
|
||||
|
||||
_Released:_ Apr 30, 2019.
|
||||
|
||||
* **NEW:** MSVC support, from @CaseyCarter :tada: (See the docs for the list of supported compilers.)
|
||||
* **NEW:** `view::enumerate`, from @MikeGitb
|
||||
* **NEW:** `view::addressof`, from @tower120
|
||||
* **NEW:** `unstable_remove_if` algorithm and action, from @tower120
|
||||
* **NEW:** `adjacent_remove_if` algorithm and action, from @cjdb
|
||||
* **NEW:** `ostream_joiner`, from @sv1990
|
||||
* `view::drop_while` and `view::take_while` get projection support, from @mrpi
|
||||
* `view::filter` and `view::remove_if` get projection support, from @mrpi
|
||||
* `view::unique` accepts optional comparison operator, from @tete17
|
||||
* `action::slice` supports sliding from the end, from @tete17
|
||||
* Support coroutines on MSVC, from @CaseyCarter
|
||||
* Faster `view::generate_n`, from GitHub user @tower120
|
||||
* Improved aligned new detection for libc++ on iOS, from @mtak-
|
||||
* Various CMake improvements, from @johelegp
|
||||
* `view_adaptor` supports `basic_iterator`-style mixins, from @tower120
|
||||
* Fix `ranges::advance` for random-access iterators for `n==0`, from @tower120
|
||||
* Bugs fixed: [#755](https://github.com/ericniebler/range-v3/issues/755), [#759](https://github.com/ericniebler/range-v3/issues/759), [#942](https://github.com/ericniebler/range-v3/issues/942), [#946](https://github.com/ericniebler/range-v3/issues/946), [#952](https://github.com/ericniebler/range-v3/issues/952), [#975](https://github.com/ericniebler/range-v3/issues/975), [#978](https://github.com/ericniebler/range-v3/issues/978), [#986](https://github.com/ericniebler/range-v3/issues/986), [#996](https://github.com/ericniebler/range-v3/issues/996), [#1041](https://github.com/ericniebler/range-v3/issues/1041), [#1047](https://github.com/ericniebler/range-v3/issues/1047), [#1088](https://github.com/ericniebler/range-v3/issues/1088), [#1094](https://github.com/ericniebler/range-v3/issues/1094), [#1107](https://github.com/ericniebler/range-v3/issues/1107), [#1129](https://github.com/ericniebler/range-v3/issues/1129)
|
||||
|
||||
\section v0-4-0 Version 0.4.0
|
||||
|
||||
_Released:_ Oct 18, 2018.
|
||||
|
||||
- Minor interface-breaking changes:
|
||||
* `single_view` returns by `const &` (see [#817](https://github.com/ericniebler/range-v3/issues/817)).
|
||||
* `reverse_view` of a non-Sized, non-Bounded RandomAccess range (eg., a null-terminated string) no longer satisfies SizedRange.
|
||||
* The `generate` and `generate_n` views now return the generated values by xvalue reference (`T &&`) to the value cached within the view (see [#905](https://github.com/ericniebler/range-v3/issues/905)).
|
||||
* Views no longer prefer returning constant iterators when they can; some views have different constant and mutable iterators.
|
||||
- Enhancements:
|
||||
* Views can successfully adapt other views that have different constant and mutable iterators.
|
||||
* The `single` and `empty` views are much closer to the versions as specified in [P0896](http://wg21.link/P0896).
|
||||
- Bug fixes:
|
||||
* "single_view should not copy the value" [#817](https://github.com/ericniebler/range-v3/issues/817).
|
||||
* "Calling back() on strided range does not return the correct last value in range" [#901](https://github.com/ericniebler/range-v3/issues/901).
|
||||
* "generate(foo) | take(n) calls foo n+1 times" [#819](https://github.com/ericniebler/range-v3/issues/819).
|
||||
* "generate seems broken with move-only return types" [#905](https://github.com/ericniebler/range-v3/issues/905).
|
||||
* "Unexpected behavior in generate with return by reference" [#807](https://github.com/ericniebler/range-v3/issues/807).
|
||||
* "Inconsistent behaviour of ranges::distance with ranges::view::zip using infinite views." [#783](https://github.com/ericniebler/range-v3/issues/783).
|
||||
* "Infinite loop when using ranges::view::cycle with an infinite range" [#780](https://github.com/ericniebler/range-v3/issues/780).
|
||||
* "Composing ranges::view::cycle with ranges::view::slice" [#778](https://github.com/ericniebler/range-v3/issues/778).
|
||||
* "cartesian_product view, now with moar bugs." [#919](https://github.com/ericniebler/range-v3/issues/919).
|
||||
|
||||
|
||||
\section v0-3-7 Version 0.3.7
|
||||
|
||||
_Released:_ Sept 19, 2018.
|
||||
|
||||
- Improved support for clang-cl (thanks to @CaseyCarter).
|
||||
- Fix for `any_view<T, category::sized | category::input>` (see #869).
|
||||
- Fix `iter_move` of a `ranges::reverse_iterator` (see #888).
|
||||
- Fix `move_sentinel` comparisons (see #889).
|
||||
- Avoid ambiguity created by `boost::advance` and `std::advance` (see #893).
|
||||
|
||||
\section v0-3-6 Version 0.3.6
|
||||
|
||||
_Released:_ May 15, 2018.
|
||||
|
||||
- NEW: `view::exclusive_scan` (thanks to GitHub user @mitsutaka-takeda).
|
||||
- All views get non-`const` overloads of `.empty()` and `.size()` (see [ericniebler/stl2\#793](https://github.com/ericniebler/stl2/issues/793)).
|
||||
- Upgrade Conan support for conan 1.0.
|
||||
- `subspan` interface tweaks.
|
||||
- Fix bug in `view::split` (see [this stackoverflow question](https://stackoverflow.com/questions/49015671)).
|
||||
- Fix bug in `view::stride` (see [ericniebler/stl2\#805](https://github.com/ericniebler/stl2/issues/805)).
|
||||
- Fix `const`-correctness problem in `view::chunk` (see [this stackoverflow question](https://stackoverflow.com/questions/49210190)).
|
||||
- Replace uses of `ranges::result_of` with `ranges::invoke_result`.
|
||||
- Fix potential buffer overrun of `view::drop` over RandomAccessRanges.
|
||||
- Lots of `view::cartesian_product` fixes (see [ericniebler/stl2\#820](https://github.com/ericniebler/stl2/issues/820), [ericniebler/stl2\#823](https://github.com/ericniebler/stl2/issues/823)).
|
||||
- Work around gcc-8 regression regarding `volatile` `std::initializer_list`s (see [ericniebler/stl2\#826](https://github.com/ericniebler/stl2/issues/826)).
|
||||
- Fix `const`-correctness problem of `view::take`.
|
||||
|
||||
\section v0-3-5 Version 0.3.5
|
||||
|
||||
_Released:_ February 17, 2018.
|
||||
|
||||
- Rvalues may satisfy `Writable` (see [ericniebler/stl2\#387](https://github.com/ericniebler/stl2/issues/387)).
|
||||
- `view_interface` gets a bounds-checking `at` method.
|
||||
- `chunk_view` works on Input ranges.
|
||||
- Fix bug in `group_by_view`.
|
||||
- Improved concept checks for `partial_sum` numeric algorithm.
|
||||
- Define `ContiguousIterator` concept and `contiguous_iterator_tag` iterator
|
||||
category tag.
|
||||
- Sundry `span` fixes.
|
||||
- `action::insert` avoids interfering with `vector`'s exponentional growth
|
||||
strategy.
|
||||
- Add an experimental `shared` view for views that need container-like scratch
|
||||
space to do their work.
|
||||
- Faster, simpler `reverse_view`.
|
||||
- Rework `ranges::reference_wrapper` to avoid [LWG\#2993](https://wg21.link/lwg2993).
|
||||
- Reworked `any_view`, the type-erased view wrapper.
|
||||
- `equal` algorithm is `constexpr` in C++14.
|
||||
- `stride_view` no longer needs an `atomic` data member.
|
||||
- `const`-correct `drop_view`.
|
||||
- `adjacent_filter_view` supports bidirectional iteration.
|
||||
- Massive `view_adaptor` cleanup to remove the need for a `mutable` data
|
||||
member holding the adapted view.
|
||||
- Fix `counting_iterator` post-increment bug.
|
||||
- `tail_view` of an empty range is an empty range, not undefined behavior.
|
||||
- Various portability fixes for gcc and clang trunk.
|
||||
|
||||
\section v0-3-0 Version 0.3.0
|
||||
|
||||
_Released:_ June 30, 2017.
|
||||
|
||||
- Input views may now be move-only (from @CaseyCarter)
|
||||
- Input `any_view`s are now *much* more efficient (from @CaseyCarter)
|
||||
- Better support for systems lacking a working `<thread>` header (from @CaseyCarter)
|
||||
|
||||
\section v0-2-6 Version 0.2.6
|
||||
|
||||
_Released:_ June 21, 2017.
|
||||
|
||||
- Experimental coroutines with `ranges::experimental::generator` (from @CaseyCarter)
|
||||
- `ranges::optional` now behaves like `std::optional` (from @CaseyCarter)
|
||||
- Extensive bug fixes with Input ranges (from @CaseyCarter)
|
||||
|
||||
\section v0-2-5 Version 0.2.5
|
||||
|
||||
_Released:_ May 16, 2017.
|
||||
|
||||
- `view::chunk` works on Input ranges (from @CaseyCarter)
|
||||
- `for_each_n` algorithm (from @khlebnikov)
|
||||
- Portability fixes for MinGW, clang-3.6 and -3.7, and gcc-7; and cmake 3.0
|
||||
|
||||
\section v0-2-4 Version 0.2.4
|
||||
|
||||
_Released:_ April 12, 2017.
|
||||
|
||||
Fix the following bug:
|
||||
- `action::stable_sort` of `vector` broken on Clang 3.8.1 since ~last Xmas (ericniebler/range-v3#632).
|
||||
|
||||
\section v0-2-3 Version 0.2.3
|
||||
|
||||
_Released:_ April 4, 2017.
|
||||
|
||||
Fix the following bug:
|
||||
- iterators that return move-only types by value do not satisfy Readable (ericniebler/stl2#399).
|
||||
|
||||
\section v0-2-2 Version 0.2.2
|
||||
|
||||
_Released:_ March 30, 2017.
|
||||
|
||||
New in this release:
|
||||
- `view::linear_distribute(from,to,n)` - A view of `n` elements between `from` and `to`, distributed evenly.
|
||||
- `view::indices(n)` - A view of the indices `[0,1,2...n-1]`.
|
||||
- `view::closed_indices(n)` - A view of the indices `[0,1,2...n]`.
|
||||
|
||||
This release deprecates `view::ints(n)` as confusing to new users.
|
||||
|
||||
\section v0-2-1 Version 0.2.1
|
||||
|
||||
_Released:_ March 22, 2017.
|
||||
|
||||
New in this release:
|
||||
- `view::cartesian_product`
|
||||
- `action::reverse`
|
||||
|
||||
\section v0-2-0 Version 0.2.0
|
||||
|
||||
_Released:_ March 13, 2017.
|
||||
|
||||
Bring many interfaces into sync with the Ranges TS.
|
||||
- Many interfaces are simply renamed. The following table shows the old names
|
||||
and the new. (All names are in the `ranges::v3` namespace.)
|
||||
| Old Name | New Name |
|
||||
|-------------------------------|---------------------------|
|
||||
| `indirect_swap` | `iter_swap` |
|
||||
| `indirect_move` | `iter_move` |
|
||||
| `iterator_value_t` | `value_type_t` |
|
||||
| `iterator_reference_t` | `reference_t` |
|
||||
| `iterator_difference_t` | `difference_type_t` |
|
||||
| `iterator_size_t` | `size_type_t` |
|
||||
| `iterator_rvalue_reference_t` | `rvalue_reference_t` |
|
||||
| `iterator_common_reference_t` | `iter_common_reference_t` |
|
||||
| `range_value_t` | `range_value_type_t` |
|
||||
| `range_difference_t` | `range_difference_type_t` |
|
||||
| `range_size_t` | `range_size_type_t` |
|
||||
| `range_iterator_t` | `iterator_t` |
|
||||
| `range_sentinel_t` | `sentinel_t` |
|
||||
- `common_iterator` now requires that its two types (`Iterator` and `Sentinel`)
|
||||
are different. Use `common_iterator_t<I, S>` to get the old behavior (i.e., if the two types are the same, it is an alias for `I`; otherwise, it is
|
||||
`common_iterator<I, S>`).
|
||||
- The following iterator adaptors now work with iterators that return proxies
|
||||
from their postfix increment operator (i.e., `operator++(int)`):
|
||||
* `common_iterator`
|
||||
* `counted_iterator`
|
||||
- The following customization points are now implemented per the Ranges TS
|
||||
spec and will no longer find the associated unconstrained overload in
|
||||
namespace `std::`:
|
||||
* `ranges::begin`
|
||||
* `ranges::end`
|
||||
* `ranges::size`
|
||||
* `ranges::swap`
|
||||
* `ranges::iter_swap`
|
||||
|
||||
(In practice, this has very little effect but it may effect overloading in
|
||||
rare situations.)
|
||||
- `ranges::is_swappable` now only takes one template parameter. The new
|
||||
`ranges::is_swappable_with<T, U>` tests whether `T` and `U` are swappable.
|
||||
`ranges::is_swappable<T>` is equivalent to `ranges::is_swappable_with<T &, T &>`.
|
||||
- The following object concepts have changed to conform with the Ranges TS
|
||||
specification, and approved changes (see [P0547](http://wg21.link/p0547)):
|
||||
* `Destructible`
|
||||
* `Constructible`
|
||||
* `DefaultConstructible`
|
||||
* `MoveConstructible`
|
||||
* `MoveConstructible`
|
||||
* `Movable`
|
||||
* `Assignable`
|
||||
- The `View` concept is no longer satisfied by reference types.
|
||||
- The syntax for defining a concept has changed slightly. See [iterator/concepts.hpp](https://github.com/ericniebler/range-v3/blob/master/include/range/v3/iterator/concepts.hpp) for examples.
|
||||
|
||||
\section v0-1-1 Version 0.1.1
|
||||
|
||||
- Small tweak to `Writable` concept to fix #537.
|
||||
|
||||
\section v0-1-0 Version 0.1.0
|
||||
|
||||
- March 8, 2017, Begin semantic versioning
|
2031
vendor/range-v3/range-v3/doc/std/D4128.md
vendored
2031
vendor/range-v3/range-v3/doc/std/D4128.md
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user