aboutsummaryrefslogtreecommitdiff
path: root/cmake/modules/FindCurl.cmake
blob: 4e289cbe0e5f8395caef1e201192b929fdb376ca (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
#.rst:
# FindCurl
# --------
# Finds the Curl library
#
# This will define the following target:
#
#   ${APP_NAME_LC}::Curl   - The Curl library

if(NOT TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME})
  include(cmake/scripts/common/ModuleHelpers.cmake)

  macro(buildCurl)

    find_package(NGHttp2 REQUIRED QUIET)
    find_package(OpenSSL REQUIRED QUIET)
    find_package(Brotli REQUIRED QUIET)

    # Darwin platforms link against toolchain provided zlib regardless
    # They will fail when searching for static. All other platforms, prefer static
    # if possible (requires cmake 3.24+ otherwise variable is a no-op)
    # Windows still uses dynamic lib for zlib for other purposes, dont mix
    if(NOT CMAKE_SYSTEM_NAME MATCHES "Darwin" AND NOT (WIN32 OR WINDOWS_STORE))
      set(ZLIB_USE_STATIC_LIBS ON)
    endif()
    find_package(ZLIB REQUIRED)
    unset(ZLIB_USE_STATIC_LIBS)

    set(CURL_VERSION ${${MODULE}_VER})
    # Curl debug uses postfix -d for all platforms
    set(CURL_DEBUG_POSTFIX -d)

    if(WIN32 OR WINDOWS_STORE)
      set(CURL_C_FLAGS -DNGHTTP2_STATICLIB)
      set(PLATFORM_LINK_LIBS crypt32.lib)
    endif()

    set(patches "${CORE_SOURCE_DIR}/tools/depends/target/${MODULE_LC}/01-win-nghttp2-add-name.patch")

    generate_patchcommand("${patches}")

    set(CMAKE_ARGS -DBUILD_CURL_EXE=OFF
                   -DBUILD_SHARED_LIBS=OFF
                   -DBUILD_STATIC_LIBS=ON
                   -DBUILD_LIBCURL_DOCS=OFF
                   -DENABLE_CURL_MANUAL=OFF
                   -DCURL_DISABLE_TESTS=OFF
                   -DCURL_DISABLE_LDAP=ON
                   -DCURL_DISABLE_LDAPS=ON
                   -DCURL_DISABLE_SMB=OFF
                   -DCURL_USE_OPENSSL=ON
                   -DOPENSSL_ROOT_DIR=${DEPENDS_PATH}
                   -DCURL_BROTLI=ON
                   -DUSE_NGHTTP2=ON
                   -DUSE_LIBIDN2=OFF
                   -DCURL_USE_LIBSSH2=OFF
                   -DCURL_USE_GSSAPI=OFF
                   -DCURL_CA_FALLBACK=ON
                   ${OPTIONAL_ARGS})

    BUILD_DEP_TARGET()

    # Link libraries for target interface
    set(PC_CURL_LINK_LIBRARIES Brotli::Brotli OpenSSL::Crypto OpenSSL::SSL NGHttp2::NGHttp2 ZLIB::ZLIB ${PLATFORM_LINK_LIBS})

    # Add dependencies to build target
    add_dependencies(${MODULE_LC} NGHttp2::NGHttp2)
    add_dependencies(${MODULE_LC} OpenSSL::SSL)
    add_dependencies(${MODULE_LC} OpenSSL::Crypto)
    add_dependencies(${MODULE_LC} ZLIB::ZLIB)
    add_dependencies(${MODULE_LC} Brotli::Brotli)
  endmacro()

  set(MODULE_LC curl)

  SETUP_BUILD_VARS()

  find_package(CURL CONFIG QUIET
                    HINTS ${DEPENDS_PATH}/lib/cmake
                    ${${CORE_PLATFORM_NAME_LC}_SEARCH_CONFIG})

  # Check for existing Curl. If version >= CURL-VERSION file version, dont build
  # A corner case, but if a linux/freebsd user WANTS to build internal curl, build anyway
  if((CURL_VERSION VERSION_LESS ${${MODULE}_VER} AND ENABLE_INTERNAL_CURL) OR
     ((CORE_SYSTEM_NAME STREQUAL linux OR CORE_SYSTEM_NAME STREQUAL freebsd) AND ENABLE_INTERNAL_CURL))

    buildCurl()
  else()
    # Maybe need to look explicitly for CURL::libcurl_static/shared?
    if(NOT TARGET CURL::libcurl)
      find_package(PkgConfig)

      # We only rely on pkgconfig for non windows platforms
      if(PKG_CONFIG_FOUND AND NOT (WIN32 OR WINDOWS_STORE))
        pkg_check_modules(CURL libcurl QUIET)

        # First item is the full path of the library file found
        # pkg_check_modules does not populate a variable of the found library explicitly
        list(GET CURL_LINK_LIBRARIES 0 CURL_LIBRARY_RELEASE)

        # Add link libraries for static lib usage
        if(${CURL_LIBRARY} MATCHES ".+\.a$" AND CURL_LINK_LIBRARIES)
          # Remove duplicates
          list(REMOVE_DUPLICATES CURL_LINK_LIBRARIES)

          # Remove own library - eg libcurl.a
          list(FILTER CURL_LINK_LIBRARIES EXCLUDE REGEX ".*curl.*\.a$")
          set(PC_CURL_LINK_LIBRARIES ${CURL_LINK_LIBRARIES})
        endif()

        # pkgconfig sets CURL_INCLUDEDIR, map this to our "standard" variable name
        set(CURL_INCLUDE_DIR ${CURL_INCLUDEDIR})
      else()
        find_path(CURL_INCLUDE_DIR NAMES curl/curl.h
                                   HINTS ${DEPENDS_PATH}/include
                                   ${${CORE_PLATFORM_LC}_SEARCH_CONFIG})
        find_library(CURL_LIBRARY_RELEASE NAMES curl libcurl libcurl_imp
                                          HINTS ${DEPENDS_PATH}/lib
                                          ${${CORE_PLATFORM_LC}_SEARCH_CONFIG})
      endif()
    else()
      # CURL::libcurl is an alias. We need to get the actual aias target, as we cant make an
      # alias of an alias (ie our ${APP_NAME_LC}::Curl cant be an alias of Curl::libcurl)
      get_target_property(_CURL_ALIASTARGET CURL::libcurl ALIASED_TARGET)

      # This is for the case where a distro provides a non standard (Debug/Release) config type
      # eg Debian's config file is CURLConfigTargets-none.cmake
      # convert this back to either DEBUG/RELEASE or just RELEASE
      # we only do this because we use find_package_handle_standard_args for config time output
      # and it isnt capable of handling TARGETS, so we have to extract the info
      get_target_property(_CURL_CONFIGURATIONS ${_CURL_ALIASTARGET} IMPORTED_CONFIGURATIONS)
      foreach(_curl_config IN LISTS _CURL_CONFIGURATIONS)
        # Some non standard config (eg None on Debian)
        # Just set to RELEASE var so select_library_configurations can continue to work its magic
        string(TOUPPER ${_curl_config} _curl_config_UPPER)
        if((NOT ${_curl_config_UPPER} STREQUAL "RELEASE") AND
           (NOT ${_curl_config_UPPER} STREQUAL "DEBUG"))
          get_target_property(CURL_LIBRARY_RELEASE ${_CURL_ALIASTARGET} IMPORTED_LOCATION_${_curl_config_UPPER})
        else()
          get_target_property(CURL_LIBRARY_${_curl_config_UPPER} ${_CURL_ALIASTARGET} IMPORTED_LOCATION_${_curl_config_UPPER})
        endif()
      endforeach()

      get_target_property(CURL_INCLUDE_DIR CURL::libcurl INTERFACE_INCLUDE_DIRECTORIES)
    endif()
  endif()

  include(SelectLibraryConfigurations)
  select_library_configurations(CURL)
  unset(CURL_LIBRARIES)

  include(FindPackageHandleStandardArgs)
  find_package_handle_standard_args(Curl
                                    REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
                                    VERSION_VAR CURL_VERSION)

  if(CURL_FOUND)
    # cmake target and not building internal
    if(TARGET CURL::libcurl AND NOT TARGET curl)
      # CURL::libcurl is an alias. We need to get the actual aias target, as we cant make an
      # alias of an alias (ie our ${APP_NAME_LC}::Curl cant be an alias of Curl::libcurl)
      if(NOT _CURL_ALIASTARGET)
        get_target_property(_CURL_ALIASTARGET CURL::libcurl ALIASED_TARGET)
      endif()

      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} ALIAS ${_CURL_ALIASTARGET})
    else()
      add_library(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} UNKNOWN IMPORTED)
      set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
                                                                       INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIR}")

      if(CURL_LIBRARY_RELEASE)
        set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
                                                                         IMPORTED_CONFIGURATIONS RELEASE
                                                                         IMPORTED_LOCATION_RELEASE "${CURL_LIBRARY_RELEASE}")
      endif()
      if(CURL_LIBRARY_DEBUG)
        set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
                                                                         IMPORTED_CONFIGURATIONS DEBUG
                                                                         IMPORTED_LOCATION_DEBUG "${CURL_LIBRARY_DEBUG}")
      endif()

      # Add link libraries for static lib usage found from pkg-config
      if(PC_CURL_LINK_LIBRARIES)
        set_target_properties(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} PROPERTIES
                                                                         INTERFACE_LINK_LIBRARIES "${PC_CURL_LINK_LIBRARIES}")
      endif()

      if(WIN32 OR WINDOWS_STORE)
        set_property(TARGET ${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB")
      endif()

    endif()

    if(TARGET curl)
      add_dependencies(${APP_NAME_LC}::${CMAKE_FIND_PACKAGE_NAME} curl)
    endif()

    # Add internal build target when a Multi Config Generator is used
    # We cant add a dependency based off a generator expression for targeted build types,
    # https://gitlab.kitware.com/cmake/cmake/-/issues/19467
    # therefore if the find heuristics only find the library, we add the internal build
    # target to the project to allow user to manually trigger for any build type they need
    # in case only a specific build type is actually available (eg Release found, Debug Required)
    # This is mainly targeted for windows who required different runtime libs for different
    # types, and they arent compatible
    if(_multiconfig_generator)
      if(NOT TARGET curl)
        buildCurl()
        set_target_properties(curl PROPERTIES EXCLUDE_FROM_ALL TRUE)
      endif()
      add_dependencies(build_internal_depends curl)
    endif()
  else()
    if(Curl_FIND_REQUIRED)
      message(FATAL_ERROR "Curl libraries were not found.")
    endif()
  endif()
endif()