blob: a3f511bfaa95572faef931c569340832c46aba66 [file] [log] [blame]
Soby Mathewb4c6df42022-11-09 11:13:29 +00001#
2# SPDX-License-Identifier: BSD-3-Clause
3# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4#
5
6#[=======================================================================[.rst:
7ArmPreprocessSource
8-------------------
9
10.. default-domain:: cmake
11
12.. command:: arm_preprocess_source
13
14Preprocess a file with the C preprocessor.
15
16.. code:: cmake
17
18 arm_preprocess_source(<target> <source>)
19
20Creates a target ``<target>`` which preprocesses an input file ``<source>``. The
21target created by this macro can then be used as a dependency for a higher-level
22target. The output file can be retrieved from the :prop_tgt:`LOCATION_<CONFIG>
23<prop_tgt:LOCATION_<CONFIG>>` target property.
24
25The following target properties are passed to the preprocessor:
26
27- :prop_tgt:`COMPILE_OPTIONS <prop_tgt:COMPILE_OPTIONS>`
28- :prop_tgt:`COMPILE_DEFINITIONS <prop_tgt:COMPILE_DEFINITIONS>`
29- :prop_tgt:`INCLUDE_DIRECTORIES <prop_tgt:INCLUDE_DIRECTORIES>`
30
31.. note::
32
33 The created target automatically inherits :variable:`CMAKE_C_FLAGS
34 <variable:CMAKE_<LANG>_FLAGS>` and :variable:`CMAKE_C_FLAGS_<CONFIG>
35 <variable:CMAKE_<LANG>_FLAGS_<CONFIG>>`.
36
37For example, if you wish to preprocess a file while providing the preprocessor
38definition ``-DFOO=BAR``, you would use:
39
40.. code:: cmake
41
42 arm_preprocess_source(foo "bar.ld.S")
43
44 set_target_properties(foo PROPERTIES
45 COMPILE_DEFINITIONS "FOO=BAR")
46
47 get_target_property(location foo LOCATION_${CMAKE_BUILD_TYPE})
48
49 message(STATUS "My preprocessed file is here: ${location}")
50
51For processing linker scripts specifically, see the
52:command:`arm_target_linker_script` command instead.
53#]=======================================================================]
54
55include_guard()
56
57macro(arm_preprocess_source target source)
58 #
59 # We start by trying to get the source file relative to the current source
60 # directory, which means that we can mirror where it goes in the binary
61 # directory. This just replicates what CMake does with source files it's
62 # aware of.
63 #
64
65 get_filename_component(preprocessed_source "${source}.i" ABSOLUTE)
66 file(RELATIVE_PATH preprocessed_source "${CMAKE_CURRENT_SOURCE_DIR}"
67 "${preprocessed_source}")
68
69 #
70 # If we're using a multi-config generator, we need to place the output file
71 # into the correct configuration directory.
72 #
73
74 get_property(multi_config GLOBAL
75 PROPERTY "GENERATOR_IS_MULTI_CONFIG")
76
77 if(multi_config)
78 string(PREPEND preprocessed_source "$<CONFIG>/")
79 endif()
80
81 #
82 # Make the source path absolute so that we don't need to care which
83 # working directory the preprocessor uses.
84 #
85
86 string(PREPEND preprocessed_source "${CMAKE_CURRENT_BINARY_DIR}/")
87
88 #
89 # Create a single target for all configurations. It's differentiated based
90 # on the generator expression in the dependency.
91 #
92
93 add_custom_target(${target}
94 DEPENDS "${preprocessed_source}")
95
96 #
97 # Now that we've got that out of the way, we need to generate the
98 # preprocessing command for each of the enabled configurations. Multi-config
99 # generators will use `CMAKE_CONFIGURATION_TYPES`, whereas single-config
100 # generators will use `CMAKE_BUILD_TYPE`. Only one is ever non-empty.
101 #
102
103 foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES CMAKE_BUILD_TYPE)
104 #
105 # CMake provides the `CMAKE_C_CREATE_PREPROCESSED_SOURCE` variable,
106 # which describes the command line required to preprocess a C source
107 # file. This variable is in a format similar to this:
108 #
109 # <CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> >
110 # <PREPROCESSED_SOURCE>
111 #
112 # We do some processing on this variable to convert these
113 # bracket-surrounded names to variables we set. For example, `<DEFINES>`
114 # is replaced with `${DEFINES}`. We then need to do some string
115 # replacement magic to expand that string out to the value of the actual
116 # variable.
117 #
118 # The values for some of these, namely include directories, definitions
119 # and other compiler options, come from properties set on the target by
120 # the caller. These are typically taken from the target that this
121 # preprocessed source file belongs to.
122 #
123
124 set(command ${CMAKE_C_CREATE_PREPROCESSED_SOURCE})
125 string(REPLACE " " ";" command ${command})
126
127 get_filename_component(SOURCE "${source}" ABSOLUTE)
128 string(REPLACE "$<CONFIG>" "${config}" PREPROCESSED_SOURCE
129 "${preprocessed_source}")
130
131 separate_arguments(FLAGS UNIX_COMMAND
132 "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${config}} -P -x c")
133
134 if(CMAKE_C_COMPILER_TARGET)
135 if(CMAKE_C_COMPILER_ID MATCHES "Clang")
136 list(APPEND FLAGS "-target" "${CMAKE_C_COMPILER_TARGET}")
137 endif()
138 endif()
139
140 unset(DEFINES)
141 unset(INCLUDES)
142
143 list(APPEND FLAGS "$<TARGET_PROPERTY:${target},COMPILE_OPTIONS>")
144 list(APPEND DEFINES "$<TARGET_PROPERTY:${target},COMPILE_DEFINITIONS>")
145 list(APPEND INCLUDES "$<TARGET_PROPERTY:${target},INCLUDE_DIRECTORIES>")
146
147 set(DEFINES "$<$<BOOL:${DEFINES}>:-D$<JOIN:${DEFINES},$<SEMICOLON>-D>>")
148 set(INCLUDES "$<$<BOOL:${INCLUDES}>:-I$<JOIN:${INCLUDES},$<SEMICOLON>-I>>")
149
150 string(REGEX REPLACE "<([[A-Z_]+)>" "\${\\1}" command "${command}")
151 string(REGEX MATCH "\\\${[^}]*}" match "${command}")
152
153 while(match)
154 string(REGEX REPLACE "\\\${(.*)}" "\\1" variable "${match}")
155 string(REPLACE "\${${variable}}" "${${variable}}" command "${command}")
156 string(REGEX MATCH "\\\${[^}]*}" match "${command}")
157 endwhile()
158
159 add_custom_command(
160 OUTPUT "${PREPROCESSED_SOURCE}"
161 MAIN_DEPENDENCY ${source}
162 COMMAND "${command}"
163 VERBATIM COMMAND_EXPAND_LISTS)
164
165 set_target_properties(${target} PROPERTIES
166 LOCATION_${config} "${PREPROCESSED_SOURCE}")
167 endforeach()
168endmacro()