blob: eca5bde6aeb9f1c877d807dc42ea1c936919be23 [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
Carles Cufi41f1f642019-06-17 11:47:11 +020015from pathlib import Path
Carles Cufi31bdad52019-04-26 21:53:02 +020016from west import log
Carles Cufi98980c62019-06-05 16:04:29 +020017from west.configuration import config
18from west.util import escapes_directory
Torsten Rasmussen8408af62021-11-22 10:29:56 +010019from domains import Domains
Carles Cufi31bdad52019-04-26 21:53:02 +020020
21DEFAULT_BUILD_DIR = 'build'
22'''Name of the default Zephyr build directory.'''
23
24DEFAULT_CMAKE_GENERATOR = 'Ninja'
25'''Name of the default CMake generator.'''
26
Carles Cufi98980c62019-06-05 16:04:29 +020027FIND_BUILD_DIR_DESCRIPTION = '''\
Martí Bolívar6e4c2b92020-06-25 12:58:58 -070028If the build directory is not given, the default is {}/ unless the
Martí Bolívareb95bed2020-02-10 06:40:29 -080029build.dir-fmt configuration variable is set. The current directory is
30checked after that. If either is a Zephyr build directory, it is used.
Carles Cufi98980c62019-06-05 16:04:29 +020031'''.format(DEFAULT_BUILD_DIR)
Carles Cufi31bdad52019-04-26 21:53:02 +020032
Carles Cufi41f1f642019-06-17 11:47:11 +020033def _resolve_build_dir(fmt, guess, cwd, **kwargs):
Carles Cufi98980c62019-06-05 16:04:29 +020034 # Remove any None values, we do not want 'None' as a string
35 kwargs = {k: v for k, v in kwargs.items() if v is not None}
36 # Check if source_dir is below cwd first
37 source_dir = kwargs.get('source_dir')
38 if source_dir:
39 if escapes_directory(cwd, source_dir):
40 kwargs['source_dir'] = os.path.relpath(source_dir, cwd)
41 else:
42 # no meaningful relative path possible
43 kwargs['source_dir'] = ''
Carles Cufi31bdad52019-04-26 21:53:02 +020044
Carles Cufi41f1f642019-06-17 11:47:11 +020045 try:
46 return fmt.format(**kwargs)
47 except KeyError:
48 if not guess:
49 return None
Carles Cufi98980c62019-06-05 16:04:29 +020050
Carles Cufi41f1f642019-06-17 11:47:11 +020051 # Guess the build folder by iterating through all sub-folders from the
52 # root of the format string and trying to resolve. If resolving fails,
53 # proceed to iterate over subfolders only if there is a single folder
54 # present on each iteration.
55 parts = Path(fmt).parts
56 b = Path('.')
57 for p in parts:
58 # default to cwd in the first iteration
59 curr = b
60 b = b.joinpath(p)
61 try:
62 # if fmt is an absolute path, the first iteration will always
63 # resolve '/'
64 b = Path(str(b).format(**kwargs))
65 except KeyError:
66 # Missing key, check sub-folders and match if a single one exists
67 while True:
Carles Cufi4a504442019-09-06 15:18:41 +020068 if not curr.exists():
69 return None
Carles Cufi41f1f642019-06-17 11:47:11 +020070 dirs = [f for f in curr.iterdir() if f.is_dir()]
71 if len(dirs) != 1:
72 return None
73 curr = dirs[0]
74 if is_zephyr_build(str(curr)):
75 return str(curr)
76 return str(b)
77
78def find_build_dir(dir, guess=False, **kwargs):
Carles Cufi31bdad52019-04-26 21:53:02 +020079 '''Heuristic for finding a build directory.
80
Carles Cufi98980c62019-06-05 16:04:29 +020081 The default build directory is computed by reading the build.dir-fmt
82 configuration option, defaulting to DEFAULT_BUILD_DIR if not set. It might
83 be None if the build.dir-fmt configuration option is set but cannot be
84 resolved.
85 If the given argument is truthy, it is returned. Otherwise, if
86 the default build folder is a build directory, it is returned.
87 Next, if the current working directory is a build directory, it is
88 returned. Finally, the default build directory is returned (may be None).
89 '''
90
Carles Cufi31bdad52019-04-26 21:53:02 +020091 if dir:
92 build_dir = dir
93 else:
94 cwd = os.getcwd()
Carles Cufi98980c62019-06-05 16:04:29 +020095 default = config.get('build', 'dir-fmt', fallback=DEFAULT_BUILD_DIR)
Carles Cufi41f1f642019-06-17 11:47:11 +020096 default = _resolve_build_dir(default, guess, cwd, **kwargs)
97 log.dbg('config dir-fmt: {}'.format(default), level=log.VERBOSE_EXTREME)
Carles Cufi98980c62019-06-05 16:04:29 +020098 if default and is_zephyr_build(default):
Carles Cufi49c4b1c2019-06-03 12:43:38 +020099 build_dir = default
100 elif is_zephyr_build(cwd):
Carles Cufi31bdad52019-04-26 21:53:02 +0200101 build_dir = cwd
102 else:
Carles Cufi98980c62019-06-05 16:04:29 +0200103 build_dir = default
104 log.dbg('build dir: {}'.format(build_dir), level=log.VERBOSE_EXTREME)
105 if build_dir:
106 return os.path.abspath(build_dir)
107 else:
108 return None
Carles Cufi31bdad52019-04-26 21:53:02 +0200109
110def is_zephyr_build(path):
111 '''Return true if and only if `path` appears to be a valid Zephyr
112 build directory.
113
114 "Valid" means the given path is a directory which contains a CMake
Martí Bolívarf752c5e2020-10-06 16:36:24 -0700115 cache with a 'ZEPHYR_BASE' or 'ZEPHYR_TOOLCHAIN_VARIANT' variable.
116
117 (The check for ZEPHYR_BASE introduced sometime after Zephyr 2.4 to
118 fix https://github.com/zephyrproject-rtos/zephyr/issues/28876; we
119 keep support for the second variable around for compatibility with
120 versions 2.2 and earlier, which didn't have ZEPHYR_BASE in cache.
121 The cached ZEPHYR_BASE was added in
122 https://github.com/zephyrproject-rtos/zephyr/pull/23054.)
Carles Cufi31bdad52019-04-26 21:53:02 +0200123 '''
124 try:
Jan Van Winkel4d975db2019-05-04 14:44:56 +0200125 cache = zcmake.CMakeCache.from_build_dir(path)
Carles Cufi31bdad52019-04-26 21:53:02 +0200126 except FileNotFoundError:
127 cache = {}
128
Martí Bolívarf752c5e2020-10-06 16:36:24 -0700129 if 'ZEPHYR_BASE' in cache or 'ZEPHYR_TOOLCHAIN_VARIANT' in cache:
130 log.dbg(f'{path} is a zephyr build directory',
Carles Cufi31bdad52019-04-26 21:53:02 +0200131 level=log.VERBOSE_EXTREME)
132 return True
Martí Bolívarf752c5e2020-10-06 16:36:24 -0700133
134 log.dbg(f'{path} is NOT a valid zephyr build directory',
135 level=log.VERBOSE_EXTREME)
136 return False
Torsten Rasmussen8408af62021-11-22 10:29:56 +0100137
138
139def load_domains(path):
140 '''Load domains from a domains.yaml.
141
142 If domains.yaml is not found, then a single 'app' domain referring to the
143 top-level build folder is created and returned.
144 '''
145 domains_file = Path(path) / 'domains.yaml'
146
147 if not domains_file.is_file():
148 return Domains.from_data({'default': 'app',
149 'build_dir': path,
150 'domains': [{'name': 'app', 'build_dir': path}]})
151
152 return Domains.from_file(domains_file)