Compare commits

...

19 Commits

Author SHA1 Message Date
Maarten L. Hekkelman
4782a4e07d Merge branch 'develop' into trunk 2023-12-12 09:31:30 +01:00
Maarten L. Hekkelman
dbc14206dc New readme 2023-12-12 09:21:59 +01:00
Maarten L. Hekkelman
f4296d8858 docs ci 2023-12-12 08:26:44 +01:00
Maarten L. Hekkelman
75c4c2286d readme, docs ci 2023-12-12 08:08:40 +01:00
Maarten L. Hekkelman
b14237e8e6 Not nice 2023-12-05 16:01:20 +01:00
Maarten L. Hekkelman
df3263e4bd test 2023-12-05 15:57:51 +01:00
Maarten L. Hekkelman
ff7b413abf windows gunzip workaround 2023-12-05 15:43:52 +01:00
Maarten L. Hekkelman
07224779e6 find_program works a bit different than expected 2023-12-05 15:26:26 +01:00
Maarten L. Hekkelman
c031a3c24e Moved data file components.cif to rsrc 2023-12-05 11:53:39 +01:00
Maarten L. Hekkelman
1e74f7912c using eigen3 2023-12-05 08:59:10 +01:00
Maarten L. Hekkelman
d91f8d0876 Use fetchcontent for eigen, again 2023-12-04 18:59:03 +01:00
Maarten L. Hekkelman
43d4644fba fix license badge 2023-12-03 15:32:55 +01:00
Maarten L. Hekkelman
61a924d208 attempt to build documentation in github 2023-12-03 15:30:40 +01:00
Maarten L. Hekkelman
2692f2c1bf no update script for macOS
install with sudo in CI
2023-11-29 15:22:59 +01:00
Maarten L. Hekkelman
88b3c87bae fixes for automatic update of data 2023-11-29 14:37:28 +01:00
Maarten L. Hekkelman
24fe6ee583 Unused comparison result... tssk 2023-11-22 14:15:51 +01:00
Maarten L. Hekkelman
b83ef112a9 Fix in structure::change_residue 2023-11-13 10:50:27 +01:00
Maarten L. Hekkelman
dd9110a3a8 Add option to not write data files 2023-11-07 16:48:44 +01:00
Maarten L. Hekkelman
88c23e1b0f Add option to not write data files 2023-11-07 16:38:47 +01:00
11 changed files with 265 additions and 85 deletions

View File

@@ -0,0 +1,65 @@
# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform.
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml
name: publish docs
on:
push:
branches: [ "trunk" ]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set reusable strings
# Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file.
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"
- name: Install dependencies Ubuntu
run: sudo apt-get update && sudo apt-get install cmake doxygen
- uses: actions/setup-python@v4
with:
python-version: '3.9'
cache: 'pip' # caching pip dependencies
- run: pip install -r docs/requirements.txt
- name: Configure CMake
run: cmake -S . -B ${{ steps.strings.outputs.build-output-dir }} -DBUILD_DOCUMENTATION=ON -DBUILD_TESTING=OFF
- name: Run Sphinx
run: |
cmake --build ${{ steps.strings.outputs.build-output-dir }} --target Sphinx-libcifpp
ls -l ${{ steps.strings.outputs.build-output-dir }}
ls -l ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: ${{ steps.strings.outputs.build-output-dir }}/docs/sphinx
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: docs
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2

View File

@@ -45,13 +45,15 @@ jobs:
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_BUILD_TYPE=Release
-DCIFPP_DOWNLOAD_CCD=OFF
-S ${{ github.workspace }}
- name: Build
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config Release
- name: Test
working-directory: ${{ steps.strings.outputs.build-output-dir }}
run: ctest --build-config Release --output-on-failure
- name: Install
if: matrix.os != 'windows-latest'
run: sudo cmake --install ${{ steps.strings.outputs.build-output-dir }} --config Release

2
.gitignore vendored
View File

@@ -2,7 +2,7 @@ build/
.vscode/
.vs/
tools/update-libcifpp-data
data/components.cif*
rsrc/components.cif*
CMakeSettings.json
msvc/
src/revision.hpp

View File

@@ -66,25 +66,22 @@ if(NOT(BUILD_FOR_CCP4 AND WIN32))
option(BUILD_SHARED_LIBS "Build a shared library instead of a static one" OFF)
endif()
# Lots of code depend on the availability of the components.cif file
option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" ON)
if(BUILD_FOR_CCP4)
unset(CIFPP_DOWNLOAD_CCD)
unset(CIFPP_INSTALL_UPDATE_SCRIPT)
else()
# Lots of code depend on the availability of the components.cif file
option(CIFPP_DOWNLOAD_CCD "Download the CCD file components.cif during installation" ON)
# An optional cron script can be installed to keep the data files up-to-date
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT BUILD_FOR_CCP4)
option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" ON)
# An optional cron script can be installed to keep the data files up-to-date
if(UNIX AND NOT APPLE)
option(CIFPP_INSTALL_UPDATE_SCRIPT "Install the script to update CCD and dictionary files" ON)
endif()
endif()
# When CCP4 is sourced in the environment, we can recreate the symmetry operations table
if(EXISTS "$ENV{CCP4}")
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
option(CIFPP_RECREATE_SYMOP_DATA "Recreate SymOp data table in case it is out of date" ON)
else()
set(CIFPP_RECREATE_SYMOP_DATA OFF)
message(WARNING "Symop data table recreation requested, but file syminfo.lib was not found in $ENV{CLIBD}")
endif()
else()
set(CIFPP_RECREATE_SYMOP_DATA OFF)
message("Not trying to recreate symop_table_data.hpp since CCP4 is not defined")
if(EXISTS "$ENV{CCP4}/lib/data/syminfo.lib")
option(CIFPP_RECREATE_SYMOP_DATA "Recreate SymOp data table in case it is out of date" ON)
endif()
# CCP4 build
@@ -209,20 +206,28 @@ endif()
find_package(ZLIB REQUIRED)
find_package(Eigen3 QUIET)
# Using Eigen3 is a bit of a thing. We don't want to build it completely since we
# only need a couple of header files. Nothing special. But often, eigen3 is already
# installed and then we prefer that.
find_package(Eigen3 3.4 QUIET)
if(Eigen3_FOUND)
if(Eigen3_FOUND AND TARGET Eigen3::Eigen)
get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES)
else()
ExternalProject_Add(
local_Eigen3
# Create a private copy of eigen3 and populate it only, no need to build
FetchContent_Declare(
my-eigen3
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/external
)
set(EIGEN3_D local_Eigen3)
set(EIGEN_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/external/include/eigen3)
FetchContent_GetProperties(my-eigen3)
if(NOT my-eigen3_POPULATED)
FetchContent_Populate(my-eigen3)
endif()
set(EIGEN_INCLUDE_DIR ${my-eigen3_SOURCE_DIR})
endif()
include(FindFilesystem)
@@ -333,15 +338,12 @@ target_include_directories(cifpp
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
PRIVATE
"${EIGEN_INCLUDE_DIR}"
"${BOOST_REGEX_INCLUDE_DIR}"
"${EIGEN_INCLUDE_DIR}"
)
target_link_libraries(cifpp PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES})
if(${EIGEN3_D})
add_dependencies(cifpp ${EIGEN3_D})
endif()
target_link_libraries(cifpp
PUBLIC Threads::Threads ZLIB::ZLIB ${CIFPP_REQUIRED_LIBRARIES})
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
target_link_options(cifpp PRIVATE -undefined dynamic_lookup)
@@ -349,7 +351,7 @@ endif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CIFPP_DOWNLOAD_CCD)
# download the components.cif file from CCD
set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/data/components.cif)
set(COMPONENTS_CIF ${PROJECT_SOURCE_DIR}/rsrc/components.cif)
if(EXISTS ${COMPONENTS_CIF})
file(SIZE ${COMPONENTS_CIF} CCD_FILE_SIZE)
@@ -361,27 +363,25 @@ if(CIFPP_DOWNLOAD_CCD)
endif()
if(NOT EXISTS ${COMPONENTS_CIF})
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/data)
file(MAKE_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
endif()
# Since the file(DOWNLOAD) command in cmake does not use
# compression, we try to download the gzipped version and
# decompress it ourselves.
find_program(GUNZIP gunzip)
if(GUNZIP)
if(WIN32 OR GUNZIP STREQUAL "GUNZIP-NOTFOUND")
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF}
SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
else()
if(NOT EXISTS "${COMPONENTS_CIF}.gz")
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz ${COMPONENTS_CIF}.gz
SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
endif()
add_custom_command(OUTPUT ${COMPONENTS_CIF}
COMMAND ${GUNZIP} ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/data/)
else()
file(DOWNLOAD https://files.wwpdb.org/pub/pdb/data/monomers/components.cif ${COMPONENTS_CIF}
SHOW_PROGRESS STATUS CCD_FETCH_STATUS)
COMMAND "${GUNZIP}" ${COMPONENTS_CIF}.gz
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/rsrc/)
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
endif()
# Do not continue if downloading went wrong
@@ -391,8 +391,6 @@ if(CIFPP_DOWNLOAD_CCD)
message(FATAL_ERROR "Error trying to download CCD file: ${CCD_FETCH_STATUS}")
endif()
endif()
add_custom_target(COMPONENTS ALL DEPENDS ${COMPONENTS_CIF})
endif()
# Installation directories
@@ -511,7 +509,6 @@ write_basic_package_version_file(
)
if(BUILD_TESTING)
# We're using the older version 2 of Catch2
FetchContent_Declare(
Catch2
@@ -538,7 +535,7 @@ if(BUILD_TESTING)
add_executable(${CIFPP_TEST} ${CIFPP_TEST_SOURCE} "${CMAKE_CURRENT_SOURCE_DIR}/test/test-main.cpp")
target_link_libraries(${CIFPP_TEST} PRIVATE Threads::Threads cifpp::cifpp Catch2::Catch2)
target_include_directories(${CIFPP_TEST} PRIVATE ${EIGEN_INCLUDE_DIR})
target_include_directories(${CIFPP_TEST} PRIVATE "${EIGEN_INCLUDE_DIR}")
if(MSVC)
# Specify unwind semantics so that MSVC knowns how to handle exceptions
@@ -564,7 +561,7 @@ if(CIFPP_INSTALL_UPDATE_SCRIPT)
else()
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/cron.weekly" CACHE PATH "The cron directory, for the update script")
endif()
elseif(UNIX) # assume all others are like FreeBSD...
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(CIFPP_CRON_DIR "${CIFPP_ETC_DIR}/periodic/weekly" CACHE PATH "The cron directory, for the update script")
else()
message(FATAL_ERROR "Don't know where to install the update script")
@@ -602,7 +599,7 @@ set(CPACK_SOURCE_TGZ ON)
set(CPACK_SOURCE_TBZ2 OFF)
set(CPACK_SOURCE_TXZ OFF)
set(CPACK_SOURCE_TZ OFF)
set(CPACK_SOURCE_IGNORE_FILES "/data/components.cif;/build;/.vscode;/.git")
set(CPACK_SOURCE_IGNORE_FILES "/rsrc/components.cif;/build;/.vscode;/.git")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME})
include(CPack)

162
README.md
View File

@@ -1,10 +1,34 @@
[![github CI](https://github.com/pdb-redo/libcifpp/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/pdb-redo/libcifpp/actions)
[![GitHub License](https://img.shields.io/github/license/pdb-redo/libcifpp)](https://github.com/pdb-redo/libcifpp/LICENSE)
# libcifpp
This library contains code to work with mmCIF and legacy PDB files.
As the name implies, this library was originally written to work with mmCIF files
using C++ as programming language. The design of this library leanes heavily on
the structure of CIF files. These files can be thought of as a text dump of a
relational databank with, often but not always, a very strict schema describing
the data. These schema's are called dictionaries.
Using information from the content of a mmCIF file and an optional schema,
libcifpp allows you to access the data in the file as a collection of datablock
each containing a collection of categories with rows of data. The categories can
be searched for data using queries written in regular C++ syntax. When a dictionary
was specified, inserted data is checked for validity. Likewise removal of data
may result in cascaded removal of linked data in other categories using
parent/child relationship information.
Since there were still many programs using the legacy PDB format at the time
development started, a layer was added that converts data to and from PDB format
into mmCIF format. This means you can manipulate PDB files as if they were
normal mmCIF files.
Apart from this basic functionality, libcifpp also offers code to help with
symmetry calculations, 3d manipulations and obtaining information from the CCD
[Chemical Component Dictionary](https://www.wwpdb.org/data/ccd).
## Documentation
The documentation can be found at https://www.hekkelman.com/libcifpp-doc/
The documentation can be found at [github.io](https://pdb-redo.github.io/libcifpp/)
## Synopsis
@@ -64,54 +88,138 @@ int main(int argc, char *argv[])
You might be able to use libcifpp from a package manager used by your
OS distribution. But most likely this package will be out-of-date.
Therefore it is recommended to build *libcifpp* from code. It is not
hard to do.
hard to do. But it is recommended to read the following instructions
carefully.
### Requirements
The code for this library was written in C++17. You therefore need a
recent compiler to build it. For the development gcc 9.4 and clang 9.0
recent compiler to build it. For the development gcc >= 9.4 and clang >= 9.0
have been used as well as MSVC version 2019.
Other requirements are:
The other requirement you really need to have installed on your computer
is a version of [CMake](https://cmake.org). For now the minimum version
is 3.16 but that may soon change into a higher version. You should also
install the gui version of CMake to set build options easily, on Debian
I prefer to use the curses version installed with `cmake-curses-gui`.
It is very useful to have [mrc](https://github.com/mhekkel/mrc) available.
However, this is only an option if you use Windows or an operating system
using the ELF executable format (i.e. Linux or FreeBSD). MRC is a resource
compiler that allows including data files into the executable making them
easier to install.
Other libraries you might want to install beforehand are:
- [cmake](https://cmake.org) A build tool.
- [mrc](https://github.com/mhekkel/mrc), a resource compiler that
allows including data files into the executable making them easier to
install. Strictly speaking this is optional, but at the expense of
functionality.
- [libeigen](https://eigen.tuxfamily.org/index.php?title=Main_Page), a
library to do amongst others matrix calculations. This usually can be
installed using your package manager, in Debian/Ubuntu it is called
`libeigen3-dev`
- [zlib](https://github.com/madler/zlib), the development version of this
library. On Debian/Ubuntu this is the package `zlib1g-dev`.
- [boost](https://www.boost.org).
The Boost libraries are only needed in case you want to build the test
code or if you are using GCC. That last condition is due to a long
standing bug in the implementation of std::regex. It simply crashes
on the regular expressions used in the mmcif_pdbx dictionary and so
we use the boost regex implementation instead.
- [boost](https://www.boost.org), in Debian/Ubuntu this is `libboost-dev`.
The Boost libraries are only needed in case you are using GCC due to a long
standing bug in GNU's implementation of std::regex. It simply crashes
on the regular expressions used in the mmcif_pdbx dictionary and so
we use the boost regex implementation instead.
### Building
Building the code is as simple as typing:
First you need to download the code:
```console
git clone https://github.com/PDB-REDO/libcifpp.git --recurse-submodules
git clone https://github.com/PDB-REDO/libcifpp.git
cd libcifpp
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build
```
This checks out the source code from github, creates a new directory
where cmake stores its files. Run a configure, build the code and then
it installs the library and auxiliary files.
You should start by considering where to install libcifpp. If you have
sufficient permissions on your computer you perhaps should use the
default but libcifpp can be configured to be installed anywhere
including e.g. *$HOME/.local*.
Next step is to configure, for this use the CMake gui application. If you
installed the curses version of cmake you can type `ccmake`. On Windows
you can use `cmake-gui.exe`.
To install in the default location:
```console
ccmake -S . -B build
```
To install elsewhere, e.g. *$HOME/.local*:
```console
ccmake -S . -B build -DCMAKE_INSTALL_PREFIX=$HOME/.local
```
In the cmake window, start the configure command (use button or press 'c').
After the first configure step you will see a list of settable options.
Alter these to match your preferences. Most options are self explaining
and contain a description. Some may need a bit more explanation:
- CIFPP_DATA_DIR, this directory will be used to store initial versions
of the mmcif_pdbx dictionary as well as the optional CCD file.
- CIFPP_DOWNLOAD_CCD
The CCD file is huge and perhaps you think you don't
need it. In that case you can leave this OFF. But that will limit the
use cases.
- CIFPP_INSTALL_UPDATE_SCRIPT
The files in CIFPP_DATA_DIR are quickly becoming out of date. On
FreeBSD and Linux you can install a script that updates these files
on a weekly basis.
- CIFPP_CRON_DIR
The directory where the update script is to be installed.
- CIFPP_ETC_DIR
The update script will only work if the file called *libcifpp.conf*
in this *etc* directory will contain an uncommented line with
```console
update=true
```
- CIFPP_CACHE_DIR
When you installed and enabled the update script, new files are
written to this directory.
- CIFPP_RECREATE_SYMOP_DATA
If you had CCP4 sourced into your environment, this option allows
you to recreate the symop data file.
- BUILD_FOR_CCP4
Build a special version of libcifpp to be installed in the CCP4
environment.
After setting these options you can run the configure step again and
then use generate to create the makefiles.
Building and installing is then as simple as:
```console
cmake --build build
cmake --install build
```
If this fails due to lack of permissions, you can try:
```console
sudo cmake --install build
```
Tests are created by default, and to test the code you can run:
```console
cmake --build build
ctest --test-dir build
ctest --test-dir build
```

View File

@@ -2006,7 +2006,10 @@ void structure::change_residue(residue &res, const std::string &newCompound,
continue;
if (a2.empty() or a2 == ".")
{
i->set_property("label_comp_id", newCompound);
remove_atom(*i);
}
else if (a1 != a2)
{
auto ra = r.front();

Binary file not shown.

View File

@@ -37,8 +37,8 @@ TEST_CASE("rename")
{
cif::VERBOSE = 3;
if (std::filesystem::exists(gTestDir / ".." / "data" / "ccd-subset.cif"))
cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
if (std::filesystem::exists(gTestDir / ".." / "rsrc" / "ccd-subset.cif"))
cif::add_file_resource("components.cif", gTestDir / ".." / "rsrc" / "ccd-subset.cif");
if (std::filesystem::exists(gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic"))
cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");

View File

@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
cif::add_file_resource("mmcif_pdbx.dic", gTestDir / ".." / "rsrc" / "mmcif_pdbx.dic");
// initialize CCD location
cif::add_file_resource("components.cif", gTestDir / ".." / "data" / "ccd-subset.cif");
cif::add_file_resource("components.cif", gTestDir / ".." / "rsrc" / "ccd-subset.cif");
cif::compound_factory::instance().push_dictionary(gTestDir / "HEM.cif");

View File

@@ -10,7 +10,7 @@ euid=${EUID:-$(id -u)}
if [ "${euid}" -ne 0 ]; then
echo "Please run as root"
exit
exit 1
fi
if [ -f "@CIFPP_ETC_DIR@/libcifpp.conf" ]; then
@@ -19,12 +19,13 @@ fi
# check to see if we're supposed to run at all
if [ "$update" != "true" ]; then
exit
exit 0
fi
# if cache directory doesn't exist, exit.
if ! [ -d "@CIFPP_CACHE_DIR@" ]; then
exit
echo "Cache directory '@CIFPP_CACHE_DIR@' does not exist"
exit 1
fi
# Create a temp file in the right directory and
@@ -60,12 +61,16 @@ update_dictionary() {
# Update the dictionaries
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz"
update_dictionary "@CIFPP_CACHE_DIR@/components.cif" "https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz"
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_pdbx.dic" "https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic.gz"
update_dictionary "@CIFPP_CACHE_DIR@/mmcif_ma.dic" "https://github.com/ihmwg/ModelCIF/raw/master/dist/mmcif_ma.dic"
# notify subscribers, will fail on FreeBSD
# notify subscribers, using find instead of run-parts to make it work on FreeBSD as well
if [ -d "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" ] && [ -x /bin/run-parts ]; then
run-parts --arg "@CIFPP_CACHE_DIR@" -- "@CIFPP_ETC_DIR@/libcifpp/cache-update.d"
if [ -d "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" ]; then
find "@CIFPP_ETC_DIR@/libcifpp/cache-update.d" \
-exec test -x {} \; -and -not -exec test -d {} \; \
-exec {} "@CIFPP_CACHE_DIR@" \;
fi
exit 0