blob: ca6845f01da5800ba1f6de2712630d811e7f0a97 [file] [log] [blame]
Carles Cufi31bdad52019-04-26 21:53:02 +02001# Copyright 2018 (c) Foundries.io.
2#
3# SPDX-License-Identifier: Apache-2.0
4
5'''Common definitions for building Zephyr applications.
6
7This provides some default settings and convenience wrappers for
8building Zephyr applications needed by multiple commands.
9
10See build.py for the build command itself.
11'''
12
Jan Van Winkel4d975db2019-05-04 14:44:56 +020013import zcmake
Carles Cufi31bdad52019-04-26 21:53:02 +020014import os
Daniel DeGrasse6a5b88e2022-10-05 16:04:44 -050015import sys
Carles Cufi41f1f642019-06-17 11:47:11 +020016from pathlib import Path
Carles Cufi31bdad52019-04-26 21:53:02 +020017from west import log
Carles Cufi98980c62019-06-05 16:04:29 +020018from west.configuration import config
19from west.util import escapes_directory
Daniel DeGrasse6a5b88e2022-10-05 16:04:44 -050020
21# Domains.py must be imported from the pylib directory, since
22# twister also uses the implementation
23script_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
24sys.path.insert(0, os.path.join(script_dir, "pylib/build_helpers/"))
Torsten Rasmussen8408af62021-11-22 10:29:56 +010025from domains import Domains
Carles Cufi31bdad52019-04-26 21:53:02 +020026
27DEFAULT_BUILD_DIR = 'build'
28'''Name of the default Zephyr build directory.'''
29
30DEFAULT_CMAKE_GENERATOR = 'Ninja'
31'''Name of the default CMake generator.'''
32
Carles Cufi98980c62019-06-05 16:04:29 +020033FIND_BUILD_DIR_DESCRIPTION = '''\
Martí Bolívar6e4c2b92020-06-25 12:58:58 -070034If the build directory is not given, the default is {}/ unless the
Martí Bolívareb95bed2020-02-10 06:40:29 -080035build.dir-fmt configuration variable is set. The current directory is
36checked after that. If either is a Zephyr build directory, it is used.
Carles Cufi98980c62019-06-05 16:04:29 +020037'''.format(DEFAULT_BUILD_DIR)
Carles Cufi31bdad52019-04-26 21:53:02 +020038
Carles Cufi41f1f642019-06-17 11:47:11 +020039def _resolve_build_dir(fmt, guess, cwd, **kwargs):
Carles Cufi98980c62019-06-05 16:04:29 +020040 # Remove any None values, we do not want 'None' as a string
41 kwargs = {k: v for k, v in kwargs.items() if v is not None}
42 # Check if source_dir is below cwd first
43 source_dir = kwargs.get('source_dir')
44 if source_dir:
45 if escapes_directory(cwd, source_dir):
46 kwargs['source_dir'] = os.path.relpath(source_dir, cwd)
47 else:
48 # no meaningful relative path possible
49 kwargs['source_dir'] = ''
Carles Cufi31bdad52019-04-26 21:53:02 +020050
Carles Cufi41f1f642019-06-17 11:47:11 +020051 try:
52 return fmt.format(**kwargs)
53 except KeyError:
54 if not guess:
55 return None
Carles Cufi98980c62019-06-05 16:04:29 +020056
Carles Cufi41f1f642019-06-17 11:47:11 +020057 # Guess the build folder by iterating through all sub-folders from the
58 # root of the format string and trying to resolve. If resolving fails,
59 # proceed to iterate over subfolders only if there is a single folder
60 # present on each iteration.
61 parts = Path(fmt).parts
62 b = Path('.')
63 for p in parts:
64 # default to cwd in the first iteration
65 curr = b
66 b = b.joinpath(p)
67 try:
68 # if fmt is an absolute path, the first iteration will always
69 # resolve '/'
70 b = Path(str(b).format(**kwargs))
71 except KeyError:
72 # Missing key, check sub-folders and match if a single one exists
73 while True:
Carles Cufi4a504442019-09-06 15:18:41 +020074 if not curr.exists():
75 return None
Carles Cufi41f1f642019-06-17 11:47:11 +020076 dirs = [f for f in curr.iterdir() if f.is_dir()]
77 if len(dirs) != 1:
78 return None
79 curr = dirs[0]
80 if is_zephyr_build(str(curr)):
81 return str(curr)
82 return str(b)
83
84def find_build_dir(dir, guess=False, **kwargs):
Carles Cufi31bdad52019-04-26 21:53:02 +020085 '''Heuristic for finding a build directory.
86
Carles Cufi98980c62019-06-05 16:04:29 +020087 The default build directory is computed by reading the build.dir-fmt
88 configuration option, defaulting to DEFAULT_BUILD_DIR if not set. It might
89 be None if the build.dir-fmt configuration option is set but cannot be
90 resolved.
91 If the given argument is truthy, it is returned. Otherwise, if
92 the default build folder is a build directory, it is returned.
93 Next, if the current working directory is a build directory, it is
94 returned. Finally, the default build directory is returned (may be None).
95 '''
96
Carles Cufi31bdad52019-04-26 21:53:02 +020097 if dir:
98 build_dir = dir
99 else:
100 cwd = os.getcwd()
Carles Cufi98980c62019-06-05 16:04:29 +0200101 default = config.get('build', 'dir-fmt', fallback=DEFAULT_BUILD_DIR)
Carles Cufi41f1f642019-06-17 11:47:11 +0200102 default = _resolve_build_dir(default, guess, cwd, **kwargs)
103 log.dbg('config dir-fmt: {}'.format(default), level=log.VERBOSE_EXTREME)
Carles Cufi98980c62019-06-05 16:04:29 +0200104 if default and is_zephyr_build(default):
Carles Cufi49c4b1c2019-06-03 12:43:38 +0200105 build_dir = default
106 elif is_zephyr_build(cwd):
Carles Cufi31bdad52019-04-26 21:53:02 +0200107 build_dir = cwd
108 else:
Carles Cufi98980c62019-06-05 16:04:29 +0200109 build_dir = default
110 log.dbg('build dir: {}'.format(build_dir), level=log.VERBOSE_EXTREME)
111 if build_dir:
112 return os.path.abspath(build_dir)
113 else:
114 return None
Carles Cufi31bdad52019-04-26 21:53:02 +0200115
116def is_zephyr_build(path):
117 '''Return true if and only if `path` appears to be a valid Zephyr
118 build directory.
119
120 "Valid" means the given path is a directory which contains a CMake
Martí Bolívarf752c5e2020-10-06 16:36:24 -0700121 cache with a 'ZEPHYR_BASE' or 'ZEPHYR_TOOLCHAIN_VARIANT' variable.
122
123 (The check for ZEPHYR_BASE introduced sometime after Zephyr 2.4 to
124 fix https://github.com/zephyrproject-rtos/zephyr/issues/28876; we
125 keep support for the second variable around for compatibility with
126 versions 2.2 and earlier, which didn't have ZEPHYR_BASE in cache.
127 The cached ZEPHYR_BASE was added in
128 https://github.com/zephyrproject-rtos/zephyr/pull/23054.)
Carles Cufi31bdad52019-04-26 21:53:02 +0200129 '''
130 try:
Jan Van Winkel4d975db2019-05-04 14:44:56 +0200131 cache = zcmake.CMakeCache.from_build_dir(path)
Carles Cufi31bdad52019-04-26 21:53:02 +0200132 except FileNotFoundError:
133 cache = {}
134
Martí Bolívarf752c5e2020-10-06 16:36:24 -0700135 if 'ZEPHYR_BASE' in cache or 'ZEPHYR_TOOLCHAIN_VARIANT' in cache:
136 log.dbg(f'{path} is a zephyr build directory',
Carles Cufi31bdad52019-04-26 21:53:02 +0200137 level=log.VERBOSE_EXTREME)
138 return True
Martí Bolívarf752c5e2020-10-06 16:36:24 -0700139
140 log.dbg(f'{path} is NOT a valid zephyr build directory',
141 level=log.VERBOSE_EXTREME)
142 return False
Torsten Rasmussen8408af62021-11-22 10:29:56 +0100143
144
145def load_domains(path):
146 '''Load domains from a domains.yaml.
147
148 If domains.yaml is not found, then a single 'app' domain referring to the
149 top-level build folder is created and returned.
150 '''
151 domains_file = Path(path) / 'domains.yaml'
152
153 if not domains_file.is_file():
Grzegorz Swiderski652b0a82023-09-28 09:36:18 +0200154 return Domains.from_yaml(f'''\
155default: app
156build_dir: {path}
157domains:
158 - name: app
159 build_dir: {path}
160flash_order:
161 - app
162''')
Torsten Rasmussen8408af62021-11-22 10:29:56 +0100163
164 return Domains.from_file(domains_file)