1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (c) 2022-2023, Nordic Semiconductor ASA
4
5# FindZephyr-sdk module for supporting module search mode of Zephyr SDK.
6#
7# It is possible to control the behavior of the Zephyr-SDK package using
8# COMPONENTS.
9# The Zephyr-SDK package supports the components:
10# - LOAD: Load a Zephyr-SDK. This is the default behavior if no COMPONENTS is specified.
11#         Its purpose is to allow the find_package basic signature mode to lookup Zephyr
12#         SDK and based on user / environment settings of selected toolchain decide if
13#         the Zephyr SDK CMake package should be loaded.
14#
15#         It extends the Zephyr-sdk CMake package by providing more flexibility in when
16#         the Zephyr SDK is loaded and loads additional host tools from the Zephyr SDK.
17#
18#         The module defines the following variables when used in normal search and load mode:
19#         'ZEPHYR_SDK_INSTALL_DIR'
20#         Install location of the Zephyr SDK
21#
22#         'ZEPHYR_TOOLCHAIN_VARIANT'
23#         Zephyr toolchain variant to use if not defined already.
24#
25#         'Zephyr-sdk_FOUND'
26#         True if the Zephyr SDK was found.
27
28# - LIST: Will list all available Zephyr SDKs found in the system but not load
29#         any Sdk. This can be used to fetch available Zephyr-SDKs before doing
30#         an actual load.
31#         LIST component will define the following lists:
32#         - Zephyr-sdk      : Version of a Zephyr-SDK
33#         - Zephyr-sdk_DIRS : Install dir of the Zephyr-SDK
34#         Each entry in Zephyr-SDK has a corresponding entry in Zephyr-SDK_DIRS.
35#         For example:
36#         index:  Zephyr-sdk:    Zephyr-sdk_DIRS:
37#         0       0.15.0         /opt/zephyr-sdk-0.15.0
38#         1       0.16.0         /home/<user>/zephyr-sdk-0.16.0
39#
40
41include(extensions)
42
43# Set internal variables if set in environment.
44zephyr_get(ZEPHYR_TOOLCHAIN_VARIANT)
45
46zephyr_get(ZEPHYR_SDK_INSTALL_DIR)
47
48if("${Zephyr-sdk_FIND_COMPONENTS}" STREQUAL "")
49  set(Zephyr-sdk_FIND_COMPONENTS LOAD)
50endif()
51
52# Load Zephyr SDK Toolchain.
53# There are three scenarios where Zephyr SDK should be looked up:
54# 1) Zephyr specified as toolchain (ZEPHYR_SDK_INSTALL_DIR still used if defined)
55# 2) No toolchain specified == Default to Zephyr toolchain
56# Until we completely deprecate it
57if(("zephyr" STREQUAL ${ZEPHYR_TOOLCHAIN_VARIANT}) OR
58   (NOT DEFINED ZEPHYR_TOOLCHAIN_VARIANT) OR
59   (DEFINED ZEPHYR_SDK_INSTALL_DIR) OR
60   (Zephyr-sdk_FIND_REQUIRED))
61
62  # No toolchain was specified, so inform user that we will be searching.
63  if (NOT Zephyr-sdk_FIND_QUIETLY AND
64      NOT DEFINED ZEPHYR_SDK_INSTALL_DIR AND
65      NOT DEFINED ZEPHYR_TOOLCHAIN_VARIANT)
66    message(STATUS "ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK")
67  endif()
68
69  # This ensure packages are sorted in descending order.
70  SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION_CURRENT ${CMAKE_FIND_PACKAGE_SORT_DIRECTION})
71  SET(CMAKE_FIND_PACKAGE_SORT_ORDER_CURRENT ${CMAKE_FIND_PACKAGE_SORT_ORDER})
72  SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
73  SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
74
75  if(DEFINED ZEPHYR_SDK_INSTALL_DIR AND LOAD IN_LIST Zephyr-sdk_FIND_COMPONENTS)
76    # The Zephyr SDK will automatically set the toolchain variant.
77    # To support Zephyr SDK tools (DTC, and other tools) with 3rd party toolchains
78    # then we keep track of current toolchain variant.
79    set(ZEPHYR_CURRENT_TOOLCHAIN_VARIANT ${ZEPHYR_TOOLCHAIN_VARIANT})
80    find_package(Zephyr-sdk ${Zephyr-sdk_FIND_VERSION_COMPLETE}
81                 REQUIRED QUIET CONFIG HINTS ${ZEPHYR_SDK_INSTALL_DIR}
82    )
83    if(DEFINED ZEPHYR_CURRENT_TOOLCHAIN_VARIANT)
84      set(ZEPHYR_TOOLCHAIN_VARIANT ${ZEPHYR_CURRENT_TOOLCHAIN_VARIANT})
85    endif()
86  else()
87    # Paths that are used to find installed Zephyr SDK versions
88    SET(zephyr_sdk_search_paths
89        /usr
90        /usr/local
91        /opt
92        $ENV{HOME}
93        $ENV{HOME}/.local
94        $ENV{HOME}/.local/opt
95        $ENV{HOME}/bin)
96
97    # Search for Zephyr SDK version 0.0.0 which does not exist, this is needed to
98    # return a list of compatible versions and find the best suited version that
99    # is available.
100    find_package(Zephyr-sdk 0.0.0 EXACT QUIET CONFIG PATHS ${zephyr_sdk_search_paths})
101
102    # Remove duplicate entries and sort naturally in descending order.
103    foreach(version config IN ZIP_LISTS Zephyr-sdk_CONSIDERED_VERSIONS Zephyr-sdk_CONSIDERED_CONFIGS)
104      if(NOT DEFINED Zephyr-sdk-${version}_DIR)
105        set(Zephyr-sdk-${version}_DIR ${config})
106      endif()
107    endforeach()
108
109    list(REMOVE_DUPLICATES Zephyr-sdk_CONSIDERED_VERSIONS)
110    list(SORT Zephyr-sdk_CONSIDERED_VERSIONS COMPARE NATURAL ORDER DESCENDING)
111
112    if(LIST IN_LIST Zephyr-sdk_FIND_COMPONENTS)
113      set(Zephyr-sdk)
114      set(Zephyr-sdk_DIRS)
115      # Only list the Zephyr SDKs installed in the system.
116      foreach(version ${Zephyr-sdk_CONSIDERED_VERSIONS})
117        cmake_path(GET Zephyr-sdk-${version}_DIR PARENT_PATH dir)
118        cmake_path(GET dir PARENT_PATH dir)
119        list(APPEND Zephyr-sdk ${version})
120        list(APPEND Zephyr-sdk_DIRS ${dir})
121        if (NOT Zephyr-sdk_FIND_QUIETLY)
122          message(STATUS "Zephyr-sdk, version=${version}, dir=${dir}")
123        endif()
124      endforeach()
125    else()
126      if("${Zephyr-sdk_FIND_VERSION_RANGE_MAX}" STREQUAL "INCLUDE")
127        set(upper_bound _EQUAL)
128      endif()
129
130      if(NOT DEFINED Zephyr-sdk_FIND_VERSION_RANGE)
131        # Range not given, max out to ensure max version is not in effect.
132        set(Zephyr-sdk_FIND_VERSION_MAX 99999999)
133      endif()
134
135      # Loop over each found Zepher SDK version until one is found that is compatible.
136      foreach(zephyr_sdk_candidate ${Zephyr-sdk_CONSIDERED_VERSIONS})
137        if("${zephyr_sdk_candidate}" VERSION_GREATER_EQUAL "${Zephyr-sdk_FIND_VERSION}"
138           AND "${zephyr_sdk_candidate}" VERSION_LESS${upper_bound} "${Zephyr-sdk_FIND_VERSION_MAX}"
139        )
140          # Find the path for the current version being checked and get the directory
141          # of the Zephyr SDK so it can be checked.
142          cmake_path(GET Zephyr-sdk-${zephyr_sdk_candidate}_DIR PARENT_PATH zephyr_sdk_current_check_path)
143          cmake_path(GET zephyr_sdk_current_check_path PARENT_PATH zephyr_sdk_current_check_path)
144
145          # Then see if this version is compatible.
146          find_package(Zephyr-sdk ${Zephyr-sdk_FIND_VERSION_COMPLETE} QUIET CONFIG PATHS ${zephyr_sdk_current_check_path} NO_DEFAULT_PATH)
147
148          if (${Zephyr-sdk_FOUND})
149            # A compatible version of the Zephyr SDK has been found which is the highest
150            # supported version, exit.
151            break()
152          endif()
153        endif()
154      endforeach()
155
156      if (NOT ${Zephyr-sdk_FOUND})
157        # This means no compatible Zephyr SDK versions were found, set the version
158        # back to the minimum version so that it is displayed in the error text.
159        find_package(Zephyr-sdk ${Zephyr-sdk_FIND_VERSION_COMPLETE} REQUIRED CONFIG PATHS ${zephyr_sdk_search_paths})
160      endif()
161    endif()
162  endif()
163
164  SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION ${CMAKE_FIND_PACKAGE_SORT_DIRECTION_CURRENT})
165  SET(CMAKE_FIND_PACKAGE_SORT_ORDER ${CMAKE_FIND_PACKAGE_SORT_ORDER_CURRENT})
166endif()
167
168# Clean up temp variables
169set(zephyr_sdk_search_paths)
170set(zephyr_sdk_found_versions)
171set(zephyr_sdk_found_configs)
172set(zephyr_sdk_current_index)
173set(zephyr_sdk_current_check_path)
174
175if(LOAD IN_LIST Zephyr-sdk_FIND_COMPONENTS)
176  if(DEFINED ZEPHYR_SDK_INSTALL_DIR)
177    # Cache the Zephyr SDK install dir.
178    set(ZEPHYR_SDK_INSTALL_DIR ${ZEPHYR_SDK_INSTALL_DIR} CACHE PATH "Zephyr SDK install directory")
179  endif()
180
181  if(Zephyr-sdk_FOUND)
182    include(${ZEPHYR_SDK_INSTALL_DIR}/cmake/zephyr/host-tools.cmake)
183
184    if (NOT Zephyr-sdk_FIND_QUIETLY)
185      message(STATUS "Found host-tools: zephyr ${SDK_VERSION} (${ZEPHYR_SDK_INSTALL_DIR})")
186    endif()
187  endif()
188endif()
189set(ZEPHYR_TOOLCHAIN_PATH ${ZEPHYR_SDK_INSTALL_DIR})
190