1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# 4# SPDX-License-Identifier: GPL-2.0-only 5# 6 7cmake_minimum_required(VERSION 3.8.2) 8 9# Wrapper function around find_file that generates a fatal error if it isn't found 10# Is equivalent to find_file except that it adds CMAKE_CURRENT_SOURCE_DIR as a path and sets 11# CMAKE_FIND_ROOT_PATH_BOTH 12function(RequireFile config_name file_name) 13 find_file( 14 ${config_name} "${file_name}" 15 PATHS "${CMAKE_CURRENT_SOURCE_DIR}" 16 CMAKE_FIND_ROOT_PATH_BOTH ${ARGV} 17 ) 18 if("${${config_name}}" STREQUAL "${config_name}-NOTFOUND") 19 message(FATAL_ERROR "Failed to find required file ${file_name}") 20 endif() 21 mark_as_advanced(FORCE ${config_name}) 22endfunction(RequireFile) 23 24# Helper function for converting a filename to an absolute path. It first converts to 25# an absolute path based in the current source directory, and if the results in a file 26# that doesn't exist it returns an absolute path based from the binary directory 27# This file check is done at generation time and is considered safe as source files 28# should not be being added as part of the build step (except into the build directory) 29function(get_absolute_source_or_binary output input) 30 get_filename_component(test "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 31 if(NOT EXISTS "${test}") 32 get_filename_component(test "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") 33 endif() 34 set("${output}" "${test}" PARENT_SCOPE) 35endfunction(get_absolute_source_or_binary) 36 37function(get_absolute_list_source_or_binary output input) 38 get_filename_component(test "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") 39 if(NOT EXISTS "${test}") 40 get_absolute_source_or_binary(test ${input}) 41 endif() 42 set("${output}" "${test}" PARENT_SCOPE) 43endfunction() 44 45# Generates a custom command that preprocesses an input file into an output file 46# Uses the current compilation settings as well as any EXTRA_FLAGS provided. Can also 47# be given any EXTRA_DEPS to depend upon 48# A target with the name `output_target` will be generated to create a target based dependency 49# for the output file 50# Output and input files will be converted to absolute paths based on the following rules 51# * Output is assumed to be in CMAKE_CURRENT_BINARY_DIR 52# * Input is assumed to be in CMAKE_CURRENT_SOURCE_DIR if it resolves to a file that exists 53# otherwise it is assumed to be in CMAKE_CURRENT_BINARY_DIR 54function(cppfile output output_target input) 55 cmake_parse_arguments(PARSE_ARGV 3 "CPP" "" "EXACT_NAME" "EXTRA_DEPS;EXTRA_FLAGS") 56 if(NOT "${CPP_UNPARSED_ARGUMENTS}" STREQUAL "") 57 message(FATAL_ERROR "Unknown arguments to cppfile: ${CPP_UNPARSED_ARGUMENTS}") 58 endif() 59 get_absolute_source_or_binary(input "${input}") 60 set(file_copy_name "${output_target}_temp.c") 61 # If EXACT_NAME then we copy the input file to the name given by the caller. Otherwise 62 # generate a rule for copying the input file to a default name. 63 if(CPP_EXACT_NAME) 64 set(file_copy_name ${CPP_EXACT_NAME}) 65 endif() 66 add_custom_command( 67 OUTPUT ${file_copy_name} 68 COMMAND 69 ${CMAKE_COMMAND} -E copy ${input} ${CMAKE_CURRENT_BINARY_DIR}/${file_copy_name} 70 COMMENT "Creating C input file for preprocessor" 71 DEPENDS ${CPP_EXTRA_DEPS} ${input} 72 ) 73 add_custom_target(${output_target}_copy_in DEPENDS ${file_copy_name}) 74 # Now generate an object library to persuade cmake to just do compilation and not try 75 # and link our 'object' files 76 add_library(${output_target}_temp_lib OBJECT ${file_copy_name}) 77 add_dependencies(${output_target}_temp_lib ${output_target}_copy_in) 78 # Give the preprecess flag 79 target_compile_options(${output_target}_temp_lib PRIVATE -E) 80 # Give any other flags from the user 81 target_compile_options(${output_target}_temp_lib PRIVATE ${CPP_EXTRA_FLAGS}) 82 # Now copy from the random name cmake gave our object file into the one desired by the user 83 add_custom_command( 84 OUTPUT ${output} 85 COMMAND 86 ${CMAKE_COMMAND} -E copy $<TARGET_OBJECTS:${output_target}_temp_lib> ${output} 87 DEPENDS ${output_target}_temp_lib $<TARGET_OBJECTS:${output_target}_temp_lib> 88 ) 89 add_custom_target(${output_target} DEPENDS ${output}) 90endfunction(cppfile) 91 92# Function to generate a custom command to process a bitfield file. The input 93# (pbf_path) is either a .bf file or, if you used pre-processor directives, a 94# pre-processed .bf file. As this invokes a python tool that places a file 95# in the current working directory a unqiue 'work_dir' needs to be provided 96# for this command to execute in 97# This function is not intended to be used directly, rather one of its wrappers 98# that is specialized to generate a specific kind of output should be used 99# These wrappers work by passing the additional 'args' that get passed on to 100# the bitfield generator 101function(GenBFCommand args target_name pbf_path pbf_target deps) 102 # Since we're going to change the working directory first convert any paths to absolute 103 get_filename_component( 104 target_name_absolute 105 "${target_name}" 106 ABSOLUTE 107 BASE_DIR 108 "${CMAKE_CURRENT_BINARY_DIR}" 109 ) 110 get_absolute_source_or_binary(pbf_path_absolute "${pbf_path}") 111 add_custom_command( 112 OUTPUT "${target_name_absolute}" 113 COMMAND 114 "${PYTHON3}" "${BF_GEN_PATH}" "${args}" "${pbf_path_absolute}" "${target_name_absolute}" 115 DEPENDS 116 "${BF_GEN_PATH}" 117 "${pbf_path_absolute}" 118 "${pbf_target}" 119 ${deps} 120 COMMENT "Generating from ${pbf_path}" COMMAND_EXPAND_LISTS 121 VERBATIM 122 ) 123endfunction(GenBFCommand) 124 125# Wrapper function for generating both a target and command to process a bitfield file 126function(GenBFTarget args target_name target_file pbf_path pbf_target deps) 127 GenBFCommand("${args}" "${target_file}" "${pbf_path}" "${pbf_target}" "${deps}") 128 add_custom_target(${target_name} DEPENDS "${target_file}") 129endfunction(GenBFTarget) 130 131# Wrapper around GenBFTarget for generating a C header file out of a bitfield specification 132# environment is empty for kernel generation and "libsel4" for generating non kernel headers 133# prunes is an optional list of files that will be passed as --prune options to the bitfield 134# generator 135function(GenHBFTarget environment target_name target_file pbf_path pbf_target prunes deps) 136 set(args "") 137 if(NOT "${environment}" STREQUAL "") 138 list(APPEND args --environment "${environment}") 139 endif() 140 foreach(prune IN LISTS prunes) 141 get_absolute_source_or_binary(prune_absolute "${prune}") 142 list(APPEND args "--prune" "${prune_absolute}") 143 endforeach() 144 list(APPEND deps ${prunes}) 145 GenBFTarget("${args}" "${target_name}" "${target_file}" "${pbf_path}" "${pbf_target}" "${deps}") 146endfunction(GenHBFTarget) 147 148# Wrapper for generating different kinds of .thy files from bitfield specifications 149function(GenThyBFTarget args target_name target_file pbf_path pbf_target prunes deps) 150 get_filename_component(cspec_dir "${CSPEC_DIR}" ABSOLUTE BASE_DIR) 151 list(APPEND args --cspec-dir "${cspec_dir}") 152 if(SKIP_MODIFIES) 153 list(APPEND args "--skip_modifies") 154 endif() 155 foreach(prune IN LISTS prunes) 156 list(APPEND args "--prune" "${prune}") 157 endforeach() 158 GenBFTarget("${args}" "${target_name}" "${target_file}" "${pbf_path}" "${pbf_target}" "${deps}") 159endfunction(GenThyBFTarget) 160 161# Generate hol definitions from a bitfield specification 162function(GenDefsBFTarget target_name target_file pbf_path pbf_target prunes deps) 163 set(args "") 164 list(APPEND args --hol_defs) 165 GenThyBFTarget( 166 "${args}" 167 "${target_name}" 168 "${target_file}" 169 "${pbf_path}" 170 "${pbf_target}" 171 "${prunes}" 172 "${deps}" 173 ) 174endfunction(GenDefsBFTarget) 175 176# Generate proofs from a bitfield specification 177function(GenProofsBFTarget target_name target_file pbf_path pbf_target prunes deps) 178 set(args "") 179 # Get an absolute path to cspec_dir so that the final theory file is portable 180 list( 181 APPEND 182 args 183 --hol_proofs 184 --umm_types 185 "${UMM_TYPES}" 186 ) 187 if(SORRY_BITFIELD_PROOFS) 188 list(APPEND args "--sorry_lemmas") 189 endif() 190 list( 191 APPEND 192 args 193 "--toplevel;$<JOIN:$<TARGET_PROPERTY:kernel_config_target,TOPLEVELTYPES>,;--toplevel;>" 194 ) 195 list(APPEND deps "${UMM_TYPES}") 196 GenThyBFTarget( 197 "${args}" 198 "${target_name}" 199 "${target_file}" 200 "${pbf_path}" 201 "${pbf_target}" 202 "${prunes}" 203 "${deps}" 204 ) 205endfunction(GenProofsBFTarget) 206 207macro(cfg_str_add_disabled cfg_str name) 208 # include in liner files can't use double slash comments 209 list(APPEND ${cfg_str} "/* disabled: CONFIG_${name} */") 210endmacro() 211 212macro(cfg_str_add_define cfg_str name value comment) 213 set(cfg_define_str "#define CONFIG_${name} ${value}") 214 if(NOT "${comment}" STREQUAL "") 215 # include in liner files can't use double slash comments 216 string(APPEND cfg_define_str " /* ${comment} */") 217 endif() 218 list(APPEND ${cfg_str} ${cfg_define_str}) 219endmacro() 220 221macro(cfg_str_add cfg_str name value) 222 if("${value}" STREQUAL "") 223 cfg_str_add_define(${cfg_str} ${name} "" "empty") 224 else() 225 cfg_str_add_define(${cfg_str} ${name} "${value}" "") 226 endif() 227endmacro() 228 229macro(cfg_str_add_as_1 cfg_str name var) 230 cfg_str_add_define(${cfg_str} ${name} "1" "${var}=${${var}}") 231endmacro() 232 233# config_option(cmake_option_name c_config_name doc DEFAULT default [DEPENDS deps] [DEFAULT_DISABLE default_disabled]) 234# Defines a toggleable configuration option that will be present in the cache and the 235# cmake-gui 236# optionname is the name of the cache variable that can be used directly in cmake scripts 237# to get the value of the option 238# configname is the name (prefixed with CONFIG_) that will appear in generated 239# C configuration headers 240# DEFAULT is the default value of the config that it should initially be set to 241# doc Help string to explain the option in the cmake-gui 242# An additional DEPENDS arguments may be passed, which is a list of conditions to evaluate and if true, 243# the option will exist. If the option doesn't exist it will be set to DEFAULT_DISABLED, or if 244# that wasn't provided then just DEFAULT 245# If the option is true it adds to the global configure_string variable (see add_config_library) 246function(config_option optionname configname doc) 247 cmake_parse_arguments(PARSE_ARGV 3 "CONFIG" "" "DEPENDS;DEFAULT_DISABLED;DEFAULT" "") 248 if(NOT "${CONFIG_UNPARSED_ARGUMENTS}" STREQUAL "") 249 message(FATAL_ERROR "Unknown arguments to config_option") 250 endif() 251 if("${CONFIG_DEFAULT_DISABLED}" STREQUAL "") 252 set(CONFIG_DEFAULT_DISABLED "${CONFIG_DEFAULT}") 253 endif() 254 set(valid ON) 255 if(NOT "${CONFIG_DEPENDS}" STREQUAL "") 256 # Check the passed in dependencies. This loop and logic is inspired by the 257 # actual cmake_dependent_option code 258 foreach(test ${CONFIG_DEPENDS}) 259 string( 260 REGEX 261 REPLACE 262 " +" 263 ";" 264 test 265 "${test}" 266 ) 267 if(NOT (${test})) 268 set(valid OFF) 269 break() 270 endif() 271 endforeach() 272 endif() 273 if(valid) 274 # Check for an existing value, and set the option to that, otherwise use the default 275 # Also reset the default if we switched from disabled to enabled 276 if((DEFINED ${optionname}) AND (NOT DEFINED ${optionname}_DISABLED)) 277 set(${optionname} "${${optionname}}" CACHE BOOL "${doc}" FORCE) 278 else() 279 set(${optionname} "${CONFIG_DEFAULT}" CACHE BOOL "${doc}" FORCE) 280 unset(${optionname}_DISABLED CACHE) 281 endif() 282 # This is a directory scope setting used to allow or prevent config options 283 # from appearing in the cmake config GUI 284 if(SEL4_CONFIG_DEFAULT_ADVANCED) 285 mark_as_advanced(${optionname}) 286 endif() 287 else() 288 set(${optionname} "${CONFIG_DEFAULT_DISABLED}" CACHE INTERNAL "${doc}" FORCE) 289 set(${optionname}_DISABLED TRUE CACHE INTERNAL "" FORCE) 290 endif() 291 set(local_config_string "${configure_string}") 292 if(${optionname}) 293 cfg_str_add_as_1(local_config_string ${configname} ${optionname}) 294 else() 295 cfg_str_add_disabled(local_config_string ${configname}) 296 endif() 297 set(configure_string "${local_config_string}" PARENT_SCOPE) 298endfunction(config_option) 299 300# Set a configuration option to a particular value. This value will not appear in 301# the cmake-gui, but will produce an internal cmake cache variable and generated 302# configuration headers. 303macro(config_set optionname configname value) 304 set(${optionname} "${value}" CACHE INTERNAL "" FORCE) 305 set(c_define "CONFIG_${configname}") 306 if("${value}" STREQUAL "OFF") 307 cfg_str_add_disabled(configure_string ${configname}) 308 else() 309 if("${value}" STREQUAL "ON") 310 cfg_str_add_as_1(configure_string ${configname} ${optionname}) 311 else() 312 # we have to quote ${value} here because it could be empty 313 cfg_str_add(configure_string ${configname} "${value}") 314 endif() 315 endif() 316endmacro(config_set) 317 318# config_cmake_string(cmake_option_name c_config_name doc DEFAULT default [DEPENDS dep] 319# [DEFAULT_DISABLED default_disabled] [UNDEF_DISABLED] [QUOTE] 320# Defines a configuration option that is a user configurable string. Most parameters 321# are the same as config_option 322# UNQUOTE if specified says this is something with more semantics like a number or identifier 323# and should not be quoted in the output 324# [UNDEF_DISABLED] can be specified to explicitly disable generation of any output value when 325# the configuration dependencies are unmet 326# Adds to the global configure_string variable (see add_config_library) 327function(config_string optionname configname doc) 328 cmake_parse_arguments( 329 PARSE_ARGV 330 3 331 "CONFIG" 332 "UNQUOTE;UNDEF_DISABLED" 333 "DEPENDS;DEFAULT_DISABLED;DEFAULT" 334 "" 335 ) 336 if(NOT "${CONFIG_UNPARSED_ARGUMENTS}" STREQUAL "") 337 message(FATAL_ERROR "Unknown arguments to config_option: ${CONFIG_UNPARSED_ARGUMENTS}") 338 endif() 339 if("${CONFIG_DEFAULT}" STREQUAL "") 340 message(FATAL_ERROR "No default specified for ${config_option}") 341 endif() 342 if("${CONFIG_DEFAULT_DISABLED}" STREQUAL "") 343 set(CONFIG_DEFAULT_DISABLED "${CONFIG_DEFAULT}") 344 endif() 345 set(valid ON) 346 set(local_config_string "${configure_string}") 347 if(NOT "${CONFIG_DEPENDS}" STREQUAL "") 348 # Check the passed in dependencies. This loop and logic is inspired by the 349 # actual cmake_dependent_option code 350 foreach(test ${CONFIG_DEPENDS}) 351 string( 352 REGEX 353 REPLACE 354 " +" 355 ";" 356 test 357 "${test}" 358 ) 359 if(NOT (${test})) 360 set(valid OFF) 361 break() 362 endif() 363 endforeach() 364 endif() 365 set(cfg_tag_option "") 366 if(valid) 367 # See if we transitioned from disabled to enabled. We do this by having an 368 # _UNAVAILABLE variable. We want to ensure that if the option previously had 369 # unmet conditions that we reset its value to 'default'. This is needed 370 # because whilst the option had unmet conditions it still potentially had 371 # a value in the form of the optional disabled_value 372 set(force "") 373 if(${optionname}_UNAVAILABLE) 374 set(force "FORCE") 375 unset(${optionname}_UNAVAILABLE CACHE) 376 endif() 377 set(${optionname} "${CONFIG_DEFAULT}" CACHE STRING "${doc}" ${force}) 378 set(cfg_tag_option ${optionname}) 379 # This is a directory scope setting used to allow or prevent config options 380 # from appearing in the cmake config GUI 381 if(SEL4_CONFIG_DEFAULT_ADVANCED) 382 mark_as_advanced(${optionname}) 383 endif() 384 else() 385 if(CONFIG_UNDEF_DISABLED) 386 unset(${optionname} CACHE) 387 else() 388 # Forcively change the value to its disabled_value 389 set(${optionname} "${CONFIG_DEFAULT_DISABLED}" CACHE INTERNAL "" FORCE) 390 set(cfg_tag_option ${optionname}) 391 endif() 392 # Sset _UNAVAILABLE so we can detect when the option because enabled again 393 set(${optionname}_UNAVAILABLE ON CACHE INTERNAL "" FORCE) 394 endif() 395 if(cfg_tag_option) 396 if(CONFIG_UNQUOTE) 397 set(quote "") 398 else() 399 set(quote "\"") 400 endif() 401 cfg_str_add(local_config_string ${configname} "${quote}@${cfg_tag_option}@${quote}") 402 endif() 403 set(configure_string "${local_config_string}" PARENT_SCOPE) 404endfunction(config_string) 405 406# Defines a multi choice / select configuration option 407# optionname is the name of the cache variable that can be used directly in cmake scripts 408# to get the value of the option 409# configname is the name (prefixed with CONFIG_) that will appear in generated 410# C configuration headers and is set to the string of the selected config 411# doc Help string to explain the option in the cmake-gui 412# Then any number of additional arguments may be supplied each describing one of the potential 413# configuration choices. Each additional argument is a list of (option_value, option_cache, 414# option_config, [condition]...) 415# option_value is the string that represents this option. this is what the user will see 416# in the cmake-gui and what configname will get defined to if this option is selected 417# option_cache is like optionname and is set to ON when this option is selected and OFF 418# if it is not 419# condition may be repeated as many times and all conditions must be true for this choice 420# to appear in the list 421# If no valid choices are given (either because none are given or the ones that were given 422# did not have their conditions met) then this option will be disabled and not appear in 423# the cmake-gui 424# Adds to the global configure_string variable (see add_config_library) 425function(config_choice optionname configname doc) 426 # Cannot use ARGN because each argument itself is a list 427 math(EXPR limit "${ARGC} - 1") 428 set(local_config_string "${configure_string}") 429 # force_default represents whether we need to force a new value or not. We would need 430 # to force a new value for example if we detect that the current selected choice is 431 # no longer (due to conditions) a valid choice 432 set(force_default "") 433 # Used to track the first time we see a valid enabled choice. The first valid choice 434 # becomes the default and if we never find a valid choice then we know to disable this config 435 set(first ON) 436 # This tracks whether or not the current selected choice is one of the ones that we 437 # have been passed. If we fail to find the currently selected choice then, similar to 438 # if the current choice is invalid to do an unment condition, we must switch to some 439 # valid default 440 set(found_current OFF) 441 foreach(i RANGE 3 ${limit}) 442 set(option "${ARGV${i}}") 443 # Extract the constant parts of the choice information and just leave any 444 # conditional information 445 list(GET option 0 option_value) 446 list(GET option 1 option_cache) 447 list(GET option 2 option_config) 448 list( 449 REMOVE_AT 450 option 451 0 452 1 453 2 454 ) 455 # Construct a list of all of our options 456 list(APPEND all_strings "${option_value}") 457 # By default we assume is valid, we may change our mind after checking dependencies 458 # (if there are any). This loop is again based off the one in cmake_dependent_option 459 set(valid ON) 460 foreach(truth IN LISTS option) 461 string( 462 REGEX 463 REPLACE 464 " +" 465 ";" 466 truth 467 "${truth}" 468 ) 469 if(NOT (${truth})) 470 # This choice isn't valid due to unmet conditions so we must check if we have 471 # currently selected this choice. If so trigger the force_default 472 if("${${optionname}}" STREQUAL "${option_value}") 473 set(force_default "FORCE") 474 endif() 475 set(valid OFF) 476 endif() 477 endforeach() 478 if(valid) 479 # Is a valid option, add to the strings list 480 list(APPEND strings "${option_value}") 481 if(first) 482 set(first OFF) 483 set(first_cache "${option_cache}") 484 set(first_config "${option_config}") 485 # Use the first valid option we find as the default. This default is will be 486 # used if there is no current value, or for some reason we need to override 487 # the current value (see force_default above) 488 set(default "${option_value}") 489 endif() 490 # Check if this option is the one that is currently set 491 if("${${optionname}}" STREQUAL "${option_value}") 492 set(${option_cache} ON CACHE INTERNAL "" FORCE) 493 cfg_str_add_as_1(local_config_string ${option_config} ${option_cache}) 494 set(found_current ON) 495 else() 496 set(${option_cache} OFF CACHE INTERNAL "" FORCE) 497 cfg_str_add_disabled(local_config_string ${option_config}) 498 endif() 499 else() 500 # Remove this config as it's not valid 501 unset(${option_cache} CACHE) 502 endif() 503 endforeach() 504 if(NOT found_current) 505 # Currently selected option wasn't found so reset to a default that we know is valid 506 set(force_default "FORCE") 507 endif() 508 if(first) 509 # None of the choices were valid. Remove this option so its not visible 510 unset(${optionname} CACHE) 511 else() 512 cfg_str_add(local_config_string ${configname} "@${optionname}@") 513 set(configure_string "${local_config_string}" PARENT_SCOPE) 514 set(${optionname} "${default}" CACHE STRING "${doc}" ${force_default}) 515 # This is a directory scope setting used to allow or prevent config options 516 # from appearing in the cmake config GUI 517 if(SEL4_CONFIG_DEFAULT_ADVANCED) 518 mark_as_advanced(${optionname}) 519 endif() 520 set_property(CACHE ${optionname} PROPERTY STRINGS ${strings}) 521 if(NOT found_current) 522 # The option is actually enabled, but we didn't enable the correct 523 # choice earlier, since we didn't know we were going to revert to 524 # the default. So add the option setting here 525 set(${first_cache} ON CACHE INTERNAL "" FORCE) 526 cfg_str_add_as_1(local_config_string ${first_config} ${first_cache}) 527 endif() 528 endif() 529 # Save all possible options to an internal value. This is to allow enumerating the options elsewhere. 530 # We create a new variable because cmake doesn't support arbitrary properties on cache variables. 531 set(${optionname}_all_strings ${all_strings} CACHE INTERNAL "" FORCE) 532 set(configure_string "${local_config_string}" PARENT_SCOPE) 533endfunction(config_choice) 534 535# Defines a target for a 'configuration' library, which generates a header based 536# upon current state of cache/variables and a provided template string. Additionally 537# the generated library gets added to a known global list of 'configuration' libraries 538# This list can be used if someone wants all the configurations 539# Whilst this function takes an explicit configure_template, generally this will always 540# be '${configure_string}' as that is the global variable automatically appended to 541# by the config_ helper macros and functions above 542# This generates a library that can be linked against with 543# target_link_library(<target> ${prefix}_Config) 544# Which will allow you to do #include <${prefix}/gen_config.h> 545function(add_config_library prefix configure_template) 546 set(config_dir "${CMAKE_CURRENT_BINARY_DIR}/gen_config") 547 set(config_file "${config_dir}/${prefix}/gen_config.h") 548 string(CONFIGURE "${configure_template}" config_header_contents) 549 # Turn the list of configurations into a valid C file of different lines 550 string( 551 REPLACE 552 ";" 553 "\n" 554 config_header_contents 555 "${config_header_contents}" 556 ) 557 file(GENERATE OUTPUT "${config_file}" CONTENT "\n#pragma once\n\n${config_header_contents}") 558 add_custom_target(${prefix}_Gen DEPENDS "${config_file}") 559 add_library(${prefix}_Config INTERFACE) 560 target_include_directories(${prefix}_Config INTERFACE "${config_dir}") 561 add_dependencies(${prefix}_Config ${prefix}_Gen ${config_file}) 562 set_property(GLOBAL APPEND PROPERTY CONFIG_LIBRARIES "${prefix}") 563 # Set a property on the library that is a list of the files we generated. This 564 # allows custom build commands to easily get a file dependency list so they can 565 # 'depend' upon this target easily 566 set_property(TARGET ${prefix}_Gen APPEND PROPERTY GENERATED_FILES "${config_file}") 567endfunction(add_config_library) 568 569macro(get_generated_files output target) 570 get_property(${output} TARGET ${target} PROPERTY GENERATED_FILES) 571endmacro(get_generated_files) 572 573# This rule tries to emulate an 'autoconf' header. autoconf generated headers 574# were previously used as configuration, so this rule provides a way for previous 575# applications and libraries to build without modification. The config_list 576# is a list of 'prefix' values that have been passed to add_config_library 577# This generates a library with ${targetname} that when linked against 578# will allow code to simply #include <autoconf.h> 579function(generate_autoconf targetname config_list) 580 set(link_list "") 581 set(gen_list "") 582 set(config_header_contents "\n#pragma once\n\n") 583 foreach(config IN LISTS config_list) 584 list(APPEND link_list "${config}_Config") 585 get_generated_files(gens ${config}_Gen) 586 list(APPEND gen_list ${gens}) 587 string(APPEND config_header_contents "#include <${config}/gen_config.h>\n") 588 endforeach() 589 set(config_dir "${CMAKE_CURRENT_BINARY_DIR}/autoconf") 590 set(config_file "${config_dir}/autoconf.h") 591 592 file(GENERATE OUTPUT "${config_file}" CONTENT "${config_header_contents}") 593 add_custom_target(${targetname}_Gen DEPENDS "${config_file}") 594 add_library(${targetname} INTERFACE) 595 target_link_libraries(${targetname} INTERFACE ${link_list}) 596 target_include_directories(${targetname} INTERFACE "${config_dir}") 597 add_dependencies(${targetname} ${targetname}_Gen ${config_file}) 598 # Set our GENERATED_FILES property to include the GENERATED_FILES of all of our input 599 # configurations, as well as the files we generated 600 set_property( 601 TARGET ${targetname}_Gen 602 APPEND 603 PROPERTY GENERATED_FILES "${config_file}" ${gen_list} 604 ) 605endfunction(generate_autoconf) 606 607# Macro that allows for appending to a specified list only if all the supplied conditions are true 608macro(list_append_if list dep) 609 set(list_append_local_list ${${list}}) 610 set(list_append_valid ON) 611 foreach(truth IN ITEMS ${dep}) 612 string( 613 REGEX 614 REPLACE 615 " +" 616 ";" 617 truth 618 "${truth}" 619 ) 620 if(NOT (${truth})) 621 set(list_append_valid OFF) 622 break() 623 endif() 624 endforeach() 625 if(list_append_valid) 626 list(APPEND list_append_local_list ${ARGN}) 627 endif() 628 set(${list} ${list_append_local_list} PARENT_SCOPE) 629endmacro(list_append_if) 630 631# Checks if a file is older than its dependencies 632# Will set `stale` to TRUE if outfile doesn't exist, 633# or if outfile is older than any file in `deps_list`. 634# Will also set `stale` to TRUE if the arguments given to this macro 635# change compared to the previous invocation. 636# stale: A variable to overwrite with TRUE or FALSE 637# outfile: A value that is a valid file path 638# deps_list: A variable that holds a list of file paths 639# arg_cache: A variable that holds a file to store arguments to 640# e.g: 641# set(dts_list "filea" "fileb" "filec") 642# set(KernelDTBPath "${CMAKE_CURRENT_BINARY_DIR}/kernel.dtb") 643# check_outfile_stale(regen ${KernelDTBPath} dts_list ${CMAKE_CURRENT_BINARY_DIR}/dts.cmd 644# if (regen) 645# regen_file(${KernelDTBPath}) 646# endif() 647# 648# The above call will set regen to TRUE if the file referred 649# to by KernelDTBPath doesn't exist, or is older than any files 650# in KernelDTSIntermediate or if regen, ${KernelDTBPath} and dts_list resolve to different files. 651macro(check_outfile_stale stale outfile deps_list arg_cache) 652 set(_outfile_command "${stale} ${outfile} ${${deps_list}}") 653 if(NOT EXISTS "${arg_cache}") 654 set(_prev_command "") 655 else() 656 file(READ "${arg_cache}" _prev_command) 657 endif() 658 if(NOT "${_outfile_command}" STREQUAL "${_prev_command}") 659 set(${stale} TRUE) 660 else() 661 set(${stale} FALSE) 662 endif() 663 if(EXISTS ${outfile} AND NOT ${stale}) 664 set(${stale} FALSE) 665 foreach(dep IN LISTS ${deps_list}) 666 if("${dep}" IS_NEWER_THAN "${outfile}") 667 set(${stale} TRUE) 668 break() 669 endif() 670 endforeach() 671 else() 672 set(${stale} TRUE) 673 endif() 674 if(${stale}) 675 file(WRITE "${arg_cache}" "${_outfile_command}") 676 endif() 677endmacro() 678 679# This macro only works when cmake is invoked with -P (script mode) on a kernel 680# verified configuration. The result is configuring and building a verified kernel. 681# CMAKE_ARGC and CMAKE_ARGV# contain command line argument information. 682# It runs the following commands to produce kernel.elf and kernel_all_pp.c: 683# cmake -G Ninja ${args} -C ${CMAKE_ARGV2} ${CMAKE_CURRENT_LIST_DIR}/.. 684# ninja kernel.elf 685# ninja kernel_all_pp_wrapper 686macro(cmake_script_build_kernel) 687 if(NOT "${CMAKE_ARGC}" STREQUAL "") 688 set(args "") 689 foreach(i RANGE 3 ${CMAKE_ARGC}) 690 if("${CMAKE_ARGV${i}}" STREQUAL "FORCE") 691 # Consume arg and force reinit of build dir by deleting CMakeCache.txt 692 file(REMOVE CMakeCache.txt) 693 file(REMOVE gcc.cmake) 694 else() 695 list(APPEND args ${CMAKE_ARGV${i}}) 696 endif() 697 endforeach() 698 execute_process( 699 COMMAND 700 cmake -G Ninja ${args} -C ${CMAKE_ARGV2} ${CMAKE_CURRENT_LIST_DIR}/.. 701 INPUT_FILE /dev/stdin 702 OUTPUT_FILE /dev/stdout 703 ERROR_FILE /dev/stderr 704 ) 705 execute_process( 706 COMMAND ninja kernel.elf 707 INPUT_FILE /dev/stdin 708 OUTPUT_FILE /dev/stdout 709 ERROR_FILE /dev/stderr 710 ) 711 execute_process( 712 COMMAND ninja kernel_all_pp_wrapper 713 INPUT_FILE /dev/stdin 714 OUTPUT_FILE /dev/stdout 715 ERROR_FILE /dev/stderr 716 ) 717 return() 718 endif() 719endmacro() 720