1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (c) 2021, Nordic Semiconductor ASA
4
5# Snippets support
6#
7# This module:
8#
9# - searches for snippets in zephyr and any modules
10# - validates the SNIPPET input variable, if any
11#
12# If SNIPPET contains a snippet name that is not found, an error
13# will be raised, and the list of valid snippets will be printed.
14#
15# Outcome:
16# The following variables will be defined when this module completes:
17#
18# - SNIPPET_AS_LIST: CMake list of snippet names, created from the
19#   SNIPPET variable
20# - SNIPPET_ROOT: CMake list of snippet roots, deduplicated and with
21#   ZEPHYR_BASE appended at the end
22#
23# The following variables may be updated when this module completes:
24# - DTC_OVERLAY_FILE
25# - OVERLAY_CONFIG
26#
27# The following targets will be defined when this CMake module completes:
28# - snippets: when invoked, a list of valid snippets will be printed
29#
30# Optional variables:
31# - SNIPPET_ROOT: input CMake list of snippet roots (directories containing
32#   additional snippet implementations); this should not include ZEPHYR_BASE,
33#   as that will be added by this module
34
35include_guard(GLOBAL)
36
37include(extensions)
38
39# Warn the user if SNIPPET changes later. Such changes are ignored.
40zephyr_check_cache(SNIPPET WATCH)
41
42# Putting the body into a function prevents us from polluting the
43# parent scope. We'll set our outcome variables in the parent scope of
44# the function to ensure the outcome of the module.
45function(zephyr_process_snippets)
46  set(snippets_py ${ZEPHYR_BASE}/scripts/snippets.py)
47  set(snippets_generated ${CMAKE_BINARY_DIR}/zephyr/snippets_generated.cmake)
48  set_ifndef(SNIPPET_NAMESPACE SNIPPET)
49  set_ifndef(SNIPPET_PYTHON_EXTRA_ARGS "")
50  set_ifndef(SNIPPET_APP_DIR "${APPLICATION_SOURCE_DIR}")
51
52  # Set SNIPPET_AS_LIST, removing snippets_generated.cmake if we are
53  # running cmake again and snippets are no longer requested.
54  if(NOT DEFINED SNIPPET)
55    set(SNIPPET_AS_LIST "" PARENT_SCOPE)
56    file(REMOVE ${snippets_generated})
57  else()
58    string(REPLACE " " ";" SNIPPET_AS_LIST "${${SNIPPET_NAMESPACE}}")
59    set(SNIPPET_AS_LIST "${SNIPPET_AS_LIST}" PARENT_SCOPE)
60  endif()
61
62  # Set SNIPPET_ROOT.
63  zephyr_get(SNIPPET_ROOT MERGE SYSBUILD GLOBAL)
64  list(APPEND SNIPPET_ROOT ${SNIPPET_APP_DIR})
65  list(APPEND SNIPPET_ROOT ${ZEPHYR_BASE})
66  unset(real_snippet_root)
67  foreach(snippet_dir ${SNIPPET_ROOT})
68    # The user might have put a symbolic link in here, for example.
69    file(REAL_PATH ${snippet_dir} real_snippet_dir)
70    list(APPEND real_snippet_root ${real_snippet_dir})
71  endforeach()
72  set(SNIPPET_ROOT ${real_snippet_root})
73  list(REMOVE_DUPLICATES SNIPPET_ROOT)
74  set(SNIPPET_ROOT "${SNIPPET_ROOT}" PARENT_SCOPE)
75
76  # Generate and include snippets_generated.cmake.
77  # The Python script is responsible for checking for unknown
78  # snippets.
79  set(snippet_root_args)
80  foreach(root IN LISTS SNIPPET_ROOT)
81    list(APPEND snippet_root_args --snippet-root "${root}")
82  endforeach()
83  set(requested_snippet_args)
84  foreach(snippet_name ${SNIPPET_AS_LIST})
85    list(APPEND requested_snippet_args --snippet "${snippet_name}")
86  endforeach()
87  execute_process(COMMAND ${PYTHON_EXECUTABLE}
88    ${snippets_py}
89    ${snippet_root_args}
90    ${requested_snippet_args}
91    --cmake-out ${snippets_generated}
92    ${SNIPPET_PYTHON_EXTRA_ARGS}
93    OUTPUT_VARIABLE output
94    ERROR_VARIABLE output
95    RESULT_VARIABLE ret)
96  if(${ret})
97    message(FATAL_ERROR "${output}")
98  endif()
99  include(${snippets_generated})
100
101  # Create the 'snippets' target. Each snippet is printed in a
102  # separate command because build system files are not fond of
103  # newlines.
104  list(TRANSFORM SNIPPET_NAMES PREPEND "COMMAND;${CMAKE_COMMAND};-E;echo;"
105    OUTPUT_VARIABLE snippets_target_cmd)
106  add_custom_target(snippets ${snippets_target_cmd} USES_TERMINAL)
107
108  # If snippets were requested, print messages for each one.
109  if(SNIPPET_AS_LIST)
110    # Print the requested snippets.
111    set(snippet_names "Snippet(s):")
112    foreach(snippet IN LISTS SNIPPET_AS_LIST)
113      string(APPEND snippet_names " ${snippet}")
114    endforeach()
115    message(STATUS "${snippet_names}")
116  endif()
117
118  # Re-run cmake if any files we depend on changed.
119  set_property(DIRECTORY APPEND PROPERTY
120    CMAKE_CONFIGURE_DEPENDS
121    ${snippets_py}
122    ${SNIPPET_PATHS}            #  generated variable
123    )
124endfunction()
125
126zephyr_process_snippets()
127