1# The MIT License (MIT)
2
3# Copyright (c) 2018 JFrog
4
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
22
23
24
25# This file comes from: https://github.com/conan-io/cmake-conan. Please refer
26# to this repository for issues and documentation.
27
28# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called.
29# It will take CMake current settings (os, compiler, compiler version, architecture)
30# and translate them to conan settings for installing and retrieving dependencies.
31
32# It is intended to facilitate developers building projects that have conan dependencies,
33# but it is only necessary on the end-user side. It is not necessary to create conan
34# packages, in fact it shouldn't be use for that. Check the project documentation.
35
36
37include(CMakeParseArguments)
38
39function(_get_msvc_ide_version result)
40    set(${result} "" PARENT_SCOPE)
41    if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500)
42        set(${result} 8 PARENT_SCOPE)
43    elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600)
44        set(${result} 9 PARENT_SCOPE)
45    elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700)
46        set(${result} 10 PARENT_SCOPE)
47    elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800)
48        set(${result} 11 PARENT_SCOPE)
49    elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900)
50        set(${result} 12 PARENT_SCOPE)
51    elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910)
52        set(${result} 14 PARENT_SCOPE)
53    elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920)
54        set(${result} 15 PARENT_SCOPE)
55    else()
56        message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]")
57    endif()
58endfunction()
59
60function(conan_cmake_settings result)
61    message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER})
62    message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID})
63    message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION})
64    message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS})
65    message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE})
66    message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE})
67    message(STATUS "GENERATOR " ${CMAKE_GENERATOR})
68    message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64})
69
70    message(STATUS "Conan ** WARNING** : This detection of settings from cmake is experimental and incomplete. "
71                    "Please check 'conan.cmake' and contribute")
72
73    parse_arguments(${ARGV})
74    set(arch ${ARGUMENTS_ARCH})
75
76    if(CONAN_CMAKE_MULTI)
77        set(_SETTINGS -g cmake_multi)
78    else()
79        set(_SETTINGS -g cmake)
80    endif()
81    if(ARGUMENTS_BUILD_TYPE)
82        set(_SETTINGS ${_SETTINGS} -s build_type=${ARGUMENTS_BUILD_TYPE})
83    elseif(CMAKE_BUILD_TYPE)
84        set(_SETTINGS ${_SETTINGS} -s build_type=${CMAKE_BUILD_TYPE})
85    else()
86        message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)")
87    endif()
88
89    #handle -s os setting
90    if(CMAKE_SYSTEM_NAME)
91        #use default conan os setting if CMAKE_SYSTEM_NAME is not defined
92        set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME})
93        if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
94            set(CONAN_SYSTEM_NAME Macos)
95        endif()
96        set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD)
97        list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index)
98        if (${_index} GREATER -1)
99            #check if the cmake system is a conan supported one
100            set(_SETTINGS ${_SETTINGS} -s os=${CONAN_SYSTEM_NAME})
101        else()
102            message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}")
103        endif()
104    endif()
105
106    get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
107    if (";${_languages};" MATCHES ";CXX;")
108        set(LANGUAGE CXX)
109        set(USING_CXX 1)
110    elseif (";${_languages};" MATCHES ";C;")
111        set(LANGUAGE C)
112        set(USING_CXX 0)
113    else ()
114        message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.")
115    endif()
116
117    if(arch)
118        set(_SETTINGS ${_SETTINGS} -s arch=${arch})
119    endif()
120
121    if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU)
122        # using GCC
123        # TODO: Handle other params
124        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
125        list(GET VERSION_LIST 0 MAJOR)
126        list(GET VERSION_LIST 1 MINOR)
127        set(COMPILER_VERSION ${MAJOR}.${MINOR})
128        if(${MAJOR} GREATER 4)
129            set(COMPILER_VERSION ${MAJOR})
130        endif()
131        set(_SETTINGS ${_SETTINGS} -s compiler=gcc -s compiler.version=${COMPILER_VERSION})
132        if (USING_CXX)
133          conan_cmake_detect_gnu_libcxx(_LIBCXX)
134            set(_SETTINGS ${_SETTINGS} -s compiler.libcxx=${_LIBCXX})
135        endif ()
136    elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang)
137        # using AppleClang
138        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
139        list(GET VERSION_LIST 0 MAJOR)
140        list(GET VERSION_LIST 1 MINOR)
141        set(_SETTINGS ${_SETTINGS} -s compiler=apple-clang -s compiler.version=${MAJOR}.${MINOR})
142        if (USING_CXX)
143            set(_SETTINGS ${_SETTINGS} -s compiler.libcxx=libc++)
144        endif ()
145    elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang)
146        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
147        list(GET VERSION_LIST 0 MAJOR)
148        list(GET VERSION_LIST 1 MINOR)
149        if(APPLE)
150            cmake_policy(GET CMP0025 APPLE_CLANG_POLICY_ENABLED)
151            if(NOT APPLE_CLANG_POLICY_ENABLED)
152                message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it")
153                set(_SETTINGS ${_SETTINGS} -s compiler=apple-clang -s compiler.version=${MAJOR}.${MINOR})
154            else()
155                set(_SETTINGS ${_SETTINGS} -s compiler=clang -s compiler.version=${MAJOR}.${MINOR})
156            endif()
157            if (USING_CXX)
158                set(_SETTINGS ${_SETTINGS} -s compiler.libcxx=libc++)
159            endif ()
160        else()
161            set(_SETTINGS ${_SETTINGS} -s compiler=clang -s compiler.version=${MAJOR}.${MINOR})
162            if (USING_CXX)
163                conan_cmake_detect_gnu_libcxx(_LIBCXX)
164                set(_SETTINGS ${_SETTINGS} -s compiler.libcxx=${_LIBCXX})
165            endif ()
166        endif()
167    elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC)
168        set(_VISUAL "Visual Studio")
169        _get_msvc_ide_version(_VISUAL_VERSION)
170        if("${_VISUAL_VERSION}" STREQUAL "")
171            message(FATAL_ERROR "Conan: Visual Studio not recognized")
172        else()
173            set(_SETTINGS ${_SETTINGS} -s compiler=${_VISUAL} -s compiler.version=${_VISUAL_VERSION})
174        endif()
175
176        if(NOT arch)
177            if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64")
178                set(_SETTINGS ${_SETTINGS} -s arch=x86_64)
179            elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM")
180                message(STATUS "Conan: Using default ARM architecture from MSVC")
181                set(_SETTINGS ${_SETTINGS} -s arch=armv6)
182            elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86")
183                set(_SETTINGS ${_SETTINGS} -s arch=x86)
184            else ()
185                message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]")
186            endif()
187        endif()
188
189        conan_cmake_detect_vs_runtime(_vs_runtime)
190        message(STATUS "Detected VS runtime: ${_vs_runtime}")
191        set(_SETTINGS ${_SETTINGS} -s compiler.runtime=${_vs_runtime})
192
193        if (CMAKE_GENERATOR_TOOLSET)
194            set(_SETTINGS ${_SETTINGS} -s compiler.toolset=${CMAKE_VS_PLATFORM_TOOLSET})
195        elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja"))
196            set(_SETTINGS ${_SETTINGS} -s compiler.toolset=${CMAKE_VS_PLATFORM_TOOLSET})
197        endif()
198    # Temp fix due to https://github.com/conan-io/cmake-conan/issues/166
199    elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Intel)
200        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
201        list(GET VERSION_LIST 0 MAJOR)
202        list(GET VERSION_LIST 1 MINOR)
203        set(COMPILER_VERSION ${MAJOR}.${MINOR})
204        set(_CONAN_SETTING_COMPILER icc)
205        set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION})
206        set(_SETTINGS ${_SETTINGS} -s compiler=icc -s compiler.version=${COMPILER_VERSION})
207        if (USING_CXX)
208            conan_cmake_detect_gnu_libcxx(_LIBCXX)
209            set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
210        endif ()
211    else()
212        message(FATAL_ERROR "Conan: compiler setup not recognized")
213    endif()
214
215    foreach(ARG ${ARGUMENTS_SETTINGS})
216        set(_SETTINGS ${_SETTINGS} -s ${ARG})
217    endforeach()
218
219    set(${result} ${_SETTINGS} PARENT_SCOPE)
220endfunction()
221
222
223function(conan_cmake_detect_gnu_libcxx result)
224    # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake
225    if(DEFINED _GLIBCXX_USE_CXX11_ABI)
226        if(_GLIBCXX_USE_CXX11_ABI)
227            set(${result} libstdc++11 PARENT_SCOPE)
228            return()
229        else()
230            set(${result} libstdc++ PARENT_SCOPE)
231            return()
232        endif()
233    endif()
234
235    # Check if there's any add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
236    get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS)
237    foreach(define ${defines})
238        if(define STREQUAL "_GLIBCXX_USE_CXX11_ABI=0")
239            set(${result} libstdc++ PARENT_SCOPE)
240            return()
241        endif()
242    endforeach()
243
244    # Use C++11 stdlib as default if gcc is 5.1+
245    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.1")
246      set(${result} libstdc++ PARENT_SCOPE)
247    else()
248      set(${result} libstdc++11 PARENT_SCOPE)
249    endif()
250endfunction()
251
252
253function(conan_cmake_detect_vs_runtime result)
254    string(TOUPPER ${CMAKE_BUILD_TYPE} build_type)
255    set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS)
256    foreach(variable ${variables})
257        string(REPLACE " " ";" flags ${${variable}})
258        foreach (flag ${flags})
259            if(${flag} STREQUAL "/MD" OR ${flag} STREQUAL "/MDd" OR ${flag} STREQUAL "/MT" OR ${flag} STREQUAL "/MTd")
260                string(SUBSTRING ${flag} 1 -1 runtime)
261                set(${result} ${runtime} PARENT_SCOPE)
262                return()
263            endif()
264        endforeach()
265    endforeach()
266    if(${build_type} STREQUAL "DEBUG")
267        set(${result} "MDd" PARENT_SCOPE)
268    else()
269        set(${result} "MD" PARENT_SCOPE)
270    endif()
271endfunction()
272
273
274macro(parse_arguments)
275  set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_OUTPUT_DIRS)
276  set(oneValueArgs CONANFILE DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE PROFILE ARCH BUILD_TYPE)
277  set(multiValueArgs REQUIRES OPTIONS IMPORTS SETTINGS BUILD CONAN_COMMAND)
278  cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
279endmacro()
280
281function(conan_cmake_install)
282    # Calls "conan install"
283    # Argument BUILD is equivalant to --build={missing, PkgName,...} or
284    # --build when argument is 'BUILD all' (which builds all packages from source)
285    # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source
286    # cmake does not identify conan as command, even if it is +x and it is in the path
287    parse_arguments(${ARGV})
288
289    set(CONAN_BUILD_POLICY "")
290    foreach(ARG ${ARGUMENTS_BUILD})
291        if(${ARG} STREQUAL "all")
292            set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build)
293            break()
294        else()
295            set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG})
296        endif()
297    endforeach()
298    if(ARGUMENTS_CONAN_COMMAND)
299       set(conan_command ${ARGUMENTS_CONAN_COMMAND})
300    else()
301      set(conan_command conan)
302    endif()
303    set(CONAN_OPTIONS "")
304    if(ARGUMENTS_CONANFILE)
305      set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE})
306      # A conan file has been specified - apply specified options as well if provided
307      foreach(ARG ${ARGUMENTS_OPTIONS})
308          set(CONAN_OPTIONS ${CONAN_OPTIONS} -o ${ARG})
309      endforeach()
310    else()
311      set(CONANFILE ".")
312    endif()
313    if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE)
314      set(settings -pr ${ARGUMENTS_DEBUG_PROFILE})
315    endif()
316    if(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE)
317      set(settings -pr ${ARGUMENTS_RELEASE_PROFILE})
318    endif()
319    if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE)
320      set(settings -pr ${ARGUMENTS_RELWITHDEBINFO_PROFILE})
321    endif()
322    if(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE)
323      set(settings -pr ${ARGUMENTS_MINSIZEREL_PROFILE})
324    endif()
325    if(ARGUMENTS_PROFILE)
326      set(settings -pr ${ARGUMENTS_PROFILE})
327    endif()
328    if(ARGUMENTS_UPDATE)
329      set(CONAN_INSTALL_UPDATE --update)
330    endif()
331    set(conan_args install ${CONANFILE} ${settings} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_OPTIONS})
332
333    string (REPLACE ";" " " _conan_args "${conan_args}")
334    message(STATUS "Conan executing: ${conan_command} ${_conan_args}")
335
336    execute_process(COMMAND ${conan_command} ${conan_args}
337                     RESULT_VARIABLE return_code
338                     WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
339
340    if(NOT "${return_code}" STREQUAL "0")
341      message(FATAL_ERROR "Conan install failed='${return_code}'")
342    endif()
343
344endfunction()
345
346
347function(conan_cmake_setup_conanfile)
348  parse_arguments(${ARGV})
349  if(ARGUMENTS_CONANFILE)
350    # configure_file will make sure cmake re-runs when conanfile is updated
351    configure_file(${ARGUMENTS_CONANFILE} ${ARGUMENTS_CONANFILE}.junk)
352    file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${ARGUMENTS_CONANFILE}.junk)
353  else()
354    conan_cmake_generate_conanfile(${ARGV})
355  endif()
356endfunction()
357
358function(conan_cmake_generate_conanfile)
359  # Generate, writing in disk a conanfile.txt with the requires, options, and imports
360  # specified as arguments
361  # This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR)
362  parse_arguments(${ARGV})
363  set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt")
364
365  file(WRITE ${_FN} "[generators]\ncmake\n\n[requires]\n")
366  foreach(ARG ${ARGUMENTS_REQUIRES})
367    file(APPEND ${_FN} ${ARG} "\n")
368  endforeach()
369
370  file(APPEND ${_FN} ${ARG} "\n[options]\n")
371  foreach(ARG ${ARGUMENTS_OPTIONS})
372    file(APPEND ${_FN} ${ARG} "\n")
373  endforeach()
374
375  file(APPEND ${_FN} ${ARG} "\n[imports]\n")
376  foreach(ARG ${ARGUMENTS_IMPORTS})
377    file(APPEND ${_FN} ${ARG} "\n")
378  endforeach()
379endfunction()
380
381
382macro(conan_load_buildinfo)
383    if(CONAN_CMAKE_MULTI)
384      set(_CONANBUILDINFO conanbuildinfo_multi.cmake)
385    else()
386      set(_CONANBUILDINFO conanbuildinfo.cmake)
387    endif()
388    # Checks for the existence of conanbuildinfo.cmake, and loads it
389    # important that it is macro, so variables defined at parent scope
390    if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${_CONANBUILDINFO}")
391      message(STATUS "Conan: Loading ${_CONANBUILDINFO}")
392      include(${CMAKE_CURRENT_BINARY_DIR}/${_CONANBUILDINFO})
393    else()
394      message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}")
395    endif()
396endmacro()
397
398
399macro(conan_cmake_run)
400    parse_arguments(${ARGV})
401
402    if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED)
403        set(CONAN_CMAKE_MULTI ON)
404        message(STATUS "Conan: Using cmake-multi generator")
405    else()
406        set(CONAN_CMAKE_MULTI OFF)
407    endif()
408    if(NOT CONAN_EXPORTED)
409        conan_cmake_setup_conanfile(${ARGV})
410        if(CONAN_CMAKE_MULTI)
411            foreach(CMAKE_BUILD_TYPE "Release" "Debug")
412                conan_cmake_settings(settings ${ARGV})
413                conan_cmake_install(SETTINGS ${settings} ${ARGV})
414            endforeach()
415            set(CMAKE_BUILD_TYPE)
416        else()
417            conan_cmake_settings(settings ${ARGV})
418            conan_cmake_install(SETTINGS ${settings} ${ARGV})
419        endif()
420    endif()
421
422    conan_load_buildinfo()
423
424    if(ARGUMENTS_BASIC_SETUP)
425        foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS)
426            if(ARGUMENTS_${_option})
427                if(${_option} STREQUAL "CMAKE_TARGETS")
428                    list(APPEND _setup_options "TARGETS")
429                else()
430                    list(APPEND _setup_options ${_option})
431                endif()
432            endif()
433        endforeach()
434        conan_basic_setup(${_setup_options})
435    endif()
436endmacro()
437