| 1 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | # Utility functions used by other modules. |
| 3 | |
| 4 | include_guard() |
| 5 | |
| 6 | # Fix up CMAKE_SYSTEM_PROCESSOR to actually match the build target |
| 7 | function(netdata_fixup_system_processor) |
| 8 | if(OS_WINDOWS) |
| 9 | return() |
| 10 | endif() |
| 11 | |
| 12 | if(CMAKE_TOOLCHAIN_FILE) |
| 13 | return() |
| 14 | endif() |
| 15 | |
| 16 | execute_process( |
| 17 | COMMAND ${CMAKE_C_COMPILER} ${CMAKE_C_FLAGS} -dumpmachine |
| 18 | COMMAND cut -f 1 -d - |
| 19 | RESULT_VARIABLE return_code |
| 20 | OUTPUT_VARIABLE output_data |
| 21 | ) |
| 22 | |
| 23 | if(return_code EQUAL 0) |
| 24 | set(CMAKE_SYSTEM_PROCESSOR "${output_data}" PARENT_SCOPE) |
| 25 | else() |
| 26 | message(WARNING "Failed to detect target processor architecture, using CMake default") |
| 27 | endif() |
| 28 | endfunction() |
| 29 | |
| 30 | # Determine the version of the host kernel. |
| 31 | # |
| 32 | # Only works on UNIX-like systems, stores the version in the cache |
| 33 | # variable HOST_KERNEL_VERSION. |
| 34 | function(netdata_detect_host_kernel_version) |
| 35 | if(DEFINED HOST_KERNEL_VERSION) |
| 36 | return() |
| 37 | endif() |
| 38 | |
| 39 | message(CHECK_START "Determining host kernel version") |
| 40 | |
| 41 | if(NOT CMAKE_CROSSCOMPILING) |
| 42 | include(CheckIncludeFile) |
| 43 | |
| 44 | check_include_file("linux/version.h" CAN_USE_VERSION_H) |
| 45 | |
| 46 | if(CAN_USE_VERSION_H) |
| 47 | message(CHECK_START "Checking version using linux/version.h") |
| 48 | file(WRITE "${CMAKE_BINARY_DIR}/kversion-test.c" " |
| 49 | #include <stdio.h> |
| 50 | #include <linux/version.h> |
| 51 | |
| 52 | int main() { |
| 53 | printf(\"%i.%i.%i\", LINUX_VERSION_MAJOR, LINUX_VERSION_PATCHLEVEL, LINUX_VERSION_SUBLEVEL); |
| 54 | } |
| 55 | ") |
| 56 | |
| 57 | try_run(_run_success _compile_success |
| 58 | ${CMAKE_BINARY_DIR} |
| 59 | SOURCES ${CMAKE_BINARY_DIR}/kversion-test.c |
| 60 | RUN_OUTPUT_VARIABLE _kversion_output) |
| 61 | |
| 62 | if(_compile_success AND _run_success EQUAL 0) |
| 63 | message(CHECK_PASS "success") |
| 64 | set(_kversion_value "${_kversion_output}") |
| 65 | else() |
| 66 | message(CHECK_FAIL "failed") |
| 67 | endif() |
| 68 | endif() |
| 69 | endif() |
| 70 | |
| 71 | if(NOT DEFINED _kversion_value) |
| 72 | message(CHECK_START "Checking version using uname") |
| 73 | execute_process(COMMAND uname -r |
| 74 | RESULT_VARIABLE _uname_result |
| 75 | OUTPUT_VARIABLE _uname_output) |
| 76 | |
| 77 | if(NOT _uname_result EQUAL 0) |
| 78 | message(CHECK_FAIL "failed") |
| 79 | message(CHECK_FAIL "unknown") |
| 80 | set(HOST_KERNEL_VERSION "0.0.0" CACHE STRING "Detected host kernel version") |
| 81 | return() |
| 82 | else() |
| 83 | message(CHECK_PASS "success") |
| 84 | endif() |
| 85 | |
| 86 | set(_kversion_value "${_uname_output}") |
| 87 | endif() |
| 88 | |
| 89 | string(REGEX REPLACE "-.+$" "" _kversion "${_kversion_value}") |
| 90 | message(CHECK_PASS "${_kversion}") |
| 91 | set(HOST_KERNEL_VERSION "${_kversion}" CACHE STRING "Detected host kernel version") |
| 92 | endfunction() |
| 93 | |
| 94 | # Check what libc we're using. |
| 95 | # |
| 96 | # Sets the specified variable to the name of the libc or "unknown" |
| 97 | function(netdata_identify_libc _libc_name) |
| 98 | if(NOT DEFINED _ND_DETECTED_LIBC) |
| 99 | message(CHECK_START "Detecting libc implementation using ldd") |
| 100 | |
| 101 | execute_process(COMMAND ldd --version |
| 102 | COMMAND grep -q -i -E "glibc|gnu libc" |
| 103 | RESULT_VARIABLE LDD_RESULT |
| 104 | OUTPUT_VARIABLE LDD_OUTPUT |
| 105 | ERROR_VARIABLE LDD_OUTPUT) |
| 106 | |
| 107 | if(NOT LDD_RESULT) |
| 108 | set(${_libc_name} glibc PARENT_SCOPE) |
| 109 | set(_ND_DETECTED_LIBC glibc CACHE INTERNAL "") |
| 110 | message(CHECK_PASS "glibc") |
| 111 | return() |
| 112 | endif() |
| 113 | |
| 114 | execute_process(COMMAND sh -c "ldd --version 2>&1 | grep -q -i 'musl'" |
| 115 | RESULT_VARIABLE LDD_RESULT |
| 116 | OUTPUT_VARIABLE LDD_OUTPUT |
| 117 | ERROR_VARIABLE LDD_OUTPUT) |
| 118 | |
| 119 | if(NOT LDD_RESULT) |
| 120 | set(${_libc_name} musl PARENT_SCOPE) |
| 121 | set(_ND_DETECTED_LIBC musl CACHE INTERNAL "") |
| 122 | message(CHECK_PASS "musl") |
| 123 | return() |
| 124 | endif() |
| 125 | |
| 126 | message(CHECK_FAIL "unknown") |
| 127 | |
| 128 | message(CHECK_START "Looking for libc.so.6") |
| 129 | find_program(LIBC_PATH libc.so.6 |
| 130 | PATHS /lib /lib64 /usr/lib /usr/lib64 |
| 131 | NO_DEFAULT_PATH |
| 132 | NO_PACKAGE_ROOT_PATH |
| 133 | NO_CMAKE_PATH |
| 134 | NO_CMAKE_ENVIRONMENT_PATH |
| 135 | NO_SYSTEM_ENVIRONMENT_PATH |
| 136 | NO_CMAKE_SYSTEM_PATH |
| 137 | NO_CMAKE_INSTALL_PREFIX |
| 138 | NO_CMAKE_FIND_ROOT_PATH) |
| 139 | |
| 140 | if(NOT "${LIBC_PATH}" EQUAL "LIBC_PATH-NOTFOUND") |
| 141 | message(CHECK_PASS "found") |
| 142 | message(CHECK_START "Detecting libc implementation using libc.so.6") |
| 143 | |
| 144 | execute_process(COMMAND "${LIBC_PATH}" |
| 145 | COMMAND head -n 1 |
| 146 | COMMAND grep -q -i -E "gnu libc|gnu c library" |
| 147 | RESULT_VARIABLE LIBC_RESULT |
| 148 | OUTPUT_VARIABLE LIBC_OUTPUT |
| 149 | ERROR_VARIABLE LIBC_ERROR) |
| 150 | |
| 151 | if(NOT LIBC_RESULT) |
| 152 | set(${_libc_name} glibc PARENT_SCOPE) |
| 153 | set(_ND_DETECTED_LIBC glibc CACHE INTERNAL "") |
| 154 | message(CHECK_PASS "glibc") |
| 155 | return() |
| 156 | else() |
| 157 | message(CHECK_FAIL "unknown") |
| 158 | endif() |
| 159 | else() |
| 160 | message(CHECK_FAIL "not found") |
| 161 | endif() |
| 162 | |
| 163 | set(${_libc_name} unknown PARENT_SCOPE) |
| 164 | set(_ND_DETECTED_LIBC unknown CACHE INTERNAL "") |
| 165 | else() |
| 166 | set(${_libc_name} ${_ND_DETECTED_LIBC} PARENT_SCOPE) |
| 167 | endif() |
| 168 | endfunction() |
| 169 | |
| 170 | # Extract a tar archive. |
| 171 | # |
| 172 | # This will use CMake’s native support if available, but will still |
| 173 | # fall back cleanly if CMake is too old. |
| 174 | function(extract_gzipped_tarball tarball target) |
| 175 | if(CMAKE_VERSION VERSION_LESS 3.18) |
| 176 | find_program(TAR NAMES tar bsdtar DOC "TAR archive program") |
| 177 | |
| 178 | if(TAR STREQUAL "TAR-NOTFOUND") |
| 179 | message(FATAL_ERROR "Unable to find tar command") |
| 180 | endif() |
| 181 | |
| 182 | find_program(GZIP NAMES gzip DOC "GZIP compression program") |
| 183 | |
| 184 | if(GZIP STREQUAL "GZIP-NOTFOUND") |
| 185 | message(FATAL_ERROR "Unable to find gzip command") |
| 186 | endif() |
| 187 | |
| 188 | file(MAKE_DIRECTORY "${target}") |
| 189 | execute_process(COMMAND tar -x -z -f "${tarball}" -C "${target}" |
| 190 | RESULT_VARIABLE result) |
| 191 | |
| 192 | if(result) |
| 193 | message(FATAL_ERROR "Failed to extract ${tarball}") |
| 194 | endif() |
| 195 | else() |
| 196 | file(ARCHIVE_EXTRACT |
| 197 | INPUT "${tarball}" |
| 198 | DESTINATION "${target}") |
| 199 | endif() |
| 200 | endfunction() |
| 201 | |
| 202 | # Get a recursive list of all sub-directories of the specified directory, |
| 203 | # relative to that directory. |
| 204 | function(subdirlist result curdir) |
| 205 | file(GLOB_RECURSE children |
| 206 | LIST_DIRECTORIES TRUE |
| 207 | RELATIVE ${curdir} |
| 208 | ${curdir}/*) |
| 209 | |
| 210 | set(dirlist "") |
| 211 | |
| 212 | foreach(child ${children}) |
| 213 | if(IS_DIRECTORY ${curdir}/${child}) |
| 214 | list(APPEND dirlist ${child}) |
| 215 | endif() |
| 216 | endforeach() |
| 217 | |
| 218 | set(${result} ${dirlist} PARENT_SCOPE) |
| 219 | endfunction() |
| 220 | |
| 221 | # Precompile python code in the specified directory relative to the |
| 222 | # CMake install prefix at install time. |
| 223 | # This must be called _after_ the install directive for the python code |
| 224 | # in the specified directory |
| 225 | function(precompile_python dir component) |
| 226 | find_package(Python3) |
| 227 | |
| 228 | if(NOT ${Python3_Interpreter_FOUND}) |
| 229 | message(STATUS "Could not find Python3, skipping precompilation of Python code.") |
| 230 | return() |
| 231 | endif() |
| 232 | |
| 233 | set(prefix [=[${CMAKE_INSTALL_PREFIX}]=]) |
| 234 | |
| 235 | install( |
| 236 | CODE "message(STATUS \"Precompiling Python3 code in ${prefix}/${dir}\")" |
| 237 | COMPONENT ${component} |
| 238 | ) |
| 239 | install( |
| 240 | CODE "execute_process(COMMAND ${Python3_Interpreter} -O -m compileall -j0 -o2 ${prefix}/${dir} WORKING_DIRECTORY ${prefix}/${dir})" |
| 241 | COMPONENT ${component} |
| 242 | ) |
| 243 | endfunction() |
| 244 | |
| 245 | # Load the URL and hash for a vendored component |
| 246 | function(get_vendored_url_and_hash component prefix) |
| 247 | set(url_file "${CMAKE_SOURCE_DIR}/packaging/vendor/${component}.url") |
| 248 | set(hash_file "${CMAKE_SOURCE_DIR}/packaging/vendor/${component}.sha256") |
| 249 | |
| 250 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.29) |
| 251 | if(NOT IS_READABLE ${url_file}) |
| 252 | message(FATAL_ERROR "Unable to read data from ${url_file}") |
| 253 | endif() |
| 254 | else() |
| 255 | if(NOT EXISTS ${url_file}) |
| 256 | message(FATAL_ERROR "Unable to read data from ${url_file}") |
| 257 | endif() |
| 258 | endif() |
| 259 | |
| 260 | file(READ "${url_file}" "url") |
| 261 | string(STRIP "${url}" "url") |
| 262 | |
| 263 | if("${url}" STREQUAL "") |
| 264 | message(FATAL_ERROR "${url_file} is empty") |
| 265 | endif() |
| 266 | |
| 267 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.29) |
| 268 | if(NOT IS_READABLE ${hash_file}) |
| 269 | message(FATAL_ERROR "Unable to read data from ${hash_file}") |
| 270 | endif() |
| 271 | else() |
| 272 | if(NOT EXISTS ${hash_file}) |
| 273 | message(FATAL_ERROR "Unable to read data from ${hash_file}") |
| 274 | endif() |
| 275 | endif() |
| 276 | |
| 277 | file(READ "${hash_file}" "hash") |
| 278 | string(STRIP "${hash}" "hash") |
| 279 | |
| 280 | if("${hash}" STREQUAL "") |
| 281 | message(FATAL_ERROR "${hash_file} is empty") |
| 282 | endif() |
| 283 | |
| 284 | set("${prefix}_URL" "${url}" PARENT_SCOPE) |
| 285 | set("${prefix}_HASH" "${hash}" PARENT_SCOPE) |
| 286 | endfunction() |