blob: 6a955c8ba44c117b13e51dbbb63f7e367f87fde2 [file] [log] [blame]
Chris Kayd1808732021-07-19 17:23:28 +01001#[=======================================================================[.rst:
2ArmTargetLinkerScript
3---------------------
4
5.. default-domain:: cmake
6
7.. command:: arm_target_linker_script
8
9Set the linker script for a target.
10
11.. code-block:: cmake
12
13 arm_target_linker_script(
14 TARGET <target> SCRIPT <script>
15 [PREPROCESSOR SUBTARGET <subtarget> LANGUAGE <language>])
16
17Sets the linker script for the target ``<target>`` to the script ``<script>``,
18which is optionally first preprocessed with the preprocessor for the language
19given by ``<language>``, which creates the target ``<subtarget>``.
20
21When preprocessing, the following properties are automatically inherited from
22the target ``<target>`` and may also be set on the sub-target ``<subtarget>`` in
23order to pass additional information to the preprocessor:
24
25 - :prop_tgt:`COMPILE_OPTIONS <prop_tgt:COMPILE_OPTIONS>`
26 - :prop_tgt:`COMPILE_DEFINITIONS <prop_tgt:COMPILE_DEFINITIONS>`
27 - :prop_tgt:`INCLUDE_DIRECTORIES <prop_tgt:INCLUDE_DIRECTORIES>`
28
29Additionally, the linker script automatically inherits flags from both
30:variable:`CMAKE_<LANG>_FLAGS <variable:CMAKE_<LANG>_FLAGS>` and
31:variable:`CMAKE_<LANG>_FLAGS_<CONFIG> <variable:CMAKE_<LANG>_FLAGS_<CONFIG>>`.
32
33.. code-block:: cmake
34 :caption: Usage example
35 :linenos:
36
37 add_executable(my-executable "main.c")
38
39 arm_target_linker_script(
40 TARGET my-executable SCRIPT "linker.ld"
41 PREPROCESSOR TARGET my-executable-lds LANGUAGE C)
42
43 set_property(
44 TARGET my-executable-lds APPEND
45 PROPERTY COMPILE_DEFINITIONS "LINKER=1")
46#]=======================================================================]
47
48include_guard()
49
50include(ArmAssert)
51include(ArmPreprocessSource)
52
53function(arm_target_linker_script)
54 set(options "")
55 set(single-args "TARGET;SCRIPT")
56 set(multi-args "PREPROCESSOR")
57
58 cmake_parse_arguments(PARSE_ARGV 0 ARG
59 "${options}" "${single-args}" "${multi-args}")
60
61 arm_assert(
62 CONDITION DEFINED ARG_TARGET
63 MESSAGE "No value was given for the `TARGET` argument.")
64
65 arm_assert(
66 CONDITION DEFINED ARG_SCRIPT
67 MESSAGE "No value was given for the `SCRIPT` argument.")
68
69 cmake_path(ABSOLUTE_PATH ARG_SCRIPT NORMALIZE
70 BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
71
72 if(DEFINED ARG_PREPROCESSOR)
73 _arm_target_linker_script_preprocess(${ARG_PREPROCESSOR}
74 TARGET "${ARG_TARGET}" SCRIPT "${ARG_SCRIPT}"
75 OUTPUT ARG_SCRIPT)
76 endif()
77
78 get_target_property(language "${ARG_TARGET}" LINKER_LANGUAGE)
79
80 if(CMAKE_${language}_COMPILER_ID STREQUAL "ARMClang")
81 target_link_options("${ARG_TARGET}"
82 PUBLIC "LINKER:--scatter" "LINKER:${ARG_SCRIPT}")
83 else()
84 target_link_options("${ARG_TARGET}"
85 PUBLIC "LINKER:-T" "LINKER:${ARG_SCRIPT}")
86 endif()
87endfunction()
88
89function(_arm_target_linker_script_preprocess)
90 set(options "")
91 set(single-args "TARGET;SCRIPT;OUTPUT;SUBTARGET;LANGUAGE")
92
93 cmake_parse_arguments(PARSE_ARGV 0 ARG
94 "${options}" "${single-args}" "${multi-args}")
95
96 arm_assert(
97 CONDITION (DEFINED ARG_SUBTARGET) AND
98 (DEFINED ARG_LANGUAGE)
99 MESSAGE "The preprocessor `SUBTARGET` and `LANGUAGE` arguments must "
100 "both be provided when preprocessing.")
101
102 _arm_target_linker_script_preprocess_path(
103 TARGET "${ARG_SUBTARGET}" SCRIPT "${ARG_SCRIPT}"
104 OUTPUT path)
105
106 arm_preprocess_source(
107 TARGET "${ARG_SUBTARGET}" LANGUAGE "${ARG_LANGUAGE}"
108 SOURCE "${ARG_SCRIPT}" OUTPUT "${path}"
109 INHIBIT_LINEMARKERS)
110
111 set(compile-options "$<TARGET_PROPERTY:${ARG_TARGET},COMPILE_OPTIONS>")
112 set(compile-definitions "$<TARGET_PROPERTY:${ARG_TARGET},COMPILE_DEFINITIONS>")
113 set(include-directories "$<TARGET_PROPERTY:${ARG_TARGET},INCLUDE_DIRECTORIES>")
114
115 foreach(config IN LISTS CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES)
116 string(TOUPPER "${config}" config)
117
118 separate_arguments(config-compile-options
119 NATIVE_COMMAND "${CMAKE_${preprocessor-language}_FLAGS_${config}}")
120 list(PREPEND compile-options
121 "$<$<CONFIG:${config}>:${config-compile-options}>")
122 endforeach()
123
124 separate_arguments(global-compile-options
125 NATIVE_COMMAND "${CMAKE_${preprocessor-language}_FLAGS}")
126 list(PREPEND compile-options "${global-compile-options}")
127
128 set_target_properties("${ARG_SUBTARGET}"
129 PROPERTIES COMPILE_OPTIONS "${compile-options}"
130 COMPILE_DEFINITIONS "${compile-definitions}"
131 INCLUDE_DIRECTORIES "${include-directories}")
132
133 add_dependencies(${ARG_TARGET} "${ARG_SUBTARGET}")
134
135 set(${ARG_OUTPUT} "${path}" PARENT_SCOPE)
136endfunction()
137
138function(_arm_target_linker_script_preprocess_path result target script)
139 set(options "")
140 set(single-args "OUTPUT;TARGET;SCRIPT")
141 set(multi-args "")
142
143 cmake_parse_arguments(PARSE_ARGV 0 ARG
144 "${options}" "${single-args}" "${multi-args}")
145
146 #
147 # Figure out where we're going to place our preprocessed file. This depends
148 # on whether we're using a multi-config generator or not:
149 #
150 # - Single-config: CMakeFiles/${subtarget}.dir
151 # - Multi-config: CMakeFiles/${subtarget}.dir/$<CONFIG>
152 #
153
154 get_property(multi-config GLOBAL
155 PROPERTY GENERATOR_IS_MULTI_CONFIG)
156
157 set(path "${CMAKE_CURRENT_BINARY_DIR}")
158
159 cmake_path(APPEND_STRING path "${CMAKE_FILES_DIRECTORY}")
160 cmake_path(APPEND path "${ARG_TARGET}.dir")
161
162 if(multi-config)
163 cmake_path(APPEND path "$<CONFIG>")
164 endif()
165
166 #
167 # Try to mirror the behaviour of CMake when deciding the relativized path
168 # for the preprocessed file. If the source file is a child of the current
169 # source directory we use its path relative to that, but otherwise we take
170 # its relative path part. As an example:
171 #
172 # - ${CMAKE_CURRENT_SOURCE_DIR}/foo/bar.c -> foo/bar.c.i
173 # - C:/foo/bar.c -> foo/bar.c.i
174 #
175
176 cmake_path(IS_PREFIX CMAKE_CURRENT_SOURCE_DIR "${ARG_SCRIPT}" is-child)
177
178 if(is-child)
179 cmake_path(RELATIVE_PATH ARG_SCRIPT OUTPUT_VARIABLE relative-script)
180 else()
181 cmake_path(GET ARG_SCRIPT RELATIVE_PART relative-script)
182 endif()
183
184 cmake_path(APPEND path "${relative-script}.i")
185
186 set(${ARG_OUTPUT} "${path}" PARENT_SCOPE)
187endfunction()