summaryrefslogtreecommitdiff
path: root/cmake/Modules/FindPCAP.cmake
blob: 11074655f45763c7e4c1b468fd157e487a21e170 (plain)
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
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
#
# Try to find libpcap.
#
# To tell this module where to look, a user may set the environment variable
# PCAP_ROOT to point cmake to the *root* of a directory with include and
# lib subdirectories for pcap.dll (e.g WpdPack or npcap-sdk).
# Alternatively, PCAP_ROOT may also be set from cmake command line or GUI
# (e.g cmake -DPCAP_ROOT=C:\path\to\pcap [...])
#

if(WIN32)
  #
  # Building for Windows.
  #
  # libpcap isn't set up to install .pc files or pcap-config on Windows,
  # and it's not clear that either of them would work without a lot
  # of additional effort.  WinPcap doesn't supply them, and neither
  # does Npcap.
  #
  # So just search for them directly.  Look for both pcap and wpcap.
  # Don't bother looking for static libraries; unlike most UN*Xes
  # (with the exception of AIX), where different extensions are used
  # for shared and static, Windows uses .lib both for import libraries
  # for DLLs and for static libraries.
  #
  # We don't directly set PCAP_INCLUDE_DIRS or PCAP_LIBRARIES, as
  # they're not supposed to be cache entries, and find_path() and
  # find_library() set cache entries.
  #
  find_path(PCAP_INCLUDE_DIR pcap.h)

  # The 64-bit Packet.lib is located under /x64
  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    #
    # For the WinPcap and Npcap SDKs, the Lib subdirectory of the top-level
    # directory contains 32-bit libraries; the 64-bit libraries are in the
    # Lib/x64 directory.
    #
    # The only way to *FORCE* CMake to look in the Lib/x64 directory
    # without searching in the Lib directory first appears to be to set
    # CMAKE_LIBRARY_ARCHITECTURE to "x64".
    #
    set(CMAKE_LIBRARY_ARCHITECTURE "x64")
  endif()
  find_library(PCAP_LIBRARY NAMES pcap wpcap)

  #
  # Do the standard arg processing, including failing if it's a
  # required package.
  #
  include(FindPackageHandleStandardArgs)
  find_package_handle_standard_args(PCAP
    DEFAULT_MSG
    PCAP_INCLUDE_DIR
    PCAP_LIBRARY
  )
  mark_as_advanced(
    PCAP_INCLUDE_DIR
    PCAP_LIBRARY
  )
  if(PCAP_FOUND)
    set(PCAP_LIBRARIES ${PCAP_LIBRARY})
    set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
  endif()
else(WIN32)
  #
  # Building for UN*X.
  #
  # See whether we were handed a QUIET argument, so we can pass it on
  # to pkg_search_module.  Do *NOT* pass on the REQUIRED argument,
  # because, if pkg-config isn't found, or it is but it has no .pc
  # files for libpcap, that is *not* necessarily an indication that
  # libpcap isn't available - not all systems ship pkg-config, and
  # libpcap didn't have .pc files until libpcap 1.9.0.
  #
  if(PCAP_FIND_QUIETLY)
    set(_quiet "QUIET")
  endif()

  #
  # First, try pkg-config.
  # Before doing so, set the PKG_CONFIG_PATH environment variable
  # to include all the directories in CMAKE_PREFIX_PATH.
  #
  # *If* we were to require CMake 3.1 or later on UN*X,
  # pkg_search_module() would do this for us, but, for now,
  # we're not doing that, in case somebody's building with
  # CMake on some "long-term support" version, predating
  # CMake 3.1, of an OS that supplies an earlier
  # version as a package.
  #
  # If we ever set a minimum of 3.1 or later on UN*X, we should
  # remove the environment variable changes.
  #
  # This is based on code in the CMake 3.12.4 FindPkgConfig.cmake,
  # which is "Distributed under the OSI-approved BSD 3-Clause License."
  #
  find_package(PkgConfig)

  #
  # Get the current PKG_CONFIG_PATH setting.
  #
  set(_pkg_config_path "$ENV{PKG_CONFIG_PATH}")

  #
  # Save it, so we can restore it after we run pkg-config.
  #
  set(_saved_pkg_config_path "${_pkg_config_path}")

  if(NOT "${CMAKE_PREFIX_PATH}" STREQUAL "")
    #
    # Convert it to a CMake-style path, before we add additional
    # values to it.
    #
    if(NOT "${_pkg_config_path}" STREQUAL "")
      file(TO_CMAKE_PATH "${_pkg_config_path}" _pkg_config_path)
    endif()

    #
    # Turn CMAKE_PREFIX_PATH into a list of extra paths to add
    # to _pkg_config_path.
    #
    set(_extra_paths "")
    list(APPEND _extra_paths ${CMAKE_PREFIX_PATH})

    # Create a list of the possible pkgconfig subfolder (depending on
    # the system
    set(_lib_dirs)
    if(NOT DEFINED CMAKE_SYSTEM_NAME
        OR (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
            AND NOT CMAKE_CROSSCOMPILING))
      if(EXISTS "/etc/debian_version") # is this a debian system ?
        if(CMAKE_LIBRARY_ARCHITECTURE)
          list(APPEND _lib_dirs "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig")
        endif()
      else()
        # not debian, check the FIND_LIBRARY_USE_LIB32_PATHS and FIND_LIBRARY_USE_LIB64_PATHS properties
        get_property(uselib32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS)
        if(uselib32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
          list(APPEND _lib_dirs "lib32/pkgconfig")
        endif()
        get_property(uselib64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
        if(uselib64 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
          list(APPEND _lib_dirs "lib64/pkgconfig")
        endif()
        get_property(uselibx32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIBX32_PATHS)
        if(uselibx32 AND CMAKE_INTERNAL_PLATFORM_ABI STREQUAL "ELF X32")
          list(APPEND _lib_dirs "libx32/pkgconfig")
        endif()
      endif()
    endif()
    if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND NOT CMAKE_CROSSCOMPILING)
      list(APPEND _lib_dirs "libdata/pkgconfig")
    endif()
    list(APPEND _lib_dirs "lib/pkgconfig")
    list(APPEND _lib_dirs "share/pkgconfig")

    # Check if directories exist and eventually append them to the
    # pkgconfig path list
    foreach(_prefix_dir ${_extra_paths})
      foreach(_lib_dir ${_lib_dirs})
        if(EXISTS "${_prefix_dir}/${_lib_dir}")
          list(APPEND _pkg_config_path "${_prefix_dir}/${_lib_dir}")
          list(REMOVE_DUPLICATES _pkg_config_path)
        endif()
      endforeach()
    endforeach()

    if(NOT "${_pkg_config_path}" STREQUAL "")
      # remove empty values from the list
      list(REMOVE_ITEM _pkg_config_path "")
      file(TO_NATIVE_PATH "${_pkg_config_path}" _pkg_config_path)
      if(UNIX)
        string(REPLACE ";" ":" _pkg_config_path "${_pkg_config_path}")
        string(REPLACE "\\ " " " _pkg_config_path "${_pkg_config_path}")
      endif()
      set(ENV{PKG_CONFIG_PATH} "${_pkg_config_path}")
    endif()
  endif()
  pkg_search_module(CONFIG_PCAP ${_quiet} libpcap)
  set(ENV{PKG_CONFIG_PATH} "${_saved_pkg_config_path}")

  if(NOT CONFIG_PCAP_FOUND)
    #
    # That didn't work.  Try pcap-config.
    #
    find_program(PCAP_CONFIG pcap-config)
    if(PCAP_CONFIG)
      #
      # We have pcap-config; use it.
      #
      if(NOT "${_quiet}" STREQUAL "QUIET")
        message(STATUS "Found pcap-config")
      endif()

      #
      # If this is a vendor-supplied pcap-config, which we define as
      # being "a pcap-config in /usr/bin or /usr/ccs/bin" (the latter
      # is for Solaris and Sun/Oracle Studio), there are some issues.
      # Work around them.
      #
      if("${PCAP_CONFIG}" STREQUAL /usr/bin/pcap-config OR
         "${PCAP_CONFIG}" STREQUAL /usr/ccs/bin/pcap-config)
        #
        # It's vendor-supplied.
        #
        if(APPLE)
          #
          # This is macOS or another Darwin-based OS.
          #
          # That means that /usr/bin/pcap-config it may provide
          # -I/usr/local/include with --cflags and -L/usr/local/lib
          # with --libs; if there's no pcap installed under /usr/local,
          # that will cause the build to fail, and if there is a pcap
          # installed there, you'll get that pcap even if you don't
          # want it.  Remember that, so we ignore those values.
          #
          set(_broken_apple_pcap_config TRUE)
        elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.][0-9.]*")
          #
          # This is Solaris 2 or later, i.e. SunOS 5.x.
          #
          # At least on Solaris 11; there's /usr/bin/pcap-config, which
          # reports -L/usr/lib with --libs, causing the 32-bit libraries
          # to be found, and there's /usr/bin/{64bitarch}/pcap-config,
          # where {64bitarch} is a name for the 64-bit version of the
          # instruction set, which reports -L /usr/lib/{64bitarch},
          # causing the 64-bit libraries to be found.
          #
          # So if we're building 64-bit targets, we replace PCAP_CONFIG
          # with /usr/bin/{64bitarch}; we get {64bitarch} as the
          # output of "isainfo -n".
          #
          if(CMAKE_SIZEOF_VOID_P EQUAL 8)
            execute_process(COMMAND "isainfo" "-n"
              RESULT_VARIABLE ISAINFO_RESULT
              OUTPUT_VARIABLE ISAINFO_OUTPUT
              OUTPUT_STRIP_TRAILING_WHITESPACE
            )
            if(ISAINFO_RESULT EQUAL 0)
              #
              # Success - change PCAP_CONFIG.
              #
              string(REPLACE "/bin/" "/bin/${ISAINFO_OUTPUT}/" PCAP_CONFIG "${PCAP_CONFIG}")
            endif()
          endif()
        endif()
      endif()

      #
      # Now get the include directories.
      #
      execute_process(COMMAND "${PCAP_CONFIG}" "--cflags"
        RESULT_VARIABLE PCAP_CONFIG_RESULT
        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
        OUTPUT_STRIP_TRAILING_WHITESPACE
      )
      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
        message(FATAL_ERROR "pcap-config --cflags failed")
      endif()
      separate_arguments(CFLAGS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
      set(CONFIG_PCAP_INCLUDE_DIRS "")
      foreach(_arg IN LISTS CFLAGS_LIST)
        if(_arg MATCHES "^-I")
          #
          # Extract the directory by removing the -I.
          #
          string(REGEX REPLACE "-I" "" _dir ${_arg})
          #
          # Work around macOS (and probably other Darwin) brokenness,
          # by not adding /usr/local/include if it's from the broken
          # Apple pcap-config.
          #
          if(NOT _broken_apple_pcap_config OR
             NOT "${_dir}" STREQUAL /usr/local/include)
            # Add it to CONFIG_PCAP_INCLUDE_DIRS
            list(APPEND CONFIG_PCAP_INCLUDE_DIRS ${_dir})
          endif()
        endif()
      endforeach()

      #
      # Now, get the library directories and libraries for dynamic linking.
      #
      execute_process(COMMAND "${PCAP_CONFIG}" "--libs"
        RESULT_VARIABLE PCAP_CONFIG_RESULT
        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
        OUTPUT_STRIP_TRAILING_WHITESPACE
      )
      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
        message(FATAL_ERROR "pcap-config --libs failed")
      endif()
      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
      set(CONFIG_PCAP_LIBRARY_DIRS "")
      set(CONFIG_PCAP_LIBRARIES "")
      foreach(_arg IN LISTS LIBS_LIST)
        if(_arg MATCHES "^-L")
          #
          # Extract the directory by removing the -L.
          #
          string(REGEX REPLACE "-L" "" _dir ${_arg})
          #
          # Work around macOS (and probably other Darwin) brokenness,
          # by not adding /usr/local/lib if it's from the broken
          # Apple pcap-config.
          #
          if(NOT _broken_apple_pcap_config OR
             NOT "${_dir}" STREQUAL /usr/local/lib)
            # Add this directory to CONFIG_PCAP_LIBRARY_DIRS
            list(APPEND CONFIG_PCAP_LIBRARY_DIRS ${_dir})
          endif()
        elseif(_arg MATCHES "^-l")
          string(REGEX REPLACE "-l" "" _lib ${_arg})
          list(APPEND CONFIG_PCAP_LIBRARIES ${_lib})
        endif()
      endforeach()

      #
      # Now, get the library directories and libraries for static linking.
      #
      execute_process(COMMAND "${PCAP_CONFIG}" "--libs" "--static"
        RESULT_VARIABLE PCAP_CONFIG_RESULT
        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
      )
      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
        message(FATAL_ERROR "pcap-config --libs --static failed")
      endif()
      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
      set(CONFIG_PCAP_STATIC_LIBRARY_DIRS "")
      set(CONFIG_PCAP_STATIC_LIBRARIES "")
      foreach(_arg IN LISTS LIBS_LIST)
        if(_arg MATCHES "^-L")
          #
          # Extract the directory by removing the -L.
          #
          string(REGEX REPLACE "-L" "" _dir ${_arg})
          #
          # Work around macOS (and probably other Darwin) brokenness,
          # by not adding /usr/local/lib if it's from the broken
          # Apple pcap-config.
          #
          if(NOT _broken_apple_pcap_config OR
             NOT "${_dir}" STREQUAL /usr/local/lib)
            # Add this directory to CONFIG_PCAP_STATIC_LIBRARY_DIRS
            list(APPEND CONFIG_PCAP_STATIC_LIBRARY_DIRS ${_dir})
          endif()
        elseif(_arg MATCHES "^-l")
          string(REGEX REPLACE "-l" "" _lib ${_arg})
          #
          # Try to find that library, so we get its full path, as
          # we do with dynamic libraries.
          #
          list(APPEND CONFIG_PCAP_STATIC_LIBRARIES ${_lib})
        endif()
      endforeach()

      #
      # We've set CONFIG_PCAP_INCLUDE_DIRS, CONFIG_PCAP_LIBRARIES, and
      # CONFIG_PCAP_STATIC_LIBRARIES above; set CONFIG_PCAP_FOUND.
      #
      set(CONFIG_PCAP_FOUND YES)
    endif()
  endif()

  #
  # If CONFIG_PCAP_FOUND is set, we have information from pkg-config and
  # pcap-config; we need to convert library names to library full paths.
  #
  # If it's not set, we have to look for the libpcap headers and library
  # ourselves.
  #
  if(CONFIG_PCAP_FOUND)
    #
    # Use CONFIG_PCAP_INCLUDE_DIRS as the value for PCAP_INCLUDE_DIRS.
    #
    set(PCAP_INCLUDE_DIRS "${CONFIG_PCAP_INCLUDE_DIRS}")

    #
    # CMake *really* doesn't like the notion of specifying
    # "here are the directories in which to look for libraries"
    # except in find_library() calls; it *really* prefers using
    # full paths to library files, rather than library names.
    #
    foreach(_lib IN LISTS CONFIG_PCAP_LIBRARIES)
      find_library(_libfullpath ${_lib} HINTS ${CONFIG_PCAP_LIBRARY_DIRS})
      list(APPEND PCAP_LIBRARIES ${_libfullpath})
      #
      # Remove that from the cache; we're using it as a local variable,
      # but find_library insists on making it a cache variable.
      #
      unset(_libfullpath CACHE)
   endforeach()

    #
    # Now do the same for the static libraries.
    #
    set(SAVED_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}")
    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
    foreach(_lib IN LISTS CONFIG_PCAP_STATIC_LIBRARIES)
      find_library(_libfullpath ${_lib} HINTS ${CONFIG_PCAP_LIBRARY_DIRS})
      list(APPEND PCAP_STATIC_LIBRARIES ${_libfullpath})
      #
      # Remove that from the cache; we're using it as a local variable,
      # but find_library insists on making it a cache variable.
      #
      unset(_libfullpath CACHE)
    endforeach()
    set(CMAKE_FIND_LIBRARY_SUFFIXES "${SAVED_CMAKE_FIND_LIBRARY_SUFFIXES}")

    #
    # We found libpcap using pkg-config or pcap-config.
    #
    set(PCAP_FOUND YES)
  else(CONFIG_PCAP_FOUND)
    #
    # We didn't have pkg-config, or we did but it didn't have .pc files
    # for libpcap, and we don't have pkg-config, so we have to look for
    # the headers and libraries ourself.
    #
    # We don't directly set PCAP_INCLUDE_DIRS or PCAP_LIBRARIES, as
    # they're not supposed to be cache entries, and find_path() and
    # find_library() set cache entries.
    #
    # Try to find the header file.
    #
    find_path(PCAP_INCLUDE_DIR pcap.h)

    #
    # Try to find the library
    #
    find_library(PCAP_LIBRARY pcap)

    # Try to find the static library (XXX - what about AIX?)
    set(SAVED_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}")
    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
    find_library(PCAP_STATIC_LIBRARY pcap)
    set(CMAKE_FIND_LIBRARY_SUFFIXES "${SAVED_CMAKE_FIND_LIBRARY_SUFFIXES}")

    #
    # This will fail if REQUIRED is set and PCAP_INCLUDE_DIR or
    # PCAP_LIBRARY aren't set.
    #
    include(FindPackageHandleStandardArgs)
    find_package_handle_standard_args(PCAP
      DEFAULT_MSG
      PCAP_INCLUDE_DIR
      PCAP_LIBRARY
    )

    mark_as_advanced(
      PCAP_INCLUDE_DIR
      PCAP_LIBRARY
      PCAP_STATIC_LIBRARY
    )

    if(PCAP_FOUND)
      set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
      set(PCAP_LIBRARIES ${PCAP_LIBRARY})
      set(PCAP_STATIC_LIBRARIES ${PCAP_STATIC_LIBRARY})
    endif(PCAP_FOUND)
  endif(CONFIG_PCAP_FOUND)
endif(WIN32)