aboutsummaryrefslogtreecommitdiffstats
path: root/CMakeLists.txt
blob: 8a3d5f5a912407df0dcd477549c5f5e591d7adb7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# 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. When updating the version, check make_dist for all
# the places it has to be updated. The doc configuration and CI build
# also find the version number here. generate_auto_job also reads the
# version from here.
project(qpdf
  VERSION 11.9.0
  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(
  CHECK_SIZES "Compare sizes.cc with classes in public API" OFF
  "NOT MAINTAINER_MODE" ON)
CMAKE_DEPENDENT_OPTION(
  GENERATE_AUTO_JOB "Automatically regenerate job files" OFF
  "NOT MAINTAINER_MODE" ON)
CMAKE_DEPENDENT_OPTION(
  ENABLE_QTC "Enable QTC test coverage" 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(ENABLE_COVERAGE "Enable coverage reporting" 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)

option(FUTURE "Include ABI-breaking changes CONSIDERED for the next major release" OFF)
option(CXX_NEXT "Build with next C++ standard version" OFF)

# *** 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()

set(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON)
add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:POINTERHOLDER_TRANSITION=4>)

if(ENABLE_QTC)
  set(ENABLE_QTC_ARG)
else()
  add_compile_definitions(QPDF_DISABLE_QTC=1)
  set(ENABLE_QTC_ARG --disable-tc)
endif()

if(FUTURE)
  add_compile_definitions(QPDF_FUTURE=1)
endif()

enable_testing()
set(RUN_QTEST perl ${qpdf_SOURCE_DIR}/run-qtest ${ENABLE_QTC_ARG})

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.

if(FUTURE)
  math(EXPR qpdf_SOVERSION 0)
  set(qpdf_LIBVERSION 0)
else()
  math(EXPR qpdf_SOVERSION "${PROJECT_VERSION_MAJOR} + 18")
  set(qpdf_LIBVERSION ${qpdf_SOVERSION}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
endif()

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()

if(CXX_NEXT)
  set(CMAKE_CXX_STANDARD 20)
else()
  set(CMAKE_CXX_STANDARD 17)
endif()
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)
set(ATOMIC_LIBRARY)
function(check_atomic)
  foreach(I 0 1)
    if(I)
      set(CMAKE_REQUIRED_LIBRARIES atomic)
    endif()
    check_cxx_source_compiles(
      "#include <atomic>
int main() {
    static std::atomic<unsigned long long> a{0};
    a = a.fetch_add(1LL);
    return 0;
}"
      ATOMIC_WORKED${I})
    if(ATOMIC_WORKED0)
      return()
    endif()
  endforeach()
  if(ATOMIC_WORKED1)
    set(ATOMIC_LIBRARY atomic PARENT_SCOPE)
  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 <windows.h>
#include <string.h>
#include <stdio.h>
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")

if(ENABLE_COVERAGE)
  add_compile_options(--coverage -O0)
  add_link_options(--coverage)
endif()

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()

set(auto_job_inputs
  # Keep in sync with SOURCES in generate_auto_job
  generate_auto_job
  CMakeLists.txt
  manual/_ext/qpdf.py
  job.yml
  manual/cli.rst
  manual/qpdf.1.in
)

set(auto_job_outputs
  # Keep in sync with DESTS in generate_auto_job
  libqpdf/qpdf/auto_job_decl.hh
  libqpdf/qpdf/auto_job_init.hh
  libqpdf/qpdf/auto_job_help.hh
  libqpdf/qpdf/auto_job_schema.hh
  libqpdf/qpdf/auto_job_json_decl.hh
  libqpdf/qpdf/auto_job_json_init.hh
  manual/qpdf.1
)

if(GENERATE_AUTO_JOB)
  add_custom_command(
    OUTPUT ${auto_job_outputs}
    COMMAND ${qpdf_SOURCE_DIR}/generate_auto_job --generate
    WORKING_DIRECTORY ${qpdf_SOURCE_DIR}
    DEPENDS ${auto_job_inputs})
  add_custom_target(auto_job_files ALL DEPENDS ${auto_job_outputs})
endif()

add_test(
  NAME check-assert
  COMMAND perl ${qpdf_SOURCE_DIR}/check_assert)

# add_subdirectory order affects test order
add_subdirectory(include)
add_subdirectory(libqpdf)
add_subdirectory(compare-for-test)
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 "  QTC test coverage: ${ENABLE_QTC}")
message(STATUS "  include future changes: ${FUTURE}")
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()