Skip to content

Commit 5e6722d

Browse files
committed
Ported updates to targets, utils and settings scripts
1 parent 42c62ab commit 5e6722d

File tree

5 files changed

+98
-39
lines changed

5 files changed

+98
-39
lines changed

tools/options.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from tools.targets import TARGET_NAMES
2020

2121

22-
def get_default_options_parser():
22+
def get_default_options_parser(add_clean=True, add_options=True):
2323
parser = OptionParser()
2424

2525
targetnames = TARGET_NAMES
@@ -35,10 +35,12 @@ def get_default_options_parser():
3535
help="build using the given TOOLCHAIN (%s)" % ', '.join(toolchainlist),
3636
metavar="TOOLCHAIN")
3737

38-
parser.add_option("-c", "--clean", action="store_true", default=False,
39-
help="clean the build directory")
38+
if add_clean:
39+
parser.add_option("-c", "--clean", action="store_true", default=False,
40+
help="clean the build directory")
4041

41-
parser.add_option("-o", "--options", action="append",
42-
help='Add a build option ("save-asm": save the asm generated by the compiler, "debug-info": generate debugging information, "analyze": run Goanna static code analyzer")')
42+
if add_options:
43+
parser.add_option("-o", "--options", action="append",
44+
help='Add a build option ("save-asm": save the asm generated by the compiler, "debug-info": generate debugging information, "analyze": run Goanna static code analyzer")')
4345

4446
return parser

tools/settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,6 @@
9999
try:
100100
# Allow to overwrite the default settings without the need to edit the
101101
# settings file stored in the repository
102-
from tools.private_settings import *
102+
from mbed_settings import *
103103
except ImportError:
104-
print '[WARNING] Using default settings. Define your settings in the file "tools/private_settings.py" or in "./mbed_settings.py"'
104+
print '[WARNING] Using default settings. Define your settings in the file "./mbed_settings.py"'

tools/targets.py

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import json
3838
import inspect
3939
import sys
40-
40+
from tools.utils import json_file_to_dict
4141

4242
########################################################################################################################
4343
# Generic Target class that reads and interprets the data in targets.json
@@ -58,29 +58,19 @@ def wrapper(*args, **kwargs):
5858
class Target:
5959
# Cumulative attributes can have values appended to them, so they
6060
# need to be computed differently than regular attributes
61-
__cumulative_attributes = ['extra_labels', 'macros', 'device_has']
61+
__cumulative_attributes = ['extra_labels', 'macros', 'device_has', 'features']
6262

63-
# Utility function: traverse a dictionary and change all the strings in the dictionary to
64-
# ASCII from Unicode. Needed because the original mbed target definitions were written in
65-
# Python and used only ASCII strings, but the Python JSON decoder always returns Unicode
66-
# Based on http://stackoverflow.com/a/13105359
67-
@staticmethod
68-
def to_ascii(input):
69-
if isinstance(input, dict):
70-
return dict([(Target.to_ascii(key), Target.to_ascii(value)) for key, value in input.iteritems()])
71-
elif isinstance(input, list):
72-
return [Target.to_ascii(element) for element in input]
73-
elif isinstance(input, unicode):
74-
return input.encode('ascii')
75-
else:
76-
return input
63+
# {target_name: target_instance} map for all the targets in the system
64+
__target_map = {}
65+
66+
# List of targets that were added dynamically using "add_py_targets" (see below)
67+
__py_targets = set()
7768

7869
# Load the description of JSON target data
7970
@staticmethod
8071
@cached
8172
def get_json_target_data():
82-
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../hal/targets.json"), "rt") as f:
83-
return Target.to_ascii(json.load(f))
73+
return json_file_to_dict(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'hal', 'targets.json'))
8474

8575
# Get the members of this module using Python's "inspect" module
8676
@staticmethod
@@ -172,21 +162,58 @@ def __getattr_helper(self, attrname):
172162
return v if attrname != "progen" else self.__add_paths_to_progen(v)
173163

174164
# Return the value of an attribute
175-
# This function only looks for the attribute's value in the cache, the real work of computing the
176-
# attribute's value is done in the function above (__getattr_helper)
165+
# This function only computes the attribute's value once, then adds it to the instance attributes
166+
# (in __dict__), so the next time it is returned directly
177167
def __getattr__(self, attrname):
178-
if not self.attr_cache.has_key(attrname):
179-
self.attr_cache[attrname] = self.__getattr_helper(attrname)
180-
return self.attr_cache[attrname]
168+
v = self.__getattr_helper(attrname)
169+
self.__dict__[attrname] = v
170+
return v
171+
172+
# Add one or more new target(s) represented as a Python dictionary in 'new_targets'
173+
# It it an error to add a target with a name that exists in "targets.json"
174+
# However, it is OK to add a target that was previously added via "add_py_targets"
175+
# (this makes testing easier without changing the regular semantics)
176+
@staticmethod
177+
def add_py_targets(new_targets):
178+
crt_data = Target.get_json_target_data()
179+
# First add all elemnts to the internal dictionary
180+
for tk, tv in new_targets.items():
181+
if crt_data.has_key(tk) and (not tk in Target.__py_targets):
182+
raise Exception("Attempt to add target '%s' that already exists" % tk)
183+
crt_data[tk] = tv
184+
Target.__py_targets.add(tk)
185+
# Then create the new instances and update global variables if needed
186+
for tk, tv in new_targets.items():
187+
# Is the target already created?
188+
old_target = Target.__target_map.get(tk, None)
189+
# Instantiate this target. If it is public, update the data in
190+
# in TARGETS, TARGET_MAP, TARGET_NAMES
191+
new_target = Target(tk)
192+
if tv.get("public", True):
193+
if old_target: # remove the old target from TARGETS and TARGET_NAMES
194+
TARGETS.remove(old_target)
195+
TARGET_NAMES.remove(tk)
196+
# Add the new target
197+
TARGETS.append(new_target)
198+
TARGET_MAP[tk] = new_target
199+
TARGET_NAMES.append(tk)
200+
# Update the target cache
201+
Target.__target_map[tk] = new_target
202+
203+
# Return the target instance starting from the target name
204+
@staticmethod
205+
def get_target(name):
206+
if not Target.__target_map.has_key(name):
207+
Target.__target_map[name] = Target(name)
208+
return Target.__target_map[name]
181209

182210
def __init__(self, name):
183211
self.name = name
184212

185213
# Compute resolution order once (it will be used later in __getattr__)
186214
self.resolution_order = self.__get_resolution_order(self.name, [])
187-
188-
# Attribute cache: once an attribute's value is computed, don't compute it again
189-
self.attr_cache = {}
215+
# Create also a list with only the names of the targets in the resolution order
216+
self.resolution_order_names = [t[0] for t in self.resolution_order]
190217

191218
def program_cycle_s(self):
192219
try:
@@ -195,7 +222,12 @@ def program_cycle_s(self):
195222
return 4 if self.is_disk_virtual else 1.5
196223

197224
def get_labels(self):
198-
return [self.name] + CORE_LABELS[self.core] + self.extra_labels
225+
labels = [self.name] + CORE_LABELS[self.core] + self.extra_labels
226+
# Automatically define UVISOR_UNSUPPORTED if the target doesn't specifically
227+
# define UVISOR_SUPPORTED
228+
if not "UVISOR_SUPPORTED" in labels:
229+
labels.append("UVISOR_UNSUPPORTED")
230+
return labels
199231

200232
# For now, this function only allows "post binary" hooks (hooks that are executed after
201233
# the binary image is extracted from the executable file)
@@ -364,7 +396,7 @@ def binary_hook(t_self, resources, elf, binf):
364396
########################################################################################################################
365397

366398
# Instantiate all public targets
367-
TARGETS = [Target(name) for name, value in Target.get_json_target_data().items() if value.get("public", True)]
399+
TARGETS = [Target.get_target(name) for name, value in Target.get_json_target_data().items() if value.get("public", True)]
368400

369401
# Map each target name to its unique instance
370402
TARGET_MAP = dict([(t.name, t) for t in TARGETS])

tools/upload_results.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,4 +370,4 @@ def main(arguments):
370370
args.func(args)
371371

372372
if __name__ == '__main__':
373-
main(sys.argv[1:])
373+
main(sys.argv[1:])

tools/utils.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
from shutil import copyfile
2222
from os.path import isdir, join, exists, split, relpath, splitext
2323
from subprocess import Popen, PIPE, STDOUT, call
24-
24+
import json
25+
from collections import OrderedDict
2526

2627
def cmd(l, check=True, verbose=False, shell=False, cwd=None):
2728
text = l if shell else ' '.join(l)
@@ -34,8 +35,12 @@ def cmd(l, check=True, verbose=False, shell=False, cwd=None):
3435

3536
def run_cmd(command, wd=None, redirect=False):
3637
assert is_cmd_valid(command[0])
37-
p = Popen(command, stdout=PIPE, stderr=STDOUT if redirect else PIPE, cwd=wd)
38-
_stdout, _stderr = p.communicate()
38+
try:
39+
p = Popen(command, stdout=PIPE, stderr=STDOUT if redirect else PIPE, cwd=wd)
40+
_stdout, _stderr = p.communicate()
41+
except:
42+
print "[OS ERROR] Command: "+(' '.join(command))
43+
raise
3944
return _stdout, _stderr, p.returncode
4045

4146

@@ -170,3 +175,23 @@ def check_required_modules(required_modules, verbose=True):
170175
return False
171176
else:
172177
return True
178+
179+
# Utility function: traverse a dictionary and change all the strings in the dictionary to
180+
# ASCII from Unicode. Useful when reading ASCII JSON data, because the JSON decoder always
181+
# returns Unicode string.
182+
# Based on http://stackoverflow.com/a/13105359
183+
def dict_to_ascii(input):
184+
if isinstance(input, dict):
185+
return OrderedDict([(dict_to_ascii(key), dict_to_ascii(value)) for key, value in input.iteritems()])
186+
elif isinstance(input, list):
187+
return [dict_to_ascii(element) for element in input]
188+
elif isinstance(input, unicode):
189+
return input.encode('ascii')
190+
else:
191+
return input
192+
193+
# Read a JSON file and return its Python representation, transforming all the strings from Unicode
194+
# to ASCII. The order of keys in the JSON file is preserved.
195+
def json_file_to_dict(fname):
196+
with open(fname, "rt") as f:
197+
return dict_to_ascii(json.load(f, object_pairs_hook=OrderedDict))

0 commit comments

Comments
 (0)