diff --git a/recipes/qpdf/all/conandata.yml b/recipes/qpdf/all/conandata.yml new file mode 100644 index 0000000000000..6a5e7ead04475 --- /dev/null +++ b/recipes/qpdf/all/conandata.yml @@ -0,0 +1,12 @@ +sources: + "11.1.1": + url: "https://github.com/qpdf/qpdf/archive/refs/tags/v11.1.1.tar.gz" + sha256: "785edab622a1bc7e25e1537ad2c325005d48c5c7957f7abedff405deb80fa59a" +patches: + "11.1.1": + - patch_file: "patches/0001-libqpdf-cmake-deps-jpeg-zlib.patch" + patch_description: "Inject Conan Deps, disable qpdf-dep handling: update libqpdf/CMakeLists.txt by disabling cmake fails, caused by pkg_config fail to find dependencies. Add conan generated cmake dependencies instead." + patch_type: "conan" + - patch_file: "patches/0002-exclude-unnecessary-cmake-subprojects.patch" + patch_description: "Exclude unnecessary targets: update CMakeLists.txt removing subdir includes for binaries, tests, examples, docs and fuzzing" + patch_type: "conan" diff --git a/recipes/qpdf/all/conanfile.py b/recipes/qpdf/all/conanfile.py new file mode 100644 index 0000000000000..749ef210e85f2 --- /dev/null +++ b/recipes/qpdf/all/conanfile.py @@ -0,0 +1,181 @@ +from conan import ConanFile +from conan.errors import ConanInvalidConfiguration +from conan.tools.build import check_min_cppstd +from conan.tools.scm import Version +from conan.tools.files import replace_in_file, apply_conandata_patches, export_conandata_patches, get, copy, rmdir +from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout +from conan.tools.microsoft import is_msvc, check_min_vs +from conan.tools.env import VirtualBuildEnv +import os + +required_conan_version = ">=1.52.0" + +class PackageConan(ConanFile): + name = "qpdf" + description = "QPDF is a command-line tool and C++ library that performs content-preserving transformations on PDF files." + license = "Apache-2.0" + url = "https://github.com/conan-io/conan-center-index" + homepage = "https://github.com/qpdf/qpdf" + topics = ("pdf") + settings = "os", "arch", "compiler", "build_type" + options = { + "shared": [True, False], + "fPIC": [True, False], + "with_ssl": ["internal", "openssl", "gnutls"], + "with_jpeg": ["libjpeg", "libjpeg-turbo", "mozjpeg"], + } + default_options = { + "shared": False, + "fPIC": True, + "with_ssl": "openssl", + "with_jpeg": "libjpeg", + } + + @property + def _minimum_cpp_standard(self): + return 14 + + @property + def _compilers_minimum_version(self): + return { + "gcc": "5", + "clang": "3.4", + "apple-clang": "10", + } + + def export_sources(self): + export_conandata_patches(self) + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def configure(self): + if self.options.shared: + try: + del self.options.fPIC + except Exception: + pass + + def layout(self): + cmake_layout(self, src_folder="src") + + def requirements(self): + # https://qpdf.readthedocs.io/en/stable/installation.html#basic-dependencies + self.requires("zlib/1.2.13") + if self.options.with_ssl == "openssl": + self.requires("openssl/1.1.1q") + elif self.options.with_ssl == "gnutls": + raise ConanInvalidConfiguration("GnuTLS is not available in Conan Center yet.") + if self.options.with_jpeg == "libjpeg": + self.requires("libjpeg/9e") + elif self.options.with_jpeg == "libjpeg-turbo": + self.requires("libjpeg-turbo/2.1.4") + elif self.options.with_jpeg == "mozjpeg": + self.requires("mozjpeg/4.1.1") + + + def validate(self): + if self.info.settings.compiler.cppstd: + check_min_cppstd(self, self._minimum_cpp_standard) + if is_msvc(self): + check_min_vs(self, "150") + else: + minimum_version = self._compilers_minimum_version.get(str(self.info.settings.compiler), False) + if minimum_version and Version(self.info.settings.compiler.version) < minimum_version: + raise ConanInvalidConfiguration(f"{self.ref} requires C++{self._minimum_cpp_standard}, which your compiler does not support.") + + def build_requirements(self): + self.tool_requires("cmake/3.24.1") + self.tool_requires("pkgconf/1.9.3") + + def source(self): + get(self, **self.conan_data["sources"][self.version], + destination=self.source_folder, strip_root=True) + + def generate(self): + tc = CMakeToolchain(self) + tc.variables["BUILD_STATIC_LIBS"] = not self.options.shared + # https://qpdf.readthedocs.io/en/latest/installation.html#build-time-crypto-selection + tc.variables["USE_IMPLICIT_CRYPTO"] = False + tc.cache_variables["CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP"] = True + if self.options.with_ssl == "internal": + tc.variables["REQUIRE_CRYPTO_NATIVE"] = True + tc.variables["REQUIRE_CRYPTO_GNUTLS"] = False + tc.variables["REQUIRE_CRYPTO_OPENSSL"] = False + if self.options.with_ssl == "openssl": + tc.variables["REQUIRE_CRYPTO_NATIVE"] = False + tc.variables["REQUIRE_CRYPTO_GNUTLS"] = False + tc.variables["REQUIRE_CRYPTO_OPENSSL"] = True + if self.options.with_ssl == "gnutls": + tc.variables["REQUIRE_CRYPTO_NATIVE"] = False + tc.variables["REQUIRE_CRYPTO_GNUTLS"] = True + tc.variables["REQUIRE_CRYPTO_OPENSSL"] = False + tc.generate() + # TODO: after https://github.com/conan-io/conan/issues/11962 is solved + # we might obsolete here cmake deps generation and cmake patching and get + # the possibility to go for to pkg_config based dependency discovery instead. + # At the moment, even with the linked work-around, the linkage is mixed-up + tc = CMakeDeps(self) + tc.generate() + tc = VirtualBuildEnv(self) + tc.generate(scope="build") + + def _patch_sources(self): + apply_conandata_patches(self) + # we generally expect to have one crypto in-place, but need to patch the found mechanics + # since we avoid currently the correct pkg_config + replace_in_file(self, os.path.join(self.source_folder, "libqpdf", "CMakeLists.txt"), + "set(FOUND_CRYPTO OFF)", "set(FOUND_CRYPTO ON)") + if self.options.with_ssl == "openssl": + replace_in_file(self, os.path.join(self.source_folder, "libqpdf", "CMakeLists.txt"), + "set(USE_CRYPTO_OPENSSL OFF)", "set(USE_CRYPTO_OPENSSL ON)") + replace_in_file(self, os.path.join(self.source_folder, "libqpdf", "CMakeLists.txt"), + "find_package(ZLIB REQUIRED)", + "find_package(ZLIB REQUIRED)\nfind_package(OpenSSL REQUIRED)\n") + replace_in_file(self, os.path.join(self.source_folder, "libqpdf", "CMakeLists.txt"), + "PUBLIC JPEG::JPEG ZLIB::ZLIB", "PUBLIC JPEG::JPEG ZLIB::ZLIB OpenSSL::SSL") + if self.options.with_ssl == "gnutls": + replace_in_file(self, os.path.join(self.source_folder, "libqpdf", "CMakeLists.txt"), + "set(USE_CRYPTO_GNUTLS OFF)", "set(USE_CRYPTO_GNUTLS ON)") + if self.options.with_ssl == "internal": + replace_in_file(self, os.path.join(self.source_folder, "libqpdf", "CMakeLists.txt"), + "set(USE_CRYPTO_NATIVE OFF)", "set(USE_CRYPTO_NATIVE ON)") + + def build(self): + self._patch_sources() + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + copy(self, pattern="LICENSE.txt", dst=os.path.join(self.package_folder, "licenses"), src=self.source_folder) + cmake = CMake(self) + cmake.install() + + rmdir(self, os.path.join(self.package_folder, "lib", "pkgconfig")) + rmdir(self, os.path.join(self.package_folder, "lib", "cmake")) + rmdir(self, os.path.join(self.package_folder, "share")) + + def package_info(self): + self.cpp_info.set_property("cmake_file_name", "qpdf") + + self.cpp_info.components["libqpdf"].libs = ["qpdf"] + self.cpp_info.components["libqpdf"].set_property("pkg_config_name", "libqpdf") + self.cpp_info.components["libqpdf"].set_property("cmake_target_name", "qpdf::libqpdf") + self.cpp_info.components["libqpdf"].requires.append("zlib::zlib") + self.cpp_info.components["libqpdf"].requires.append(f"{self.options.with_jpeg}::{self.options.with_jpeg}") + + if self.options.with_ssl == "openssl": + self.cpp_info.components["libqpdf"].requires.append("openssl::openssl") + + if self.settings.os in ["Linux", "FreeBSD"]: + self.cpp_info.components["libqpdf"].system_libs.append("m") + + # TODO: to remove in conan v2 once cmake_find_package_* generators removed + self.cpp_info.filenames["cmake_find_package"] = "qpdf" + self.cpp_info.filenames["cmake_find_package_multi"] = "qpdf" + self.cpp_info.names["cmake_find_package"] = "qpdf" + self.cpp_info.names["cmake_find_package_multi"] = "qpdf" + self.cpp_info.components["libqpdf"].names["cmake_find_package"] = "libqpdf" + self.cpp_info.components["libqpdf"].names["cmake_find_package_multi"] = "libqpdf" diff --git a/recipes/qpdf/all/patches/0001-libqpdf-cmake-deps-jpeg-zlib.patch b/recipes/qpdf/all/patches/0001-libqpdf-cmake-deps-jpeg-zlib.patch new file mode 100644 index 0000000000000..0d8edceb557a6 --- /dev/null +++ b/recipes/qpdf/all/patches/0001-libqpdf-cmake-deps-jpeg-zlib.patch @@ -0,0 +1,113 @@ +diff --git a/libqpdf/CMakeLists.txt b/libqpdf/CMakeLists.txt +index 7053e205..9f5962f7 100644 +--- a/libqpdf/CMakeLists.txt ++++ b/libqpdf/CMakeLists.txt +@@ -128,13 +128,8 @@ include(CheckSymbolExists) + set(dep_include_directories) + set(dep_link_directories) + set(dep_link_libraries) +-set(ANYTHING_MISSING 0) + +-if(WIN32 AND (EXISTS ${qpdf_SOURCE_DIR}/external-libs)) +- set(EXTERNAL_LIBS 1) +-else() +- set(EXTERNAL_LIBS 0) +-endif() ++set(EXTERNAL_LIBS 0) + + if(EXTERNAL_LIBS) + set(EXTLIBDIR ${qpdf_SOURCE_DIR}/external-libs) +@@ -161,9 +156,6 @@ if(NOT EXTERNAL_LIBS) + if(ZLIB_H_PATH AND ZLIB_LIB_PATH) + list(APPEND dep_include_directories ${ZLIB_H_PATH}) + list(APPEND dep_link_libraries ${ZLIB_LIB_PATH}) +- else() +- message(SEND_ERROR "zlib not found") +- set(ANYTHING_MISSING 1) + endif() + endif() + endif() +@@ -182,9 +174,6 @@ if(NOT EXTERNAL_LIBS) + list(APPEND dep_include_directories ${LIBJPEG_H_PATH}) + list(APPEND dep_link_libraries ${LIBJPEG_LIB_PATH}) + set(JPEG_INCLUDE ${LIBJPEG_H_PATH}) +- else() +- message(SEND_ERROR "libjpeg not found") +- set(ANYTHING_MISSING 1) + endif() + endif() + endif() +@@ -220,9 +209,6 @@ if(USE_IMPLICIT_CRYPTO OR REQUIRE_CRYPTO_OPENSSL) + list(APPEND dep_link_libraries ${OPENSSL_LIB_PATH}) + set(USE_CRYPTO_OPENSSL ON) + set(FOUND_CRYPTO ON) +- elseif(REQUIRE_CRYPTO_OPENSSL) +- message(SEND_ERROR "openssl not found") +- set(ANYTHING_MISSING 1) + endif() + endif() + endif() +@@ -241,9 +227,6 @@ if(USE_IMPLICIT_CRYPTO OR REQUIRE_CRYPTO_GNUTLS) + list(APPEND dep_link_libraries ${GNUTLS_LIB_PATH}) + set(USE_CRYPTO_GNUTLS ON) + set(FOUND_CRYPTO ON) +- elseif(REQUIRE_CRYPTO_GNUTLS) +- message(SEND_ERROR "gnutls not found") +- set(ANYTHING_MISSING 1) + endif() + endif() + endif() +@@ -268,14 +251,9 @@ if(FOUND_CRYPTO) + set(DEFAULT_CRYPTO "native") + endif() + endif() +-else() +- message(SEND_ERROR "no crypto provider is available") +- set(ANYTHING_MISSING 1) +-endif() +-if(ANYTHING_MISSING) +- message(FATAL_ERROR "Missing dependencies; unable to continue") + endif() + ++ + message(STATUS "") + message(STATUS "*** Crypto Summary ***") + message(STATUS " GNU TLS crypto enabled: " ${USE_CRYPTO_GNUTLS}) +@@ -403,6 +381,10 @@ endif() + # use PIC for the object library so we don't have to compile twice. + set(OBJECT_LIB libqpdf_object) + add_library(${OBJECT_LIB} OBJECT ${libqpdf_SOURCES}) ++find_package(JPEG REQUIRED) ++find_package(ZLIB REQUIRED) ++target_link_libraries(${OBJECT_LIB} PUBLIC JPEG::JPEG ZLIB::ZLIB) ++ + if(OBJECT_LIB_IS_PIC) + target_compile_definitions(${OBJECT_LIB} PRIVATE libqpdf_EXPORTS) + endif() +@@ -498,8 +480,6 @@ if(BUILD_SHARED_LIBS) + PUBLIC + $ + $) +- target_link_directories(${SHARED_LIB} PRIVATE ${dep_link_directories}) +- target_link_libraries(${SHARED_LIB} PRIVATE ${dep_link_libraries}) + if(ATOMIC_LIBRARY) + target_link_libraries(${SHARED_LIB} PRIVATE ${ATOMIC_LIBRARY}) + endif() +@@ -507,6 +487,8 @@ if(BUILD_SHARED_LIBS) + target_link_options(${SHARED_LIB} PRIVATE ${LD_VERSION_FLAGS}) + endif() + ++ target_link_libraries(${SHARED_LIB} PUBLIC JPEG::JPEG ZLIB::ZLIB) ++ + target_include_directories(${SHARED_LIB} + PRIVATE ${qpdf_SOURCE_DIR}/libqpdf ${CMAKE_CURRENT_BINARY_DIR}) + +@@ -544,6 +526,8 @@ if(BUILD_STATIC_LIBS) + target_link_libraries(${STATIC_LIB} INTERFACE ${ATOMIC_LIBRARY}) + endif() + ++ target_link_libraries(${STATIC_LIB} PUBLIC JPEG::JPEG ZLIB::ZLIB) ++ + # Avoid name clashes on Windows with the the DLL import library. + if(NOT DEFINED STATIC_SUFFIX AND BUILD_SHARED_LIBS) + if (WIN32) diff --git a/recipes/qpdf/all/patches/0002-exclude-unnecessary-cmake-subprojects.patch b/recipes/qpdf/all/patches/0002-exclude-unnecessary-cmake-subprojects.patch new file mode 100644 index 0000000000000..2e40ca26d687e --- /dev/null +++ b/recipes/qpdf/all/patches/0002-exclude-unnecessary-cmake-subprojects.patch @@ -0,0 +1,17 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5c0915f3..6c4945d3 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -323,12 +323,6 @@ add_test( + # add_subdirectory order affects test order + add_subdirectory(include) + add_subdirectory(libqpdf) +-add_subdirectory(qpdf) +-add_subdirectory(libtests) +-add_subdirectory(examples) +-add_subdirectory(zlib-flate) +-add_subdirectory(manual) +-add_subdirectory(fuzz) + + # We don't need to show everything -- just the things that we really + # need to be sure are right or that are turned on or off with complex diff --git a/recipes/qpdf/all/test_package/CMakeLists.txt b/recipes/qpdf/all/test_package/CMakeLists.txt new file mode 100644 index 0000000000000..2892d11ff1f83 --- /dev/null +++ b/recipes/qpdf/all/test_package/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.8) + +project(test_package CXX) + +find_package(qpdf REQUIRED CONFIG) + +add_executable(${PROJECT_NAME} test_package.cpp) +target_link_libraries(${PROJECT_NAME} PRIVATE qpdf::libqpdf) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +# msvc has problems consuming #warning macro +# therefore we need a code-path in the include avoiding this warning https://github.com/qpdf/qpdf/issues/804 +target_compile_definitions(${PROJECT_NAME} PUBLIC POINTERHOLDER_TRANSITION=4) diff --git a/recipes/qpdf/all/test_package/conanfile.py b/recipes/qpdf/all/test_package/conanfile.py new file mode 100644 index 0000000000000..57b63cfec4d16 --- /dev/null +++ b/recipes/qpdf/all/test_package/conanfile.py @@ -0,0 +1,24 @@ +from conan import ConanFile +from conan.tools.build import can_run +from conan.tools.cmake import cmake_layout, CMake +import os + +class TestPackageConan(ConanFile): + settings = "os", "arch", "compiler", "build_type" + generators = "CMakeDeps", "CMakeToolchain", "VirtualRunEnv" + test_type = "explicit" + + def requirements(self): + self.requires(self.tested_reference_str) + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def test(self): + if can_run(self): + self.run(os.path.join(self.cpp.build.bindirs[0], "test_package"), env="conanrun") diff --git a/recipes/qpdf/all/test_package/test_package.cpp b/recipes/qpdf/all/test_package/test_package.cpp new file mode 100644 index 0000000000000..f79386dd766bb --- /dev/null +++ b/recipes/qpdf/all/test_package/test_package.cpp @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + std::cout << "QPDF_VERSION " << QPDF_VERSION << "\n"; + + try { + QPDF pdf; + pdf.emptyPDF(); + QPDFWriter w(pdf, "empty_example.pdf"); + w.write(); + } catch (std::exception& e) { + std::cerr << e.what() << "\n"; + exit(2); + } + + return 0; +} diff --git a/recipes/qpdf/all/test_v1_package/CMakeLists.txt b/recipes/qpdf/all/test_v1_package/CMakeLists.txt new file mode 100644 index 0000000000000..ccb1cc676af03 --- /dev/null +++ b/recipes/qpdf/all/test_v1_package/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.8) + +project(test_package CXX) + +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup(TARGETS) + +find_package(qpdf REQUIRED CONFIG) + +add_executable(${PROJECT_NAME} ../test_package/test_package.cpp) +target_link_libraries(${PROJECT_NAME} PRIVATE qpdf::libqpdf) +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) +# msvc has problems consuming #warning macro +# therefore we need a code-path in the include avoiding this warning https://github.com/qpdf/qpdf/issues/804 +target_compile_definitions(${PROJECT_NAME} PUBLIC POINTERHOLDER_TRANSITION=4) diff --git a/recipes/qpdf/all/test_v1_package/conanfile.py b/recipes/qpdf/all/test_v1_package/conanfile.py new file mode 100644 index 0000000000000..c492184eec19c --- /dev/null +++ b/recipes/qpdf/all/test_v1_package/conanfile.py @@ -0,0 +1,19 @@ +from conans import ConanFile, CMake +from conan.tools.build import cross_building +import os + + +# legacy validation with Conan 1.x +class TestPackageV1Conan(ConanFile): + settings = "os", "arch", "compiler", "build_type" + generators = "cmake", "cmake_find_package_multi" + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def test(self): + if not cross_building(self): + bin_path = os.path.join("bin", "test_package") + self.run(bin_path, run_environment=True) diff --git a/recipes/qpdf/config.yml b/recipes/qpdf/config.yml new file mode 100644 index 0000000000000..35a13df3f72ef --- /dev/null +++ b/recipes/qpdf/config.yml @@ -0,0 +1,3 @@ +versions: + "11.1.1": + folder: all