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
49  # Set SNIPPET_AS_LIST, removing snippets_generated.cmake if we are
50  # running cmake again and snippets are no longer requested.
51  if (NOT DEFINED SNIPPET)
52    set(SNIPPET_AS_LIST "" PARENT_SCOPE)
53    file(REMOVE ${snippets_generated})
54  else()
55    string(REPLACE " " ";" SNIPPET_AS_LIST "${SNIPPET}")
56    set(SNIPPET_AS_LIST "${SNIPPET_AS_LIST}" PARENT_SCOPE)
57  endif()
58
59  # Set SNIPPET_ROOT.
60  list(APPEND SNIPPET_ROOT ${APPLICATION_SOURCE_DIR})
61  list(APPEND SNIPPET_ROOT ${ZEPHYR_BASE})
62  unset(real_snippet_root)
63  foreach(snippet_dir ${SNIPPET_ROOT})
64    # The user might have put a symbolic link in here, for example.
65    file(REAL_PATH ${snippet_dir} real_snippet_dir)
66    list(APPEND real_snippet_root ${real_snippet_dir})
67  endforeach()
68  set(SNIPPET_ROOT ${real_snippet_root})
69  list(REMOVE_DUPLICATES SNIPPET_ROOT)
70  set(SNIPPET_ROOT "${SNIPPET_ROOT}" PARENT_SCOPE)
71
72  # Generate and include snippets_generated.cmake.
73  # The Python script is responsible for checking for unknown
74  # snippets.
75  set(snippet_root_args)
76  foreach(root IN LISTS SNIPPET_ROOT)
77    list(APPEND snippet_root_args --snippet-root "${root}")
78  endforeach()
79  set(requested_snippet_args)
80  foreach(snippet_name ${SNIPPET_AS_LIST})
81    list(APPEND requested_snippet_args --snippet "${snippet_name}")
82  endforeach()
83  execute_process(COMMAND ${PYTHON_EXECUTABLE}
84    ${snippets_py}
85    ${snippet_root_args}
86    ${requested_snippet_args}
87    --cmake-out ${snippets_generated}
88    OUTPUT_VARIABLE output
89    ERROR_VARIABLE output
90    RESULT_VARIABLE ret)
91  if(${ret})
92    message(FATAL_ERROR "${output}")
93  endif()
94  include(${snippets_generated})
95
96  # Create the 'snippets' target. Each snippet is printed in a
97  # separate command because build system files are not fond of
98  # newlines.
99  list(TRANSFORM SNIPPET_NAMES PREPEND "COMMAND;${CMAKE_COMMAND};-E;echo;"
100    OUTPUT_VARIABLE snippets_target_cmd)
101  add_custom_target(snippets ${snippets_target_cmd} USES_TERMINAL)
102
103  # If snippets were requested, print messages for each one.
104  if(SNIPPET_AS_LIST)
105    # Print the requested snippets.
106    set(snippet_names "Snippet(s):")
107    foreach(snippet IN LISTS SNIPPET_AS_LIST)
108      string(APPEND snippet_names " ${snippet}")
109    endforeach()
110    message(STATUS "${snippet_names}")
111  endif()
112
113  # Re-run cmake if any files we depend on changed.
114  set_property(DIRECTORY APPEND PROPERTY
115    CMAKE_CONFIGURE_DEPENDS
116    ${snippets_py}
117    ${SNIPPET_PATHS}            #  generated variable
118    )
119endfunction()
120
121zephyr_process_snippets()
122