21
21
import shutil
22
22
import inspect
23
23
import sys
24
+ from collections import namedtuple
24
25
from tools .patch import patch
25
26
from tools .paths import TOOLS_BOOTLOADERS
26
27
from tools .utils import json_file_to_dict
27
28
29
+ __all__ = ["target" , "TARGETS" , "TARGET_MAP" , "TARGET_NAMES" , "CORE_LABELS" ,
30
+ "HookError" , "generate_py_target" , "Target" ,
31
+ "CUMULATIVE_ATTRIBUTES" , "get_resolution_order" ]
32
+
28
33
CORE_LABELS = {
29
34
"ARM7TDMI-S" : ["ARM7" , "LIKE_CORTEX_ARM7" ],
30
35
"Cortex-M0" : ["M0" , "CORTEX_M" , "LIKE_CORTEX_M0" ],
@@ -60,11 +65,58 @@ def wrapper(*args, **kwargs):
60
65
return CACHES [(func .__name__ , args )]
61
66
return wrapper
62
67
63
- class Target (object ):
68
+
69
+ # Cumulative attributes can have values appended to them, so they
70
+ # need to be computed differently than regular attributes
71
+ CUMULATIVE_ATTRIBUTES = ['extra_labels' , 'macros' , 'device_has' , 'features' ]
72
+
73
+
74
+ def get_resolution_order (json_data , target_name , order , level = 0 ):
75
+ """ Return the order in which target descriptions are searched for
76
+ attributes. This mimics the Python 2.2 method resolution order, which
77
+ is what the old targets.py module used. For more details, check
78
+ http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
79
+ The resolution order contains (name, level) tuples, where "name" is the
80
+ name of the class and "level" is the level in the inheritance hierarchy
81
+ (the target itself is at level 0, its first parent at level 1, its
82
+ parent's parent at level 2 and so on)
83
+ """
84
+ # the resolution order can't contain duplicate target names
85
+ if target_name not in [l [0 ] for l in order ]:
86
+ order .append ((target_name , level ))
87
+ parents = json_data [target_name ].get ("inherits" , [])
88
+ for par in parents :
89
+ order = get_resolution_order (json_data , par , order , level + 1 )
90
+ return order
91
+
92
+
93
+ def target (name , json_data ):
94
+ """Construct a target object"""
95
+ resolution_order = get_resolution_order (json_data , name , [])
96
+ resolution_order_names = [tgt for tgt , _ in resolution_order ]
97
+ return Target (name = name ,
98
+ json_data = {key : value for key , value in json_data .items ()
99
+ if key in resolution_order_names },
100
+ resolution_order = resolution_order ,
101
+ resolution_order_names = resolution_order_names )
102
+
103
+ def generate_py_target (new_targets , name ):
104
+ """Add one or more new target(s) represented as a Python dictionary
105
+ in 'new_targets'. It is an error to add a target with a name that
106
+ already exists.
107
+ """
108
+ base_targets = Target .get_json_target_data ()
109
+ for new_target in new_targets .keys ():
110
+ if new_target in base_targets :
111
+ raise Exception ("Attempt to add target '%s' that already exists"
112
+ % new_target )
113
+ total_data = {}
114
+ total_data .update (new_targets )
115
+ total_data .update (base_targets )
116
+ return target (name , total_data )
117
+
118
+ class Target (namedtuple ("Target" , "name json_data resolution_order resolution_order_names" )):
64
119
"""An object to represent a Target (MCU/Board)"""
65
- # Cumulative attributes can have values appended to them, so they
66
- # need to be computed differently than regular attributes
67
- cumulative_attributes = ['extra_labels' , 'macros' , 'device_has' , 'features' ]
68
120
69
121
# Default location of the 'targets.json' file
70
122
__targets_json_location_default = os .path .join (
@@ -95,24 +147,6 @@ def get_module_data():
95
147
return dict ([(m [0 ], m [1 ]) for m in
96
148
inspect .getmembers (sys .modules [__name__ ])])
97
149
98
- def __get_resolution_order (self , target_name , order , level = 0 ):
99
- """ Return the order in which target descriptions are searched for
100
- attributes. This mimics the Python 2.2 method resolution order, which
101
- is what the old targets.py module used. For more details, check
102
- http://makina-corpus.com/blog/metier/2014/python-tutorial-understanding-python-mro-class-search-path
103
- The resolution order contains (name, level) tuples, where "name" is the
104
- name of the class and "level" is the level in the inheritance hierarchy
105
- (the target itself is at level 0, its first parent at level 1, its
106
- parent's parent at level 2 and so on)
107
- """
108
- # the resolution order can't contain duplicate target names
109
- if target_name not in [l [0 ] for l in order ]:
110
- order .append ((target_name , level ))
111
- parents = self .get_json_target_data ()[target_name ].get ("inherits" , [])
112
- for par in parents :
113
- order = self .__get_resolution_order (par , order , level + 1 )
114
- return order
115
-
116
150
@staticmethod
117
151
def __add_paths_to_progen (data ):
118
152
"""Modify the exporter specification ("progen") by changing all
@@ -133,14 +167,14 @@ def __getattr_cumulative(self, attrname):
133
167
"""Look for the attribute in the class and its parents, as defined by
134
168
the resolution order
135
169
"""
136
- tdata = self .get_json_target_data ()
170
+ tdata = self .json_data
137
171
# For a cumulative attribute, figure out when it was defined the
138
172
# last time (in attribute resolution order) then follow the "_add"
139
173
# and "_remove" data fields
140
- for idx , target in enumerate (self .resolution_order ):
174
+ for idx , tgt in enumerate (self .resolution_order ):
141
175
# the attribute was defined at this level in the resolution
142
176
# order
143
- if attrname in tdata [target [0 ]]:
177
+ if attrname in tdata [tgt [0 ]]:
144
178
def_idx = idx
145
179
break
146
180
else :
@@ -192,13 +226,13 @@ def __getattr_cumulative(self, attrname):
192
226
193
227
def __getattr_helper (self , attrname ):
194
228
"""Compute the value of a given target attribute"""
195
- if attrname in self . cumulative_attributes :
229
+ if attrname in CUMULATIVE_ATTRIBUTES :
196
230
return self .__getattr_cumulative (attrname )
197
231
else :
198
- tdata = self .get_json_target_data ()
232
+ tdata = self .json_data
199
233
starting_value = None
200
- for target in self .resolution_order :
201
- data = tdata [target [0 ]]
234
+ for tgt in self .resolution_order :
235
+ data = tdata [tgt [0 ]]
202
236
if data .has_key (attrname ):
203
237
starting_value = data [attrname ]
204
238
break
@@ -226,17 +260,8 @@ def __getattr__(self, attrname):
226
260
@cached
227
261
def get_target (target_name ):
228
262
""" Return the target instance starting from the target name """
229
- return Target (target_name )
263
+ return target (target_name , Target . get_json_target_data () )
230
264
231
- def __init__ (self , target_name ):
232
- self .name = target_name
233
-
234
- # Compute resolution order once (it will be used later in __getattr__)
235
- self .resolution_order = self .__get_resolution_order (self .name , [])
236
- # Create also a list with only the names of the targets in the
237
- # resolution order
238
- self .resolution_order_names = [target [0 ] for target
239
- in self .resolution_order ]
240
265
241
266
@property
242
267
def program_cycle_s (self ):
@@ -248,7 +273,8 @@ def program_cycle_s(self):
248
273
except AttributeError :
249
274
return 4 if self .is_disk_virtual else 1.5
250
275
251
- def get_labels (self ):
276
+ @property
277
+ def labels (self ):
252
278
"""Get all possible labels for this target"""
253
279
labels = [self .name ] + CORE_LABELS [self .core ] + self .extra_labels
254
280
# Automatically define UVISOR_UNSUPPORTED if the target doesn't
@@ -487,9 +513,9 @@ def get_target_detect_codes():
487
513
""" Returns dictionary mapping detect_code -> platform_name
488
514
"""
489
515
result = {}
490
- for target in TARGETS :
491
- for detect_code in target .detect_code :
492
- result [detect_code ] = target .name
516
+ for tgt in TARGETS :
517
+ for detect_code in tgt .detect_code :
518
+ result [detect_code ] = tgt .name
493
519
return result
494
520
495
521
def set_targets_json_location (location = None ):
@@ -500,9 +526,9 @@ def set_targets_json_location(location=None):
500
526
# re-initialization does not create new variables, it keeps the old ones
501
527
# instead. This ensures compatibility with code that does
502
528
# "from tools.targets import TARGET_NAMES"
503
- TARGETS [:] = [Target .get_target (target ) for target , obj
529
+ TARGETS [:] = [Target .get_target (tgt ) for tgt , obj
504
530
in Target .get_json_target_data ().items ()
505
531
if obj .get ("public" , True )]
506
532
TARGET_MAP .clear ()
507
- TARGET_MAP .update (dict ([(target .name , target ) for target in TARGETS ]))
533
+ TARGET_MAP .update (dict ([(tgt .name , tgt ) for tgt in TARGETS ]))
508
534
TARGET_NAMES [:] = TARGET_MAP .keys ()
0 commit comments