From b8aff90997116a84350018f88f1eabdaa368d11b Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sat, 5 Mar 2022 08:24:51 -0500 Subject: [PATCH] Add cmake configuration files --- CMakeLists.txt | 328 +++++++++++++++++++++ cmake/qpdfCheckFlag.cmake | 21 ++ examples/CMakeLists.txt | 52 ++++ fuzz/CMakeLists.txt | 178 ++++++++++++ include/CMakeLists.txt | 12 + libqpdf/CMakeLists.txt | 581 ++++++++++++++++++++++++++++++++++++++ libtests/CMakeLists.txt | 53 ++++ manual/CMakeLists.txt | 133 +++++++++ qpdf/CMakeLists.txt | 78 +++++ qpdfConfig.cmake.in | 2 + zlib-flate/CMakeLists.txt | 16 ++ 11 files changed, 1454 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/qpdfCheckFlag.cmake create mode 100644 examples/CMakeLists.txt create mode 100644 fuzz/CMakeLists.txt create mode 100644 include/CMakeLists.txt create mode 100644 libqpdf/CMakeLists.txt create mode 100644 libtests/CMakeLists.txt create mode 100644 manual/CMakeLists.txt create mode 100644 qpdf/CMakeLists.txt create mode 100644 qpdfConfig.cmake.in create mode 100644 zlib-flate/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..d3284626 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,328 @@ +# manual/installation.rst mentions the minimum cmake version. +cmake_minimum_required(VERSION 3.16) + +# make_dist expects the version line to be on a line by itself after +# the project line. +project(qpdf + VERSION 10.6.3 + LANGUAGES C CXX) + +# Enable correct rpath handling for MacOSX +cmake_policy(SET CMP0042 NEW) +# Honor CMAKE_REQUIRED_LIBRARIES when checking for include files +cmake_policy(SET CMP0075 NEW) + +# *** OPTIONS *** + +# Keep all options here. It's easier to see the interdependencies this +# way than spreading them throughout the files. +# +# ***** Keep manual/installation.rst (_build-options) up to date. ***** + +include(CMakeDependentOption) + +# CMAKE_DEPENDENT_OPTION( +# OPTION "Description" default-value-if-visible +# "when-visible" value-if-not-visible) + +# Don't write tests based on MAINTAINER_MODE or CI_MODE. Instead, use +# them as the basis for dependent options. + +option(MAINTAINER_MODE "Set options for developing qpdf" OFF) +CMAKE_DEPENDENT_OPTION( + CI_MODE "Set options for running in CI" OFF + "NOT MAINTAINER_MODE" OFF) +CMAKE_DEPENDENT_OPTION( + WERROR "Treat compiler warnings as errors" OFF + "NOT MAINTAINER_MODE; NOT CI_MODE" ON) +CMAKE_DEPENDENT_OPTION( + GENERATE_AUTO_JOB "Automatically regenerate job files" OFF + "NOT MAINTAINER_MODE" ON) +CMAKE_DEPENDENT_OPTION( + SHOW_FAILED_TEST_OUTPUT "Show qtest output on failure" OFF + "NOT CI_MODE" ON) + +# To allow building doc to be disabled in maintainer mode, handle the +# condition manually rather than using a dependent option. +if(MAINTAINER_MODE) + set(default_BUILD_DOC ON) +else() + set(default_BUILD_DOC OFF) +endif() +option(BUILD_DOC "Build documentation" ${default_BUILD_DOC}) +# The values of BUILD_DOC_HTML and BUILD_DOC_PDF are ignored without +# BUILD_DOC, so setting them to ON when not visible forces them to be +# on in MAINTAINER_MODE and is harmless if BUILD_DOC is off. +CMAKE_DEPENDENT_OPTION( + BUILD_DOC_HTML "Build HTML documentation" + ON "BUILD_DOC;NOT MAINTAINER_MODE" ON) +CMAKE_DEPENDENT_OPTION( + BUILD_DOC_PDF "Build PDF documentation" + ON "BUILD_DOC;NOT MAINTAINER_MODE" ON) +CMAKE_DEPENDENT_OPTION( + BUILD_DOC_DIST "Create distribution of manual" ON + "BUILD_DOC_PDF;BUILD_DOC_HTML" OFF) + +option(BUILD_SHARED_LIBS "Build qpdf shared libraries" ON) +option(BUILD_STATIC_LIBS "Build qpdf static libraries" ON) +option(QTEST_COLOR "Whether qtest's output should be in color" ON) +option(USE_INSECURE_RANDOM "Use insecure random numbers" OFF) +option(SKIP_OS_SECURE_RANDOM + "Suppress use of OS-provided secure random numbers" OFF) +CMAKE_DEPENDENT_OPTION( + AVOID_WINDOWS_HANDLE "Avoid use of HANDLE in Windows" OFF + "WIN32" OFF) + +option(OSS_FUZZ "Specific build configuration for the oss-fuzz project" OFF) + +option(USE_IMPLICIT_CRYPTO "Enable any available external crypto provider" ON) +CMAKE_DEPENDENT_OPTION( + ALLOW_CRYPTO_NATIVE "Allow native crypto as as fallback" ON + "USE_IMPLICIT_CRYPTO" OFF) +CMAKE_DEPENDENT_OPTION( + REQUIRE_CRYPTO_NATIVE "Require native crypto provider" OFF + "NOT MAINTAINER_MODE; NOT CI_MODE" ON) +option(REQUIRE_CRYPTO_OPENSSL "Require openssl crypto" OFF) +option(REQUIRE_CRYPTO_GNUTLS "Require gnutls crypto" OFF) +set(DEFAULT_CRYPTO CACHE STRING "") +option(DEFAULT_CRYPTO + "Specify default crypto; otherwise chosen automatically" "") + +# INSTALL_MANUAL is not dependent on building docs. When creating some +# distributions, we build the doc in one run, copy doc-dist in, and +# install it elsewhere. +option(INSTALL_MANUAL "Install documentation" OFF) + +option(INSTALL_PKGCONFIG "Install pkgconfig file" ON) +option(INSTALL_CMAKE_PACKAGE "Install cmake package files" ON) +option(INSTALL_EXAMPLES "Install example files" ON) + +# *** END OPTIONS *** + +if(NOT (BUILD_STATIC_LIBS OR BUILD_SHARED_LIBS)) + message( + FATAL_ERROR "At least one of static or shared libraries must be built") +endif() + +add_compile_definitions($<$:POINTERHOLDER_TRANSITION=2>) + +enable_testing() +set(RUN_QTEST perl ${qpdf_SOURCE_DIR}/run-qtest) + +if(WIN32) + find_program(COPY_COMMAND NAMES cp copy) + if(COPY_COMMAND STREQUAL "COPY_COMMAND-NOTFOUND") + set(COPY_COMMAND "copy") + endif() +else() + set(COPY_COMMAND "cp") +endif() + +# For a long time, qpdf used libtool's version system. We are no +# longer doing that, but to avoid potential conflict with older +# versions, continue to have the shared library symlink point to a +# file whose version shares minor and patch with the project version +# and major with the SOVERSION. Starting with the transition to cmake, +# increment SOVERSION every time we increment the project major +# version. This works because qpdf uses semantic versioning. qpdf 10.x +# was libqpdf28, so start from there. +math(EXPR qpdf_SOVERSION "${PROJECT_VERSION_MAJOR} + 18") +set(qpdf_LIBVERSION ${qpdf_SOVERSION}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR " +Please build with cmake in a subdirectory, e.g. + mkdir build + cmake .. + cmake --build . +Please remove CMakeCache.txt and the CMakeFiles directories.") +endif() + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +if(WIN32 AND NOT SKIP_OS_SECURE_RANDOM) + list(APPEND CMAKE_REQUIRED_LIBRARIES Advapi32) +endif() + +include(CheckCXXSourceCompiles) +function(check_atomic) + foreach(I 0 1) + if(I) + set(CMAKE_REQUIRED_LIBRARIES atomic) + endif() + check_cxx_source_compiles( + "#include +int main() { + static std::atomic a{0}; + a = a.fetch_add(1); + return 0; +}" + ATOMIC_WORKED${I}) + if(ATOMIC_WORKED0) + return() + endif() + endforeach() + if(ATOMIC_WORKED1) + list(APPEND CMAKE_REQUIRED_LIBRARIES atomic) + endif() +endfunction() +check_atomic() + +set(WINDOWS_WMAIN_COMPILE "") +set(WINDOWS_WMAIN_LINK "") +if(WIN32) + function(check_wmain) + foreach(I 0 1) + if(NOT WINDOWS_WMAIN_COMPILE) + if(I) + set(CMAKE_REQUIRED_LINK_OPTIONS -municode) + endif() + check_cxx_source_compiles( + "#include +#include +#include +extern \"C\" +int wmain(int argc, wchar_t* argv[]) +{ + size_t x = wcslen(argv[0]); + return 0; +} +" + WMAIN_WORKED${I}) + endif() + endforeach() + if(WMAIN_WORKED1 OR WMAIN_WORKED1) + set(WINDOWS_WMAIN_COMPILE -DWINDOWS_WMAIN PARENT_SCOPE) + if(WMAIN_WORKED1 AND NOT WMAIN_WORKED0) + set(WINDOWS_WMAIN_LINK -municode PARENT_SCOPE) + endif() + endif() + endfunction() + check_wmain() +endif() + +if(MSVC) + list(APPEND CMAKE_REQUIRED_LINK_OPTIONS -link setargv.obj) + list(APPEND WINDOWS_WMAIN_LINK -link wsetargv.obj) +endif() + +include(GNUInstallDirs) + +# Compiler flags +# +# **NOTE** -- each flag must have its own cache variable. + +include(qpdfCheckFlag) +if(WERROR) + if(MSVC) + add_compile_options(/WX) + else() + qpdf_maybe_add_flag(C -Werror flag_werror) + endif() +endif() + +if(MSVC) + # /Gy combines identical functions -- useful with C++ templates + add_compile_options(/Gy) + add_compile_options(/W3) # warning level 3 +else() + qpdf_maybe_add_flag(C -Wall flag_wall) + qpdf_maybe_add_flag(C -Wconversion flag_conversion) + qpdf_maybe_add_flag(C -Wsign-conversion flag_sign-conversion) + qpdf_maybe_add_flag(C -Wshadow=local flag_shadow_local) + qpdf_maybe_add_flag(CXX -Wold-style-cast flag_old-style-cast) +endif() + +# We don't include the jpeg library's include path in the PUBLIC +# interface for libqpdf since only Pl_DCT.hh requires it. This is +# documented. Some examples and tests use it though so we have to +# define it. CMakeLists.txt for libqpdf sets the value for +# JPEG_INCLUDE which can be selectively added to include paths other +# tools. +set(JPEG_INCLUDE) + +if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + set(WORDSIZE 64) +else() + set(WORDSIZE 32) +endif() +if(MSVC) + set(CPACK_SYSTEM_NAME "msvc${WORDSIZE}") +elseif(MINGW) + set(CPACK_SYSTEM_NAME "mingw${WORDSIZE}") +endif() +set(CPACK_RESOURCE_FILE_LICENSE "${qpdf_SOURCE_DIR}/LICENSE.txt") +set(CPACK_PACKAGE_HOMEPAGE_URL "https://qpdf.sourceforge.io/") +set(CPACK_NSIS_MUI_ICON "${qpdf_SOURCE_DIR}/logo/qpdf.ico") + +include(CPack) + +# Install components -- documented in _installation in +# manual/installation.rst. +set(COMPONENT_DEV "dev") +set(COMPONENT_LIB "lib") # runtime library +set(COMPONENT_CLI "cli") +set(COMPONENT_DOC "doc") +set(COMPONENT_EXAMPLES "examples") # example sources + +if(WIN32) + include(InstallRequiredSystemLibraries) +endif() + +# 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 +# logic. +get_property(MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +message(STATUS "") +message(STATUS "*** Summary ***") +message(STATUS " qpdf version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +if(MULTI_CONFIG) + message(STATUS " build type: specify --config at build time") +elseif(CMAKE_BUILD_TYPE) + message(STATUS " build type: ${CMAKE_BUILD_TYPE}") +endif() +message(STATUS " build shared libraries: ${BUILD_SHARED_LIBS}") +message(STATUS " build static libraries: ${BUILD_STATIC_LIBS}") +message(STATUS " build manual: ${BUILD_DOC}") +message(STATUS " compiler warnings are errors: ${WERROR}") +message(STATUS " system: ${CPACK_SYSTEM_NAME}") +message(STATUS "") +message(STATUS "*** Options Summary ***") +foreach(PROP + COMPILE_OPTIONS INTERFACE_COMPILE_OPTIONS + COMPILE_DEFINITIONS INTERFACE_COMPILE_DEFINITIONS + INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES + INTERFACE_SYSTEM_INCLUDE_DIRECTORIES + LINK_OPTIONS INTERFACE_LINK_OPTIONS + LINK_LIBRARIES INTERFACE_LINK_LIBRARIES + LINK_DIRECTORIES INTERFACE_LINK_DIRECTORIES) + get_target_property(VAL libqpdf ${PROP}) + if(NOT (VAL STREQUAL "VAL-NOTFOUND")) + message(STATUS " ${PROP}: ${VAL}") + endif() +endforeach() +if(APPLE) + message(STATUS " CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}") +endif() +message(STATUS "") +message(STATUS "See above for crypto summary.") +message(STATUS "") +if(NOT (MULTI_CONFIG OR CMAKE_BUILD_TYPE)) + message(WARNING " CMAKE_BUILD_TYPE is not set; using default settings") +endif() diff --git a/cmake/qpdfCheckFlag.cmake b/cmake/qpdfCheckFlag.cmake new file mode 100644 index 00000000..8bd72b88 --- /dev/null +++ b/cmake/qpdfCheckFlag.cmake @@ -0,0 +1,21 @@ +include(CheckCXXCompilerFlag) +include(CheckCCompilerFlag) + +function(qpdf_maybe_add_flag lang flag var) + if(${lang} STREQUAL "C") + check_c_compiler_flag(${flag} ${var}) + elseif(${lang} STREQUAL "CXX") + check_cxx_compiler_flag(${flag} ${var}) + endif() + if(${var}) + message(STATUS "Using ${flag}: YES") + if(${lang} STREQUAL "C") + # Add for C and C++ + add_compile_options(${flag}) + else() + add_compile_options($<$:${flag}>) + endif() + else() + message(STATUS "Using ${flag}: NO") + endif() +endfunction() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..40ecd603 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,52 @@ +set(EXAMPLE_CXX_PROGRAMS + pdf-attach-file + pdf-bookmarks + pdf-count-strings + pdf-create + pdf-custom-filter + pdf-double-page-size + pdf-filter-tokens + pdf-invert-images + pdf-mod-info + pdf-name-number-tree + pdf-npages + pdf-overlay-page + pdf-parse-content + pdf-set-form-values + pdf-split-pages + qpdf-job) +set(EXAMPLE_C_PROGRAMS + pdf-c-objects + pdf-linearize + qpdfjob-c) + +foreach(PROG ${EXAMPLE_CXX_PROGRAMS}) + add_executable(${PROG} ${PROG}.cc) + target_link_libraries(${PROG} libqpdf) +endforeach() +foreach(PROG ${EXAMPLE_C_PROGRAMS}) + add_executable(${PROG} ${PROG}.c) + target_link_libraries(${PROG} libqpdf) + set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX) +endforeach() +target_include_directories(pdf-create PRIVATE ${JPEG_INCLUDE}) + +add_test( + NAME examples + COMMAND ${RUN_QTEST} + --top ${qpdf_SOURCE_DIR} + --bin $ + --bin $ + --bin $ # for Windows to find DLL + --code ${qpdf_SOURCE_DIR}/examples + --color ${QTEST_COLOR} + --show-on-failure ${SHOW_FAILED_TEST_OUTPUT} + --tc "${qpdf_SOURCE_DIR}/examples/*.cc" + --tc "${qpdf_SOURCE_DIR}/examples/*.c") + +file(GLOB EXAMPLES_SRC "*.c" "*.cc") +if(INSTALL_EXAMPLES) + install(FILES ${EXAMPLES_SRC} + DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples + COMPONENT ${COMPONENT_EXAMPLES}) +endif() diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt new file mode 100644 index 00000000..04743005 --- /dev/null +++ b/fuzz/CMakeLists.txt @@ -0,0 +1,178 @@ +# This directory contains support for Google's oss-fuzz project. See +# https://github.com/google/oss-fuzz/tree/master/projects/qpdf + +set(FUZZERS + qpdf_fuzzer + ascii85_fuzzer + dct_fuzzer + flate_fuzzer + hex_fuzzer + lzw_fuzzer + pngpredictor_fuzzer + runlength_fuzzer + tiffpredictor_fuzzer) + +# The oss-fuzz project provides LIB_FUZZING_ENGINE and OUT environment +# variables. For local testing, provide values if not set. +set(LIB_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE}) +if(NOT LIB_FUZZING_ENGINE) + # When running from oss-fuzz, LIB_FUZZING_ENGINE points to a + # static library that contains main. + add_library(standalone_fuzzer STATIC standalone_fuzz_target_runner.cc) + target_include_directories( + standalone_fuzzer PRIVATE ${qpdf_SOURCE_DIR}/include) + set(LIB_FUZZING_ENGINE standalone_fuzzer) +endif() +set(FUZZ_OUT $ENV{OUT}) +if(NOT FUZZ_OUT) + set(FUZZ_OUT ${CMAKE_CURRENT_BINARY_DIR}/fuzz-install) +endif() + +if(OSS_FUZZ) + # We need to link jpeg and zlib statically for oss-fuzz. Construct + # our own object library without the external dependencies and add + # what we need. + add_library(libqpdf_fuzz STATIC $) + target_link_libraries(libqpdf_fuzz INTERFACE libjpeg.a libz.a) + target_include_directories(libqpdf_fuzz + PUBLIC + ${JPEG_INCLUDE} + ${qpdf_SOURCE_DIR}/include + ${qpdf_SOURCE_DIR}/libqpdf) +else() + add_library(libqpdf_fuzz ALIAS libqpdf_object) +endif() + +foreach(PROG ${FUZZERS}) + add_executable(${PROG} ${PROG}.cc) + target_link_libraries(${PROG} ${LIB_FUZZING_ENGINE}) + target_link_libraries(${PROG} libqpdf_fuzz) +endforeach() + +# Files from the test suite that are good for seeding the fuzzer. +# Update count for qpdf in @fuzzers qtest/fuzz.test if you change this list. +set(CORPUS_FROM_TEST + stream-data.pdf + lin5.pdf + field-types.pdf + image-streams-small.pdf + need-appearances.pdf + outlines-with-actions.pdf + outlines-with-old-root-dests.pdf + page-labels-and-outlines.pdf + page-labels-num-tree.pdf + dr-with-indirect-item.pdf + fuzz-16214.pdf + issue-99b.pdf + issue-99.pdf + issue-100.pdf + issue-101.pdf + issue-106.pdf + issue-117.pdf + issue-119.pdf + issue-120.pdf + issue-141a.pdf + issue-141b.pdf + issue-143.pdf + issue-146.pdf + issue-147.pdf + issue-148.pdf + issue-149.pdf + issue-150.pdf + issue-202.pdf + issue-263.pdf + issue-335a.pdf + issue-335b.pdf) + +# Any file that qpdf_fuzzer should be tested with can be named +# something.fuzz and dropped into qpdf_extra. Update count for qpdf in +# @fuzzers qtest/fuzz.test if you change this list. +set(CORPUS_OTHER + 15316.fuzz + 15387.fuzz + 15390.fuzz + 15442.fuzz + 15445.fuzz + 15983.fuzz + 16172.fuzz + 16301.fuzz + 16953.fuzz + 18241.fuzz + 18247.fuzz + 23172.fuzz + 23599.fuzz + 23642.fuzz + 23642-mod.fuzz + 26761.fuzz + 26994.fuzz + 27393.fuzz + 28262.fuzz + 30507.fuzz + 37740.fuzz) + +set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) +file(MAKE_DIRECTORY ${CORPUS_DIR}) +function(copy_fuzz FROM) + file(SHA1 ${FROM} SHA) + set(OUT ${CORPUS_DIR}/${SHA}) + add_custom_command( + OUTPUT ${OUT} + COMMAND ${COPY_COMMAND} $ $) + set(CORPUS_FILE ${OUT} PARENT_SCOPE) +endfunction() + +list(APPEND CORPUS_FILES) +foreach(F ${CORPUS_FROM_TEST}) + copy_fuzz(${qpdf_SOURCE_DIR}/qpdf/qtest/qpdf/${F}) + list(APPEND CORPUS_FILES ${CORPUS_FILE}) +endforeach() +foreach(F ${CORPUS_OTHER}) + copy_fuzz(${CMAKE_CURRENT_SOURCE_DIR}/qpdf_extra/${F}) + list(APPEND CORPUS_FILES ${CORPUS_FILE}) +endforeach() +add_custom_target(qpdf_corpus ALL + DEPENDS ${CORPUS_FILES}) + +add_test( + NAME fuzz + COMMAND ${RUN_QTEST} + --env QPDF_FUZZ_CORPUS=${CORPUS_DIR} + --top ${qpdf_SOURCE_DIR} + --bin $ + --bin $ + --code ${qpdf_SOURCE_DIR}/fuzz + --color ${QTEST_COLOR} + --show-on-failure ${SHOW_FAILED_TEST_OUTPUT}) + +if(OSS_FUZZ) + list(APPEND SEED_CORPUS_ZIPS) + foreach(F ${FUZZERS}) + if(F STREQUAL qpdf_fuzzer) + set(SEED_DIR ${CORPUS_DIR}) + else() + set(SEED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${F}_seed_corpus) + endif() + set(SEED_ZIP ${CMAKE_CURRENT_BINARY_DIR}/${F}_seed_corpus.zip) + add_custom_command(OUTPUT ${SEED_ZIP} + COMMAND zip -q -r ${SEED_ZIP} . + WORKING_DIRECTORY ${SEED_DIR}) + list(APPEND SEED_CORPUS_ZIPS ${SEED_ZIP}) + endforeach() + add_custom_target(seed_corpus_zips ALL DEPENDS ${SEED_CORPUS_ZIPS}) + add_dependencies(seed_corpus_zips qpdf_corpus) + add_custom_target(fuzzers) + add_dependencies(fuzzers ${FUZZERS} seed_corpus_zips) + install( + TARGETS ${FUZZERS} + DESTINATION ${FUZZ_OUT} + EXCLUDE_FROM_ALL + COMPONENT fuzz) + install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/pdf.dict + ${CMAKE_CURRENT_SOURCE_DIR}/qpdf_fuzzer.options + ${SEED_CORPUS_ZIPS} + DESTINATION ${FUZZ_OUT} + EXCLUDE_FROM_ALL + COMPONENT fuzz) +endif() diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 00000000..3110ba9c --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,12 @@ +set(qpdf_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) + +# While globs are not considered best practice, it makes sense for +# installation o header files. When compiling, you specify an entire +# directory, so not doing the same at installation time creates a high +# risk that forgetting to explicitly add a header to an installation +# list would not be detected in CI or at any time until an end user +# tries to build code. +install(DIRECTORY qpdf + TYPE INCLUDE + COMPONENT ${COMPONENT_DEV} + FILES_MATCHING PATTERN "*.h" PATTERN "*.hh") diff --git a/libqpdf/CMakeLists.txt b/libqpdf/CMakeLists.txt new file mode 100644 index 00000000..2d8c7b65 --- /dev/null +++ b/libqpdf/CMakeLists.txt @@ -0,0 +1,581 @@ + +if(GENERATE_AUTO_JOB) + execute_process( + COMMAND ${qpdf_SOURCE_DIR}/generate_auto_job --generate + WORKING_DIRECTORY ${qpdf_SOURCE_DIR}) +endif() + +set(libqpdf_crypto_native + AES_PDF_native.cc + MD5_native.cc + QPDFCrypto_native.cc + RC4_native.cc + SHA2_native.cc + rijndael.cc + sha2.c + sha2big.c) + +set(libqpdf_crypto_openssl + QPDFCrypto_openssl.cc) + +set(libqpdf_crypto_gnutls + QPDFCrypto_gnutls.cc) + +set(libqpdf_SOURCES + BitStream.cc + BitWriter.cc + Buffer.cc + BufferInputSource.cc + ClosedFileInputSource.cc + ContentNormalizer.cc + CryptoRandomDataProvider.cc + FileInputSource.cc + InputSource.cc + InsecureRandomDataProvider.cc + JSON.cc + JSONHandler.cc + MD5.cc + NNTree.cc + OffsetInputSource.cc + PDFVersion.cc + Pipeline.cc + Pl_AES_PDF.cc + Pl_ASCII85Decoder.cc + Pl_ASCIIHexDecoder.cc + Pl_Buffer.cc + Pl_Concatenate.cc + Pl_Count.cc + Pl_DCT.cc + Pl_Discard.cc + Pl_Flate.cc + Pl_LZWDecoder.cc + Pl_MD5.cc + Pl_PNGFilter.cc + Pl_QPDFTokenizer.cc + Pl_RC4.cc + Pl_RunLength.cc + Pl_SHA2.cc + Pl_StdioFile.cc + Pl_TIFFPredictor.cc + QPDF.cc + QPDFAcroFormDocumentHelper.cc + QPDFAnnotationObjectHelper.cc + QPDFArgParser.cc + QPDFCryptoProvider.cc + QPDFEFStreamObjectHelper.cc + QPDFEmbeddedFileDocumentHelper.cc + QPDFExc.cc + QPDFFileSpecObjectHelper.cc + QPDFFormFieldObjectHelper.cc + QPDFJob.cc + QPDFJob_argv.cc + QPDFJob_config.cc + QPDFJob_json.cc + QPDFMatrix.cc + QPDFNameTreeObjectHelper.cc + QPDFNumberTreeObjectHelper.cc + QPDFObjGen.cc + QPDFObject.cc + QPDFObjectHandle.cc + QPDFOutlineDocumentHelper.cc + QPDFOutlineObjectHelper.cc + QPDFPageDocumentHelper.cc + QPDFPageLabelDocumentHelper.cc + QPDFPageObjectHelper.cc + QPDFStreamFilter.cc + QPDFSystemError.cc + QPDFTokenizer.cc + QPDFUsage.cc + QPDFWriter.cc + QPDFXRefEntry.cc + QPDF_Array.cc + QPDF_Bool.cc + QPDF_Dictionary.cc + QPDF_InlineImage.cc + QPDF_Integer.cc + QPDF_Name.cc + QPDF_Null.cc + QPDF_Operator.cc + QPDF_Real.cc + QPDF_Reserved.cc + QPDF_Stream.cc + QPDF_String.cc + QPDF_encryption.cc + QPDF_linearization.cc + QPDF_optimization.cc + QPDF_pages.cc + QTC.cc + QUtil.cc + RC4.cc + ResourceFinder.cc + SecureRandomDataProvider.cc + SF_FlateLzwDecode.cc + SparseOHArray.cc + qpdf-c.cc + qpdfjob-c.cc) + +include(FindPkgConfig) +include(CheckTypeSize) +include(CheckIncludeFile) +include(CheckCSourceCompiles) +include(CheckCSourceRuns) +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() + +if(EXTERNAL_LIBS) + set(EXTLIBDIR ${qpdf_SOURCE_DIR}/external-libs) + list(APPEND dep_include_directories ${EXTLIBDIR}/include) + set(JPEG_INCLUDE ${EXTLIBDIR}/include) + list(APPEND dep_link_libraries + z jpeg ssl crypto msvcrt ws2_32 shell32 advapi32 gdi32 user32 crypt32) + if (MSVC) + list(APPEND dep_link_directories ${EXTLIBDIR}/lib-msvc${WORDSIZE}) + else() + list(APPEND dep_link_directories ${EXTLIBDIR}/lib-mingw${WORDSIZE}) + endif() +endif() + +if(NOT EXTERNAL_LIBS) + pkg_check_modules(pc_zlib zlib) + if(pc_zlib_FOUND) + list(APPEND dep_include_directories ${pc_zlib_INCLUDEDIR}) + list(APPEND dep_link_directories ${pc_zlib_LIBDIR}) + list(APPEND dep_link_libraries ${pc_zlib_LIBRARIES}) + else() + find_path(ZLIB_H_PATH zlib.h) + find_library(ZLIB_LIB_PATH z zlib) + 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() + +if(NOT EXTERNAL_LIBS) + pkg_check_modules(pc_libjpeg libjpeg) + if(pc_libjpeg_FOUND) + list(APPEND dep_include_directories ${pc_libjpeg_INCLUDEDIR}) + list(APPEND dep_link_directories ${pc_libjpeg_LIBDIR}) + list(APPEND dep_link_libraries ${pc_libjpeg_LIBRARIES}) + set(JPEG_INCLUDE ${pc_libjpeg_INCLUDEDIR}) + else() + find_path(LIBJPEG_H_PATH jpeglib.h) + find_library(LIBJPEG_LIB_PATH jpeg) + if(LIBJPEG_H_PATH AND LIBJPEG_LIB_PATH) + 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() + +# Update JPEG_INCLUDE in PARENT_SCOPE after we have finished setting it. +set(JPEG_INCLUDE ${JPEG_INCLUDE} PARENT_SCOPE) + +# Crypto provider selection. Prefer external crypto providers. If +# implicit selection is allowed, use native only when no other options +# are available or when explicitly requested. Allowing native as a +# fallback can be disabled using the ALLOW_CRYPTO_NATIVE option. +list(APPEND CRYPTO_PKG) + +set(USE_CRYPTO_GNUTLS OFF) +set(USE_CRYPTO_OPENSSL OFF) +set(USE_CRYPTO_NATIVE OFF) +set(FOUND_CRYPTO OFF) + +if(USE_IMPLICIT_CRYPTO OR REQUIRE_CRYPTO_OPENSSL) + if(EXTERNAL_LIBS) + set(USE_CRYPTO_OPENSSL ON) + else() + pkg_check_modules(pc_openssl openssl>=1.1.0) + if(pc_openssl_FOUND) + set(USE_CRYPTO_OPENSSL ON) + set(FOUND_CRYPTO ON) + set(CRYPTO_PKG "${CRYPTO_PKG}, openssl>=1.1.0") + else() + find_path(OPENSSL_H_PATH openssl/evp.h) + find_library(OPENSSL_LIB_PATH crypto) + if(OPENSSL_H_PATH AND OPENSSL_LIB_PATH) + list(APPEND dep_include_directories ${OPENSSL_H_PATH}) + 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() +endif() +if(USE_IMPLICIT_CRYPTO OR REQUIRE_CRYPTO_GNUTLS) + pkg_check_modules(pc_gnutls gnutls) + if(pc_gnutls_FOUND) + set(USE_CRYPTO_GNUTLS ON) + set(FOUND_CRYPTO ON) + set(CRYPTO_PKG "${CRYPTO_PKG}, gnutls") + else() + find_path(GNUTLS_H_PATH gnutls/gnutls.h) + find_library(GNUTLS_LIB_PATH gnutls) + if(GNUTLS_H_PATH AND GNUTLS_LIB_PATH) + list(APPEND dep_include_directories ${GNUTLS_H_PATH}) + 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() +if(REQUIRE_CRYPTO_NATIVE) + set(USE_CRYPTO_NATIVE ON) + set(FOUND_CRYPTO ON) +elseif(USE_IMPLICIT_CRYPTO) + if(ALLOW_CRYPTO_NATIVE AND (NOT FOUND_CRYPTO)) + set(USE_CRYPTO_NATIVE ON) + set(FOUND_CRYPTO ON) + endif() +endif() +if(FOUND_CRYPTO) + if(NOT DEFAULT_CRYPTO) + # The preferred order of crypto providers is documented in + # manual/installation.rst in the crypto.build section. + if(USE_CRYPTO_GNUTLS) + set(DEFAULT_CRYPTO "gnutls") + elseif(USE_CRYPTO_OPENSSL) + set(DEFAULT_CRYPTO "openssl") + else() + 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}) +message(STATUS " OpenSSL crypto enabled: " ${USE_CRYPTO_OPENSSL}) +message(STATUS " Native crypto enabled: " ${USE_CRYPTO_NATIVE}) +message(STATUS " Default crypto: " ${DEFAULT_CRYPTO}) +message(STATUS "") + +if(USE_CRYPTO_OPENSSL) + list(APPEND libqpdf_SOURCES ${libqpdf_crypto_openssl}) + if(NOT EXTERNAL_LIBS) + list(APPEND dep_include_directories ${pc_openssl_INCLUDEDIR}) + list(APPEND dep_link_directories ${pc_openssl_LIBDIR}) + list(APPEND dep_link_libraries ${pc_openssl_LIBRARIES}) + endif() +endif() +if(USE_CRYPTO_GNUTLS) + list(APPEND libqpdf_SOURCES ${libqpdf_crypto_gnutls}) + list(APPEND dep_include_directories ${pc_gnutls_INCLUDEDIR}) + list(APPEND dep_link_directories ${pc_gnutls_LIBDIR}) + list(APPEND dep_link_libraries ${pc_gnutls_LIBRARIES}) +endif() +if(USE_CRYPTO_NATIVE) + list(APPEND libqpdf_SOURCES ${libqpdf_crypto_native}) +endif() + +if(APPLE) + # 2022: in CI (GitHub actions), pkg-config for zlib was adding a + # broken directory to the include path. This effectively filters it + # out. + list(FILTER dep_include_directories EXCLUDE REGEX "^/Library/") +endif() + +list(REMOVE_DUPLICATES dep_include_directories) +list(REMOVE_DUPLICATES dep_link_directories) +list(REMOVE_DUPLICATES dep_link_libraries) + +check_type_size(size_t SIZEOF_SIZE_T) +check_include_file("inttypes.h" HAVE_INTTYPES_H) +check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO) +check_symbol_exists(fseeko64 "stdio.h" HAVE_FSEEKO64) +check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R) +check_symbol_exists(random "stdlib.h" HAVE_RANDOM) +find_file(RANDOM_DEVICE + "urandom" "arandom" "arandom" PATHS "/dev" NO_DEFAULT_PATH) + +check_c_source_compiles( +"#include +#include +int main(int argc, char* argv[]) { + tzset(); + printf(\"%ld\", timezone); + return 0; +}" + HAVE_EXTERN_LONG_TIMEZONE) + +check_c_source_compiles( +"#include +int main(int argc, char* argv[]) { + struct tm tm; + tm.tm_gmtoff = 1; + return 0; +}" + HAVE_EXTERN_TM_GMTOFF) + +check_c_source_compiles( +"#include +#include +int main(int argc, char* argv[]) { + int a[sizeof(off_t) >= 8 ? 1 : -1]; +}" + LFS_WITHOUT_MACROS) + +check_c_source_compiles( +"#define _FILE_OFFSET_BITS 64 +#include +#include +int main(int argc, char* argv[]) { + int a[sizeof(off_t) >= 8 ? 1 : -1]; +}" + LFS_WITH_MACROS) +if(LFS_WITH_MACROS AND NOT LFS_WITHOUT_MACROS) + set(_FILE_OFFSET_BITS 64) +endif() + +function(qpdf_check_ll_fmt fmt var) + if(NOT DEFINED LL_FMT) + check_c_source_runs( + "#define _CRT_SECURE_NO_WARNINGS +#include +#include +int main(int argc, char* argv[]) { + long long int a = 123456789012345ll; + char s[30]; + sprintf(s, \"${fmt}\", a); + return (strcmp(s, \"123456789012345\") == 0) ? 0 : 1; +}" ${var}) + if(${var}) + set(LL_FMT "${fmt}" PARENT_SCOPE) + endif() + endif() +endfunction() + +qpdf_check_ll_fmt("%lld" fmt_lld) +qpdf_check_ll_fmt("%I64d" fmt_i64d) +qpdf_check_ll_fmt("%I64lld" fmt_i64lld) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/qpdf/qpdf-config.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/qpdf/qpdf-config.h" + NEWLINE_STYLE UNIX) + +if(NOT BUILD_STATIC_LIBS) + set(OBJECT_LIB_IS_PIC ON) +else() + set(OBJECT_LIB_IS_PIC OFF) +endif() + +# Build an "object library" for use in libtests so we don't have to +# export symbols that are not officially part of the public API. If we +# are building static libraries, the object library won't use +# position-independent code and will provided objects for the static +# library. If we are only building the shared library, go ahead and +# 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}) +set_target_properties(${OBJECT_LIB} PROPERTIES + POSITION_INDEPENDENT_CODE ${OBJECT_LIB_IS_PIC}) +target_include_directories(${OBJECT_LIB} + SYSTEM PRIVATE ${dep_include_directories}) +target_include_directories(${OBJECT_LIB} + PUBLIC + ${JPEG_INCLUDE} + ${qpdf_INCLUDE} + ${qpdf_SOURCE_DIR}/libqpdf + ${CMAKE_CURRENT_BINARY_DIR}) +target_link_directories(${OBJECT_LIB} INTERFACE ${dep_link_directories}) +target_link_libraries(${OBJECT_LIB} INTERFACE ${dep_link_libraries}) + +set(LD_VERSION_FLAGS "") +function(ld_version_script) + # Check if the linker supports linker scripts, and use if it does. + # This functionality is currently constrained to compilers using GNU + # ld on ELF systems or systems that emulation this behavior. + set(ld_script + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/qpdf-tmp/conftest.map") + file(WRITE ${ld_script} +"VERS_1 { + global: sym; +}; + +VERS_2 { + global: sym; +} VERS_1; +") + set(CMAKE_REQUIRED_LINK_OPTIONS -Wl,--version-script=${ld_script}) + check_c_source_compiles("int main() { return 0; }" HAVE_LD_SCRIPT) + if(HAVE_LD_SCRIPT) + set(LD_VERSION_FLAGS + -Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libqpdf.map PARENT_SCOPE) + configure_file( + "${qpdf_SOURCE_DIR}/libqpdf.map.in" + "${CMAKE_CURRENT_BINARY_DIR}/libqpdf.map" + NEWLINE_STYLE UNIX) + endif() +endfunction() +if(NOT WIN32) + ld_version_script() +endif() + +if(BUILD_SHARED_LIBS) + add_compile_definitions(DLL_EXPORT) + + set(SHARED_LIB libqpdf) + if(OBJECT_LIB_IS_PIC) + add_library(${SHARED_LIB} SHARED $) + else() + add_library(${SHARED_LIB} SHARED ${libqpdf_SOURCES}) + endif() + if(WIN32) + # Goal: the DLL import library should be libqpdf.a or qpdf.lib so + # that linking with -lqpdf gets you a shared library link on all + # platforms. The DLL should be qpdf${SONAME}.dll rather than just + # qpdf.dll. qpdf has always done this, and it gives us some + # protection against binary incompatible DLLs being installed. + set(SHARED_OUT qpdf${qpdf_SOVERSION}) # Put API version number in DLL + if(MINGW) + # Reference: Platform/Windows-GNU.cmake in the cmake installation + set(CMAKE_SHARED_LIBRARY_PREFIX "") # libqpdf$v.dll -> qpdf$v.dll + set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a") # libqpdf.dll.a -> libqpdf.a + endif() + if(MSVC) + # Avoid linker warning from mixing libraries built with /MT and /MD. + set_target_properties(${SHARED_LIB} + PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD") + endif() + else() + set(SHARED_OUT qpdf) + endif() + # Setting OUTPUT_NAME and ARCHIVE_OUTPUT_NAME separate enables us to + # have a versioned DLL and an unversioned import library, which + # gives us semantics similar to ELF shared libraries and makes + # linking against qpdf the same across all platforms. + set_target_properties(${SHARED_LIB} PROPERTIES + OUTPUT_NAME ${SHARED_OUT} + ARCHIVE_OUTPUT_NAME qpdf + VERSION ${qpdf_LIBVERSION} + SOVERSION ${qpdf_SOVERSION} + POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}) + + target_include_directories(${SHARED_LIB} + SYSTEM PRIVATE ${dep_include_directories}) + target_include_directories(${SHARED_LIB} + PUBLIC + $ + $) + target_link_directories(${SHARED_LIB} PRIVATE ${dep_link_directories}) + target_link_libraries(${SHARED_LIB} PRIVATE ${dep_link_libraries}) + if(LD_VERSION_FLAGS) + target_link_options(${SHARED_LIB} PRIVATE ${LD_VERSION_FLAGS}) + endif() + + target_include_directories(${SHARED_LIB} + PRIVATE ${qpdf_SOURCE_DIR}/libqpdf ${CMAKE_CURRENT_BINARY_DIR}) + + install(TARGETS ${SHARED_LIB} + EXPORT libqpdfTargets + TYPE LIBRARY + COMPONENT ${COMPONENT_LIB} + NAMELINK_COMPONENT ${COMPONENT_DEV} + INCLUDES ${qpdf_INCLUDE}) +endif() + +if(BUILD_STATIC_LIBS) + if(BUILD_SHARED_LIBS) + set(STATIC_LIB libqpdf_static) + else() + set(STATIC_LIB libqpdf) + endif() + if(OBJECT_LIB_IS_PIC) + add_library(${STATIC_LIB} STATIC ${libqpdf_SOURCES}) + else() + add_library(${STATIC_LIB} STATIC $) + endif() + + target_include_directories(${STATIC_LIB} + SYSTEM PRIVATE ${dep_include_directories}) + target_include_directories(${STATIC_LIB} + PUBLIC + $ + $) + target_link_directories(${STATIC_LIB} + INTERFACE $ + PRIVATE $) + target_link_libraries(${STATIC_LIB} INTERFACE ${dep_link_libraries}) + + # Avoid name clashes on Windows with the the DLL import library. + if(NOT DEFINED STATIC_SUFFIX AND BUILD_SHARED_LIBS) + if (WIN32) + set(STATIC_SUFFIX "_static") + else() + set(STATIC_SUFFIX "") + endif() + endif() + + set_target_properties(${STATIC_LIB} PROPERTIES + OUTPUT_NAME qpdf${STATIC_SUFFIX} + VERSION ${PROJECT_VERSION}) + target_include_directories(${STATIC_LIB} + PRIVATE ${qpdf_SOURCE_DIR}/libqpdf ${CMAKE_CURRENT_BINARY_DIR}) + + install(TARGETS ${STATIC_LIB} + EXPORT libqpdfTargets + TYPE ARCHIVE + COMPONENT ${COMPONENT_DEV} + INCLUDES ${qpdf_INCLUDE}) +endif() + +configure_file( + "${qpdf_SOURCE_DIR}/libqpdf.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/libqpdf.pc" + @ONLY NEWLINE_STYLE UNIX) +if(INSTALL_PKGCONFIG) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libqpdf.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + COMPONENT ${COMPONENT_DEV}) +endif() + +if(INSTALL_CMAKE_PACKAGE) + include(CMakePackageConfigHelpers) + configure_package_config_file( + ${qpdf_SOURCE_DIR}/qpdfConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/qpdfConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf) + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/qpdfConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion) + install(EXPORT libqpdfTargets + NAMESPACE qpdf:: + FILE libqpdfTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf) + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/qpdfConfigVersion.cmake + ${CMAKE_CURRENT_BINARY_DIR}/qpdfConfig.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf) +endif() diff --git a/libtests/CMakeLists.txt b/libtests/CMakeLists.txt new file mode 100644 index 00000000..96f93482 --- /dev/null +++ b/libtests/CMakeLists.txt @@ -0,0 +1,53 @@ +set(TEST_PROGRAMS + cxx11 + aes + arg_parser + ascii85 + bits + buffer + closed_file_input_source + concatenate + dct_compress + dct_uncompress + flate + hex + input_source + json + json_handler + json_parse + lzw + main_from_wmain + matrix + md5 + nntree + numrange + pdf_version + pointer_holder + predictors + qintc + qutil + random + rc4 + runlength + sha2 + sparse_array) +foreach(PROG ${TEST_PROGRAMS}) + add_executable(${PROG} ${PROG}.cc) + target_link_libraries(${PROG} libqpdf_object) +endforeach() + +# Since libtests link with the object library and don't use the DLL, +# we don't need to (and shouldn't) add the libqpdf target directory to +# the path for libtests. +add_test( + NAME libtests + COMMAND ${RUN_QTEST} + --top ${qpdf_SOURCE_DIR} + --bin $ + --bin $ + --code ${qpdf_SOURCE_DIR}/libtests + --color ${QTEST_COLOR} + --show-on-failure ${SHOW_FAILED_TEST_OUTPUT} + --tc "${qpdf_SOURCE_DIR}/libtests/*.cc" + --tc "${qpdf_SOURCE_DIR}/libqpdf/*.cc" + --tc "${qpdf_SOURCE_DIR}/libqpdf/qpdf/bits_functions.hh") diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt new file mode 100644 index 00000000..737d2335 --- /dev/null +++ b/manual/CMakeLists.txt @@ -0,0 +1,133 @@ +# There is a FindLATEX module, but it does some things we don't care +# about and doesn't do everything we need. +find_program(LATEX latex) +find_program(PDFLATEX pdflatex) +find_program(LATEXMK latexmk) +find_program(SPHINX sphinx-build) + +# *** NOTE: Never check BUILD_DOC_PDF if BUILD_DOC is not set. See +# *** comments in top-level CMakeLists.txt. + +if(BUILD_DOC) + if(SPHINX STREQUAL SPHINX-NOTFOUND) + message(FATAL_ERROR "sphinx-build is required for building documentation") + endif() + if(BUILD_DOC_PDF AND + ((LATEX STREQUAL LATEX-NOTFOUND) OR + (LATEXMK STREQUAL LATEXMK-NOTFOUND) OR + (PDFLATEX STREQUAL PDFLATEX-NOTFOUND))) + message(FATAL_ERROR + "latex, latexmk, and pdflatex are required to build PDF documentation") + endif() +endif() + +set(MANUAL_SRC ${qpdf_SOURCE_DIR}/manual) +foreach(F qpdf.1 fix-qdf.1 zlib-flate.1) + configure_file( + ${MANUAL_SRC}/${F}.in + ${CMAKE_CURRENT_BINARY_DIR}/${F} + NEWLINE_STYLE UNIX) +endforeach() + +SET(MANUAL_DEPS + conf.py + _ext/qpdf.py + acknowledgement.rst + cli.rst + design.rst + download.rst + encryption.rst + index.rst + installation.rst + json.rst + library.rst + license.rst + linearization.rst + object-streams.rst + overview.rst + packaging.rst + qdf.rst + qpdf-job.rst + release-notes.rst + weak-crypto.rst) + +# Prevent targets that run ${SPHINX} from running in parallel by using +# dependencies. This avoids clashes in temporary files that cause the +# build to fail with the error "_pickle.UnpicklingError: pickle data +# was truncated". It would be better if we could use order-only +# dependencies like gnu make to make it possible to build the targets +# independently. +set(DOC_HTML_OUTPUT html/index.html) +set(DOC_SINGLEHTML_OUTPUT singlehtml/index.html) +set(DOC_PDF_OUTPUT latex/qpdf.pdf) +if(BUILD_DOC) + add_custom_command(OUTPUT ${DOC_HTML_OUTPUT} + COMMAND ${SPHINX} -M html ${MANUAL_SRC} ${CMAKE_CURRENT_BINARY_DIR} + COMMAND touch ${DOC_HTML_OUTPUT} + DEPENDS ${MANUAL_DEPS}) + add_custom_command(OUTPUT ${DOC_SINGLEHTML_OUTPUT} + COMMAND ${SPHINX} -M singlehtml ${MANUAL_SRC} ${CMAKE_CURRENT_BINARY_DIR} + COMMAND touch ${DOC_SINGLEHTML_OUTPUT} + DEPENDS ${MANUAL_DEPS}) + add_custom_command(OUTPUT ${DOC_PDF_OUTPUT} + COMMAND ${SPHINX} -M latexpdf ${MANUAL_SRC} ${CMAKE_CURRENT_BINARY_DIR} + COMMAND touch ${DOC_PDF_OUTPUT} + DEPENDS ${MANUAL_DEPS}) + add_custom_target(doc_html DEPENDS ${DOC_HTML_OUTPUT}) + add_custom_target(doc_singlehtml DEPENDS ${DOC_SINGLEHTML_OUTPUT}) + add_dependencies(doc_singlehtml doc_html) + add_custom_target(doc_pdf DEPENDS ${DOC_PDF_OUTPUT}) + add_custom_target(doc ALL) + if(BUILD_DOC_PDF) + add_dependencies(doc doc_pdf) + if(BUILD_DOC_HTML) + add_dependencies(doc_pdf doc_singlehtml) + endif() + elseif(BUILD_DOC_HTML) + add_dependencies(doc doc_singlehtml) + endif() + + if(BUILD_DOC_DIST) + set(DOC_DIST_HTML doc-dist/manual-html) + set(DOC_DIST_SINGLEHTML doc-dist/manual-single-page-html) + set(DOC_DIST_PDF doc-dist/qpdf-manual.pdf) + add_custom_command( + OUTPUT + ${DOC_DIST_HTML} + ${DOC_DIST_SINGLEHTML} + ${DOC_DIST_PDF} + COMMAND rm -rf doc-dist + COMMAND mkdir -p doc-dist + COMMAND cp -r html ${DOC_DIST_HTML} + COMMAND cp -r singlehtml ${DOC_DIST_SINGLEHTML} + COMMAND cp -r ${DOC_PDF_OUTPUT} ${DOC_DIST_PDF} + DEPENDS ${DOC_HTML_OUTPUT} ${DOC_SINGLEHTML_OUTPUT} ${DOC_PDF_OUTPUT}) + add_custom_target(doc_dist ALL + DEPENDS ${DOC_DIST_HTML} ${DOC_DIST_SINGLEHTML} ${DOC_DIST_PDF}) + add_dependencies(doc_dist doc) + endif() +endif() + +# INSTALL_MANUAL is not dependent on building doc -- we sometimes drop in +# pre-built doc when creating distributions. +if(INSTALL_MANUAL) + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-dist/ + TYPE DOC + COMPONENT ${COMPONENT_DOC}) +else() + install(FILES ${qpdf_SOURCE_DIR}/README-doc.txt + TYPE DOC + COMPONENT ${COMPONENT_DOC}) +endif() + +if(NOT WIN32) + # There's no reason to install manual pages in a Windows + # environment, especially when all they do is refer people to the + # manual. + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/qpdf.1 + ${CMAKE_CURRENT_BINARY_DIR}/fix-qdf.1 + ${CMAKE_CURRENT_BINARY_DIR}/zlib-flate.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + COMPONENT ${COMPONENT_DOC}) +endif() diff --git a/qpdf/CMakeLists.txt b/qpdf/CMakeLists.txt new file mode 100644 index 00000000..300174ce --- /dev/null +++ b/qpdf/CMakeLists.txt @@ -0,0 +1,78 @@ +set(MAIN_CXX_PROGRAMS + qpdf + fix-qdf + pdf_from_scratch + test_driver + test_large_file + test_parsedoffset + test_pdf_doc_encoding + test_pdf_unicode + test_renumber + test_shell_glob + test_tokenizer + test_unicode_filenames + test_xref) +set(MAIN_C_PROGRAMS + qpdf-ctest + qpdfjob-ctest) + +foreach(PROG ${MAIN_CXX_PROGRAMS}) + add_executable(${PROG} ${PROG}.cc) + target_link_libraries(${PROG} libqpdf) +endforeach() +foreach(PROG ${MAIN_C_PROGRAMS}) + add_executable(${PROG} ${PROG}.c) + target_link_libraries(${PROG} libqpdf) + set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX) +endforeach() +target_include_directories(qpdf-ctest PRIVATE ${CMAKE_BINARY_DIR}/libqpdf) + +foreach(B qpdf test_unicode_filenames fix-qdf test_shell_glob) + if(WINDOWS_WMAIN_COMPILE) + target_compile_options(${B} PRIVATE ${WINDOWS_WMAIN_COMPILE}) + endif() + if(WINDOWS_WMAIN_LINK) + target_link_options(${B} PRIVATE ${WINDOWS_WMAIN_LINK}) + endif() +endforeach() + +add_test( + NAME qpdf + COMMAND ${RUN_QTEST} + --top ${qpdf_SOURCE_DIR} + --bin $ + --bin $ # for Windows to find DLL + --code ${qpdf_SOURCE_DIR}/qpdf + --color ${QTEST_COLOR} + --show-on-failure ${SHOW_FAILED_TEST_OUTPUT} + --tc "${qpdf_SOURCE_DIR}/qpdf/*.cc" + --tc "${qpdf_SOURCE_DIR}/qpdf/*.c" + --tc "${qpdf_SOURCE_DIR}/libqpdf/*.cc") + +install(TARGETS qpdf fix-qdf + TYPE RUNTIME + COMPONENT ${COMPONENT_CLI}) + +if(MINGW) + # For MSVC, including InstallRequiredSystemLibraries in the + # top-level CMakeLists.txt is sufficient. For mingw, we have to copy + # mingw libraries in ourselves. + set(ONE_GNU_DLL extra-dlls/libstdc++-6.dll) + add_custom_command(OUTPUT ${ONE_GNU_DLL} + COMMAND + perl ${qpdf_SOURCE_DIR}/copy_dlls + qpdf.exe + ${CMAKE_BINARY_DIR}/libqpdf + extra-dlls) + add_custom_target(extra_dlls ALL DEPENDS ${ONE_GNU_DLL}) + add_dependencies(extra_dlls qpdf) + if(BUILD_SHARED_LIBS) + set(EXTRA_DLL_COMPONENT ${COMPONENT_LIB}) + else() + set(EXTRA_DLL_COMPONENT ${COMPONENT_CLI}) + endif() + # The trailing / prevents "extra-dlls" from being created in bin. + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/extra-dlls/ + TYPE BIN + COMPONENT ${EXTRA_DLL_COMPONENT}) +endif() diff --git a/qpdfConfig.cmake.in b/qpdfConfig.cmake.in new file mode 100644 index 00000000..95a79286 --- /dev/null +++ b/qpdfConfig.cmake.in @@ -0,0 +1,2 @@ +@PACKAGE_INIT@ +include("${CMAKE_CURRENT_LIST_DIR}/libqpdfTargets.cmake") diff --git a/zlib-flate/CMakeLists.txt b/zlib-flate/CMakeLists.txt new file mode 100644 index 00000000..4db31ef4 --- /dev/null +++ b/zlib-flate/CMakeLists.txt @@ -0,0 +1,16 @@ +add_executable(zlib-flate zlib-flate.cc) +target_link_libraries(zlib-flate libqpdf) + +add_test( + NAME zlib-flate + COMMAND ${RUN_QTEST} + --top ${qpdf_SOURCE_DIR} + --bin $ + --bin $ # for Windows to find DLL + --code ${qpdf_SOURCE_DIR}/zlib-flate + --color ${QTEST_COLOR} + --show-on-failure ${SHOW_FAILED_TEST_OUTPUT}) + +install(TARGETS zlib-flate + TYPE RUNTIME + COMPONENT ${COMPONENT_CLI})