• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ARMmbed / mbed-os-tools / #457

24 Aug 2024 09:15PM UTC coverage: 0.0% (-59.9%) from 59.947%
#457

push

coveralls-python

web-flow
Merge 7c6dbce13 into c467d6f14

0 of 4902 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/mbed_os_tools/test/mbed_target_info.py
1
# Copyright (c) 2018, Arm Limited and affiliates.
2
# SPDX-License-Identifier: Apache-2.0
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15

16
import os
×
17
import re
×
18
import json
×
19
from os import walk
×
20
try:
×
21
    from contextlib import suppress
×
22
except ImportError:
×
23
    from contextlib import contextmanager
×
24
    @contextmanager
×
25
    def suppress(*excs):
×
26
        try:
×
27
            yield
×
28
        except excs:
×
29
            pass
×
30
from .mbed_common_api import run_cli_process
×
31
from .mbed_greentea_log import gt_logger
×
32

33

34
## Information about some properties of targets (platforms)
35
#
36
#  "default" entry is used to fetch "global" properties if they are not
37
#  specified with each platform
38
#
39

40
TARGET_INFO_MAPPING = {
×
41
    "default" : {
42
        "program_cycle_s": 4,
43
        "binary_type": ".bin",
44
        "copy_method": "default",
45
        "reset_method": "default"
46
    },
47

48
    "K64F" : {
49
        "yotta_targets": [
50
                {
51
                    "yotta_target": "frdm-k64f-gcc",
52
                    "mbed_toolchain": "GCC_ARM"
53
                },
54
                {
55
                    "yotta_target": "frdm-k64f-armcc",
56
                    "mbed_toolchain": "ARM"
57
                }
58
             ],
59
        "properties" : {
60
                "binary_type": ".bin",
61
                "copy_method": "default",
62
                "reset_method": "default",
63
                "program_cycle_s": 4
64
            }
65
    },
66
    "RAPIDIOT_K64F" : {
67
        "properties" : {
68
                "forced_reset_timeout":7
69
            }
70
    },
71
    "NUCLEO_F401RE" : {
72
        "yotta_targets": [
73
                {
74
                    "yotta_target": "st-nucleo-f401re-gcc",
75
                    "mbed_toolchain": "GCC_ARM"
76
                }
77
             ],
78
        "properties" : {
79
                "binary_type": ".bin",
80
                "copy_method": "cp",
81
                "reset_method": "default",
82
                "program_cycle_s": 4
83
            }
84
        },
85
    "NRF51_DK" : {
86
        "yotta_targets": [
87
                {
88
                    "yotta_target": "nrf51dk-gcc",
89
                    "mbed_toolchain": "GCC_ARM"
90
                },
91
                {
92
                    "yotta_target": "nrf51dk-armcc",
93
                    "mbed_toolchain": "ARM"
94
                }
95
             ],
96
        "properties" : {
97
                "binary_type": "-combined.hex",
98
                "copy_method": "shell",
99
                "reset_method": "default",
100
                "program_cycle_s": 4
101
            }
102
        },
103
    "NRF51822" : {
104
        "yotta_targets": [
105
                {
106
                    "yotta_target": "mkit-gcc",
107
                    "mbed_toolchain": "GCC_ARM"
108
                },
109
                {
110
                    "yotta_target": "mkit-armcc",
111
                    "mbed_toolchain": "ARM"
112
                }
113
             ],
114
        "properties" : {
115
                "binary_type": "-combined.hex",
116
                "copy_method": "shell",
117
                "reset_method": "default",
118
                "program_cycle_s": 4
119
            }
120
        },
121
    "ARCH_BLE" : {
122
        "yotta_targets": [
123
                {
124
                    "yotta_target": "tinyble-gcc",
125
                    "mbed_toolchain": "GCC_ARM"
126
                }
127
             ],
128
        "properties" : {
129
                "binary_type": "-combined.hex",
130
                "copy_method": "shell",
131
                "reset_method": "default",
132
                "program_cycle_s": 4
133
            }
134
        }
135
}
136

137
TARGET_TOOLCAHINS = {
×
138
    '-armcc': 'ARM',
139
    '-gcc': 'GCC_ARM',
140
    '-iar': 'IAR',
141
}
142

143
def get_mbed_target_call_yotta_target():
×
144
    """! Calls yotta's 'yotta target' command to get information about
145
    """
146
    cmd = ['yotta', '--plain', 'target']
×
147
    gt_logger.gt_log("checking yotta target in current directory")
×
148
    gt_logger.gt_log_tab("calling yotta: %s"% " ".join(cmd))
×
149
    _stdout, _stderr, _ret = run_cli_process(cmd)
×
150
    return _stdout, _stderr, _ret
×
151

152
def parse_yotta_json_for_build_name(yotta_json_content):
×
153
    """! Function parse .yotta.json to fetch set yotta target
154
    @param yotta_json_content Content of .yotta_json file
155
    @return String with set yotta target name, None if no target found
156
    """
157
    try:
×
158
        return yotta_json_content['build']['target'].split(',')[0]
×
159
    except KeyError:
×
160
        return None
×
161

162
def get_yotta_target_from_local_config(yotta_json='.yotta.json'):
×
163
    """! Load yotta target from local configuration file
164
    @param yotta_json File in format of .yotta.json which stores current target names
165
    @return Yotta target set in currect directory, None if no info is available
166
    @details
167
    Example structure of .yotta.json file:
168
    {
169
      "build": {
170
        "target": "frdm-k64f-gcc,*",
171
        "targetSetExplicitly": true
172
      }
173
    }
174
    """
175
    if not os.path.exists(yotta_json):
×
176
        return None
×
177

178
    try:
×
179
        gt_logger.gt_log("parsing local file '%s' for target information"% yotta_json)
×
180

181
        with open(yotta_json, 'r') as f:
×
182
            return parse_yotta_json_for_build_name(json.load(f))
×
183
    except (IOError, ValueError) as e:
×
184
        gt_logger.gt_log(str(e))
×
185
    return None
×
186

187
def get_mbed_target_from_current_dir():
×
188
    """! Function uses yotta target command to check current target
189
    @return Returns current target or None if target not found (e.g. not yotta package)
190
    """
191
    # We will first try to load current target name using .yotta.json file
192
    result = get_yotta_target_from_local_config()
×
193

194
    # If we can't read .yotta.json, we will try to use command line to fetch target name
195
    if not result:
×
196
        _stdout, _stderr, _ret = get_mbed_target_call_yotta_target()
×
197
        if not _ret:
×
198
            for line in _stdout.splitlines():
×
199
                target = parse_yotta_target_cmd_output(line)
×
200
                if target:
×
201
                    result = target
×
202
                    break
×
203
    return result
×
204

205
def parse_yotta_target_cmd_output(line):
×
206
    """! Function parsed output from command 'yotta --plain target'
207
         looking for valid target names. First one will be used as 'default'
208
         of currently set yotta target
209
    @param line Line of text from 'yotta target' command
210
    @return Yotta target name, None if not parsed
211
    @details
212

213
    Example call to 'yotta target' command (all lines)
214
    $ yotta --plain target
215
    frdm-k64f-gcc 2.0.0
216
    kinetis-k64-gcc 2.2.0
217
    mbed-gcc 1.2.2
218

219
    """
220
    # Regular expression to parse stings like: 'frdm-k64f-gcc 2.0.0'
221
    m = re.search(r'[\w\d_-]+ \d+\.\d+\.\d+', line)
×
222
    if m and len(m.group()):
×
223
        result = line.split()[0]
×
224
        return result
×
225
    return None
×
226

227
def get_mbed_targets_from_yotta_local_module(mbed_classic_name, yotta_targets_path='./yotta_targets'):
×
228
    """! Function is parsing local yotta targets to fetch matching mbed device target's name
229
    @return Function returns list of possible targets or empty list if value not found
230
    """
231
    result = []
×
232

233
    if not os.path.exists(yotta_targets_path):
×
234
        return result
×
235

236
    # All local directories with yotta targets
237
    target_dirs = [target_dir_name for target_dir_name in os.listdir(yotta_targets_path) if os.path.isdir(os.path.join(yotta_targets_path, target_dir_name))]
×
238

239
    gt_logger.gt_log("local yotta target search in '%s' for compatible mbed-target '%s'"% (gt_logger.gt_bright(yotta_targets_path), gt_logger.gt_bright(mbed_classic_name.lower().strip())))
×
240

241
    for target_dir in target_dirs:
×
242
        path = os.path.join(yotta_targets_path, target_dir, 'target.json')
×
243
        try:
×
244
            with open(path, 'r') as data_file:
×
245
                target_json_data = json.load(data_file)
×
246
                yotta_target_name = parse_mbed_target_from_target_json(mbed_classic_name, target_json_data)
×
247
                if yotta_target_name:
×
248
                    target_dir_name = os.path.join(yotta_targets_path, target_dir)
×
249
                    gt_logger.gt_log_tab("inside '%s' found compatible target '%s'"% (gt_logger.gt_bright(target_dir_name), gt_logger.gt_bright(yotta_target_name)))
×
250
                    result.append(yotta_target_name)
×
251
        except IOError as e:
×
252
            gt_logger.gt_log_err(str(e))
×
253
    return result
×
254

255
def parse_mbed_target_from_target_json(mbed_classic_name, target_json_data):
×
256
    if (not target_json_data or
×
257
        'keywords' not in target_json_data or
258
        'name' not in target_json_data):
259
        return None
×
260

261
    for keyword in target_json_data['keywords']:
×
262
        target, _, name = keyword.partition(':')
×
263
        if  (target == "mbed-target" and
×
264
             name.lower() == mbed_classic_name.lower()):
265
            return target_json_data['name']
×
266

267
    return None
×
268

269
def get_mbed_targets_from_yotta(mbed_classic_name):
×
270
    """! Function is using 'yotta search' command to fetch matching mbed device target's name
271
    @return Function returns list of possible targets or empty list if value not found
272
    @details Example:
273
             $ yt search -k mbed-target:k64f target
274
             frdm-k64f-gcc 0.0.16: Official mbed build target for the mbed frdm-k64f development board.
275
             frdm-k64f-armcc 0.0.10: Official mbed build target for the mbed frdm-k64f development board, using the armcc toolchain.
276

277
             Note: Function prints on console
278
    """
279
    result = []
×
280
    cmd = ['yotta', '--plain', 'search', '-k', 'mbed-target:%s'% mbed_classic_name.lower().strip(), 'target']
×
281
    gt_logger.gt_log("yotta search for mbed-target '%s'"% gt_logger.gt_bright(mbed_classic_name.lower().strip()))
×
282
    gt_logger.gt_log_tab("calling yotta: %s"% " ".join(cmd))
×
283
    _stdout, _stderr, _ret = run_cli_process(cmd)
×
284
    if not _ret:
×
285
        for line in _stdout.splitlines():
×
286
            yotta_target_name = parse_yotta_search_cmd_output(line)
×
287
            if yotta_target_name:
×
288
                if yotta_target_name and yotta_target_name not in result:
×
289
                    result.append(yotta_target_name)
×
290
                    gt_logger.gt_log_tab("found target '%s'" % gt_logger.gt_bright(yotta_target_name))
×
291
    else:
292
        gt_logger.gt_log_err("calling yotta search failed!")
×
293
    return result
×
294

295
def parse_yotta_search_cmd_output(line):
×
296
    m = re.search('([\w\d-]+) \d+\.\d+\.\d+[$:]?', line)
×
297
    if m and len(m.groups()):
×
298
        yotta_target_name = m.groups()[0]
×
299
        return yotta_target_name
×
300
    return None
×
301

302
def add_target_info_mapping(mbed_classic_name, map_platform_to_yt_target=None, use_yotta_registry=False):
×
303
    """! Adds more target information to TARGET_INFO_MAPPING by searching in yotta registry
304
    @return Returns TARGET_INFO_MAPPING updated with new targets
305
    @details Note: function mutates TARGET_INFO_MAPPING
306
    """
307
    yotta_target_search = get_mbed_targets_from_yotta_local_module(mbed_classic_name)
×
308
    if use_yotta_registry:
×
309
        # We can also use yotta registry to check for target compatibility (slower)
310
        yotta_registry_target_search = get_mbed_targets_from_yotta(mbed_classic_name)
×
311
        yotta_target_search.extend(yotta_registry_target_search)
×
312
        yotta_target_search = list(set(yotta_target_search))    # Reduce repeated values
×
313

314
    # Add extra targets to already existing and detected in the system platforms
315
    if map_platform_to_yt_target and mbed_classic_name in map_platform_to_yt_target:
×
316
        yotta_target_search = list(set(yotta_target_search + map_platform_to_yt_target[mbed_classic_name]))
×
317

318
    # Check if this targets are already there
319
    if mbed_classic_name not in TARGET_INFO_MAPPING:
×
320
        TARGET_INFO_MAPPING[mbed_classic_name] = {
×
321
        "yotta_targets": [],
322
        "properties" : {
323
                "binary_type": ".bin",
324
                "copy_method": "shell",
325
                "reset_method": "default",
326
                "program_cycle_s": 6
327
            }
328
        }
329

330
    target_desc = TARGET_INFO_MAPPING[mbed_classic_name]
×
331
    if 'yotta_targets' not in target_desc:
×
332
        return TARGET_INFO_MAPPING
×
333

334
    # All yt targets supported by 'mbed_classic_name' board
335
    mbeds_yt_targets = []
×
336
    for target in target_desc['yotta_targets']:
×
337
        mbeds_yt_targets.append(target['yotta_target'])
×
338

339
    # Check if any of yotta targets is new to TARGET_INFO_MAPPING
340
    for new_yt_target in yotta_target_search:
×
341
        if new_yt_target in mbeds_yt_targets:
×
342
            continue
×
343

344
        gt_logger.gt_log_tab("discovered extra target '%s'"% new_yt_target)
×
345
        # We want to at least guess toolchain type by target's name suffix
346
        mbed_toolchain = 'UNKNOWN'
×
347
        for toolchain_suffix in TARGET_TOOLCAHINS:
×
348
            if new_yt_target.endswith(toolchain_suffix):
×
349
                mbed_toolchain = TARGET_TOOLCAHINS[toolchain_suffix]
×
350
                break
×
351

352
        TARGET_INFO_MAPPING[mbed_classic_name]['yotta_targets'].append({
×
353
            'yotta_target': new_yt_target,
354
            'mbed_toolchain': mbed_toolchain
355
            })
356

357
    return TARGET_INFO_MAPPING
×
358

359
def get_mbed_clasic_target_info(mbed_classic_name, map_platform_to_yt_target=None, use_yotta_registry=False):
×
360
    """! Function resolves meta-data information about target given as mbed classic name.
361
    @param mbed_classic_name Mbed classic (mbed 2.0) name e.g. K64F, LPC1768 etc.
362
    @param map_platform_to_yt_target User defined mapping platform:supported target
363
    @details Function first updated TARGET_INFO_MAPPING structure and later checks if mbed classic name is available in mapping structure
364
    @return Returns information about yotta target for specific toolchain
365
    """
366
    TARGET_INFO_MAPPING = add_target_info_mapping(mbed_classic_name, map_platform_to_yt_target, use_yotta_registry)
×
367
    return TARGET_INFO_MAPPING[mbed_classic_name] if mbed_classic_name in TARGET_INFO_MAPPING else None
×
368

369
def get_binary_type_for_platform(platform):
×
370
    """
371
    Gives binary type for the given platform.
372

373
    :param platform:
374
    :return:
375
    """
376
    #return TARGET_INFO_MAPPING[platform]['properties']["binary_type"]
377
    return get_platform_property(platform, 'binary_type')
×
378

379
def get_platform_property(platform, property):
×
380
    """
381
    Gives platform property.
382

383
    :param platform:
384
    :return: property value, None if property not found
385
    """
386

387
    default = _get_platform_property_from_default(property)
×
388
    from_targets_json = _get_platform_property_from_targets(
×
389
        platform, property, default)
390
    if from_targets_json:
×
391
        return from_targets_json
×
392
    from_info_mapping = _get_platform_property_from_info_mapping(platform, property)
×
393
    if from_info_mapping:
×
394
        return from_info_mapping
×
395
    return default
×
396

397
def _get_platform_property_from_default(property):
×
398
    with suppress(KeyError):
×
399
        return TARGET_INFO_MAPPING['default'][property]
×
400

401
def _get_platform_property_from_info_mapping(platform, property):
×
402
    with suppress(KeyError):
×
403
        return TARGET_INFO_MAPPING[platform]['properties'][property]
×
404

405
def _platform_property_from_targets_json(targets, platform, property, default):
×
406
    """! Get a platforms's property from the target data structure in
407
            targets.json. Takes into account target inheritance.
408
    @param targets Data structure parsed from targets.json
409
    @param platform Name of the platform
410
    @param property Name of the property
411
    @param default the fallback value if none is found, but the target exists
412
    @return property value, None if property not found
413

414
    """
415
    with suppress(KeyError):
×
416
        return targets[platform][property]
×
417
    with suppress(KeyError):
×
418
        for inherited_target in targets[platform]['inherits']:
×
419
            result = _platform_property_from_targets_json(targets, inherited_target, property, None)
×
420
            if result:
×
421
                return result
×
422
    if platform in targets:
×
423
        return default
×
424

425
IGNORED_DIRS = ['.build', 'BUILD', 'tools']
×
426

427
def _find_targets_json(path):
×
428
    for root, dirs, files in walk(path, followlinks=True):
×
429
        for ignored_dir in IGNORED_DIRS:
×
430
            if ignored_dir in dirs:
×
431
                dirs.remove(ignored_dir)
×
432
        if 'targets.json' in files:
×
433
            yield os.path.join(root, 'targets.json')
×
434

435
def _get_platform_property_from_targets(platform, property, default):
×
436
    """
437
    Load properties from targets.json file somewhere in the project structure
438

439
    :param platform:
440
    :return: property value, None if property not found
441
    """
442
    for targets_path in _find_targets_json(os.getcwd()):
×
443
        with suppress(IOError, ValueError):
×
444
            with open(targets_path, 'r') as f:
×
445
                targets = json.load(f)
×
446
                result = _platform_property_from_targets_json(targets, platform, property, default)
×
447
                if result:
×
448
                    return result
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc