@@ -74,23 +74,48 @@ endfunction()
7474
7575# Internal helper function to provide the correct initializer for a list in the
7676# JSON content.
77- function (internal_yaml_list_initializer var)
78- set (${var} "[]" PARENT_SCOPE)
77+ function (internal_yaml_list_initializer var genex)
78+ if (genex)
79+ set (${var} "\" @YAML-LIST@\" " PARENT_SCOPE)
80+ else ()
81+ set (${var} "[]" PARENT_SCOPE)
82+ endif ()
7983endfunction ()
8084
8185# Internal helper function to append items to a list in the JSON content.
8286# Unassigned arguments are the values to be appended.
83- function (internal_yaml_list_append var key)
87+ function (internal_yaml_list_append var genex key)
8488 set (json_content "${${var} }" )
8589 string (JSON subjson GET "${json_content} " ${key} )
86- string (JSON index LENGTH "${subjson} " )
87- list (LENGTH ARGN length )
88- math (EXPR stop "${index} + ${length} - 1" )
89- if (NOT length EQUAL 0)
90- foreach (i RANGE ${index} ${stop} )
91- list (POP_FRONT ARGN value )
92- string (JSON json_content SET "${json_content} " ${key} ${i} "\" ${value} \" " )
93- endforeach ()
90+ if (genex)
91+ # new lists are stored in CMake string format, but those imported via
92+ # yaml_load() are proper JSON arrays. When an append is requested, those
93+ # must be converted back to a CMake list.
94+ string (JSON type TYPE "${json_content} " ${key} )
95+ if (type STREQUAL ARRAY)
96+ string (JSON arraylength LENGTH "${subjson} " )
97+ internal_yaml_list_initializer(subjson TRUE )
98+ if (${arraylength} GREATER 0)
99+ math (EXPR arraystop "${arraylength} - 1" )
100+ foreach (i RANGE 0 ${arraystop} )
101+ string (JSON item GET "${json_content} " ${key} ${i} )
102+ list (APPEND subjson ${item} )
103+ endforeach ()
104+ endif ()
105+ endif ()
106+ list (APPEND subjson ${ARGN} )
107+ string (JSON json_content SET "${json_content} " ${key} "\" ${subjson} \" " )
108+ else ()
109+ # lists are stored as JSON arrays
110+ string (JSON index LENGTH "${subjson} " )
111+ list (LENGTH ARGN length )
112+ math (EXPR stop "${index} + ${length} - 1" )
113+ if (NOT length EQUAL 0)
114+ foreach (i RANGE ${index} ${stop} )
115+ list (POP_FRONT ARGN value )
116+ string (JSON json_content SET "${json_content} " ${key} ${i} "\" ${value} \" " )
117+ endforeach ()
118+ endif ()
94119 endif ()
95120 set (${var} "${json_content} " PARENT_SCOPE)
96121endfunction ()
@@ -148,6 +173,7 @@ function(yaml_create)
148173 if (DEFINED ARG_YAML_FILE)
149174 zephyr_set(FILE ${ARG_YAML_FILE} SCOPE ${ARG_YAML_NAME} )
150175 endif ()
176+ zephyr_set(GENEX FALSE SCOPE ${ARG_YAML_NAME} )
151177 zephyr_set(JSON "{}" SCOPE ${ARG_YAML_NAME} )
152178endfunction ()
153179
@@ -172,7 +198,7 @@ function(yaml_load)
172198 zephyr_set(FILE ${ARG_YAML_FILE} SCOPE ${ARG_YAML_NAME} )
173199
174200 execute_process (COMMAND ${PYTHON_EXECUTABLE} -c
175- "import json; import yaml; print(json.dumps(yaml.safe_load(open('${ARG_YAML_FILE} '))))"
201+ "import json; import yaml; print(json.dumps(yaml.safe_load(open('${ARG_YAML_FILE} ')) or {} ))"
176202 OUTPUT_VARIABLE json_load_out
177203 ERROR_VARIABLE json_load_error
178204 RESULT_VARIABLE json_load_result
@@ -184,6 +210,7 @@ function(yaml_load)
184210 )
185211 endif ()
186212
213+ zephyr_set(GENEX FALSE SCOPE ${ARG_YAML_NAME} )
187214 zephyr_set(JSON "${json_load_out} " SCOPE ${ARG_YAML_NAME} )
188215endfunction ()
189216
@@ -264,8 +291,8 @@ function(yaml_length out_var)
264291endfunction ()
265292
266293# Usage:
267- # yaml_set(NAME <name> KEY <key>... VALUE <value>)
268- # yaml_set(NAME <name> KEY <key>... [APPEND] LIST <value>...)
294+ # yaml_set(NAME <name> KEY <key>... [GENEX] VALUE <value>)
295+ # yaml_set(NAME <name> KEY <key>... [APPEND] [GENEX] LIST <value>...)
269296#
270297# Set a value or a list of values to given key.
271298#
@@ -275,18 +302,22 @@ endfunction()
275302# NAME <name> : Name of the YAML context.
276303# KEY <key>... : Name of key.
277304# VALUE <value>: New value for the key.
278- # List <values>: New list of values for the key.
305+ # LIST <values>: New list of values for the key.
279306# APPEND : Append the list of values to the list of values for the key.
307+ # GENEX : The value(s) contain generator expressions. When using this
308+ # option, also see the notes in the yaml_save() function.
280309#
281310function (yaml_set)
282- cmake_parse_arguments (ARG_YAML "APPEND" "NAME;VALUE" "KEY;LIST" ${ARGN} )
311+ cmake_parse_arguments (ARG_YAML "APPEND;GENEX " "NAME;VALUE" "KEY;LIST" ${ARGN} )
283312
284313 zephyr_check_arguments_required_all(${CMAKE_CURRENT_FUNCTION} ARG_YAML NAME KEY)
285314 zephyr_check_arguments_required_allow_empty(${CMAKE_CURRENT_FUNCTION} ARG_YAML VALUE LIST)
286315 zephyr_check_arguments_exclusive(${CMAKE_CURRENT_FUNCTION} ARG_YAML VALUE LIST)
287316 internal_yaml_context_required(NAME ${ARG_YAML_NAME} )
288317
289- zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON)
318+ if (ARG_YAML_GENEX)
319+ zephyr_set(GENEX TRUE SCOPE ${ARG_YAML_NAME} )
320+ endif ()
290321
291322 if (DEFINED ARG_YAML_LIST
292323 OR LIST IN_LIST ARG_YAML_KEYWORDS_MISSING_VALUES)
@@ -317,7 +348,7 @@ function(yaml_set)
317348 list (REVERSE yaml_key_undefined)
318349 if (NOT "${yaml_key_undefined} " STREQUAL "" )
319350 if (key_is_list)
320- internal_yaml_list_initializer(json_string)
351+ internal_yaml_list_initializer(json_string ${genex} )
321352 else ()
322353 set (json_string "\"\" " )
323354 endif ()
@@ -332,11 +363,11 @@ function(yaml_set)
332363
333364 if (key_is_list)
334365 if (NOT ARG_YAML_APPEND)
335- internal_yaml_list_initializer(json_string)
366+ internal_yaml_list_initializer(json_string ${genex} )
336367 string (JSON json_content SET "${json_content} " ${ARG_YAML_KEY} "${json_string} " )
337368 endif ()
338369
339- internal_yaml_list_append(json_content "${ARG_YAML_KEY} " ${ARG_YAML_LIST} )
370+ internal_yaml_list_append(json_content ${genex} "${ARG_YAML_KEY} " ${ARG_YAML_LIST} )
340371 else ()
341372 string (JSON json_content SET "${json_content} " ${ARG_YAML_KEY} "\" ${ARG_YAML_VALUE} \" " )
342373 endif ()
@@ -372,8 +403,12 @@ endfunction()
372403# Usage:
373404# yaml_save(NAME <name> [FILE <file>])
374405#
375- # Write the YAML context <name> to the file which were given with the earlier
376- # 'yaml_load()' or 'yaml_create()' call.
406+ # Write the YAML context <name> to <file>, or the one given with the earlier
407+ # 'yaml_load()' or 'yaml_create()' call. This will be performed immediately if
408+ # the context does not use generator expressions; otherwise, keys that include
409+ # a generator expression will initially be written as comments, and the full
410+ # contents will be available at build time. Build steps that depend on the file
411+ # being complete must depend on the '<name>_yaml_saved' target.
377412#
378413# NAME <name>: Name of the YAML context
379414# FILE <file>: Path to file to write the context.
@@ -391,22 +426,67 @@ function(yaml_save)
391426 if (NOT yaml_file)
392427 zephyr_check_arguments_required(${CMAKE_CURRENT_FUNCTION} ARG_YAML FILE)
393428 endif ()
394-
395- zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON)
396- to_yaml("${json_content} " 0 yaml_out)
397-
398429 if (DEFINED ARG_YAML_FILE)
399430 set (yaml_file ${ARG_YAML_FILE} )
400431 else ()
401432 zephyr_get_scoped(yaml_file ${ARG_YAML_NAME} FILE)
402433 endif ()
434+
435+ zephyr_get_scoped(genex ${ARG_YAML_NAME} GENEX)
436+ zephyr_get_scoped(json_content ${ARG_YAML_NAME} JSON)
437+ to_yaml("${json_content} " 0 yaml_out ${genex} )
438+
403439 if (EXISTS ${yaml_file} )
404440 FILE (RENAME ${yaml_file} ${yaml_file} .bak)
405441 endif ()
406442 FILE (WRITE ${yaml_file} "${yaml_out} " )
443+
444+ set (save_target ${ARG_YAML_NAME} _yaml_saved)
445+ if (NOT TARGET ${save_target} )
446+ # Create a target for the completion of the YAML save operation.
447+ # This will be a dummy unless genexes are used.
448+ add_custom_target (${save_target} ALL DEPENDS ${yaml_file} )
449+ set_target_properties (${save_target} PROPERTIES
450+ genex_save_count 0
451+ temp_files ""
452+ )
453+ endif ()
454+
455+ if (genex)
456+ get_property (genex_save_count TARGET ${save_target} PROPERTY genex_save_count)
457+ if (${genex_save_count} EQUAL 0)
458+ # First yaml_save() for this context with genexes enabled
459+ add_custom_command (
460+ OUTPUT ${yaml_file}
461+ DEPENDS $<TARGET_PROPERTY:${save_target} ,json_file>
462+ COMMAND ${CMAKE_COMMAND}
463+ -DJSON_FILE="$<TARGET_PROPERTY:${save_target} ,json_file>"
464+ -DYAML_FILE="${yaml_file} "
465+ -DTEMP_FILES="$<TARGET_PROPERTY:${save_target} ,temp_files>"
466+ -P ${ZEPHYR_BASE} /cmake/yaml-filter .cmake
467+ )
468+ endif ()
469+
470+ math (EXPR genex_save_count "${genex_save_count} + 1" )
471+ set_property (TARGET ${save_target} PROPERTY genex_save_count ${genex_save_count} )
472+
473+ cmake_path(SET yaml_path "${yaml_file} " )
474+ cmake_path(GET yaml_path STEM yaml_file_no_ext)
475+ set (json_file ${yaml_file_no_ext} _${genex_save_count} .json)
476+ set_property (TARGET ${save_target} PROPERTY json_file ${json_file} )
477+
478+ # comment this to keep the temporary JSON files
479+ get_property (temp_files TARGET ${save_target} PROPERTY temp_files)
480+ list (APPEND temp_files ${json_file} )
481+ set_property (TARGET ${save_target} PROPERTY temp_files ${temp_files} )
482+
483+ FILE (GENERATE OUTPUT ${json_file}
484+ CONTENT "${json_content} "
485+ )
486+ endif ()
407487endfunction ()
408488
409- function (to_yaml json level yaml)
489+ function (to_yaml json level yaml genex )
410490 if (level GREATER 0)
411491 math (EXPR level_dec "${level} - 1" )
412492 set (indent_${level} "${indent_${level_dec} } " )
@@ -425,10 +505,12 @@ function(to_yaml json level yaml)
425505 string (JSON type TYPE "${json} " ${member} )
426506 string (JSON subjson GET "${json} " ${member} )
427507 if (type STREQUAL OBJECT)
508+ # JSON object -> YAML dictionary
428509 set (${yaml} "${${yaml} }${indent_${level} }${member} :\n " )
429510 math (EXPR sublevel "${level} + 1" )
430- to_yaml("${subjson} " ${sublevel} ${yaml} )
511+ to_yaml("${subjson} " ${sublevel} ${yaml} ${genex} )
431512 elseif (type STREQUAL ARRAY)
513+ # JSON array -> YAML list
432514 set (${yaml} "${${yaml} }${indent_${level} }${member} :" )
433515 string (JSON arraylength LENGTH "${subjson} " )
434516 if (${arraylength} LESS 1)
@@ -441,7 +523,33 @@ function(to_yaml json level yaml)
441523 set (${yaml} "${${yaml} }${indent_${level} } - ${item} \n " )
442524 endforeach ()
443525 endif ()
526+ elseif (type STREQUAL STRING )
527+ # JSON string maps to multiple YAML types:
528+ # - with unexpanded generator expressions: save as YAML comment
529+ # - if it matches the special prefix: convert to YAML list
530+ # - otherwise: save as YAML scalar
531+ if (subjson MATCHES "\\ $<.*>" AND ${genex} )
532+ # Yet unexpanded generator expression: save as comment
533+ string (SUBSTRING ${indent_${level} } 1 -1 short_indent)
534+ set (${yaml} "${${yaml} }#${short_indent}${member} : ${subjson} \n " )
535+ elseif (subjson MATCHES "^@YAML-LIST@" )
536+ # List-as-string: convert to list
537+ set (${yaml} "${${yaml} }${indent_${level} }${member} :" )
538+ list (POP_FRONT subjson)
539+ if (subjson STREQUAL "" )
540+ set (${yaml} "${${yaml} } []\n " )
541+ else ()
542+ set (${yaml} "${${yaml} }\n " )
543+ foreach (item ${subjson} )
544+ set (${yaml} "${${yaml} }${indent_${level} } - ${item} \n " )
545+ endforeach ()
546+ endif ()
547+ else ()
548+ # Raw strings: save as is
549+ set (${yaml} "${${yaml} }${indent_${level} }${member} : ${subjson} \n " )
550+ endif ()
444551 else ()
552+ # Other JSON data type -> YAML scalar, as-is
445553 set (${yaml} "${${yaml} }${indent_${level} }${member} : ${subjson} \n " )
446554 endif ()
447555 endforeach ()
0 commit comments