The package rpms/dispenso.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/dispenso.git/commit/?id=8c734ea5f....
Change:
+%ifarch %{arm} %{ix86}
Thanks.
Full change:
============
commit eac48f0c49457f1dea52edb5fe05d0efff487167
Author: Michel Alexandre Salim <salimma(a)fedoraproject.org>
Date: Tue Jan 25 17:31:36 2022 -0800
Use system moodycamel, to avoid bundling and fix build issue on armv7hl
Signed-off-by: Michel Alexandre Salim <salimma(a)fedoraproject.org>
diff --git a/dispenso-1.0.0-use-system-moodycamel.patch
b/dispenso-1.0.0-use-system-moodycamel.patch
new file mode 100644
index 0000000..ea19665
--- /dev/null
+++ b/dispenso-1.0.0-use-system-moodycamel.patch
@@ -0,0 +1,14 @@
+--- dispenso-1.0.0/dispenso/CMakeLists.txt.use-system-moodycamel 2022-01-25
17:23:11.884852347 -0800
++++ dispenso-1.0.0/dispenso/CMakeLists.txt 2022-01-25 20:10:56.941350502 -0800
+@@ -22,10 +22,9 @@
+ target_include_directories(dispenso
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/..>
+- $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/third-party/moodycamel>
++ /usr/include/concurrentqueue
+ $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+-
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/dispenso/third-party/moodycamel>
+ )
+
+ set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
diff --git a/dispenso.spec b/dispenso.spec
index b9482d9..5302f54 100644
--- a/dispenso.spec
+++ b/dispenso.spec
@@ -28,10 +28,14 @@ Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz
Patch0:
%{url}/commit/d00e7402ffcc780df11024f8d2285c153b7635b1.patch#/%{name}-1.0.0-add-install.patch
# TODO: make toggleable and upstream
Patch1: %{name}-1.0.0-use-system-gtest.patch
+# being reviewed upstream
Patch2: %{name}-1.0.0-fix-32bit-build.patch
+# TODO: make toggleable and upstream
+Patch3: %{name}-1.0.0-use-system-moodycamel.patch
BuildRequires: cmake
BuildRequires: gcc-c++
+BuildRequires: moodycamel-concurrentqueue-devel
%if %{with check}
BuildRequires: gmock-devel
BuildRequires: (gtest-devel >= 1.10.0 with gtest-devel < 1.11.0)
@@ -58,7 +62,7 @@ base code on a specific version.}
%package devel
Summary: Development files for %{name}
Requires: %{name}%{?_isa} = %{version}-%{release}
-# Requires: cmake-filesystem
+Requires: moodycamel-concurrentqueue-devel
%description devel %{_description}
@@ -68,6 +72,8 @@ developing applications that use %{name}.
%prep
%autosetup -p1
+# make sure we use the system library
+rm -rf dispenso/third-party
%build
commit 8c734ea5fa797ac30d35cb982d37b78e7e52ace2
Author: Michel Alexandre Salim <salimma(a)fedoraproject.org>
Date: Tue Jan 25 10:20:41 2022 -0800
Initial Fedora package
Signed-off-by: Michel Alexandre Salim <salimma(a)fedoraproject.org>
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..018d7d2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/dispenso-1.0.0.tar.gz
diff --git a/dispenso-1.0.0-add-install.patch b/dispenso-1.0.0-add-install.patch
new file mode 100644
index 0000000..b5b6e53
--- /dev/null
+++ b/dispenso-1.0.0-add-install.patch
@@ -0,0 +1,239 @@
+From d00e7402ffcc780df11024f8d2285c153b7635b1 Mon Sep 17 00:00:00 2001
+From: Jefferson Amstutz <jamstutz(a)nvidia.com>
+Date: Mon, 24 Jan 2022 11:13:52 -0800
+Subject: [PATCH] Add CMake install target + CMake dispenso target exports
+ (#16)
+
+Summary:
+# PR Details
+
+This PR adds an `install` target for the main dispenso library, including CMake target
exports.
+
+## Description
+
+The following changes were made:
+
+- The root level `project()` version was updated to `1.0.0`, which gets used in the CMake
exports
+- The `install` target is only created when doing a stand alone build of dispenso (i.e.
not under `add_subdirectory()`), which is detected with checking if `CMAKE_SOURCE_DIR` and
`CMAKE_CURRENT_SOURCE_DIR` are equal.
+- The built-in CMake `Threads` module was used for more robust consumption of `pthreads`
downstream.
+- Updated the README with a brief description about installing and linking downstream
with CMake.
+
+## Related Issue
+
+Did not create an issue.
+
+## Motivation and Context
+
+This makes dispenso packageable and installable.
+
+Pull Request resolved:
https://github.com/facebookincubator/dispenso/pull/16
+
+Test Plan:
+No C++ code was changed. The existing build process is unchanged.
+
+## Types of changes
+
+- [ ] Docs change
+- [ ] Refactoring
+- [ ] Dependency upgrade
+- [ ] Bug fix (non-breaking change which fixes an issue)
+- [X] New feature (non-breaking change which adds functionality)
+- [ ] Breaking change (fix or feature that would cause existing functionality to change)
+
+## Checklist
+
+- [X] My code follows the code style of this project.
+- [ ] I have run clang-format.
+- [ ] My change requires a change to the documentation.
+- [X] I have updated the documentation accordingly.
+- [X] I have read the **CONTRIBUTING** document.
+- [ ] I have added tests to cover my changes.
+- [ ] All new and existing tests passed, including in ASAN and TSAN modes (if available
on your platform).
+
+Reviewed By: AndreiFB
+
+Differential Revision: D33690057
+
+Pulled By: graphicsMan
+
+fbshipit-source-id: c5ca5f74471b53c07ba21463368846547b05340e
+---
+ CMakeLists.txt | 12 +++++-
+ README.md | 26 +++++++++++
+ cmake/DispensoConfig.cmake.in | 9 ++++
+ dispenso/CMakeLists.txt | 81 +++++++++++++++++++++++++++++++++--
+ 4 files changed, 123 insertions(+), 5 deletions(-)
+ create mode 100644 cmake/DispensoConfig.cmake.in
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 3b0c55a..cd1efd7 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -1,10 +1,20 @@
+ cmake_minimum_required(VERSION 3.12)
+ project(
+ Dispenso
+- VERSION 0.1.0
++ VERSION 1.0.0
+ DESCRIPTION "Dispenso is a library for working with sets of parallel tasks"
+ LANGUAGES CXX)
+
++if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
++ set(DISPENSO_STANDALONE TRUE)
++else()
++ set(DISPENSO_STANDALONE FALSE)
++endif()
++
++if (DISPENSO_STANDALONE)
++ include(GNUInstallDirs)
++endif()
++
+ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules)
+
+ # Main project setup
+diff --git a/README.md b/README.md
+index 300dad4..c910c36 100644
+--- a/README.md
++++ b/README.md
+@@ -186,6 +186,32 @@ Install Build Tools for Visual Studio. All commands should be run
from the Devel
+ 1. `cmake PATH_TO_DISPENSO_ROOT`
+ 1. `cmake --build . --config Release`
+
++## Install dispenso
++
++Once built, the library can be installed by building the "install" target.
++Typically on Linux and MacOS, this is done with
++
++`make install`
++
++On Windows (and works on any platfrom), instead do
++
++`cmake --build . --target install`
++
++## Use an installed dispenso
++
++Once installed, a downstream CMake project can be pointed to it by using
++`CMAKE_PREFIX_PATH` or `Dispenso_DIR`, either as an environment variable or
++CMake variable. All that is required to use the library is link the imported
++CMake target `Dispenso::dispenso`, which might look like
++
++```cmake
++find_pacakge(Dispenso REQUIRED)
++target_link_libraries(myDispensoApp Dispenso::dispenso)
++```
++
++This brings in all required include paths, library files to link, and any other
++properties to the `myDispensoApp` target (your library or application).
++
+ <div id='testing'/>
+
+ # Building and running dispenso tests
+diff --git a/cmake/DispensoConfig.cmake.in b/cmake/DispensoConfig.cmake.in
+new file mode 100644
+index 0000000..be51b4b
+--- /dev/null
++++ b/cmake/DispensoConfig.cmake.in
+@@ -0,0 +1,9 @@
++@PACKAGE_INIT@
++
++include(CMakeFindDependencyMacro)
++
++find_dependency(Threads)
++
++include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME(a)_Exports.cmake")
++
++check_required_components("@PROJECT_NAME@")
+diff --git a/dispenso/CMakeLists.txt b/dispenso/CMakeLists.txt
+index 9742dd1..0ac5be5 100644
+--- a/dispenso/CMakeLists.txt
++++ b/dispenso/CMakeLists.txt
+@@ -19,12 +19,85 @@ target_compile_options(dispenso PRIVATE
+ $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion -Werror>
+ )
+
+-target_include_directories(dispenso PUBLIC .. third-party/moodycamel
"${PROJECT_BINARY_DIR}")
++target_include_directories(dispenso
++PUBLIC
++ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/..>
++ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/third-party/moodycamel>
++ $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
++ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
++
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/dispenso/third-party/moodycamel>
++)
++
++set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
++set(THREADS_PREFER_PTHREAD_FLAG TRUE)
++find_package(Threads REQUIRED)
++target_link_libraries(dispenso PUBLIC Threads::Threads)
+
+ if(WIN32)
+- target_link_libraries(dispenso Synchronization)
+-else()
+- target_link_libraries(dispenso pthread)
++ target_link_libraries(dispenso PUBLIC Synchronization)
+ endif()
+
+ target_compile_features(dispenso PUBLIC cxx_std_14)
++
++if (NOT DISPENSO_STANDALONE)
++ return()
++endif()
++
++## Install library ##
++
++set_target_properties(dispenso
++ PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})
++
++install(TARGETS dispenso
++ EXPORT ${PROJECT_NAME}_Exports
++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
++ NAMELINK_SKIP
++ # on Windows put the dlls into bin
++ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
++ # ... and the import lib into the devel package
++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
++)
++
++install(EXPORT ${PROJECT_NAME}_Exports
++ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION}
++ NAMESPACE Dispenso::
++)
++
++install(TARGETS dispenso
++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
++ NAMELINK_ONLY
++ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
++)
++
++## Install headers ##
++
++install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
++ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
++ FILES_MATCHING
++ PATTERN *.h
++)
++
++## Generate and install CMake target exports ##
++
++include(CMakePackageConfigHelpers)
++
++configure_package_config_file(
++ "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
++ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
++INSTALL_DESTINATION
++ ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION}
++)
++
++write_basic_package_version_file(
++ "${PROJECT_NAME}ConfigVersion.cmake"
++ VERSION ${PROJECT_VERSION}
++ COMPATIBILITY SameMajorVersion
++)
++
++install(FILES
++ ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
++ ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
++DESTINATION
++ ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION}
++)
diff --git a/dispenso-1.0.0-fix-32bit-build.patch b/dispenso-1.0.0-fix-32bit-build.patch
new file mode 100644
index 0000000..f98486c
--- /dev/null
+++ b/dispenso-1.0.0-fix-32bit-build.patch
@@ -0,0 +1,67 @@
+diff -ruN dispenso-1.0.0/dispenso/concurrent_vector.h
/var/lib/mock/fedora-35-i686/root/builddir/build/BUILD/dispenso-1.0.0/dispenso/concurrent_vector.h
+--- dispenso-1.0.0/dispenso/concurrent_vector.h 2022-01-10 11:06:24.000000000 -0800
++++
/var/lib/mock/fedora-35-i686/root/builddir/build/BUILD/dispenso-1.0.0/dispenso/concurrent_vector.h 2022-01-25
14:25:35.667377338 -0800
+@@ -96,7 +96,11 @@
+ * petabytes, the class can use less space. Additionally, some minor optimizations
may be
+ * possible if the max size is less than 32-bits.
+ **/
++#if INTPTR_MAX == INT32_MAX
++ static constexpr size_t kMaxVectorSize = (size_t{1} << 31) / sizeof(T);
++#else
+ static constexpr size_t kMaxVectorSize = (size_t{1} << 47) / sizeof(T);
++#endif
+ };
+
+ /**
+diff -ruN dispenso-1.0.0/dispenso/parallel_for.h
/var/lib/mock/fedora-35-i686/root/builddir/build/BUILD/dispenso-1.0.0/dispenso/parallel_for.h
+--- dispenso-1.0.0/dispenso/parallel_for.h 2022-01-10 11:06:24.000000000 -0800
++++
/var/lib/mock/fedora-35-i686/root/builddir/build/BUILD/dispenso-1.0.0/dispenso/parallel_for.h 2022-01-25
10:47:00.483342592 -0800
+@@ -174,9 +174,9 @@
+ ParForOptions options) {
+ ssize_t numThreads = std::min<ssize_t>(taskSet.numPoolThreads(),
options.maxThreads);
+ // Reduce threads used if they exceed work to be done.
+- numThreads = std::min<ssize_t>(numThreads, range.size());
++ numThreads = std::min<ssize_t>(numThreads, ssize_t(range.size()));
+
+- auto chunking = detail::staticChunkSize(range.size(), numThreads);
++ auto chunking = detail::staticChunkSize(ssize_t(range.size()), numThreads);
+ IntegerT chunkSize = static_cast<IntegerT>(chunking.ceilChunkSize);
+
+ bool perfectlyChunked = chunking.transitionTaskIndex == numThreads;
+@@ -235,13 +235,13 @@
+ ParForOptions options) {
+ ssize_t numThreads = std::min<ssize_t>(taskSet.numPoolThreads(),
options.maxThreads);
+ // Reduce threads used if they exceed work to be done.
+- numThreads = std::min<ssize_t>(numThreads, range.size());
++ numThreads = std::min<ssize_t>(numThreads, ssize_t(range.size()));
+
+ for (ssize_t i = 0; i < numThreads; ++i) {
+ states.emplace_back(defaultState());
+ }
+
+- auto chunking = detail::staticChunkSize(range.size(), numThreads);
++ auto chunking = detail::staticChunkSize(ssize_t(range.size()), numThreads);
+ IntegerT chunkSize = static_cast<IntegerT>(chunking.ceilChunkSize);
+
+ bool perfectlyChunked = chunking.transitionTaskIndex == numThreads;
+diff -ruN dispenso-1.0.0/tests/concurrent_vector_test_common.h
/var/lib/mock/fedora-35-i686/root/builddir/build/BUILD/dispenso-1.0.0/tests/concurrent_vector_test_common.h
+--- dispenso-1.0.0/tests/concurrent_vector_test_common.h 2022-01-10 11:06:24.000000000
-0800
++++
/var/lib/mock/fedora-35-i686/root/builddir/build/BUILD/dispenso-1.0.0/tests/concurrent_vector_test_common.h 2022-01-25
11:57:03.783428119 -0800
+@@ -321,7 +321,7 @@
+
+ EXPECT_LE(
+ maxCapacity,
+- 2 * std::max<size_t>(dispenso::detail::nextPow2(vec.size() + 1),
vec.default_capacity()))
++ 2 * std::max<size_t>(size_t(dispenso::detail::nextPow2(vec.size() + 1)),
vec.default_capacity()))
+ << "Num: " << num << " default: " <<
vec.default_capacity();
+
+ for (int i = 0; i < num / 2; ++i) {
+@@ -338,7 +338,7 @@
+
+ EXPECT_LE(
+ afterPopNShrinkCap,
+- 2 * std::max<size_t>(dispenso::detail::nextPow2(vec.size() + 1),
vec.default_capacity()))
++ 2 * std::max<size_t>(size_t(dispenso::detail::nextPow2(vec.size() + 1)),
vec.default_capacity()))
+ << "Size: " << vec.size() << " Num: "
<< num << " default: " << vec.default_capacity();
+
+ vec.clear();
diff --git a/dispenso-1.0.0-use-system-gtest.patch
b/dispenso-1.0.0-use-system-gtest.patch
new file mode 100644
index 0000000..508f1d2
--- /dev/null
+++ b/dispenso-1.0.0-use-system-gtest.patch
@@ -0,0 +1,29 @@
+diff -ruN dispenso-1.0.0/tests/CMakeLists.txt
dispenso-1.0.0-use-system-gtest/tests/CMakeLists.txt
+--- dispenso-1.0.0/tests/CMakeLists.txt 2022-01-10 11:06:24.000000000 -0800
++++ dispenso-1.0.0-use-system-gtest/tests/CMakeLists.txt 2022-01-24 17:27:03.304763955
-0800
+@@ -4,16 +4,8 @@
+ # Ideally these tests are all run in (Release, Debug) X (N/A, TSAN, ASAN,
-fno-exceptions)
+ ###
+
+-include(FetchContent)
+-FetchContent_Declare(
+- GoogleTest
+- GIT_REPOSITORY
https://github.com/google/googletest.git
+- GIT_TAG release-1.10.0
+-)
+-
+ # For Windows, Prevent overriding the parent project's compiler/linker settings
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+-FetchContent_MakeAvailable(GoogleTest)
+ include(GoogleTest)
+
+ macro(package_add_test TESTNAME)
+@@ -23,7 +15,7 @@
+ $<$<CXX_COMPILER_ID:MSVC>:/W3 /WX>
+ $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -pedantic
-Wconversion -Wno-sign-conversion -Werror>
+ )
+- target_link_libraries(${TESTNAME} gmock_main dispenso)
++ target_link_libraries(${TESTNAME} gmock_main gtest dispenso)
+ gtest_discover_tests(${TESTNAME}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
diff --git a/dispenso.spec b/dispenso.spec
new file mode 100644
index 0000000..b9482d9
--- /dev/null
+++ b/dispenso.spec
@@ -0,0 +1,106 @@
+%ifarch %{arm} %{ix86}
+# need to sort out tests, only 90% pass
+%bcond_with check
+%else
+# 64-bit architectures
+%if 0%{?fedora} && 0%{?fedora} < 36
+%bcond_without check
+%else
+# EPEL 8 has gtest 1.8.0, too old
+# EPEL 9 and Fedora 36 has gtest 1.11.0, API is different
+%bcond_with check
+%endif
+%endif
+
+%if 0%{?el8}
+%undefine __cmake_in_source_build
+%endif
+
+Name: dispenso
+Version: 1.0.0
+Release: %{autorelease}
+Summary: A library for working with sets of tasks in parallel
+
+License: MIT
+URL:
https://github.com/facebookincubator/dispenso
+Source0: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz
+# allow Dispenso to be installed and fix its version
+Patch0:
%{url}/commit/d00e7402ffcc780df11024f8d2285c153b7635b1.patch#/%{name}-1.0.0-add-install.patch
+# TODO: make toggleable and upstream
+Patch1: %{name}-1.0.0-use-system-gtest.patch
+Patch2: %{name}-1.0.0-fix-32bit-build.patch
+
+BuildRequires: cmake
+BuildRequires: gcc-c++
+%if %{with check}
+BuildRequires: gmock-devel
+BuildRequires: (gtest-devel >= 1.10.0 with gtest-devel < 1.11.0)
+%endif
+
+%global _description %{expand:
+Dispenso is a library for working with sets of tasks in parallel. It provides
+mechanisms for thread pools, task sets, parallel for loops, futures, pipelines,
+and more. Dispenso is a well-tested C++14 library designed to have minimal
+dependencies (some dependencies are required for the tests and benchmarks), and
+designed to be clean with compiler sanitizers (ASAN, TSAN). Dispenso is
+currently being used in dozens of projects and hundreds of C++ files at Meta
+(formerly Facebook). Dispenso also aims to avoid major disruption at every
+release. Releases will be made such that major versions are created when a
+backward incompatibility is introduced, and minor versions are created when
+substantial features have been added or bugs have been fixed, and the aim would
+be to only very rarely bump major versions. That should make the project
+suitable for use from main branch, or if you need a harder requirement, you can
+base code on a specific version.}
+
+%description %{_description}
+
+
+%package devel
+Summary: Development files for %{name}
+Requires: %{name}%{?_isa} = %{version}-%{release}
+# Requires: cmake-filesystem
+
+%description devel %{_description}
+
+The %{name}-devel package contains libraries and header files for
+developing applications that use %{name}.
+
+
+%prep
+%autosetup -p1
+
+
+%build
+%cmake \
+%if %{with check}
+ -DDISPENSO_BUILD_TESTS=ON \
+%else
+ %{nil}
+%endif
+
+%cmake_build
+
+
+%install
+%cmake_install
+
+
+%if %{with check}
+%check
+%ctest
+%endif
+
+
+%files
+%license LICENSE
+%{_libdir}/*.so.*
+
+%files devel
+%doc CODE_OF_CONDUCT.md CONTRIBUTING.md README.md
+%{_includedir}/*
+%{_libdir}/*.so
+%{_libdir}/cmake/Dispenso-%{version}
+
+
+%changelog
+%autochangelog
diff --git a/sources b/sources
new file mode 100644
index 0000000..ea63382
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+SHA512 (dispenso-1.0.0.tar.gz) =
e110f8620784b29bf1c41e3f611a775ebe00bf8106a61cbac48a2d1bac8aee05273d8d787ae57ec23143716606c7227d3d6f609df08e965019f77e9b0577f640