-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGems.cmake
More file actions
319 lines (274 loc) · 16.6 KB
/
Copy pathGems.cmake
File metadata and controls
319 lines (274 loc) · 16.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#
# Copyright (c) Contributors to the Open 3D Engine Project.
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
#
# SPDX-License-Identifier: Apache-2.0 OR MIT
#
#
# This file contains utility wrappers for dealing with the Gems system.
define_property(TARGET PROPERTY LY_PROJECT_NAME
BRIEF_DOCS "Name of the project, this target can use enabled gems from"
FULL_DOCS "If set, the when iterating over the enabled gems in ly_enabled_gems_delayed
only a project with that name can have it's enabled gem list added as a dependency to this target.
If the __NOPROJECT__ placeholder is associated with a list enabled gems, then it applies to this target regardless of this property value")
# ly_create_alias
# given an alias to create, and a list of one or more targets,
# this creates an alias that depends on all of the given targets.
function(ly_create_alias)
set(options)
set(oneValueArgs NAME NAMESPACE)
set(multiValueArgs TARGETS)
cmake_parse_arguments(ly_create_alias "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT ly_create_alias_NAME)
message(FATAL_ERROR "Provide the name of the alias to create using the NAME keyword")
endif()
if (NOT ly_create_alias_NAMESPACE)
message(FATAL_ERROR "Provide the namespace of the alias to create using the NAMESPACE keyword")
endif()
if(TARGET ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME})
message(FATAL_ERROR "Target already exists, cannot create an alias for it: ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}\n"
"Make sure the target wasn't copy and pasted here or elsewhere.")
endif()
# Using an ALIAS target inhibits finding the ALIAS target SOURCE_DIR, which is needed to determine the gem root of the alias target
# complex version - one alias to multiple targets or the alias is being made to a TARGET that doesn't exist yet.
# To actually achieve this we have to create an interface library with those dependencies,
# then we have to create an alias to that target.
# By convention we create one without a namespace then alias the namespaced one.
if(TARGET ${ly_create_alias_NAME})
message(FATAL_ERROR "Internal alias target already exists, cannot create an alias for it: ${ly_create_alias_NAME}\n"
"This could be a copy-paste error, where some part of the ly_create_alias call was changed but the other")
endif()
add_library(${ly_create_alias_NAME} INTERFACE IMPORTED GLOBAL)
set_target_properties(${ly_create_alias_NAME} PROPERTIES GEM_MODULE TRUE)
foreach(target_name ${ly_create_alias_TARGETS})
if(TARGET ${target_name})
ly_de_alias_target(${target_name} de_aliased_target_name)
if(NOT de_aliased_target_name)
message(FATAL_ERROR "Target not found in ly_create_alias call: ${target_name} - check your spelling of the target name")
endif()
else()
set(de_aliased_target_name ${target_name})
endif()
list(APPEND final_targets ${de_aliased_target_name})
endforeach()
# add_dependencies must be called with at least one dependent target
if(final_targets)
ly_parse_third_party_dependencies("${final_targets}")
ly_add_dependencies(${ly_create_alias_NAME} ${final_targets})
# copy over all the dependent target interface properties to the alias
o3de_copy_targets_usage_requirements(TARGET ${ly_create_alias_NAME} SOURCE_TARGETS ${final_targets})
endif()
# now add the final alias:
add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME})
# Store off the arguments used by ly_create_alias into a DIRECTORY property
# This will be used to re-create the calls in the generated CMakeLists.txt in the INSTALL step
# Replace the CMake list separator with a space to replicate the space separated arguments
# A single create_alias_args variable encodes two values. The alias NAME used to check if the target exists
# and the ly_create_alias arguments to replace this function call
unset(create_alias_args)
list(APPEND create_alias_args "${ly_create_alias_NAME},"
NAME ${ly_create_alias_NAME}
NAMESPACE ${ly_create_alias_NAMESPACE}
TARGETS ${ly_create_alias_TARGETS})
list(JOIN create_alias_args " " create_alias_args)
set_property(DIRECTORY APPEND PROPERTY LY_CREATE_ALIAS_ARGUMENTS "${create_alias_args}")
# Store the directory path in the GLOBAL property so that it can be accessed
# in the layout install logic. Skip if the directory has already been added
get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
endif()
endfunction()
# ly_set_gem_variant_to_load
# Associates a key, value entry of CMake target -> Gem variant
# \arg:TARGETS - list of Targets to associate with the Gem variant
# \arg:VARIANTS - Gem variant
function(ly_set_gem_variant_to_load)
set(options)
set(oneValueArgs)
set(multiValueArgs TARGETS VARIANTS)
cmake_parse_arguments(ly_set_gem_variant_to_load "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT ly_set_gem_variant_to_load_TARGETS)
message(FATAL_ERROR "You must provide at least 1 target to ${CMAKE_CURRENT_FUNCTION} using the TARGETS keyword")
endif()
# Store a list of targets
foreach(target_name ${ly_set_gem_variant_to_load_TARGETS})
# Append the target to the list of targets with variants if it has not been added
get_property(ly_targets_with_variants GLOBAL PROPERTY LY_TARGETS_WITH_GEM_VARIANTS)
if(NOT target_name IN_LIST ly_targets_with_variants)
set_property(GLOBAL APPEND PROPERTY LY_TARGETS_WITH_GEM_VARIANTS "${target_name}")
endif()
foreach(variant_name ${ly_set_gem_variant_to_load_VARIANTS})
get_property(target_gem_variants GLOBAL PROPERTY LY_GEM_VARIANTS_"${target_name}")
if(NOT variant_name IN_LIST target_gem_variants)
set_property(GLOBAL APPEND PROPERTY LY_GEM_VARIANTS_"${target_name}" "${variant_name}")
endif()
endforeach()
endforeach()
# Store of the arguments used to invoke this function in order to replicate the call in the generated CMakeLists.txt
# in the install layout
unset(set_gem_variant_args)
list(APPEND set_gem_variant_args
TARGETS ${ly_set_gem_variant_to_load_TARGETS}
VARIANTS ${ly_set_gem_variant_to_load_VARIANTS})
# Replace the list separator with space to have it be stored as a single property element
list(JOIN set_gem_variant_args " " set_gem_variant_args)
set_property(DIRECTORY APPEND PROPERTY LY_SET_GEM_VARIANT_TO_LOAD_ARGUMENTS "${set_gem_variant_args}")
endfunction()
# ly_enable_gems
# this function makes sure that the given gems, or gems listed in the variable ENABLED_GEMS
# in the GEM_FILE name, are set as runtime dependencies (and thus loaded) for the given targets
# in the context of the given project.
# note that it can't do this immediately, so it saves the data for later processing.
# Note: If you don't supply a project name, it will apply it across the board to all projects.
# this is useful in the case of "ly_enable_gems" being called for so called 'mandatory gems' inside the engine.
# if you specify a gem name with a namespace, it will be used, otherwise it will assume Gem::
function(ly_enable_gems)
set(options)
set(oneValueArgs PROJECT_NAME GEM_FILE)
set(multiValueArgs GEMS TARGETS VARIANTS)
cmake_parse_arguments(ly_enable_gems "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT ly_enable_gems_PROJECT_NAME)
message(VERBOSE "Note: ly_enable_gems called with no PROJECT_NAME name, applying to all projects: \n"
" - GEMS ${ly_enable_gems_GEMS} \n"
" - GEM_FILE ${ly_enable_gems_GEM_FILE}")
set(ly_enable_gems_PROJECT_NAME "__NOPROJECT__") # so that the token is not blank
endif()
# Backwards-Compatibility - Delegate any TARGETS and VARIANTS arguments to the ly_set_gem_variant_to_load
# command. That command is used to associate TARGETS with the list of Gem Variants they desire to use
if (ly_enable_gems_TARGETS AND ly_enable_gems_VARIANTS)
message(DEPRECATION "The TARGETS and VARIANTS arguments to \"${CMAKE_CURRENT_FUNCTION}\" is deprecated.\n"
"Please use the \"ly_set_gem_variant_to_load\" function directly to associate a Target with a Gem Variant.\n"
"This function will forward the TARGETS and VARIANTS arguments to \"ly_set_gem_variant_to_load\" for now,"
" but this functionality will be removed.")
ly_set_gem_variant_to_load(TARGETS ${ly_enable_gems_TARGETS} VARIANTS ${ly_enable_gems_VARIANTS})
endif()
if ((NOT ly_enable_gems_GEMS AND NOT ly_enable_gems_GEM_FILE) OR (ly_enable_gems_GEMS AND ly_enable_gems_GEM_FILE))
message(FATAL_ERROR "Provide exactly one of either GEM_FILE (filename) or GEMS (list of gems) keywords.")
endif()
if (ly_enable_gems_GEM_FILE)
set(store_temp ${ENABLED_GEMS})
include(${ly_enable_gems_GEM_FILE} RESULT_VARIABLE was_able_to_load_the_file)
if(NOT was_able_to_load_the_file)
message(FATAL_ERROR "could not load the GEM_FILE ${ly_enable_gems_GEM_FILE}")
endif()
if(NOT DEFINED ENABLED_GEMS)
message(WARNING "GEM_FILE ${ly_enable_gems_GEM_FILE} did not set the value of ENABLED_GEMS.\n"
"Gem Files should contain set(ENABLED_GEMS ... <list of gem names>)")
endif()
set(ly_enable_gems_GEMS ${ENABLED_GEMS})
set(ENABLED_GEMS ${store_temp}) # restore value of ENABLED_GEMS just in case...
endif()
# all the actual work has to be done later.
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS "${ly_enable_gems_PROJECT_NAME}")
define_property(GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${ly_enable_gems_PROJECT_NAME}"
BRIEF_DOCS "List of gem names to evaluate variants against" FULL_DOCS "Names of gems that will be paired with the variant name
to determine if it is valid target that should be added as an application dynamic load dependency")
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_ENABLE_GEMS_"${ly_enable_gems_PROJECT_NAME}" ${ly_enable_gems_GEMS})
# Store off the arguments used by ly_enable_gems into a DIRECTORY property
# This will be used to re-create the ly_enable_gems call in the generated CMakeLists.txt at the INSTALL step
# Replace the CMake list separator with a space to replicate the space separated TARGETS arguments
if(NOT ly_enable_gems_PROJECT_NAME STREQUAL "__NOPROJECT__")
set(replicated_project_name PROJECT_NAME ${ly_enable_gems_PROJECT_NAME})
endif()
# The GEM_FILE file is used to populate the GEMS argument via the ENABLED_GEMS variable in the file.
# Furthermore the GEM_FILE itself is not copied over to the install layout, so make its argument entry blank and use the list of GEMS
# stored in ly_enable_gems_GEMS
unset(enable_gems_args)
list(APPEND enable_gems_args
${replicated_project_name}
GEMS ${ly_enable_gems_GEMS})
list(JOIN enable_gems_args " " enable_gems_args)
set_property(DIRECTORY APPEND PROPERTY LY_ENABLE_GEMS_ARGUMENTS "${enable_gems_args}")
endfunction()
function(ly_add_gem_dependencies_to_project_variants)
set(options)
set(oneValueArgs PROJECT_NAME TARGET VARIANT)
set(multiValueArgs GEM_DEPENDENCIES)
cmake_parse_arguments(ly_add_gem_dependencies "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT ly_add_gem_dependencies_PROJECT_NAME)
message(FATAL_ERROR "Missing required PROJECT_NAME argument which is used to determine gem load prefix")
endif()
if (NOT ly_add_gem_dependencies_TARGET)
message(FATAL_ERROR "Missing required TARGET argument ")
endif()
if (NOT ly_add_gem_dependencies_VARIANT)
message(FATAL_ERROR "Missing required gem VARIANT argument needed to determine which gem variants to load for the target")
endif()
if(${ly_add_gem_dependencies_PROJECT_NAME} STREQUAL "__NOPROJECT__")
# special case, apply to all
unset(PREFIX_CLAUSE)
else()
set(PREFIX_CLAUSE "PREFIX;${ly_add_gem_dependencies_PROJECT_NAME}")
endif()
# apply the list of gem targets. Adding a gem really just means adding the appropriate dependency.
foreach(gem_name ${ly_add_gem_dependencies_GEM_DEPENDENCIES})
set(gem_target ${gem_name}.${ly_add_gem_dependencies_VARIANT})
# if the target exists, add it.
if (TARGET ${gem_target})
# Dealias actual target
ly_de_alias_target(${gem_target} dealiased_gem_target)
ly_add_target_dependencies(
${PREFIX_CLAUSE}
TARGETS ${ly_add_gem_dependencies_TARGET}
DEPENDENT_TARGETS ${dealiased_gem_target})
else()
message(VERBOSE "Gem \"${gem_name}\" does not expose a variant of ${ly_add_gem_dependencies_VARIANT}")
endif()
endforeach()
endfunction()
# call this before runtime dependencies are used to add any relevant targets
# saved by the above function
function(ly_enable_gems_delayed)
# Query the list of targets that are associated with a gem variant
get_property(targets_with_variants GLOBAL PROPERTY LY_TARGETS_WITH_GEM_VARIANTS)
# Query the projects that have made calls to ly_enable_gems
get_property(enable_gem_projects GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS)
foreach(target ${targets_with_variants})
if (NOT TARGET ${target})
message(FATAL_ERROR "ly_set_gem_variant_to_load specified TARGET '${target}' but no such target was found.")
endif()
get_property(target_gem_variants GLOBAL PROPERTY LY_GEM_VARIANTS_"${target}")
message(VERBOSE "Adding gem dependencies for \"${target}\" associated with the Gem variants of \"${target_gem_variants}\"")
# Lookup if the target is scoped to a project
# In that case the target can only use gem targets that is
# - not project specific: i.e "__NOPROJECT__"
# - or specific to the <LY_PROJECT_NAME> project
get_property(target_project_association TARGET ${target} PROPERTY LY_PROJECT_NAME)
foreach(project ${enable_gem_projects})
if (target_project_association AND
(NOT (project STREQUAL "__NOPROJECT__") AND NOT (project STREQUAL target_project_association)))
# Skip adding the gem dependencies to this target if it is associated with a project
# and the current project doesn't match
continue()
endif()
get_property(gem_dependencies GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${project}")
if (NOT gem_dependencies)
get_property(gem_dependencies_defined GLOBAL PROPERTY LY_DELAYED_ENABLE_GEMS_"${project}" DEFINED)
if (gem_dependencies_defined)
# special case, if the LY_DELAYED_ENABLE_GEMS_"${project_target_variant}" property is DEFINED
# but empty, add an entry to the LY_DELAYED_LOAD_DEPENDENCIES to have the
# cmake_dependencies.*.setreg file for the (project, target) tuple to be regenerated
# This is needed if the ENABLED_GEMS list for a project goes from >0 to 0. In this case
# the cmake_dependencies would have a stale list of gems to load unless it is regenerated
get_property(delayed_load_target_set GLOBAL PROPERTY LY_DELAYED_LOAD_"${project},${target}" SET)
if(NOT delayed_load_target_set)
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LOAD_DEPENDENCIES "${project},${target}")
set_property(GLOBAL APPEND PROPERTY LY_DELAYED_LOAD_"${project},${target}" "")
endif()
endif()
# Continue to the next iteration loop regardless as there are no gem dependencies
continue()
endif()
# Gather the Gem variants associated with this target and iterate over them to combine them with the enabled
# gems for the each project
foreach(variant ${target_gem_variants})
ly_add_gem_dependencies_to_project_variants(
PROJECT_NAME ${project}
TARGET ${target}
VARIANT ${variant}
GEM_DEPENDENCIES ${gem_dependencies})
endforeach()
endforeach()
endforeach()
endfunction()