Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 1 | # Copyright (c) 2018 Foundries.io |
| 2 | # |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | |
| 5 | import argparse |
| 6 | import os |
Martí Bolívar | 0186ead | 2019-12-09 11:05:11 -0800 | [diff] [blame^] | 7 | import shlex |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 8 | |
| 9 | from west import log |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 10 | from west.configuration import config |
Jan Van Winkel | 4d975db | 2019-05-04 14:44:56 +0200 | [diff] [blame] | 11 | from zcmake import DEFAULT_CMAKE_GENERATOR, run_cmake, run_build, CMakeCache |
Marti Bolivar | 69099e3 | 2019-05-04 13:16:15 -0600 | [diff] [blame] | 12 | from build_helpers import is_zephyr_build, find_build_dir, \ |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 13 | FIND_BUILD_DIR_DESCRIPTION |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 14 | |
Carles Cufi | 31bdad5 | 2019-04-26 21:53:02 +0200 | [diff] [blame] | 15 | from zephyr_ext_common import Forceable |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 16 | |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 17 | _ARG_SEPARATOR = '--' |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 18 | |
Marti Bolivar | 1f5e6d8 | 2019-05-01 16:48:04 -0600 | [diff] [blame] | 19 | BUILD_USAGE = '''\ |
| 20 | west build [-h] [-b BOARD] [-d BUILD_DIR] |
| 21 | [-t TARGET] [-p {auto, always, never}] [-c] [--cmake-only] |
Marti Bolivar | 3a486a8 | 2019-05-04 13:32:36 -0600 | [diff] [blame] | 22 | [-n] [-o BUILD_OPT] [-f] |
| 23 | [source_dir] -- [cmake_opt [cmake_opt ...]] |
Marti Bolivar | 1f5e6d8 | 2019-05-01 16:48:04 -0600 | [diff] [blame] | 24 | ''' |
| 25 | |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 26 | BUILD_DESCRIPTION = '''\ |
| 27 | Convenience wrapper for building Zephyr applications. |
| 28 | |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 29 | positional arguments: |
Marti Bolivar | 83a5fd8 | 2019-05-04 13:56:36 -0600 | [diff] [blame] | 30 | source_dir Use this path as the source directory |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 31 | cmake_opt Extra options to pass to CMake; implies -c |
| 32 | ''' |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 33 | |
Marti Bolivar | e6873b8 | 2019-06-02 22:48:52 -0600 | [diff] [blame] | 34 | def _banner(msg): |
| 35 | log.inf('-- west build: ' + msg, colorize=True) |
| 36 | |
Marti Bolivar | d159503 | 2019-05-04 13:14:28 -0600 | [diff] [blame] | 37 | def config_get(option, fallback): |
| 38 | return config.get('build', option, fallback=fallback) |
| 39 | |
| 40 | def config_getboolean(option, fallback): |
| 41 | return config.getboolean('build', option, fallback=fallback) |
| 42 | |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 43 | class AlwaysIfMissing(argparse.Action): |
| 44 | |
Marti Bolivar | 69099e3 | 2019-05-04 13:16:15 -0600 | [diff] [blame] | 45 | def __call__(self, parser, namespace, values, option_string=None): |
| 46 | setattr(namespace, self.dest, values or 'always') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 47 | |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 48 | class Build(Forceable): |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 49 | |
| 50 | def __init__(self): |
| 51 | super(Build, self).__init__( |
| 52 | 'build', |
Marti Bolivar | 808028b | 2019-01-28 10:45:02 -0700 | [diff] [blame] | 53 | # Keep this in sync with the string in west-commands.yml. |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 54 | 'compile a Zephyr application', |
| 55 | BUILD_DESCRIPTION, |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 56 | accepts_unknown_args=True) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 57 | |
| 58 | self.source_dir = None |
| 59 | '''Source directory for the build, or None on error.''' |
| 60 | |
| 61 | self.build_dir = None |
| 62 | '''Final build directory used to run the build, or None on error.''' |
| 63 | |
| 64 | self.created_build_dir = False |
| 65 | '''True if the build directory was created; False otherwise.''' |
| 66 | |
| 67 | self.run_cmake = False |
| 68 | '''True if CMake was run; False otherwise. |
| 69 | |
| 70 | Note: this only describes CMake runs done by this command. The |
| 71 | build system generated by CMake may also update itself due to |
| 72 | internal logic.''' |
| 73 | |
| 74 | self.cmake_cache = None |
| 75 | '''Final parsed CMake cache for the build, or None on error.''' |
| 76 | |
| 77 | def do_add_parser(self, parser_adder): |
| 78 | parser = parser_adder.add_parser( |
| 79 | self.name, |
| 80 | help=self.help, |
| 81 | formatter_class=argparse.RawDescriptionHelpFormatter, |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 82 | description=self.description, |
Marti Bolivar | 1f5e6d8 | 2019-05-01 16:48:04 -0600 | [diff] [blame] | 83 | usage=BUILD_USAGE) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 84 | |
| 85 | # Remember to update scripts/west-completion.bash if you add or remove |
| 86 | # flags |
| 87 | |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 88 | parser.add_argument('-b', '--board', help='Board to build for') |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 89 | # Hidden option for backwards compatibility |
| 90 | parser.add_argument('-s', '--source-dir', help=argparse.SUPPRESS) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 91 | parser.add_argument('-d', '--build-dir', |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 92 | help='Build directory. ' + |
| 93 | FIND_BUILD_DIR_DESCRIPTION + |
| 94 | " Otherwise the default build directory is " + |
| 95 | "created and used.") |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 96 | parser.add_argument('-t', '--target', |
Marti Bolivar | 83a5fd8 | 2019-05-04 13:56:36 -0600 | [diff] [blame] | 97 | help='''Build system target to run''') |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 98 | parser.add_argument('-p', '--pristine', choices=['auto', 'always', |
| 99 | 'never'], action=AlwaysIfMissing, nargs='?', |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 100 | help='''Control whether the build folder is made |
Marti Bolivar | 83a5fd8 | 2019-05-04 13:56:36 -0600 | [diff] [blame] | 101 | pristine before running CMake. --pristine is the |
| 102 | same as --pristine=always. If 'auto', it will |
| 103 | be made pristine only if needed.''') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 104 | parser.add_argument('-c', '--cmake', action='store_true', |
| 105 | help='Force CMake to run') |
Marti Bolivar | 1f5e6d8 | 2019-05-01 16:48:04 -0600 | [diff] [blame] | 106 | parser.add_argument('--cmake-only', action='store_true', |
| 107 | help="Just run CMake; don't build. Implies -c.") |
Marti Bolivar | 8465cf2 | 2019-05-01 17:24:23 -0600 | [diff] [blame] | 108 | parser.add_argument('-n', '--just-print', '--dry-run', '--recon', |
| 109 | dest='dry_run', action='store_true', |
Marti Bolivar | 83a5fd8 | 2019-05-04 13:56:36 -0600 | [diff] [blame] | 110 | help='''Just print the build commands; don't run |
| 111 | them''') |
Marti Bolivar | 3a486a8 | 2019-05-04 13:32:36 -0600 | [diff] [blame] | 112 | parser.add_argument('-o', '--build-opt', default=[], action='append', |
| 113 | help='''Options to pass to the build tool. |
| 114 | May be given more than once to append multiple |
| 115 | values.''') |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 116 | self.add_force_arg(parser) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 117 | return parser |
| 118 | |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 119 | def do_run(self, args, remainder): |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 120 | self.args = args # Avoid having to pass them around |
Marti Bolivar | 8b9b7e7 | 2019-05-23 09:31:31 -0600 | [diff] [blame] | 121 | self.config_board = config_get('board', None) |
Marti Bolivar | a1ef696 | 2019-05-01 17:06:15 -0600 | [diff] [blame] | 122 | log.dbg('args: {} remainder: {}'.format(args, remainder), |
| 123 | level=log.VERBOSE_EXTREME) |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 124 | # Store legacy -s option locally |
| 125 | source_dir = self.args.source_dir |
| 126 | self._parse_remainder(remainder) |
| 127 | if source_dir: |
| 128 | if self.args.source_dir: |
| 129 | log.die("source directory specified twice:({} and {})".format( |
| 130 | source_dir, self.args.source_dir)) |
| 131 | self.args.source_dir = source_dir |
| 132 | log.dbg('source_dir: {} cmake_opts: {}'.format(self.args.source_dir, |
Marti Bolivar | a1ef696 | 2019-05-01 17:06:15 -0600 | [diff] [blame] | 133 | self.args.cmake_opts), |
| 134 | level=log.VERBOSE_EXTREME) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 135 | self._sanity_precheck() |
| 136 | self._setup_build_dir() |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 137 | |
| 138 | if args.pristine is not None: |
| 139 | pristine = args.pristine |
| 140 | else: |
| 141 | # Load the pristine={auto, always, never} configuration value |
Marti Bolivar | d159503 | 2019-05-04 13:14:28 -0600 | [diff] [blame] | 142 | pristine = config_get('pristine', 'never') |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 143 | if pristine not in ['auto', 'always', 'never']: |
Marti Bolivar | 69099e3 | 2019-05-04 13:16:15 -0600 | [diff] [blame] | 144 | log.wrn( |
| 145 | 'treating unknown build.pristine value "{}" as "never"'. |
| 146 | format(pristine)) |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 147 | pristine = 'never' |
| 148 | self.auto_pristine = (pristine == 'auto') |
| 149 | |
| 150 | log.dbg('pristine: {} auto_pristine: {}'.format(pristine, |
Marti Bolivar | a1ef696 | 2019-05-01 17:06:15 -0600 | [diff] [blame] | 151 | self.auto_pristine), |
| 152 | level=log.VERBOSE_VERY) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 153 | if is_zephyr_build(self.build_dir): |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 154 | if pristine == 'always': |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 155 | self._run_pristine() |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 156 | self.run_cmake = True |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 157 | else: |
| 158 | self._update_cache() |
Marti Bolivar | 1f5e6d8 | 2019-05-01 16:48:04 -0600 | [diff] [blame] | 159 | if (self.args.cmake or self.args.cmake_opts or |
| 160 | self.args.cmake_only): |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 161 | self.run_cmake = True |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 162 | else: |
| 163 | self.run_cmake = True |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 164 | self.source_dir = self._find_source_dir() |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 165 | self._sanity_check() |
| 166 | |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 167 | board, origin = self._find_board() |
| 168 | self._run_cmake(board, origin, self.args.cmake_opts) |
Marti Bolivar | 1f5e6d8 | 2019-05-01 16:48:04 -0600 | [diff] [blame] | 169 | if args.cmake_only: |
| 170 | return |
| 171 | |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 172 | self._sanity_check() |
| 173 | self._update_cache() |
| 174 | |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 175 | self._run_build(args.target) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 176 | |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 177 | def _find_board(self): |
| 178 | board, origin = None, None |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 179 | if self.cmake_cache: |
| 180 | board, origin = (self.cmake_cache.get('CACHED_BOARD'), |
| 181 | 'CMakeCache.txt') |
| 182 | elif self.args.board: |
| 183 | board, origin = self.args.board, 'command line' |
| 184 | elif 'BOARD' in os.environ: |
| 185 | board, origin = os.environ['BOARD'], 'env' |
Marti Bolivar | 8b9b7e7 | 2019-05-23 09:31:31 -0600 | [diff] [blame] | 186 | elif self.config_board is not None: |
| 187 | board, origin = self.config_board, 'configfile' |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 188 | return board, origin |
| 189 | |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 190 | def _parse_remainder(self, remainder): |
| 191 | self.args.source_dir = None |
| 192 | self.args.cmake_opts = None |
| 193 | try: |
| 194 | # Only one source_dir is allowed, as the first positional arg |
| 195 | if remainder[0] != _ARG_SEPARATOR: |
| 196 | self.args.source_dir = remainder[0] |
| 197 | remainder = remainder[1:] |
| 198 | # Only the first argument separator is consumed, the rest are |
| 199 | # passed on to CMake |
| 200 | if remainder[0] == _ARG_SEPARATOR: |
| 201 | remainder = remainder[1:] |
Ulf Magnusson | 5d307c9 | 2019-09-02 15:35:56 +0200 | [diff] [blame] | 202 | if remainder: |
Carles Cufi | b710177 | 2019-02-21 15:58:19 +0100 | [diff] [blame] | 203 | self.args.cmake_opts = remainder |
| 204 | except IndexError: |
| 205 | return |
| 206 | |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 207 | def _sanity_precheck(self): |
| 208 | app = self.args.source_dir |
| 209 | if app: |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 210 | self.check_force( |
| 211 | os.path.isdir(app), |
| 212 | 'source directory {} does not exist'.format(app)) |
| 213 | self.check_force( |
| 214 | 'CMakeLists.txt' in os.listdir(app), |
| 215 | "{} doesn't contain a CMakeLists.txt".format(app)) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 216 | |
| 217 | def _update_cache(self): |
| 218 | try: |
Carles Cufi | 31bdad5 | 2019-04-26 21:53:02 +0200 | [diff] [blame] | 219 | self.cmake_cache = CMakeCache.from_build_dir(self.build_dir) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 220 | except FileNotFoundError: |
| 221 | pass |
| 222 | |
| 223 | def _setup_build_dir(self): |
| 224 | # Initialize build_dir and created_build_dir attributes. |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 225 | # If we created the build directory, we must run CMake. |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 226 | log.dbg('setting up build directory', level=log.VERBOSE_EXTREME) |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 227 | # The CMake Cache has not been loaded yet, so this is safe |
Ulf Magnusson | 89efaed | 2019-09-05 13:44:09 +0200 | [diff] [blame] | 228 | board, _ = self._find_board() |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 229 | source_dir = self._find_source_dir() |
| 230 | app = os.path.split(source_dir)[1] |
| 231 | build_dir = find_build_dir(self.args.build_dir, board=board, |
| 232 | source_dir=source_dir, app=app) |
| 233 | if not build_dir: |
| 234 | log.die('Unable to determine a default build folder. Check ' |
| 235 | 'your build.dir-fmt configuration option') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 236 | |
| 237 | if os.path.exists(build_dir): |
| 238 | if not os.path.isdir(build_dir): |
| 239 | log.die('build directory {} exists and is not a directory'. |
| 240 | format(build_dir)) |
| 241 | else: |
| 242 | os.makedirs(build_dir, exist_ok=False) |
| 243 | self.created_build_dir = True |
| 244 | self.run_cmake = True |
| 245 | |
| 246 | self.build_dir = build_dir |
| 247 | |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 248 | def _find_source_dir(self): |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 249 | # Initialize source_dir attribute, either from command line argument, |
| 250 | # implicitly from the build directory's CMake cache, or using the |
| 251 | # default (current working directory). |
| 252 | log.dbg('setting up source directory', level=log.VERBOSE_EXTREME) |
| 253 | if self.args.source_dir: |
| 254 | source_dir = self.args.source_dir |
| 255 | elif self.cmake_cache: |
Marti Bolivar | acda257 | 2019-04-10 17:09:17 -0600 | [diff] [blame] | 256 | source_dir = self.cmake_cache.get('CMAKE_HOME_DIRECTORY') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 257 | if not source_dir: |
Marti Bolivar | acda257 | 2019-04-10 17:09:17 -0600 | [diff] [blame] | 258 | # This really ought to be there. The build directory |
| 259 | # must be corrupted somehow. Let's see what we can do. |
| 260 | log.die('build directory', self.build_dir, |
| 261 | 'CMake cache has no CMAKE_HOME_DIRECTORY;', |
| 262 | 'please give a source_dir') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 263 | else: |
| 264 | source_dir = os.getcwd() |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 265 | return os.path.abspath(source_dir) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 266 | |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 267 | def _sanity_check_source_dir(self): |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 268 | if self.source_dir == self.build_dir: |
| 269 | # There's no forcing this. |
| 270 | log.die('source and build directory {} cannot be the same; ' |
| 271 | 'use --build-dir {} to specify a build directory'. |
| 272 | format(self.source_dir, self.build_dir)) |
| 273 | |
| 274 | srcrel = os.path.relpath(self.source_dir) |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 275 | self.check_force( |
| 276 | not is_zephyr_build(self.source_dir), |
| 277 | 'it looks like {srcrel} is a build directory: ' |
| 278 | 'did you mean --build-dir {srcrel} instead?'. |
| 279 | format(srcrel=srcrel)) |
| 280 | self.check_force( |
| 281 | 'CMakeLists.txt' in os.listdir(self.source_dir), |
| 282 | 'source directory "{srcrel}" does not contain ' |
| 283 | 'a CMakeLists.txt; is this really what you ' |
| 284 | 'want to build? (Use -s SOURCE_DIR to specify ' |
| 285 | 'the application source directory)'. |
| 286 | format(srcrel=srcrel)) |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 287 | |
| 288 | def _sanity_check(self): |
| 289 | # Sanity check the build configuration. |
| 290 | # Side effect: may update cmake_cache attribute. |
| 291 | log.dbg('sanity checking the build', level=log.VERBOSE_EXTREME) |
| 292 | self._sanity_check_source_dir() |
| 293 | |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 294 | if not self.cmake_cache: |
| 295 | return # That's all we can check without a cache. |
| 296 | |
| 297 | cached_app = self.cmake_cache.get('APPLICATION_SOURCE_DIR') |
| 298 | log.dbg('APPLICATION_SOURCE_DIR:', cached_app, |
| 299 | level=log.VERBOSE_EXTREME) |
| 300 | source_abs = (os.path.abspath(self.args.source_dir) |
| 301 | if self.args.source_dir else None) |
| 302 | cached_abs = os.path.abspath(cached_app) if cached_app else None |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 303 | |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 304 | log.dbg('pristine:', self.auto_pristine, level=log.VERBOSE_EXTREME) |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 305 | # If the build directory specifies a source app, make sure it's |
| 306 | # consistent with --source-dir. |
| 307 | apps_mismatched = (source_abs and cached_abs and |
| 308 | source_abs != cached_abs) |
| 309 | self.check_force( |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 310 | not apps_mismatched or self.auto_pristine, |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 311 | 'Build directory "{}" is for application "{}", but source ' |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 312 | 'directory "{}" was specified; please clean it, use --pristine, ' |
| 313 | 'or use --build-dir to set another build directory'. |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 314 | format(self.build_dir, cached_abs, source_abs)) |
| 315 | if apps_mismatched: |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 316 | self.run_cmake = True # If they insist, we need to re-run cmake. |
| 317 | |
Marti Bolivar | 8b9b7e7 | 2019-05-23 09:31:31 -0600 | [diff] [blame] | 318 | # If CACHED_BOARD is not defined, we need some other way to |
| 319 | # find the board. |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 320 | cached_board = self.cmake_cache.get('CACHED_BOARD') |
| 321 | log.dbg('CACHED_BOARD:', cached_board, level=log.VERBOSE_EXTREME) |
Marti Bolivar | 8b9b7e7 | 2019-05-23 09:31:31 -0600 | [diff] [blame] | 322 | # If apps_mismatched and self.auto_pristine are true, we will |
| 323 | # run pristine on the build, invalidating the cached |
| 324 | # board. In that case, we need some way of getting the board. |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 325 | self.check_force((cached_board and |
| 326 | not (apps_mismatched and self.auto_pristine)) |
Marti Bolivar | 8b9b7e7 | 2019-05-23 09:31:31 -0600 | [diff] [blame] | 327 | or self.args.board or self.config_board or |
| 328 | os.environ.get('BOARD'), |
| 329 | 'Cached board not defined, please provide it ' |
| 330 | '(provide --board, set default with ' |
| 331 | '"west config build.board <BOARD>", or set ' |
| 332 | 'BOARD in the environment)') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 333 | |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 334 | # Check consistency between cached board and --board. |
| 335 | boards_mismatched = (self.args.board and cached_board and |
| 336 | self.args.board != cached_board) |
| 337 | self.check_force( |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 338 | not boards_mismatched or self.auto_pristine, |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 339 | 'Build directory {} targets board {}, but board {} was specified. ' |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 340 | '(Clean the directory, use --pristine, or use --build-dir to ' |
| 341 | 'specify a different one.)'. |
Marti Bolivar | 8c44799 | 2019-01-30 13:53:40 -0700 | [diff] [blame] | 342 | format(self.build_dir, cached_board, self.args.board)) |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 343 | |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 344 | if self.auto_pristine and (apps_mismatched or boards_mismatched): |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 345 | self._run_pristine() |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 346 | self.cmake_cache = None |
| 347 | log.dbg('run_cmake:', True, level=log.VERBOSE_EXTREME) |
| 348 | self.run_cmake = True |
| 349 | |
| 350 | # Tricky corner-case: The user has not specified a build folder but |
| 351 | # there was one in the CMake cache. Since this is going to be |
| 352 | # invalidated, reset to CWD and re-run the basic tests. |
| 353 | if ((boards_mismatched and not apps_mismatched) and |
Marti Bolivar | 69099e3 | 2019-05-04 13:16:15 -0600 | [diff] [blame] | 354 | (not source_abs and cached_abs)): |
Carles Cufi | 98980c6 | 2019-06-05 16:04:29 +0200 | [diff] [blame] | 355 | self.source_dir = self._find_source_dir() |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 356 | self._sanity_check_source_dir() |
| 357 | |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 358 | def _run_cmake(self, board, origin, cmake_opts): |
Marti Bolivar | e6873b8 | 2019-06-02 22:48:52 -0600 | [diff] [blame] | 359 | _banner( |
| 360 | '''build configuration: |
| 361 | source directory: {} |
| 362 | build directory: {}{} |
| 363 | BOARD: {}'''. |
| 364 | format(self.source_dir, self.build_dir, |
| 365 | ' (created)' if self.created_build_dir else '', |
| 366 | ('{} (origin: {})'.format(board, origin) if board |
| 367 | else 'UNKNOWN'))) |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 368 | |
| 369 | if board is None and config_getboolean('board_warn', True): |
| 370 | log.wrn('This looks like a fresh build and BOARD is unknown;', |
| 371 | "so it probably won't work. To fix, use", |
| 372 | '--board=<your-board>.') |
| 373 | log.inf('Note: to silence the above message, run', |
| 374 | "'west config build.board_warn false'") |
| 375 | |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 376 | if not self.run_cmake: |
Marti Bolivar | a1ef696 | 2019-05-01 17:06:15 -0600 | [diff] [blame] | 377 | log.dbg('Not generating a build system; one is present.') |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 378 | return |
| 379 | |
Marti Bolivar | e6873b8 | 2019-06-02 22:48:52 -0600 | [diff] [blame] | 380 | _banner('generating a build system') |
| 381 | |
Marti Bolivar | 88fb8ba | 2019-05-04 13:14:57 -0600 | [diff] [blame] | 382 | if board is not None and origin != 'CMakeCache.txt': |
| 383 | cmake_opts = ['-DBOARD={}'.format(board)] |
| 384 | else: |
| 385 | cmake_opts = [] |
| 386 | if self.args.cmake_opts: |
| 387 | cmake_opts.extend(self.args.cmake_opts) |
| 388 | |
Martí Bolívar | 0186ead | 2019-12-09 11:05:11 -0800 | [diff] [blame^] | 389 | user_args = config_get('cmake-args', None) |
| 390 | if user_args: |
| 391 | cmake_opts.extend(shlex.split(user_args)) |
| 392 | |
Carles Cufi | 3c6584d | 2019-04-14 21:51:16 +0200 | [diff] [blame] | 393 | # Invoke CMake from the current working directory using the |
| 394 | # -S and -B options (officially introduced in CMake 3.13.0). |
| 395 | # This is important because users expect invocations like this |
| 396 | # to Just Work: |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 397 | # |
| 398 | # west build -- -DOVERLAY_CONFIG=relative-path.conf |
| 399 | final_cmake_args = ['-B{}'.format(self.build_dir), |
Carles Cufi | 3c6584d | 2019-04-14 21:51:16 +0200 | [diff] [blame] | 400 | '-S{}'.format(self.source_dir), |
Marti Bolivar | bbe890a | 2019-05-01 16:53:32 -0600 | [diff] [blame] | 401 | '-G{}'.format(config_get('generator', |
| 402 | DEFAULT_CMAKE_GENERATOR))] |
Marti Bolivar | ab82264 | 2019-01-23 08:31:06 -0700 | [diff] [blame] | 403 | if cmake_opts: |
| 404 | final_cmake_args.extend(cmake_opts) |
Marti Bolivar | 8465cf2 | 2019-05-01 17:24:23 -0600 | [diff] [blame] | 405 | run_cmake(final_cmake_args, dry_run=self.args.dry_run) |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 406 | |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 407 | def _run_pristine(self): |
Marti Bolivar | e6873b8 | 2019-06-02 22:48:52 -0600 | [diff] [blame] | 408 | _banner('making build dir {} pristine'.format(self.build_dir)) |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 409 | |
| 410 | zb = os.environ.get('ZEPHYR_BASE') |
| 411 | if not zb: |
| 412 | log.die('Internal error: ZEPHYR_BASE not set in the environment, ' |
| 413 | 'and should have been by the main script') |
| 414 | |
| 415 | if not is_zephyr_build(self.build_dir): |
Marti Bolivar | 69099e3 | 2019-05-04 13:16:15 -0600 | [diff] [blame] | 416 | log.die('Refusing to run pristine on a folder that is not a ' |
| 417 | 'Zephyr build system') |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 418 | |
| 419 | cmake_args = ['-P', '{}/cmake/pristine.cmake'.format(zb)] |
Marti Bolivar | 8465cf2 | 2019-05-01 17:24:23 -0600 | [diff] [blame] | 420 | run_cmake(cmake_args, cwd=self.build_dir, dry_run=self.args.dry_run) |
Carles Cufi | 3a88dce | 2019-04-18 16:46:12 +0200 | [diff] [blame] | 421 | |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 422 | def _run_build(self, target): |
Marti Bolivar | e6873b8 | 2019-06-02 22:48:52 -0600 | [diff] [blame] | 423 | if target: |
| 424 | _banner('running target {}'.format(target)) |
| 425 | else: |
| 426 | _banner('building application') |
Carles Cufi | b7c7591 | 2019-04-14 21:52:45 +0200 | [diff] [blame] | 427 | extra_args = ['--target', target] if target else [] |
Marti Bolivar | 3a486a8 | 2019-05-04 13:32:36 -0600 | [diff] [blame] | 428 | if self.args.build_opt: |
| 429 | extra_args.append('--') |
| 430 | extra_args.extend(self.args.build_opt) |
Marti Bolivar | 0396b6e | 2019-05-06 18:10:35 -0600 | [diff] [blame] | 431 | if self.args.verbose: |
| 432 | self._append_verbose_args(extra_args, |
| 433 | not bool(self.args.build_opt)) |
Marti Bolivar | 8465cf2 | 2019-05-01 17:24:23 -0600 | [diff] [blame] | 434 | run_build(self.build_dir, extra_args=extra_args, |
| 435 | dry_run=self.args.dry_run) |
Marti Bolivar | 0396b6e | 2019-05-06 18:10:35 -0600 | [diff] [blame] | 436 | |
| 437 | def _append_verbose_args(self, extra_args, add_dashes): |
| 438 | # These hacks are only needed for CMake versions earlier than |
| 439 | # 3.14. When Zephyr's minimum version is at least that, we can |
| 440 | # drop this nonsense and just run "cmake --build BUILD -v". |
| 441 | self._update_cache() |
| 442 | if not self.cmake_cache: |
| 443 | return |
| 444 | generator = self.cmake_cache.get('CMAKE_GENERATOR') |
| 445 | if not generator: |
| 446 | return |
| 447 | # Substring matching is for things like "Eclipse CDT4 - Ninja". |
| 448 | if 'Ninja' in generator: |
| 449 | if add_dashes: |
| 450 | extra_args.append('--') |
| 451 | extra_args.append('-v') |
| 452 | elif generator == 'Unix Makefiles': |
| 453 | if add_dashes: |
| 454 | extra_args.append('--') |
| 455 | extra_args.append('VERBOSE=1') |