1# 2# Copyright 2017, Data61 3# Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4# ABN 41 687 119 230. 5# 6# This software may be distributed and modified according to the terms of 7# the GNU General Public License version 2. Note that NO WARRANTY is provided. 8# See "LICENSE_GPLv2.txt" for details. 9# 10# @TAG(DATA61_GPL) 11# 12 13cmake_minimum_required(VERSION 3.8.2) 14 15include(CMakeDependentOption) 16 17# Wrapper function around find_file that generates a fatal error if it isn't found 18# Is equivalent to find_file except that it adds CMAKE_CURRENT_SOURCE_DIR as a path and sets 19# CMAKE_FIND_ROOT_PATH_BOTH 20function(RequireFile config_name file_name) 21 find_file(${config_name} "${file_name}" PATHS "${CMAKE_CURRENT_SOURCE_DIR}" CMAKE_FIND_ROOT_PATH_BOTH ${ARGV}) 22 if("${${config_name}}" STREQUAL "${config_name}-NOTFOUND") 23 message(FATAL_ERROR "Failed to find required file ${file_name}") 24 endif() 25 mark_as_advanced(FORCE ${config_name}) 26endfunction(RequireFile) 27 28# Helper function for converting a filename to an absolute path. It first converts to 29# an absolute path based in the current source directory, and if the results in a file 30# that doesn't exist it returns an absolute path based from the binary directory 31# This file check is done at generation time and is considered safe as source files 32# should not be being added as part of the build step (except into the build directory) 33function(get_absolute_source_or_binary output input) 34 get_filename_component(test "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 35 if(NOT EXISTS "${test}") 36 get_filename_component(test "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") 37 endif() 38 set("${output}" "${test}" PARENT_SCOPE) 39endfunction(get_absolute_source_or_binary) 40 41function(get_absolute_list_source_or_binary output input) 42 get_filename_component(test "${input}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") 43 if(NOT EXISTS "${test}") 44 get_absolute_source_or_binary(test ${input}) 45 endif() 46 set("${output}" "${test}" PARENT_SCOPE) 47endfunction() 48 49# Generates a custom command that preprocesses an input file into an output file 50# Uses the current compilation settings as well as any EXTRA_FLAGS provided. Can also 51# be given any EXTRA_DEPS to depend upon 52# A target with the name `output_target` will be generated to create a target based dependency 53# for the output file 54# Output and input files will be converted to absolute paths based on the following rules 55# * Output is assumed to be in CMAKE_CURRENT_BINARY_DIR 56# * Input is assumed to be in CMAKE_CURRENT_SOURCE_DIR if it resolves to a file that exists 57# otherwise it is assumed to be in CMAKE_CURRENT_BINARY_DIR 58function(CPPFile output output_target input) 59 cmake_parse_arguments(PARSE_ARGV 3 "CPP" "" "EXACT_NAME" "EXTRA_DEPS;EXTRA_FLAGS") 60 if(NOT "${CPP_UNPARSED_ARGUMENTS}" STREQUAL "") 61 message(FATAL_ERROR "Unknown arguments to CPPFile: ${CPP_UNPARSED_ARGUMENTS}") 62 endif() 63 get_absolute_source_or_binary(input "${input}") 64 set(file_copy_name "${output_target}_temp.c") 65 # If EXACT_NAME then we copy the input file to the name given by the caller. Otherwise 66 # generate a rule for copying the input file to a default name. 67 if(CPP_EXACT_NAME) 68 set(file_copy_name ${CPP_EXACT_NAME}) 69 endif() 70 add_custom_command(OUTPUT ${file_copy_name} 71 COMMAND ${CMAKE_COMMAND} -E copy ${input} ${CMAKE_CURRENT_BINARY_DIR}/${file_copy_name} 72 COMMENT "Creating C input file for preprocessor" 73 DEPENDS ${CPP_EXTRA_DEPS} ${input} 74 ) 75 add_custom_target(${output_target}_copy_in DEPENDS ${file_copy_name}) 76 # Now generate an object library to persuade cmake to just do compilation and not try 77 # and link our 'object' files 78 add_library(${output_target}_temp_lib OBJECT ${file_copy_name}) 79 add_dependencies(${output_target}_temp_lib ${output_target}_copy_in) 80 # Give the preprecess flag 81 target_compile_options(${output_target}_temp_lib PRIVATE -E) 82 # Give any other flags from the user 83 target_compile_options(${output_target}_temp_lib PRIVATE ${CPP_EXTRA_FLAGS}) 84 # Now copy from the random name cmake gave our object file into the one desired by the user 85 add_custom_command(OUTPUT ${output} 86 COMMAND ${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(target_name_absolute "${target_name}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") 104 get_absolute_source_or_binary(pbf_path_absolute "${pbf_path}") 105 add_custom_command(OUTPUT "${target_name_absolute}" 106 COMMAND "${PYTHON}" "${BF_GEN_PATH}" "${args}" "${pbf_path_absolute}" "${target_name_absolute}" 107 DEPENDS "${BF_GEN_PATH}" "${pbf_path_absolute}" "${pbf_target}" ${deps} 108 COMMENT "Generating from ${pbf_path}" 109 COMMAND_EXPAND_LISTS 110 VERBATIM 111 ) 112endfunction(GenBFCommand) 113 114# Wrapper function for generating both a target and command to process a bitfield file 115function(GenBFTarget args target_name target_file pbf_path pbf_target deps) 116 GenBFCommand("${args}" "${target_file}" "${pbf_path}" "${pbf_target}" "${deps}") 117 add_custom_target(${target_name} 118 DEPENDS "${target_file}" 119 ) 120endfunction(GenBFTarget) 121 122# Wrapper around GenBFTarget for generating a C header file out of a bitfield specification 123# environment is empty for kernel generation and "libsel4" for generating non kernel headers 124# prunes is an optional list of files that will be passed as --prune options to the bitfield 125# generator 126function(GenHBFTarget environment target_name target_file pbf_path pbf_target prunes deps) 127 set(args "") 128 if(NOT "${environment}" STREQUAL "") 129 list(APPEND args --environment "${environment}") 130 endif() 131 foreach(prune IN LISTS prunes) 132 get_absolute_source_or_binary(prune_absolute "${prune}") 133 list(APPEND args "--prune" "${prune_absolute}") 134 endforeach() 135 list(APPEND deps ${prunes}) 136 GenBFTarget("${args}" "${target_name}" "${target_file}" "${pbf_path}" "${pbf_target}" "${deps}") 137endfunction(GenHBFTarget) 138 139# Wrapper for generating different kinds of .thy files from bitfield specifications 140function(GenThyBFTarget args target_name target_file pbf_path pbf_target prunes deps) 141 get_filename_component(cspec_dir "${CSPEC_DIR}" ABSOLUTE BASE_DIR) 142 list(APPEND args --cspec-dir "${cspec_dir}") 143 if(SKIP_MODIFIES) 144 list(APPEND args "--skip_modifies") 145 endif() 146 foreach(prune IN LISTS prunes) 147 list(APPEND args "--prune" "${prune}") 148 endforeach() 149 GenBFTarget("${args}" "${target_name}" "${target_file}" "${pbf_path}" "${pbf_target}" "${deps}") 150endfunction(GenThyBFTarget) 151 152# Generate hol definitions from a bitfield specification 153function(GenDefsBFTarget target_name target_file pbf_path pbf_target prunes deps) 154 set(args "") 155 list(APPEND args --hol_defs) 156 GenThyBFTarget("${args}" "${target_name}" "${target_file}" "${pbf_path}" "${pbf_target}" "${prunes}" "${deps}") 157endfunction(GenDefsBFTarget) 158 159# Generate proofs from a bitfield specification 160function(GenProofsBFTarget target_name target_file pbf_path pbf_target prunes deps) 161 set(args "") 162 # Get an absolute path to cspec_dir so that the final theory file is portable 163 list(APPEND args --hol_proofs --umm_types "${UMM_TYPES}") 164 if(SORRY_BITFIELD_PROOFS) 165 list(APPEND args "--sorry_lemmas") 166 endif() 167 list(APPEND args "--toplevel;$<JOIN:$<TARGET_PROPERTY:kernel_config_target,TOPLEVELTYPES>,;--toplevel;>") 168 list(APPEND deps "${UMM_TYPES}") 169 GenThyBFTarget("${args}" "${target_name}" "${target_file}" "${pbf_path}" "${pbf_target}" "${prunes}" "${deps}") 170endfunction(GenProofsBFTarget) 171 172# config_option(cmake_option_name c_config_name doc DEFAULT default [DEPENDS deps] [DEFAULT_DISABLE default_disabled]) 173# Defines a toggleable configuration option that will be present in the cache and the 174# cmake-gui 175# optionname is the name of the cache variable that can be used directly in cmake scripts 176# to get the value of the option 177# configname is the name (prefixed with CONFIG_) that will appear in generated 178# C configuration headers 179# DEFAULT is the default value of the config that it should initially be set to 180# doc Help string to explain the option in the cmake-gui 181# An additional DEPENDS arguments may be passed, which is a list of conditions to evaluate and if true, 182# the option will exist. If the option doesn't exist it will be set to DEFAULT_DISABLED, or if 183# that wasn't provided then just DEFAULT 184# If the option is true it adds to the global configure_string variable (see add_config_library) 185function(config_option optionname configname doc) 186 cmake_parse_arguments(PARSE_ARGV 3 "CONFIG" "" "DEPENDS;DEFAULT_DISABLED;DEFAULT" "") 187 if(NOT "${CONFIG_UNPARSED_ARGUMENTS}" STREQUAL "") 188 message(FATAL_ERROR "Unknown arguments to config_option") 189 endif() 190 if("${CONFIG_DEFAULT_DISABLED}" STREQUAL "") 191 set(CONFIG_DEFAULT_DISABLED "${CONFIG_DEFAULT}") 192 endif() 193 set(valid ON) 194 if(NOT "${CONFIG_DEPENDS}" STREQUAL "") 195 # Check the passed in dependencies. This loop and logic is inspired by the 196 # actual cmake_dependent_option code 197 foreach(test ${CONFIG_DEPENDS}) 198 string(REGEX REPLACE " +" ";" test "${test}") 199 if(NOT (${test})) 200 set(valid OFF) 201 break() 202 endif() 203 endforeach() 204 endif() 205 if(valid) 206 # Check for an existing value, and set the option to that, otherwise use the default 207 # Also reset the default if we switched from disabled to enabled 208 if((DEFINED ${optionname}) AND (NOT DEFINED ${optionname}_DISABLED)) 209 set(${optionname} "${${optionname}}" CACHE BOOL "${doc}" FORCE) 210 else() 211 set(${optionname} "${CONFIG_DEFAULT}" CACHE BOOL "${doc}" FORCE) 212 unset(${optionname}_DISABLED CACHE) 213 endif() 214 else() 215 set(${optionname} "${CONFIG_DEFAULT_DISABLED}" CACHE INTERNAL "${doc}") 216 set(${optionname}_DISABLED TRUE CACHE INTERNAL "") 217 endif() 218 set(local_config_string "${configure_string}") 219 if(${optionname}) 220 list(APPEND local_config_string 221 "#define CONFIG_${configname} 1" 222 ) 223 endif() 224 set(configure_string "${local_config_string}" PARENT_SCOPE) 225endfunction(config_option) 226 227# Set a configuration option to a particular value. This value will not appear in 228# the cmake-gui, but will produce an internal cmake cache variable and generated 229# configuration headers. 230# If the option is not OFF it adds to the global configure_string variable (see add_config_library) 231macro(config_set optionname configname value) 232 set(${optionname} "${value}" CACHE INTERNAL "") 233 if("${value}" STREQUAL "ON") 234 list(APPEND configure_string 235 "#define CONFIG_${configname} 1" 236 ) 237 elseif("${value}" STREQUAL "OFF") 238 else() 239 list(APPEND configure_string 240 "#define CONFIG_${configname} ${value}" 241 ) 242 endif() 243endmacro(config_set) 244 245# config_cmake_string(cmake_option_name c_config_name doc DEFAULT default [DEPENDS dep] 246# [DEFAULT_DISABLED default_disabled] [UNDEF_DISABLED] [QUOTE] 247# Defines a configuration option that is a user configurable string. Most parameters 248# are the same as config_option 249# UNQUOTE if specified says this is something with more semantics like a number or identifier 250# and should not be quoted in the output 251# [UNDEF_DISABLED] can be specified to explicitly disable generation of any output value when 252# the configuration dependencies are unmet 253# Adds to the global configure_string variable (see add_config_library) 254function(config_string optionname configname doc) 255 cmake_parse_arguments(PARSE_ARGV 3 "CONFIG" "UNQUOTE;UNDEF_DISABLED" "DEPENDS;DEFAULT_DISABLED;DEFAULT" "") 256 if(NOT "${CONFIG_UNPARSED_ARGUMENTS}" STREQUAL "") 257 message(FATAL_ERROR "Unknown arguments to config_option: ${CONFIG_UNPARSED_ARGUMENTS}") 258 endif() 259 if("${CONFIG_DEFAULT}" STREQUAL "") 260 message(FATAL_ERROR "No default specified for ${config_option}") 261 endif() 262 if("${CONFIG_DEFAULT_DISABLED}" STREQUAL "") 263 set(CONFIG_DEFAULT_DISABLED "${CONFIG_DEFAULT}") 264 endif() 265 set(valid ON) 266 set(local_config_string "${configure_string}") 267 if(NOT "${CONFIG_DEPENDS}" STREQUAL "") 268 # Check the passed in dependencies. This loop and logic is inspired by the 269 # actual cmake_dependent_option code 270 foreach(test ${CONFIG_DEPENDS}) 271 string(REGEX REPLACE " +" ";" test "${test}") 272 if(NOT (${test})) 273 set(valid OFF) 274 break() 275 endif() 276 endforeach() 277 endif() 278 if(CONFIG_UNQUOTE) 279 set(quote "") 280 else() 281 set(quote "\"") 282 endif() 283 if(valid) 284 # See if we transitioned from disabled to enabled. We do this by having an 285 # _UNAVAILABLE variable. We want to ensure that if the option previously had 286 # unmet conditions that we reset its value to 'default'. This is needed 287 # because whilst the option had unmet conditions it still potentially had 288 # a value in the form of the optional disabled_value 289 set(force "") 290 if(${optionname}_UNAVAILABLE) 291 set(force "FORCE") 292 unset(${optionname}_UNAVAILABLE CACHE) 293 endif() 294 set(${optionname} "${CONFIG_DEFAULT}" CACHE STRING "${doc}" ${force}) 295 list(APPEND local_config_string 296 "#define CONFIG_${configname} ${quote}@${optionname}@${quote}" 297 ) 298 else() 299 if(CONFIG_UNDEF_DISABLED) 300 unset(${optionname} CACHE) 301 else() 302 # Forcively change the value to its disabled_value 303 set(${optionname} "${CONFIG_DEFAULT_DISABLED}" CACHE INTERNAL "") 304 list(APPEND local_config_string 305 "#define CONFIG_${configname} ${quote}@${optionname}@${quote}" 306 ) 307 endif() 308 # Sset _UNAVAILABLE so we can detect when the option because enabled again 309 set(${optionname}_UNAVAILABLE ON CACHE INTERNAL "") 310 endif() 311 set(configure_string "${local_config_string}" PARENT_SCOPE) 312endfunction(config_string) 313 314# Defines a multi choice / select configuration option 315# optionname is the name of the cache variable that can be used directly in cmake scripts 316# to get the value of the option 317# configname is the name (prefixed with CONFIG_) that will appear in generated 318# C configuration headers and is set to the string of the selected config 319# doc Help string to explain the option in the cmake-gui 320# Then any number of additional arguments may be supplied each describing one of the potential 321# configuration choices. Each additional argument is a list of (option_value, option_cache, 322# option_config, [condition]...) 323# option_value is the string that represents this option. this is what the user will see 324# in the cmake-gui and what configname will get defined to if this option is selected 325# option_cache is like optionname and is set to ON when this option is selected and OFF 326# if it is not 327# condition may be repeated as many times and all conditions must be true for this choice 328# to appear in the list 329# If no valid choices are given (either because none are given or the ones that were given 330# did not have their conditions met) then this option will be disabled and not appear in 331# the cmake-gui 332# Adds to the global configure_string variable (see add_config_library) 333function(config_choice optionname configname doc) 334 # Cannot use ARGN because each argument itself is a list 335 math(EXPR limit "${ARGC} - 1") 336 set(local_config_string "${configure_string}") 337 # force_default represents whether we need to force a new value or not. We would need 338 # to force a new value for example if we detect that the current selected choice is 339 # no longer (due to conditions) a valid choice 340 set(force_default "") 341 # Used to track the first time we see a valid enabled choice. The first valid choice 342 # becomes the default and if we never find a valid choice then we know to disable this config 343 set(first ON) 344 # This tracks whether or not the current selected choice is one of the ones that we 345 # have been passed. If we fail to find the currently selected choice then, similar to 346 # if the current choice is invalid to do an unment condition, we must switch to some 347 # valid default 348 set(found_current OFF) 349 foreach(i RANGE 3 ${limit}) 350 set(option "${ARGV${i}}") 351 # Extract the constant parts of the choice information and just leave any 352 # conditional information 353 list(GET option 0 option_value) 354 list(GET option 1 option_cache) 355 list(GET option 2 option_config) 356 list(REMOVE_AT option 0 1 2) 357 # Construct a list of all of our options 358 list(APPEND all_strings "${option_value}") 359 # By default we assume is valid, we may change our mind after checking dependencies 360 # (if there are any). This loop is again based off the one in cmake_dependent_option 361 set(valid ON) 362 foreach(truth IN LISTS option) 363 string(REGEX REPLACE " +" ";" truth "${truth}") 364 if(NOT (${truth})) 365 # This choice isn't valid due to unmet conditions so we must check if we have 366 # currently selected this choice. If so trigger the force_default 367 if("${${optionname}}" STREQUAL "${option_value}") 368 set(force_default "FORCE") 369 endif() 370 set(valid OFF) 371 endif() 372 endforeach() 373 if(valid) 374 # Is a valid option, add to the strings list 375 list(APPEND strings "${option_value}") 376 if(first) 377 set(first OFF) 378 set(first_cache "${option_cache}") 379 set(first_config "${option_config}") 380 # Use the first valid option we find as the default. This default is will be 381 # used if there is no current value, or for some reason we need to override 382 # the current value (see force_default above) 383 set(default "${option_value}") 384 endif() 385 # Check if this option is the one that is currently set 386 if("${${optionname}}" STREQUAL "${option_value}") 387 set(${option_cache} ON CACHE INTERNAL "") 388 list(APPEND local_config_string 389 "#define CONFIG_${option_config} 1" 390 ) 391 set(found_current ON) 392 else() 393 set(${option_cache} OFF CACHE INTERNAL "") 394 endif() 395 else() 396 # Remove this config as it's not valid 397 unset(${option_cache} CACHE) 398 endif() 399 endforeach() 400 if(NOT found_current) 401 # Currently selected option wasn't found so reset to a default that we know is valid 402 set(force_default "FORCE") 403 endif() 404 if(first) 405 # None of the choices were valid. Remove this option so its not visible 406 unset(${optionname} CACHE) 407 else() 408 list(APPEND local_config_string 409 "#define CONFIG_${configname} @${optionname}@" 410 ) 411 set(configure_string "${local_config_string}" PARENT_SCOPE) 412 set(${optionname} "${default}" CACHE STRING "${doc}" ${force_default}) 413 set_property(CACHE ${optionname} PROPERTY STRINGS ${strings}) 414 if(NOT found_current) 415 # The option is actually enabled, but we didn't enable the correct 416 # choice earlier, since we didn't know we were going to revert to 417 # the default. So add the option setting here 418 set(${first_cache} ON CACHE INTERNAL "") 419 list(APPEND local_config_string 420 "#define CONFIG_${first_config} 1" 421 ) 422 endif() 423 endif() 424 # Save all possible options to an internal value. This is to allow enumerating the options elsewhere. 425 # We create a new variable because cmake doesn't support arbitrary properties on cache variables. 426 set(${optionname}_all_strings ${all_strings} CACHE INTERNAL "") 427 set(configure_string "${local_config_string}" PARENT_SCOPE) 428endfunction(config_choice) 429 430# Defines a target for a 'configuration' library, which generates a header based 431# upon current state of cache/variables and a provided template string. Additionally 432# the generated library gets added to a known global list of 'configuration' libraries 433# This list can be used if someone wants all the configurations 434# Whilst this function takes an explicit configure_template, generally this will always 435# be '${configure_string}' as that is the global variable automatically appended to 436# by the config_ helper macros and functions above 437# This generates a library that can be linked against with 438# target_link_library(<target> ${prefix}_Config) 439# Which will allow you to do #include <${prefix}/gen_config.h> 440function(add_config_library prefix configure_template) 441 set(config_dir "${CMAKE_CURRENT_BINARY_DIR}/gen_config") 442 set(config_file "${config_dir}/${prefix}/gen_config.h") 443 string(CONFIGURE "${configure_template}" config_header_contents) 444 # Turn the list of configurations into a valid C file of different lines 445 string(REPLACE ";" "\n" config_header_contents "${config_header_contents}") 446 file(GENERATE OUTPUT "${config_file}" CONTENT "${config_header_contents}") 447 add_custom_target(${prefix}_Gen DEPENDS "${config_file}") 448 add_library(${prefix}_Config INTERFACE) 449 target_include_directories(${prefix}_Config INTERFACE "${config_dir}") 450 add_dependencies(${prefix}_Config ${prefix}_Gen ${config_file}) 451 set_property(GLOBAL APPEND PROPERTY CONFIG_LIBRARIES "${prefix}") 452 # Set a property on the library that is a list of the files we generated. This 453 # allows custom build commands to easily get a file dependency list so they can 454 # 'depend' upon this target easily 455 set_property(TARGET ${prefix}_Gen APPEND PROPERTY GENERATED_FILES "${config_file}") 456endfunction(add_config_library) 457 458macro(get_generated_files output target) 459 get_property(${output} TARGET ${target} PROPERTY GENERATED_FILES) 460endmacro(get_generated_files) 461 462# This rule tries to emulate an 'autoconf' header. autoconf generated headers 463# were previously used as configuration, so this rule provides a way for previous 464# applications and libraries to build without modification. The config_list 465# is a list of 'prefix' values that have been pssed to add_config_library 466# This generates a library with ${targetname} that when linked against 467# will allow code to simply #include <autoconf.h> 468function(generate_autoconf targetname config_list) 469 set(link_list "") 470 set(gen_list "") 471 set(include_list 472 "#ifndef AUTOCONF_${targetname}_H" 473 "#define AUTOCONF_${targetname}_H" 474 ) 475 foreach(config IN LISTS config_list) 476 list(APPEND link_list "${config}_Config") 477 get_generated_files(gens ${config}_Gen) 478 list(APPEND gen_list ${gens}) 479 list(APPEND include_list "#include <${config}/gen_config.h>") 480 endforeach() 481 list(APPEND include_list "#endif") 482 set(config_dir "${CMAKE_CURRENT_BINARY_DIR}/autoconf") 483 set(config_file "${config_dir}/autoconf.h") 484 485 string(REPLACE ";" "\n" config_header_contents "#define AUTOCONF_INCLUDED;${include_list}") 486 file(GENERATE OUTPUT "${config_file}" CONTENT "${config_header_contents}") 487 add_custom_target(${targetname}_Gen DEPENDS "${config_file}") 488 add_library(${targetname} INTERFACE) 489 target_link_libraries(${targetname} INTERFACE ${link_list}) 490 target_include_directories(${targetname} INTERFACE "${config_dir}") 491 add_dependencies(${targetname} ${targetname}_Gen ${config_file}) 492 # Set our GENERATED_FILES property to include the GENERATED_FILES of all of our input 493 # configurations, as well as the files we generated 494 set_property(TARGET ${targetname}_Gen APPEND PROPERTY GENERATED_FILES "${config_file}" ${gen_list}) 495endfunction(generate_autoconf) 496 497# Macro that allows for appending to a specified list only if all the supplied conditions are true 498macro(list_append_if list dep) 499 set(list_append_local_list ${${list}}) 500 set(list_append_valid ON) 501 foreach(truth IN ITEMS ${dep}) 502 string(REGEX REPLACE " +" ";" truth "${truth}") 503 if(NOT (${truth})) 504 set(list_append_valid OFF) 505 break() 506 endif() 507 endforeach() 508 if(list_append_valid) 509 list(APPEND list_append_local_list ${ARGN}) 510 endif() 511 set(${list} ${list_append_local_list} PARENT_SCOPE) 512endmacro(list_append_if) 513