1#
2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6
7cmake_minimum_required(VERSION 3.7.2)
8include_guard(GLOBAL)
9
10# This takes a camkes produced dependency file (this means we can assume one dependency
11# per line) and produces a cmake list of dependencies
12function(MakefileDepsToList mdfile output_variable)
13    file(READ "${mdfile}" raw_file)
14    # First remove the target of the dependency list
15    string(
16        REGEX
17        REPLACE
18            "^[^:]*: \\\\\r?\n"
19            ""
20            string_deps
21            "${raw_file}"
22    )
23    # Now turn the list of dependencies into a cmake list. We have assumed
24    # that this makefile dep file was generated by camkes and so it has one
25    # item per line
26    string(
27        REGEX
28        REPLACE
29            "\\\\\r?\n"
30            ";"
31            deps
32            "${string_deps}"
33    )
34    # Strip the space from each dep
35    foreach(dep IN LISTS deps)
36        # Strip extra spacing
37        string(STRIP "${dep}" dep)
38        list(APPEND final_deps "${dep}")
39    endforeach()
40    # Write the output to the parent
41    set("${output_variable}" "${final_deps}" PARENT_SCOPE)
42endfunction(MakefileDepsToList)
43
44# Wraps a call to execute_process with checks that only rerun execute_process
45# if the command or input files are changed. It also uses a depfile to track
46# any files that the command touches internally. This function currently won't
47# work without a depfile.
48macro(execute_process_with_stale_check invoc_file deps_file outfile extra_dependencies)
49    # We need to determine if we actually need to regenerate. We start by assuming that we do
50    set(regen TRUE)
51    if((EXISTS "${invoc_file}") AND (EXISTS "${deps_file}") AND (EXISTS "${outfile}"))
52        file(READ "${invoc_file}" old_contents)
53        if("${old_contents}" STREQUAL "${ARGN}")
54            MakefileDepsToList("${deps_file}" deps)
55            # At this point assume we do not need to regenerate, unless we found a newer file
56            set(regen FALSE)
57            foreach(dep IN LISTS deps extra_dependencies)
58                if("${dep}" IS_NEWER_THAN "${outfile}")
59                    set(regen TRUE)
60                    break()
61                endif()
62            endforeach()
63        endif()
64    endif()
65    if(regen)
66        message(STATUS "${outfile} is out of date. Regenerating...")
67        execute_process(${ARGN})
68        file(WRITE "${invoc_file}" "${ARGN}")
69    endif()
70    # Add dependencies
71    MakefileDepsToList("${deps_file}" deps)
72    set_property(
73        DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
74        APPEND
75        PROPERTY CMAKE_CONFIGURE_DEPENDS "${deps};${extra_dependencies}"
76    )
77
78endmacro(execute_process_with_stale_check)
79
80# For custom commands that invoke other build systems, we can create a depfile
81# based on a find traversal of the directory.  This saves using CMake to glob it
82# which is much slower
83macro(create_depfile_by_find ret outfile depfile dir)
84    file(RELATIVE_PATH path ${CMAKE_BINARY_DIR} ${outfile})
85    list(
86        APPEND
87            ${ret}
88            COMMAND
89            printf
90            "${path}: "
91            >
92            ${depfile}
93    )
94    list(
95        APPEND ${ret} COMMAND
96        find
97            -L
98            ${dir}
99            -type
100            f
101            -printf
102            "%p "
103            >>
104            ${depfile}
105    )
106    list(APPEND ${ret} DEPFILE ${depfile})
107endmacro()
108