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