xref: /src/contrib/tcpdump/cmake/Modules/FindPCAP.cmake (revision e6083790f217ba7f89cd2957922bd45e35466359)
1#
2# Try to find libpcap.
3#
4# To tell this module where to look, a user may set the environment variable
5# PCAP_ROOT to point cmake to the *root* of a directory with include and
6# lib subdirectories for pcap.dll (e.g WpdPack or npcap-sdk).
7# Alternatively, PCAP_ROOT may also be set from cmake command line or GUI
8# (e.g cmake -DPCAP_ROOT=C:\path\to\pcap [...])
9#
10
11if(WIN32)
12  #
13  # Building for Windows.
14  #
15  # libpcap isn't set up to install .pc files or pcap-config on Windows,
16  # and it's not clear that either of them would work without a lot
17  # of additional effort.  WinPcap doesn't supply them, and neither
18  # does Npcap.
19  #
20  # So just search for them directly.  Look for both pcap and wpcap.
21  # Don't bother looking for static libraries; unlike most UN*Xes
22  # (with the exception of AIX), where different extensions are used
23  # for shared and static, Windows uses .lib both for import libraries
24  # for DLLs and for static libraries.
25  #
26  # We don't directly set PCAP_INCLUDE_DIRS or PCAP_LIBRARIES, as
27  # they're not supposed to be cache entries, and find_path() and
28  # find_library() set cache entries.
29  #
30  find_path(PCAP_INCLUDE_DIR pcap.h)
31
32  # The 64-bit Packet.lib is located under /x64
33  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
34    #
35    # For the WinPcap and Npcap SDKs, the Lib subdirectory of the top-level
36    # directory contains 32-bit libraries; the 64-bit libraries are in the
37    # Lib/x64 directory.
38    #
39    # The only way to *FORCE* CMake to look in the Lib/x64 directory
40    # without searching in the Lib directory first appears to be to set
41    # CMAKE_LIBRARY_ARCHITECTURE to "x64".
42    #
43    set(CMAKE_LIBRARY_ARCHITECTURE "x64")
44  endif()
45  find_library(PCAP_LIBRARY NAMES pcap wpcap)
46
47  #
48  # Do the standard arg processing, including failing if it's a
49  # required package.
50  #
51  include(FindPackageHandleStandardArgs)
52  find_package_handle_standard_args(PCAP
53    DEFAULT_MSG
54    PCAP_INCLUDE_DIR
55    PCAP_LIBRARY
56  )
57  mark_as_advanced(
58    PCAP_INCLUDE_DIR
59    PCAP_LIBRARY
60  )
61  if(PCAP_FOUND)
62    set(PCAP_LIBRARIES ${PCAP_LIBRARY})
63    set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
64
65    #
66    # We need to look for wpcap.dll in \Windows\System32\Npcap first,
67    # as either:
68    #
69    #  1) WinPcap isn't installed and Npcap isn't installed in "WinPcap
70    #     API-compatible Mode", so there's no wpcap.dll in
71    #     \Windows\System32, only in \Windows\System32\Npcap;
72    #
73    #  2) WinPcap is installed and Npcap isn't installed in "WinPcap
74    #     API-compatible Mode", so the wpcap.dll in \Windows\System32
75    #     is a WinPcap DLL, but we'd prefer an Npcap DLL (we should
76    #     work with either one if we're configured against WinPcap,
77    #     and we'll probably require Npcap if we're configured against
78    #     it), and that's in \Windows\System32\Npcap;
79    #
80    #  3) Npcap is installed in "WinPcap API-compatible Mode", so both
81    #     \Windows\System32 and \Windows\System32\Npcap have an Npcap
82    #     wpcap.dll.
83    #
84    # Unfortunately, Windows has no notion of an rpath, so we can't
85    # set the rpath to include \Windows\System32\Npcap at link time;
86    # what we need to do is to link wpcap as a delay-load DLL and
87    # add \Windows\System32\Npcap to the DLL search path early in
88    # main() with a call to SetDllDirectory().
89    #
90    # We add /delayload:wpcap.dll to the linker options here.
91    #
92    # See https://npcap.com/guide/npcap-devguide.html#npcap-feature-native-dll-implicitly
93    #
94    set(PCAP_LINK_FLAGS /delayload:wpcap.dll)
95
96    #
97    # Delay-loading libraries means we need to link with delayimp.lib.
98    #
99    set(PCAP_LIBRARIES ${PCAP_LIBRARIES} delayimp.lib)
100  endif()
101else(WIN32)
102  #
103  # Building for UN*X.
104  #
105  # See whether we were handed a QUIET argument, so we can pass it on
106  # to pkg_search_module.  Do *NOT* pass on the REQUIRED argument,
107  # because, if pkg-config isn't found, or it is but it has no .pc
108  # files for libpcap, that is *not* necessarily an indication that
109  # libpcap isn't available - not all systems ship pkg-config, and
110  # libpcap didn't have .pc files until libpcap 1.9.0.
111  #
112  if(PCAP_FIND_QUIETLY)
113    set(_quiet "QUIET")
114  endif()
115
116  #
117  # First, try pkg-config.
118  # Before doing so, set the PKG_CONFIG_PATH environment variable
119  # to include all the directories in CMAKE_PREFIX_PATH.
120  #
121  # *If* we were to require CMake 3.1 or later on UN*X,
122  # pkg_search_module() would do this for us, but, for now,
123  # we're not doing that, in case somebody's building with
124  # CMake on some "long-term support" version, predating
125  # CMake 3.1, of an OS that supplies an earlier
126  # version as a package.
127  #
128  # If we ever set a minimum of 3.1 or later on UN*X, we should
129  # remove the environment variable changes.
130  #
131  # This is based on code in the CMake 3.12.4 FindPkgConfig.cmake,
132  # which is "Distributed under the OSI-approved BSD 3-Clause License."
133  #
134  find_package(PkgConfig)
135
136  #
137  # Get the current PKG_CONFIG_PATH setting.
138  #
139  set(_pkg_config_path "$ENV{PKG_CONFIG_PATH}")
140
141  #
142  # Save it, so we can restore it after we run pkg-config.
143  #
144  set(_saved_pkg_config_path "${_pkg_config_path}")
145
146  if(NOT "${CMAKE_PREFIX_PATH}" STREQUAL "")
147    #
148    # Convert it to a CMake-style path, before we add additional
149    # values to it.
150    #
151    if(NOT "${_pkg_config_path}" STREQUAL "")
152      file(TO_CMAKE_PATH "${_pkg_config_path}" _pkg_config_path)
153    endif()
154
155    #
156    # Turn CMAKE_PREFIX_PATH into a list of extra paths to add
157    # to _pkg_config_path.
158    #
159    set(_extra_paths "")
160    list(APPEND _extra_paths ${CMAKE_PREFIX_PATH})
161
162    # Create a list of the possible pkgconfig subfolder (depending on
163    # the system
164    set(_lib_dirs)
165    if(NOT DEFINED CMAKE_SYSTEM_NAME
166        OR (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
167            AND NOT CMAKE_CROSSCOMPILING))
168      if(EXISTS "/etc/debian_version") # is this a debian system ?
169        if(CMAKE_LIBRARY_ARCHITECTURE)
170          list(APPEND _lib_dirs "lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig")
171        endif()
172      else()
173        # not debian, check the FIND_LIBRARY_USE_LIB32_PATHS and FIND_LIBRARY_USE_LIB64_PATHS properties
174        get_property(uselib32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS)
175        if(uselib32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
176          list(APPEND _lib_dirs "lib32/pkgconfig")
177        endif()
178        get_property(uselib64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
179        if(uselib64 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
180          list(APPEND _lib_dirs "lib64/pkgconfig")
181        endif()
182        get_property(uselibx32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIBX32_PATHS)
183        if(uselibx32 AND CMAKE_INTERNAL_PLATFORM_ABI STREQUAL "ELF X32")
184          list(APPEND _lib_dirs "libx32/pkgconfig")
185        endif()
186      endif()
187    endif()
188    if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND NOT CMAKE_CROSSCOMPILING)
189      list(APPEND _lib_dirs "libdata/pkgconfig")
190    endif()
191    list(APPEND _lib_dirs "lib/pkgconfig")
192    list(APPEND _lib_dirs "share/pkgconfig")
193
194    # Check if directories exist and eventually append them to the
195    # pkgconfig path list
196    foreach(_prefix_dir ${_extra_paths})
197      foreach(_lib_dir ${_lib_dirs})
198        if(EXISTS "${_prefix_dir}/${_lib_dir}")
199          list(APPEND _pkg_config_path "${_prefix_dir}/${_lib_dir}")
200          list(REMOVE_DUPLICATES _pkg_config_path)
201        endif()
202      endforeach()
203    endforeach()
204
205    if(NOT "${_pkg_config_path}" STREQUAL "")
206      # remove empty values from the list
207      list(REMOVE_ITEM _pkg_config_path "")
208      file(TO_NATIVE_PATH "${_pkg_config_path}" _pkg_config_path)
209      if(UNIX)
210        string(REPLACE ";" ":" _pkg_config_path "${_pkg_config_path}")
211        string(REPLACE "\\ " " " _pkg_config_path "${_pkg_config_path}")
212      endif()
213      set(ENV{PKG_CONFIG_PATH} "${_pkg_config_path}")
214    endif()
215  endif()
216  pkg_search_module(CONFIG_PCAP ${_quiet} libpcap)
217  set(ENV{PKG_CONFIG_PATH} "${_saved_pkg_config_path}")
218
219  if(NOT CONFIG_PCAP_FOUND)
220    #
221    # That didn't work.  Try pcap-config.
222    #
223    find_program(PCAP_CONFIG pcap-config)
224    if(PCAP_CONFIG)
225      #
226      # We have pcap-config; use it.
227      #
228      if(NOT "${_quiet}" STREQUAL "QUIET")
229        message(STATUS "Found pcap-config")
230      endif()
231
232      #
233      # If this is a vendor-supplied pcap-config, which we define as
234      # being "a pcap-config in /usr/bin or /usr/ccs/bin" (the latter
235      # is for Solaris and Sun/Oracle Studio), there are some issues.
236      # Work around them.
237      #
238      if("${PCAP_CONFIG}" STREQUAL /usr/bin/pcap-config OR
239         "${PCAP_CONFIG}" STREQUAL /usr/ccs/bin/pcap-config)
240        #
241        # It's vendor-supplied.
242        #
243        if(APPLE)
244          #
245          # This is macOS or another Darwin-based OS.
246          #
247          # That means that /usr/bin/pcap-config it may provide
248          # -I/usr/local/include with --cflags and -L/usr/local/lib
249          # with --libs; if there's no pcap installed under /usr/local,
250          # that will cause the build to fail, and if there is a pcap
251          # installed there, you'll get that pcap even if you don't
252          # want it.  Remember that, so we ignore those values.
253          #
254          set(_broken_apple_pcap_config TRUE)
255        elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND CMAKE_SYSTEM_VERSION MATCHES "5[.][0-9.]*")
256          #
257          # This is Solaris 2 or later, i.e. SunOS 5.x.
258          #
259          # At least on Solaris 11; there's /usr/bin/pcap-config, which
260          # reports -L/usr/lib with --libs, causing the 32-bit libraries
261          # to be found, and there's /usr/bin/{64bitarch}/pcap-config,
262          # where {64bitarch} is a name for the 64-bit version of the
263          # instruction set, which reports -L /usr/lib/{64bitarch},
264          # causing the 64-bit libraries to be found.
265          #
266          # So if we're building 64-bit targets, we replace PCAP_CONFIG
267          # with /usr/bin/{64bitarch}; we get {64bitarch} as the
268          # output of "isainfo -n".
269          #
270          if(CMAKE_SIZEOF_VOID_P EQUAL 8)
271            execute_process(COMMAND "isainfo" "-n"
272              RESULT_VARIABLE ISAINFO_RESULT
273              OUTPUT_VARIABLE ISAINFO_OUTPUT
274              OUTPUT_STRIP_TRAILING_WHITESPACE
275            )
276            if(ISAINFO_RESULT EQUAL 0)
277              #
278              # Success - change PCAP_CONFIG.
279              #
280              string(REPLACE "/bin/" "/bin/${ISAINFO_OUTPUT}/" PCAP_CONFIG "${PCAP_CONFIG}")
281            endif()
282          endif()
283        endif()
284      endif()
285
286      #
287      # Now get the include directories.
288      #
289      execute_process(COMMAND "${PCAP_CONFIG}" "--cflags"
290        RESULT_VARIABLE PCAP_CONFIG_RESULT
291        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
292        OUTPUT_STRIP_TRAILING_WHITESPACE
293      )
294      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
295        message(FATAL_ERROR "pcap-config --cflags failed")
296      endif()
297      separate_arguments(CFLAGS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
298      set(CONFIG_PCAP_INCLUDE_DIRS "")
299      foreach(_arg IN LISTS CFLAGS_LIST)
300        if(_arg MATCHES "^-I")
301          #
302          # Extract the directory by removing the -I.
303          #
304          string(REGEX REPLACE "-I" "" _dir ${_arg})
305          #
306          # Work around macOS (and probably other Darwin) brokenness,
307          # by not adding /usr/local/include if it's from the broken
308          # Apple pcap-config.
309          #
310          if(NOT _broken_apple_pcap_config OR
311             NOT "${_dir}" STREQUAL /usr/local/include)
312            # Add it to CONFIG_PCAP_INCLUDE_DIRS
313            list(APPEND CONFIG_PCAP_INCLUDE_DIRS ${_dir})
314          endif()
315        endif()
316      endforeach()
317
318      #
319      # Now, get the library directories and libraries for dynamic linking.
320      #
321      execute_process(COMMAND "${PCAP_CONFIG}" "--libs"
322        RESULT_VARIABLE PCAP_CONFIG_RESULT
323        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
324        OUTPUT_STRIP_TRAILING_WHITESPACE
325      )
326      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
327        message(FATAL_ERROR "pcap-config --libs failed")
328      endif()
329      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
330      set(CONFIG_PCAP_LIBRARY_DIRS "")
331      set(CONFIG_PCAP_LIBRARIES "")
332      foreach(_arg IN LISTS LIBS_LIST)
333        if(_arg MATCHES "^-L")
334          #
335          # Extract the directory by removing the -L.
336          #
337          string(REGEX REPLACE "-L" "" _dir ${_arg})
338          #
339          # Work around macOS (and probably other Darwin) brokenness,
340          # by not adding /usr/local/lib if it's from the broken
341          # Apple pcap-config.
342          #
343          if(NOT _broken_apple_pcap_config OR
344             NOT "${_dir}" STREQUAL /usr/local/lib)
345            # Add this directory to CONFIG_PCAP_LIBRARY_DIRS
346            list(APPEND CONFIG_PCAP_LIBRARY_DIRS ${_dir})
347          endif()
348        elseif(_arg MATCHES "^-l")
349          string(REGEX REPLACE "-l" "" _lib ${_arg})
350          list(APPEND CONFIG_PCAP_LIBRARIES ${_lib})
351        endif()
352      endforeach()
353
354      #
355      # Now, get the library directories and libraries for static linking.
356      #
357      execute_process(COMMAND "${PCAP_CONFIG}" "--libs" "--static"
358        RESULT_VARIABLE PCAP_CONFIG_RESULT
359        OUTPUT_VARIABLE PCAP_CONFIG_OUTPUT
360      )
361      if(NOT PCAP_CONFIG_RESULT EQUAL 0)
362        message(FATAL_ERROR "pcap-config --libs --static failed")
363      endif()
364      separate_arguments(LIBS_LIST UNIX_COMMAND ${PCAP_CONFIG_OUTPUT})
365      set(CONFIG_PCAP_STATIC_LIBRARY_DIRS "")
366      set(CONFIG_PCAP_STATIC_LIBRARIES "")
367      foreach(_arg IN LISTS LIBS_LIST)
368        if(_arg MATCHES "^-L")
369          #
370          # Extract the directory by removing the -L.
371          #
372          string(REGEX REPLACE "-L" "" _dir ${_arg})
373          #
374          # Work around macOS (and probably other Darwin) brokenness,
375          # by not adding /usr/local/lib if it's from the broken
376          # Apple pcap-config.
377          #
378          if(NOT _broken_apple_pcap_config OR
379             NOT "${_dir}" STREQUAL /usr/local/lib)
380            # Add this directory to CONFIG_PCAP_STATIC_LIBRARY_DIRS
381            list(APPEND CONFIG_PCAP_STATIC_LIBRARY_DIRS ${_dir})
382          endif()
383        elseif(_arg MATCHES "^-l")
384          string(REGEX REPLACE "-l" "" _lib ${_arg})
385          #
386          # Try to find that library, so we get its full path, as
387          # we do with dynamic libraries.
388          #
389          list(APPEND CONFIG_PCAP_STATIC_LIBRARIES ${_lib})
390        endif()
391      endforeach()
392
393      #
394      # We've set CONFIG_PCAP_INCLUDE_DIRS, CONFIG_PCAP_LIBRARIES, and
395      # CONFIG_PCAP_STATIC_LIBRARIES above; set CONFIG_PCAP_FOUND.
396      #
397      set(CONFIG_PCAP_FOUND YES)
398    endif()
399  endif()
400
401  #
402  # If CONFIG_PCAP_FOUND is set, we have information from pkg-config and
403  # pcap-config; we need to convert library names to library full paths.
404  #
405  # If it's not set, we have to look for the libpcap headers and library
406  # ourselves.
407  #
408  if(CONFIG_PCAP_FOUND)
409    #
410    # Use CONFIG_PCAP_INCLUDE_DIRS as the value for PCAP_INCLUDE_DIRS.
411    #
412    set(PCAP_INCLUDE_DIRS "${CONFIG_PCAP_INCLUDE_DIRS}")
413
414    #
415    # CMake *really* doesn't like the notion of specifying
416    # "here are the directories in which to look for libraries"
417    # except in find_library() calls; it *really* prefers using
418    # full paths to library files, rather than library names.
419    #
420    foreach(_lib IN LISTS CONFIG_PCAP_LIBRARIES)
421      find_library(_libfullpath ${_lib} HINTS ${CONFIG_PCAP_LIBRARY_DIRS})
422      list(APPEND PCAP_LIBRARIES ${_libfullpath})
423      #
424      # Remove that from the cache; we're using it as a local variable,
425      # but find_library insists on making it a cache variable.
426      #
427      unset(_libfullpath CACHE)
428   endforeach()
429
430    #
431    # Now do the same for the static libraries.
432    #
433    set(SAVED_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}")
434    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
435    foreach(_lib IN LISTS CONFIG_PCAP_STATIC_LIBRARIES)
436      find_library(_libfullpath ${_lib} HINTS ${CONFIG_PCAP_LIBRARY_DIRS})
437      list(APPEND PCAP_STATIC_LIBRARIES ${_libfullpath})
438      #
439      # Remove that from the cache; we're using it as a local variable,
440      # but find_library insists on making it a cache variable.
441      #
442      unset(_libfullpath CACHE)
443    endforeach()
444    set(CMAKE_FIND_LIBRARY_SUFFIXES "${SAVED_CMAKE_FIND_LIBRARY_SUFFIXES}")
445
446    #
447    # We found libpcap using pkg-config or pcap-config.
448    #
449    set(PCAP_FOUND YES)
450  else(CONFIG_PCAP_FOUND)
451    #
452    # We didn't have pkg-config, or we did but it didn't have .pc files
453    # for libpcap, and we don't have pkg-config, so we have to look for
454    # the headers and libraries ourself.
455    #
456    # We don't directly set PCAP_INCLUDE_DIRS or PCAP_LIBRARIES, as
457    # they're not supposed to be cache entries, and find_path() and
458    # find_library() set cache entries.
459    #
460    # Try to find the header file.
461    #
462    find_path(PCAP_INCLUDE_DIR pcap.h)
463
464    #
465    # Try to find the library
466    #
467    find_library(PCAP_LIBRARY pcap)
468
469    # Try to find the static library (XXX - what about AIX?)
470    set(SAVED_CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_FIND_LIBRARY_SUFFIXES}")
471    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
472    find_library(PCAP_STATIC_LIBRARY pcap)
473    set(CMAKE_FIND_LIBRARY_SUFFIXES "${SAVED_CMAKE_FIND_LIBRARY_SUFFIXES}")
474
475    #
476    # This will fail if REQUIRED is set and PCAP_INCLUDE_DIR or
477    # PCAP_LIBRARY aren't set.
478    #
479    include(FindPackageHandleStandardArgs)
480    find_package_handle_standard_args(PCAP
481      DEFAULT_MSG
482      PCAP_INCLUDE_DIR
483      PCAP_LIBRARY
484    )
485
486    mark_as_advanced(
487      PCAP_INCLUDE_DIR
488      PCAP_LIBRARY
489      PCAP_STATIC_LIBRARY
490    )
491
492    if(PCAP_FOUND)
493      set(PCAP_INCLUDE_DIRS ${PCAP_INCLUDE_DIR})
494      set(PCAP_LIBRARIES ${PCAP_LIBRARY})
495      set(PCAP_STATIC_LIBRARIES ${PCAP_STATIC_LIBRARY})
496    endif(PCAP_FOUND)
497  endif(CONFIG_PCAP_FOUND)
498endif(WIN32)
499