1#
2# CMake build system design considerations:
3#
4# - Include directories:
5#   + Do not define include directories globally using the include_directories
6#     command but rather at the target level using the
7#     target_include_directories command. That way, it is easier to guarantee
8#     that targets are built using the proper list of include directories.
9#   + Use the PUBLIC and PRIVATE keywords to specifiy the scope of include
10#     directories. That way, a target linking to a library (using the
11#     target_link_librairies command) inherits from the library PUBLIC include
12#     directories and not from the PRIVATE ones.
13# - MBEDTLS_TARGET_PREFIX: CMake targets are designed to be alterable by calling
14#   CMake in order to avoid target name clashes, via the use of
15#   MBEDTLS_TARGET_PREFIX. The value of this variable is prefixed to the
16#   mbedtls, mbedx509, mbedcrypto and apidoc targets.
17#
18
19# We specify a minimum requirement of 3.10.2, but for now use 3.5.1 here
20# until our infrastructure catches up.
21cmake_minimum_required(VERSION 3.5.1)
22
23include(CMakePackageConfigHelpers)
24
25# https://cmake.org/cmake/help/latest/policy/CMP0011.html
26# Setting this policy is required in CMake >= 3.18.0, otherwise a warning is generated. The OLD
27# policy setting is deprecated, and will be removed in future versions.
28cmake_policy(SET CMP0011 NEW)
29# https://cmake.org/cmake/help/latest/policy/CMP0012.html
30# Setting the CMP0012 policy to NEW is required for FindPython3 to work with CMake 3.18.2
31# (there is a bug in this particular version), otherwise, setting the CMP0012 policy is required
32# for CMake versions >= 3.18.3 otherwise a deprecated warning is generated. The OLD policy setting
33# is deprecated and will be removed in future versions.
34cmake_policy(SET CMP0012 NEW)
35
36if(TEST_CPP)
37    project("mbed TLS" C CXX)
38else()
39    project("mbed TLS" C)
40endif()
41
42# Set the project root directory.
43set(MBEDTLS_DIR ${CMAKE_CURRENT_SOURCE_DIR})
44
45option(ENABLE_PROGRAMS "Build mbed TLS programs." ON)
46
47option(UNSAFE_BUILD "Allow unsafe builds. These builds ARE NOT SECURE." OFF)
48option(MBEDTLS_FATAL_WARNINGS "Compiler warnings treated as errors" ON)
49if(WIN32)
50    option(GEN_FILES "Generate the auto-generated files as needed" OFF)
51else()
52    option(GEN_FILES "Generate the auto-generated files as needed" ON)
53endif()
54
55string(REGEX MATCH "Clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER_ID}")
56string(REGEX MATCH "GNU" CMAKE_COMPILER_IS_GNU "${CMAKE_C_COMPILER_ID}")
57string(REGEX MATCH "IAR" CMAKE_COMPILER_IS_IAR "${CMAKE_C_COMPILER_ID}")
58string(REGEX MATCH "MSVC" CMAKE_COMPILER_IS_MSVC "${CMAKE_C_COMPILER_ID}")
59
60# the test suites currently have compile errors with MSVC
61if(CMAKE_COMPILER_IS_MSVC)
62    option(ENABLE_TESTING "Build mbed TLS tests." OFF)
63else()
64    option(ENABLE_TESTING "Build mbed TLS tests." ON)
65endif()
66
67# Warning string - created as a list for compatibility with CMake 2.8
68set(CTR_DRBG_128_BIT_KEY_WARN_L1 "****  WARNING!  MBEDTLS_CTR_DRBG_USE_128_BIT_KEY defined!\n")
69set(CTR_DRBG_128_BIT_KEY_WARN_L2 "****  Using 128-bit keys for CTR_DRBG limits the security of generated\n")
70set(CTR_DRBG_128_BIT_KEY_WARN_L3 "****  keys and operations that use random values generated to 128-bit security\n")
71
72set(CTR_DRBG_128_BIT_KEY_WARNING "${WARNING_BORDER}"
73                         "${CTR_DRBG_128_BIT_KEY_WARN_L1}"
74                         "${CTR_DRBG_128_BIT_KEY_WARN_L2}"
75                         "${CTR_DRBG_128_BIT_KEY_WARN_L3}"
76                         "${WARNING_BORDER}")
77
78# Python 3 is only needed here to check for configuration warnings.
79if(NOT CMAKE_VERSION VERSION_LESS 3.15.0)
80    set(Python3_FIND_STRATEGY LOCATION)
81    find_package(Python3 COMPONENTS Interpreter)
82    if(Python3_Interpreter_FOUND)
83        set(MBEDTLS_PYTHON_EXECUTABLE ${Python3_EXECUTABLE})
84    endif()
85else()
86    find_package(PythonInterp 3)
87    if(PYTHONINTERP_FOUND)
88        set(MBEDTLS_PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE})
89    endif()
90endif()
91if(MBEDTLS_PYTHON_EXECUTABLE)
92
93    # If 128-bit keys are configured for CTR_DRBG, display an appropriate warning
94    execute_process(COMMAND ${MBEDTLS_PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/config.py -f ${CMAKE_CURRENT_SOURCE_DIR}/include/mbedtls/mbedtls_config.h get MBEDTLS_CTR_DRBG_USE_128_BIT_KEY
95                        RESULT_VARIABLE result)
96    if(${result} EQUAL 0)
97        message(WARNING ${CTR_DRBG_128_BIT_KEY_WARNING})
98    endif()
99
100endif()
101
102# If this is the root project add longer list of available CMAKE_BUILD_TYPE values
103if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
104    set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}
105        CACHE STRING "Choose the type of build: None Debug Release Coverage ASan ASanDbg MemSan MemSanDbg Check CheckFull"
106        FORCE)
107endif()
108
109# Create a symbolic link from ${base_name} in the binary directory
110# to the corresponding path in the source directory.
111function(link_to_source base_name)
112    # Get OS dependent path to use in `execute_process`
113    if (CMAKE_HOST_WIN32)
114        #mklink is an internal command of cmd.exe it can only work with \
115        string(REPLACE "/" "\\" link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
116        string(REPLACE "/" "\\" target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
117    else()
118        set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
119        set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
120    endif()
121
122    if (NOT EXISTS ${link})
123        if (CMAKE_HOST_UNIX)
124            set(command ln -s ${target} ${link})
125        else()
126            if (IS_DIRECTORY ${target})
127                set(command cmd.exe /c mklink /j ${link} ${target})
128            else()
129                set(command cmd.exe /c mklink /h ${link} ${target})
130            endif()
131        endif()
132
133        execute_process(COMMAND ${command}
134            RESULT_VARIABLE result
135            ERROR_VARIABLE output)
136
137        if (NOT ${result} EQUAL 0)
138            message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
139        endif()
140    endif()
141endfunction(link_to_source)
142
143# Get the filename without the final extension (i.e. convert "a.b.c" to "a.b")
144function(get_name_without_last_ext dest_var full_name)
145    # Split into a list on '.' (but a cmake list is just a ';'-separated string)
146    string(REPLACE "." ";" ext_parts "${full_name}")
147    # Remove the last item if there are more than one
148    list(LENGTH ext_parts ext_parts_len)
149    if (${ext_parts_len} GREATER "1")
150        math(EXPR ext_parts_last_item "${ext_parts_len} - 1")
151        list(REMOVE_AT ext_parts ${ext_parts_last_item})
152    endif()
153    # Convert back to a string by replacing separators with '.'
154    string(REPLACE ";" "." no_ext_name "${ext_parts}")
155    # Copy into the desired variable
156    set(${dest_var} ${no_ext_name} PARENT_SCOPE)
157endfunction(get_name_without_last_ext)
158
159string(REGEX MATCH "Clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER_ID}")
160
161include(CheckCCompilerFlag)
162
163if(CMAKE_COMPILER_IS_GNU)
164    # some warnings we want are not available with old GCC versions
165    # note: starting with CMake 2.8 we could use CMAKE_C_COMPILER_VERSION
166    execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
167                    OUTPUT_VARIABLE GCC_VERSION)
168    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wwrite-strings")
169    if (GCC_VERSION VERSION_GREATER 3.0 OR GCC_VERSION VERSION_EQUAL 3.0)
170        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2 -Wno-format-nonliteral")
171    endif()
172    if (GCC_VERSION VERSION_GREATER 4.3 OR GCC_VERSION VERSION_EQUAL 4.3)
173        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wvla")
174    endif()
175    if (GCC_VERSION VERSION_GREATER 4.5 OR GCC_VERSION VERSION_EQUAL 4.5)
176        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wlogical-op")
177    endif()
178    if (GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
179        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
180    endif()
181    if (GCC_VERSION VERSION_GREATER 5.0)
182        CHECK_C_COMPILER_FLAG("-Wformat-signedness" C_COMPILER_SUPPORTS_WFORMAT_SIGNEDNESS)
183        if(C_COMPILER_SUPPORTS_WFORMAT_SIGNEDNESS)
184            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-signedness")
185        endif()
186    endif()
187    if (GCC_VERSION VERSION_GREATER 7.0 OR GCC_VERSION VERSION_EQUAL 7.0)
188      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-overflow=2 -Wformat-truncation")
189    endif()
190    set(CMAKE_C_FLAGS_RELEASE     "-O2")
191    set(CMAKE_C_FLAGS_DEBUG       "-O0 -g3")
192    set(CMAKE_C_FLAGS_COVERAGE    "-O0 -g3 --coverage")
193    set(CMAKE_C_FLAGS_ASAN        "-fsanitize=address -fno-common -fsanitize=undefined -fno-sanitize-recover=all -O3")
194    set(CMAKE_C_FLAGS_ASANDBG     "-fsanitize=address -fno-common -fsanitize=undefined -fno-sanitize-recover=all -O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls")
195    set(CMAKE_C_FLAGS_CHECK       "-Os")
196    set(CMAKE_C_FLAGS_CHECKFULL   "${CMAKE_C_FLAGS_CHECK} -Wcast-qual")
197endif(CMAKE_COMPILER_IS_GNU)
198
199if(CMAKE_COMPILER_IS_CLANG)
200    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wwrite-strings -Wpointer-arith -Wimplicit-fallthrough -Wshadow -Wvla -Wformat=2 -Wno-format-nonliteral")
201    set(CMAKE_C_FLAGS_RELEASE     "-O2")
202    set(CMAKE_C_FLAGS_DEBUG       "-O0 -g3")
203    set(CMAKE_C_FLAGS_COVERAGE    "-O0 -g3 --coverage")
204    set(CMAKE_C_FLAGS_ASAN        "-fsanitize=address -fno-common -fsanitize=undefined -fno-sanitize-recover=all -O3")
205    set(CMAKE_C_FLAGS_ASANDBG     "-fsanitize=address -fno-common -fsanitize=undefined -fno-sanitize-recover=all -O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls")
206    set(CMAKE_C_FLAGS_MEMSAN      "-fsanitize=memory -O3")
207    set(CMAKE_C_FLAGS_MEMSANDBG   "-fsanitize=memory -O1 -g3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2")
208    set(CMAKE_C_FLAGS_CHECK       "-Os")
209endif(CMAKE_COMPILER_IS_CLANG)
210
211if(CMAKE_COMPILER_IS_IAR)
212    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --warn_about_c_style_casts --warnings_are_errors -Ohz")
213endif(CMAKE_COMPILER_IS_IAR)
214
215if(CMAKE_COMPILER_IS_MSVC)
216    # Strictest warnings
217    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
218endif(CMAKE_COMPILER_IS_MSVC)
219
220if(MBEDTLS_FATAL_WARNINGS)
221    if(CMAKE_COMPILER_IS_MSVC)
222        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
223    endif(CMAKE_COMPILER_IS_MSVC)
224
225    if(CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_GNU)
226        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
227        if(UNSAFE_BUILD)
228            set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=cpp")
229            set(CMAKE_C_FLAGS_ASAN "${CMAKE_C_FLAGS_ASAN} -Wno-error=cpp")
230            set(CMAKE_C_FLAGS_ASANDBG "${CMAKE_C_FLAGS_ASANDBG} -Wno-error=cpp")
231        endif(UNSAFE_BUILD)
232    endif(CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_GNU)
233endif(MBEDTLS_FATAL_WARNINGS)
234
235if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
236    if(CMAKE_COMPILER_IS_GNU OR CMAKE_COMPILER_IS_CLANG)
237        set(CMAKE_SHARED_LINKER_FLAGS "--coverage")
238    endif(CMAKE_COMPILER_IS_GNU OR CMAKE_COMPILER_IS_CLANG)
239endif(CMAKE_BUILD_TYPE STREQUAL "Coverage")
240
241if(LIB_INSTALL_DIR)
242else()
243    set(LIB_INSTALL_DIR lib)
244endif()
245
246add_subdirectory(include)
247
248add_subdirectory(3rdparty)
249
250add_subdirectory(library)
251
252#
253# The C files in tests/src directory contain test code shared among test suites
254# and programs. This shared test code is compiled and linked to test suites and
255# programs objects as a set of compiled objects. The compiled objects are NOT
256# built into a library that the test suite and program objects would link
257# against as they link against the mbedcrypto, mbedx509 and mbedtls libraries.
258# The reason is that such library is expected to have mutual dependencies with
259# the aforementioned libraries and that there is as of today no portable way of
260# handling such dependencies (only toolchain specific solutions).
261#
262# Thus the below definition of the `mbedtls_test` CMake library of objects
263# target. This library of objects is used by tests and programs CMake files
264# to define the test executables.
265#
266if(ENABLE_TESTING OR ENABLE_PROGRAMS)
267    file(GLOB MBEDTLS_TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/*.c ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/drivers/*.c)
268    add_library(mbedtls_test OBJECT ${MBEDTLS_TEST_FILES})
269    target_include_directories(mbedtls_test
270        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests/include
271        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
272        PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/library)
273endif()
274
275if(ENABLE_PROGRAMS)
276    add_subdirectory(programs)
277endif()
278
279ADD_CUSTOM_TARGET(${MBEDTLS_TARGET_PREFIX}apidoc
280    COMMAND doxygen mbedtls.doxyfile
281    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/doxygen)
282
283if(ENABLE_TESTING)
284    enable_testing()
285
286    add_subdirectory(tests)
287
288    # additional convenience targets for Unix only
289    if(UNIX)
290
291        ADD_CUSTOM_TARGET(covtest
292            COMMAND make test
293            COMMAND programs/test/selftest
294            COMMAND tests/compat.sh
295            COMMAND tests/ssl-opt.sh
296        )
297
298        ADD_CUSTOM_TARGET(lcov
299            COMMAND rm -rf Coverage
300            COMMAND lcov --capture --initial --directory library/CMakeFiles/mbedtls.dir -o files.info
301            COMMAND lcov --capture --directory library/CMakeFiles/mbedtls.dir -o tests.info
302            COMMAND lcov --add-tracefile files.info --add-tracefile tests.info -o all.info
303            COMMAND lcov --remove all.info -o final.info '*.h'
304            COMMAND gendesc tests/Descriptions.txt -o descriptions
305            COMMAND genhtml --title "mbed TLS" --description-file descriptions --keep-descriptions --legend --no-branch-coverage -o Coverage final.info
306            COMMAND rm -f files.info tests.info all.info final.info descriptions
307        )
308
309        ADD_CUSTOM_TARGET(memcheck
310            COMMAND sed -i.bak s+/usr/bin/valgrind+`which valgrind`+ DartConfiguration.tcl
311            COMMAND ctest -O memcheck.log -D ExperimentalMemCheck
312            COMMAND tail -n1 memcheck.log | grep 'Memory checking results:' > /dev/null
313            COMMAND rm -f memcheck.log
314            COMMAND mv DartConfiguration.tcl.bak DartConfiguration.tcl
315        )
316    endif(UNIX)
317
318    # Make scripts needed for testing available in an out-of-source build.
319    if (NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
320        link_to_source(scripts)
321        # Copy (don't link) DartConfiguration.tcl, needed for memcheck, to
322        # keep things simple with the sed commands in the memcheck target.
323        configure_file(${CMAKE_CURRENT_SOURCE_DIR}/DartConfiguration.tcl
324                    ${CMAKE_CURRENT_BINARY_DIR}/DartConfiguration.tcl COPYONLY)
325    endif()
326endif()
327
328configure_package_config_file(
329    "cmake/MbedTLSConfig.cmake.in"
330    "cmake/MbedTLSConfig.cmake"
331        INSTALL_DESTINATION "cmake")
332
333write_basic_package_version_file(
334    "cmake/MbedTLSConfigVersion.cmake"
335        COMPATIBILITY SameMajorVersion
336        VERSION 3.0.0)
337
338install(
339    FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/MbedTLSConfig.cmake"
340          "${CMAKE_CURRENT_BINARY_DIR}/cmake/MbedTLSConfigVersion.cmake"
341    DESTINATION "cmake")
342
343export(
344    EXPORT MbedTLSTargets
345    NAMESPACE MbedTLS::
346    FILE "cmake/MbedTLSTargets.cmake")
347
348install(
349    EXPORT MbedTLSTargets
350    NAMESPACE MbedTLS::
351    DESTINATION "cmake"
352    FILE "MbedTLSTargets.cmake")
353
354if(CMAKE_VERSION VERSION_GREATER 3.15 OR CMAKE_VERSION VERSION_EQUAL 3.15)
355    # Do not export the package by default
356    cmake_policy(SET CMP0090 NEW)
357
358    # Make this package visible to the system
359    export(PACKAGE MbedTLS)
360endif()
361