Skip to content

Commit

Permalink
cmake: detection and using a not registered Zephyr repository package
Browse files Browse the repository at this point in the history
This commit adds the possibility of using a Zephyr repository package
base even when it has not been exported to CMake package registry.

It also introduces the possibility of locating and using Zephyr
CMake config package correctly when invoking CMake on an out-of-tree
project.

Signed-off-by: Torsten Rasmussen <[email protected]>
  • Loading branch information
tejlmand authored and carlescufi committed Mar 27, 2020
1 parent 3dd96d8 commit 555eb9b
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 22 deletions.
76 changes: 67 additions & 9 deletions share/zephyr-package/cmake/ZephyrConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,80 @@
# First check to see if user has provided a Zephyr base manually.
# Set Zephyr base to environment setting.
# It will be empty if not set in environment.
set(ZEPHYR_BASE $ENV{ZEPHYR_BASE})

# Find out the current Zephyr base.
get_filename_component(CURRENT_ZEPHYR_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE)
include(${CMAKE_CURRENT_LIST_DIR}/zephyr_package_search.cmake)

macro(include_boilerplate location)
set(Zephyr_FOUND True)
if(NOT NO_BOILERPLATE)
message("Including boilerplate (${location}): ${ZEPHYR_BASE}/cmake/app/boilerplate.cmake")
include(${ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
endif()
endmacro()

set(ZEPHYR_BASE $ENV{ZEPHYR_BASE})

if (ZEPHYR_BASE)
# Get rid of any double folder string before comparison, as example, user provides
# ZEPHYR_BASE=//path/to//zephyr_base/
# must also work.
get_filename_component(ZEPHYR_BASE ${ZEPHYR_BASE} ABSOLUTE)
else()
# Zephyr base is not set in environment but currently used Zephyr is located within the same tree
# as the caller (sample/test/application) code of find_package(Zephyr).
# Thus we set a Zephyr base for looking up boilerplate.cmake faster.

include_boilerplate("zephyr base")
return()
endif()

# If ZEPHYR_CANDIDATE is set, it means this file was include instead of called via find_package directly.
if(ZEPHYR_CANDIDATE)
set(IS_INCLUDED TRUE)
endif()

# Find out the current Zephyr base.
get_filename_component(CURRENT_ZEPHYR_DIR ${CMAKE_CURRENT_LIST_FILE}/${ZEPHYR_RELATIVE_DIR} ABSOLUTE)
get_filename_component(PROJECT_WORKTREE_DIR ${CMAKE_CURRENT_LIST_FILE}/${PROJECT_WORKTREE_RELATIVE_DIR} ABSOLUTE)

string(FIND "${CMAKE_CURRENT_SOURCE_DIR}" "${CURRENT_ZEPHYR_DIR}/" COMMON_INDEX)
if (COMMON_INDEX EQUAL 0)
# Project is in-zephyr-tree.
# We are in Zephyr tree.
set(ZEPHYR_BASE ${CURRENT_ZEPHYR_DIR})
include_boilerplate("in-zephyr-tree")
return()
endif()

if(IS_INCLUDED)
# A higher level did the checking and included us and as we are not in-zephyr-tree (checked above)
# then we must be in work-tree.
set(ZEPHYR_BASE ${CURRENT_ZEPHYR_DIR})
include_boilerplate("in-work-tree")
endif()

message("Including boilerplate: ${ZEPHYR_BASE}/cmake/app/boilerplate.cmake")
include(${ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
if(NOT IS_INCLUDED)
string(FIND "${CMAKE_CURRENT_SOURCE_DIR}" "${PROJECT_WORKTREE_DIR}/" COMMON_INDEX)
if (COMMON_INDEX EQUAL 0)
# Project is in-project-worktree-tree.
# This means this Zephyr is likely the correct one, but there could be an alternative installed along-side
# Thus, check if there is an even better candidate.
# This check works the following way.
# CMake finds packages will look all packages registered in the user package registry.
# As this code is processed inside registered packages, we simply test if another package has a
# comon path with the current sample.
# and if so, we will retrun here, and let CMake call into the other registered package for real
# version checking.
check_zephyr_package(PROJECT_WORKTREE_DIR ${PROJECT_WORKTREE_DIR})

# We are the best candidate, so let's include boiler plate.
set(ZEPHYR_BASE ${CURRENT_ZEPHYR_DIR})
include_boilerplate("in-work-tree")
return()
endif()

check_zephyr_package(SEARCH_PARENTS)

# Ending here means there were no candidates in-tree of the app.
# Thus, the app is build oot.
# CMake find_package has already done the version checking, so let's just include boiler plate.
# Previous find_package would have cleared Zephyr_FOUND variable, thus set it again.
set(ZEPHYR_BASE ${CURRENT_ZEPHYR_DIR})
include_boilerplate("out-of-worktree")
endif()
94 changes: 81 additions & 13 deletions share/zephyr-package/cmake/ZephyrConfigVersion.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,43 @@
# The purpose of the version file is to ensure that CMake find_package can correctly locate a
# usable Zephyr installation for building of applications.

# Checking for version 0.0.0 is a way to allow other Zephyr installation to determine if there is a better match.
# A better match would be an installed Zephyr that has a common index with currect source dir.
# Version 0.0.0 indicates that we should just return, in order to obtain our path.
if(0.0.0 STREQUAL PACKAGE_FIND_VERSION)
return()
endif()

macro(check_zephyr_version)
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
if(IS_INCLUDED)
# We are just a candidate, meaning we have been included from other installed module.
message("\n The following in-zephyr-tree configuration file were considered but not accepted:")
message("\n ${CMAKE_CURRENT_LIST_FILE}, version: ${PACKAGE_VERSION}\n")
endif()

set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
# For now, Zephyr is capable to find the right base on all older versions as long as they define
# a Zephyr config package (This code)
# In future, this is the place to update in case Zephyr 3.x is not backward compatible with version 2.x
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
endmacro()

# First check to see if user has provided a Zephyr base manually.
set(ZEPHYR_BASE $ENV{ZEPHYR_BASE})

# If ZEPHYR_CANDIDATE is set, it means this file was include instead of called via find_package directly.
if(ZEPHYR_CANDIDATE)
set(IS_INCLUDED TRUE)
else()
include(${CMAKE_CURRENT_LIST_DIR}/zephyr_package_search.cmake)
endif()

if (ZEPHYR_BASE)
# ZEPHYR_BASE was set in environment, meaning the package version must be ignored and the Zephyr
# pointed to by ZEPHYR_BASE is to be used regardless of version
Expand All @@ -16,7 +50,7 @@ if (ZEPHYR_BASE)
# ZEPHYR_BASE=//path/to//zephyr_base/
# must also work.
get_filename_component(ZEPHYR_BASE ${ZEPHYR_BASE} ABSOLUTE)
if (${ZEPHYR_BASE}/zephyr-package/cmake STREQUAL ${CMAKE_CURRENT_LIST_DIR})
if (${ZEPHYR_BASE}/share/zephyr-package/cmake STREQUAL ${CMAKE_CURRENT_LIST_DIR})
# We are the Zephyr to be used
set(PACKAGE_VERSION_COMPATIBLE TRUE)
set(PACKAGE_VERSION_EXACT TRUE)
Expand All @@ -27,25 +61,59 @@ if (ZEPHYR_BASE)
return()
endif()

# Find out the current Zephyr base.
get_filename_component(CURRENT_ZEPHYR_DIR ${CMAKE_CURRENT_LIST_DIR}/../../.. ABSOLUTE)
get_filename_component(PROJECT_WORKTREE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../.. ABSOLUTE)

# Temporary set local Zephyr base to allow using version.cmake to find this Zephyr tree current version
set(ZEPHYR_BASE ${CMAKE_CURRENT_LIST_DIR}/../../..)
set(ZEPHYR_BASE ${CURRENT_ZEPHYR_DIR})

# Tell version.cmake to not print as printing version for all Zephyr installations being tested
# will lead to confusion on which is being used.
set(NO_PRINT_VERSION True)
include(${ZEPHYR_BASE}/cmake/version.cmake)
set(ZEPHYR_BASE)

# Zephyr uses project version, but CMake package uses PACKAGE_VERSION
set(PACKAGE_VERSION ${PROJECT_VERSION})
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
# Currently, this version is capable of handling on prior versions.
# In future, in case version 3.0.0 cannot be used for project requiring
# version 2.x.x, then add such check here.
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
set(ZEPHYR_BASE)

# Do we share common index, if so, this is the correct version to check.
string(FIND "${CMAKE_CURRENT_SOURCE_DIR}" "${CURRENT_ZEPHYR_DIR}/" COMMON_INDEX)
if (COMMON_INDEX EQUAL 0)
# Project is in-zephyr-tree.

check_zephyr_version()
return()
endif()

if(NOT IS_INCLUDED)
# Only do this if we are an installed CMake Config package and checking for work-tree candidates.

string(FIND "${CMAKE_CURRENT_SOURCE_DIR}" "${PROJECT_WORKTREE_DIR}/" COMMON_INDEX)
if (COMMON_INDEX EQUAL 0)
# Project is a Zephyr workspace app.
# This means this Zephyr is likely the correct one, but there could be an alternative installed along-side
# Thus, check if there is an even better candidate.
check_zephyr_package(PROJECT_WORKTREE_DIR ${PROJECT_WORKTREE_DIR} VERSION_CHECK)

# We are the best candidate, so let's check our own version.
check_zephyr_version()
return()
endif()

# Checking for installed candidates which could also be an worktree candidates.
# This check works the following way.
# CMake finds packages will look all packages registered in the user package registry.
# As this code is processed inside registered packages, we simply test if
# another package has a comon path with the current sample, and if so, we
# will retrun here, and let CMake call into the other registered package for
# real version checking.
check_zephyr_package(CHECK_ONLY VERSION_CHECK)

# Check for workspace candidates.
check_zephyr_package(SEARCH_PARENTS VERSION_CHECK)
endif()

# Ending here means there were no candidates in-tree of the app.
# Thus, the app is build oot.
# Let's do basic CMake version checking.
check_zephyr_version()
78 changes: 78 additions & 0 deletions share/zephyr-package/cmake/zephyr_package_search.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# The purpose of this file is to provide search mechanism for locating Zephyr in-work-tree package
# even when they are not installed into CMake package system
# Linux/MacOS: ~/.cmake/packages
# Windows: Registry database

# Relative directory of worktree project dir as seen from Zephyr package file
set(PROJECT_WORKTREE_RELATIVE_DIR "../../../../..")

# Relative directory of Zephyr dir as seen from Zephyr package file
set(ZEPHYR_RELATIVE_DIR "../../../..")

# This macro returns a list of parent folders to use for later searches.
macro(get_search_paths START_PATH SEARCH_PATHS)
get_filename_component(SEARCH_PATH ${START_PATH} DIRECTORY)
while(NOT (SEARCH_PATH STREQUAL "/"))
list(APPEND SEARCH_PATHS ${SEARCH_PATH}/zephyr)
list(APPEND SEARCH_PATHS ${SEARCH_PATH})
get_filename_component(SEARCH_PATH ${SEARCH_PATH} DIRECTORY)
endwhile()
endmacro()

# This macro can check for additional Zephyr package that has a better match
# Options:
# - PROJECT_WORKTREE_DIR: Search for projects in specified worktree.
# - SEARCH_PARENTS : Search parent folder of current source file (application) to locate in-project-tree Zephyr candidates.
# - CHECK_ONLY : Only set PACKAGE_VERSION_COMPATIBLE to false if a better candidate is found, default is to also include the found candidate.
# - VERSION_CHECK : This is the version check stage by CMake find package
macro(check_zephyr_package)
set(options CHECK_ONLY INCLUDE_FILE SEARCH_PARENTS VERSION_CHECK)
set(single_args PROJECT_WORKTREE_DIR)
cmake_parse_arguments(CHECK_ZEPHYR_PACKAGE "${options}" "${single_args}" "" ${ARGN})

if(CHECK_ZEPHYR_PACKAGE_PROJECT_WORKTREE_DIR)
set(SEARCH_SETTINGS PATHS ${CHECK_ZEPHYR_PACKAGE_PROJECT_WORKTREE_DIR}/zephyr ${CHECK_ZEPHYR_PACKAGE_PROJECT_WORKTREE_DIR} NO_DEFAULT_PATH)
endif()

if(CHECK_ZEPHYR_PACKAGE_SEARCH_PARENTS)
get_search_paths(${CMAKE_CURRENT_SOURCE_DIR} SEARCH_PATHS)
set(SEARCH_SETTINGS PATHS ${SEARCH_PATHS} NO_DEFAULT_PATH)
endif()

# Searching for version zero means there will be no match, but we obtain
# a list of all potential Zephyr candidates in the tree to consider.
find_package(Zephyr 0.0.0 EXACT QUIET ${SEARCH_SETTINGS})

# The find package will also find ourself when searching in-tree, so to avoid re-including
# this file, it is removed from the list along with any duplicates.
list(REMOVE_ITEM Zephyr_CONSIDERED_CONFIGS ${CMAKE_CURRENT_LIST_DIR}/ZephyrConfig.cmake)
list(REMOVE_DUPLICATES Zephyr_CONSIDERED_CONFIGS)

foreach(ZEPHYR_CANDIDATE ${Zephyr_CONSIDERED_CONFIGS})
if(CHECK_ZEPHYR_PACKAGE_PROJECT_WORKTREE_DIR)
# Check is done in project tree already, thus check only for pure Zephyr candidates.
get_filename_component(CANDIDATE_DIR ${ZEPHYR_CANDIDATE}/${ZEPHYR_RELATIVE_DIR} ABSOLUTE)
else()
get_filename_component(CANDIDATE_DIR ${ZEPHYR_CANDIDATE}/${PROJECT_WORKTREE_RELATIVE_DIR} ABSOLUTE)
endif()

string(FIND "${CMAKE_CURRENT_SOURCE_DIR}" "${CANDIDATE_DIR}/" COMMON_INDEX)
if (COMMON_INDEX EQUAL 0)
if(CHECK_ZEPHYR_PACKAGE_CHECK_ONLY)
# A better candidate exists, thus return
set(PACKAGE_VERSION_COMPATIBLE FALSE)
return()
else()
# A better candidate exists, thus return
if(CHECK_ZEPHYR_PACKAGE_VERSION_CHECK)
string(REGEX REPLACE "\.cmake$" "Version.cmake" ZEPHYR_VERSION_CANDIDATE ${ZEPHYR_CANDIDATE})
include(${ZEPHYR_VERSION_CANDIDATE} NO_POLICY_SCOPE)
return()
else()
include(${ZEPHYR_CANDIDATE} NO_POLICY_SCOPE)
return()
endif()
endif()
endif()
endforeach()
endmacro()

0 comments on commit 555eb9b

Please sign in to comment.