diff --git a/src/_cmake b/src/_cmake index 4421598..84e5d37 100644 --- a/src/_cmake +++ b/src/_cmake @@ -172,6 +172,61 @@ _cmake_json_field() { fi } +# ---------------------- +# _cmake_json_preset_block +# +# Slice out one preset object (by its "name") from a presets-array section +# (the text following the "buildPresets"/"configurePresets" key). Returns the +# object's text from its "name" entry up to the start of the next preset's +# "name" entry. +# ---------------------- +(( $+functions[_cmake_json_preset_block] )) || +_cmake_json_preset_block() { + local section="$1" name="$2" + local after="${section#*\"name\"[[:space:]]#:[[:space:]]#\"$name\"}" + [[ $after == "$section" ]] && return 1 + print -r -- "${after%%\"name\"[[:space:]]#:*}" +} + +# ---------------------- +# _cmake_json_inherited_field +# +# Look up $field on preset $name inside array-section $section, following +# "inherits" (a string or an array of strings) when the preset itself +# doesn't set the field, depth-first with first-match-wins -- matching +# CMake's own resolution order. $4 is an internal "seen names" guard +# against cyclic (invalid) inherits. +# ---------------------- +(( $+functions[_cmake_json_inherited_field] )) || +_cmake_json_inherited_field() { + local section="$1" name="$2" field="$3" seen="$4" + [[ " $seen " == *" $name "* ]] && return 1 + + local block + block=$(_cmake_json_preset_block "$section" "$name") || return 1 + + local value + value=$(_cmake_json_field "$block" "$field") + if [[ -n $value ]]; then + print -r -- "$value" + return 0 + fi + + local parent + if [[ $block =~ '"inherits"[[:space:]]*:[[:space:]]*\[([^]]*)\]' ]]; then + for parent in ${(s:,:)match[1]}; do + parent=${parent//[\"[:space:]]/} + [[ -z $parent ]] && continue + value=$(_cmake_json_inherited_field "$section" "$parent" "$field" "$seen $name") && \ + { print -r -- "$value"; return 0 } + done + elif [[ $block =~ '"inherits"[[:space:]]*:[[:space:]]*"([^"]*)"' ]]; then + _cmake_json_inherited_field "$section" "$match[1]" "$field" "$seen $name" + return + fi + return 1 +} + # ---------------------- # _cmake_json_preset_dir # @@ -180,12 +235,9 @@ _cmake_json_field() { # $2 = build preset name # # Steps: -# a) Slice out the build preset object (from its "name" entry up to the next "name" entry) -# and read its "configurePreset". -# b) Slice out that configure preset object the same way and read its "binaryDir". -# c) Expand the ${sourceDir} macro and strip a trailing slash. -# Inheritance ("inherits") is not resolved; the caller's glob fallback covers -# those uncommon setups. +# a) Find the build preset's "configurePreset" (following "inherits" if needed). +# b) Find that configure preset's "binaryDir" (following "inherits" if needed). +# c) Expand the ${sourceDir}/${presetName} macros and strip a trailing slash. # ---------------------- (( $+functions[_cmake_json_preset_dir] )) || _cmake_json_preset_dir() { @@ -193,29 +245,23 @@ _cmake_json_preset_dir() { setopt extendedglob local json="$1" name="$2" - # --- a) locate the build preset object, read its configurePreset --- - # Everything from `"name": ""` onward: - local after="${json#*\"name\"[[:space:]]#:[[:space:]]#\"$name\"}" - [[ $after == "$json" ]] && return # name not found - # Trim at the start of the *next* preset object's name field, so we only - # look inside this preset. - local block="${after%%\"name\"[[:space:]]#:*}" + # Restrict the "name" search to the right array so a configurePreset and + # a buildPreset sharing the same name (a common CMakePresets pattern) + # don't get confused for one another. + local build_section="${json#*\"buildPresets\"}" + local configure_section="${json#*\"configurePresets\"}" local configPreset - configPreset=$(_cmake_json_field "$block" configurePreset) + configPreset=$(_cmake_json_inherited_field "$build_section" "$name" configurePreset) [[ -z $configPreset ]] && return - # --- b) locate that configure preset object, read its binaryDir --- - after="${json#*\"name\"[[:space:]]#:[[:space:]]#\"$configPreset\"}" - [[ $after == "$json" ]] && return - block="${after%%\"name\"[[:space:]]#:*}" - local bd - bd=$(_cmake_json_field "$block" binaryDir) + bd=$(_cmake_json_inherited_field "$configure_section" "$configPreset" binaryDir) [[ -z $bd ]] && return # --- c) expand macros --- bd="${bd//\$\{sourceDir\}/$PWD}" + bd="${bd//\$\{presetName\}/$configPreset}" bd="${bd%/}" print -r -- "$bd" }