19
19
import tempfile
20
20
import colorama
21
21
22
-
22
+ from copy import copy
23
23
from types import ListType
24
24
from shutil import rmtree
25
- from os .path import join , exists , basename
25
+ from os .path import join , exists , basename , abspath , normpath
26
+ from os import getcwd , walk
26
27
from time import time
28
+ import fnmatch
27
29
28
- from tools .utils import mkdir , run_cmd , run_cmd_ext , NotSupportedException
30
+ from tools .utils import mkdir , run_cmd , run_cmd_ext , NotSupportedException , ToolException
29
31
from tools .paths import MBED_TARGETS_PATH , MBED_LIBRARIES , MBED_API , MBED_HAL , MBED_COMMON
30
32
from tools .targets import TARGET_NAMES , TARGET_MAP
31
33
from tools .libraries import Library
32
34
from tools .toolchains import TOOLCHAIN_CLASSES
33
35
from jinja2 import FileSystemLoader
34
36
from jinja2 .environment import Environment
35
-
37
+ from tools . config import Config
36
38
37
39
def prep_report (report , target_name , toolchain_name , id_name ):
38
40
# Setup report keys
@@ -75,37 +77,90 @@ def add_result_to_report(report, result):
75
77
result_wrap = { 0 : result }
76
78
report [target ][toolchain ][id_name ].append (result_wrap )
77
79
80
+ def get_config (src_path , target , toolchain_name ):
81
+ # Convert src_path to a list if needed
82
+ src_paths = [src_path ] if type (src_path ) != ListType else src_path
83
+ # We need to remove all paths which are repeated to avoid
84
+ # multiple compilations and linking with the same objects
85
+ src_paths = [src_paths [0 ]] + list (set (src_paths [1 :]))
86
+
87
+ # Create configuration object
88
+ config = Config (target , src_paths )
89
+
90
+ # If the 'target' argument is a string, convert it to a target instance
91
+ if isinstance (target , str ):
92
+ try :
93
+ target = TARGET_MAP [target ]
94
+ except KeyError :
95
+ raise KeyError ("Target '%s' not found" % target )
96
+
97
+ # Toolchain instance
98
+ try :
99
+ toolchain = TOOLCHAIN_CLASSES [toolchain_name ](target , options = None , notify = None , macros = None , silent = True , extra_verbose = False )
100
+ except KeyError as e :
101
+ raise KeyError ("Toolchain %s not supported" % toolchain_name )
102
+
103
+ # Scan src_path for config files
104
+ resources = toolchain .scan_resources (src_paths [0 ])
105
+ for path in src_paths [1 :]:
106
+ resources .add (toolchain .scan_resources (path ))
107
+
108
+ config .add_config_files (resources .json_files )
109
+ return config .get_config_data ()
110
+
78
111
def build_project (src_path , build_path , target , toolchain_name ,
79
112
libraries_paths = None , options = None , linker_script = None ,
80
113
clean = False , notify = None , verbose = False , name = None , macros = None , inc_dirs = None ,
81
- jobs = 1 , silent = False , report = None , properties = None , project_id = None , project_description = None , extra_verbose = False ):
114
+ jobs = 1 , silent = False , report = None , properties = None , project_id = None , project_description = None ,
115
+ extra_verbose = False , config = None ):
82
116
""" This function builds project. Project can be for example one test / UT
83
117
"""
84
- # Toolchain instance
85
- toolchain = TOOLCHAIN_CLASSES [toolchain_name ](target , options , notify , macros , silent , extra_verbose = extra_verbose )
86
- toolchain .VERBOSE = verbose
87
- toolchain .jobs = jobs
88
- toolchain .build_all = clean
118
+
119
+ # Convert src_path to a list if needed
89
120
src_paths = [src_path ] if type (src_path ) != ListType else src_path
90
121
91
122
# We need to remove all paths which are repeated to avoid
92
123
# multiple compilations and linking with the same objects
93
124
src_paths = [src_paths [0 ]] + list (set (src_paths [1 :]))
94
- PROJECT_BASENAME = basename (src_paths [0 ])
125
+ first_src_path = src_paths [0 ] if src_paths [0 ] != "." and src_paths [0 ] != "./" else getcwd ()
126
+ abs_path = abspath (first_src_path )
127
+ project_name = basename (normpath (abs_path ))
128
+
129
+ # If the configuration object was not yet created, create it now
130
+ config = config or Config (target , src_paths )
131
+
132
+ # If the 'target' argument is a string, convert it to a target instance
133
+ if isinstance (target , str ):
134
+ try :
135
+ target = TARGET_MAP [target ]
136
+ except KeyError :
137
+ raise KeyError ("Target '%s' not found" % target )
138
+
139
+ # Toolchain instance
140
+ try :
141
+ toolchain = TOOLCHAIN_CLASSES [toolchain_name ](target , options , notify , macros , silent , extra_verbose = extra_verbose )
142
+ except KeyError as e :
143
+ raise KeyError ("Toolchain %s not supported" % toolchain_name )
144
+
145
+ toolchain .VERBOSE = verbose
146
+ toolchain .jobs = jobs
147
+ toolchain .build_all = clean
95
148
96
149
if name is None :
97
150
# We will use default project name based on project folder name
98
- name = PROJECT_BASENAME
99
- toolchain .info ("Building project %s (%s, %s)" % (PROJECT_BASENAME . upper () , target .name , toolchain_name ))
151
+ name = project_name
152
+ toolchain .info ("Building project %s (%s, %s)" % (project_name , target .name , toolchain_name ))
100
153
else :
101
154
# User used custom global project name to have the same name for the
102
- toolchain .info ("Building project %s to %s (%s, %s)" % (PROJECT_BASENAME . upper () , name , target .name , toolchain_name ))
155
+ toolchain .info ("Building project %s to %s (%s, %s)" % (project_name , name , target .name , toolchain_name ))
103
156
104
157
105
158
if report != None :
106
159
start = time ()
107
- id_name = project_id .upper ()
108
- description = project_description
160
+
161
+ # If project_id is specified, use that over the default name
162
+ id_name = project_id .upper () if project_id else name .upper ()
163
+ description = project_description if project_description else name
109
164
vendor_label = target .extra_labels [0 ]
110
165
cur_result = None
111
166
prep_report (report , target .name , toolchain_name , id_name )
@@ -139,13 +194,18 @@ def build_project(src_path, build_path, target, toolchain_name,
139
194
resources .inc_dirs .extend (inc_dirs )
140
195
else :
141
196
resources .inc_dirs .append (inc_dirs )
197
+
198
+ # Update the configuration with any .json files found while scanning
199
+ config .add_config_files (resources .json_files )
200
+ # And add the configuration macros to the toolchain
201
+ toolchain .add_macros (config .get_config_data_macros ())
202
+
142
203
# Compile Sources
143
204
for path in src_paths :
144
205
src = toolchain .scan_resources (path )
145
206
objects = toolchain .compile_sources (src , build_path , resources .inc_dirs )
146
207
resources .objects .extend (objects )
147
208
148
-
149
209
# Link Program
150
210
res , needed_update = toolchain .link_program (resources , build_path , name )
151
211
@@ -181,11 +241,11 @@ def build_project(src_path, build_path, target, toolchain_name,
181
241
# Let Exception propagate
182
242
raise e
183
243
184
-
185
244
def build_library (src_paths , build_path , target , toolchain_name ,
186
- dependencies_paths = None , options = None , name = None , clean = False ,
245
+ dependencies_paths = None , options = None , name = None , clean = False , archive = True ,
187
246
notify = None , verbose = False , macros = None , inc_dirs = None , inc_dirs_ext = None ,
188
- jobs = 1 , silent = False , report = None , properties = None , extra_verbose = False ):
247
+ jobs = 1 , silent = False , report = None , properties = None , extra_verbose = False ,
248
+ project_id = None ):
189
249
""" src_path: the path of the source directory
190
250
build_path: the path of the build directory
191
251
target: ['LPC1768', 'LPC11U24', 'LPC2368']
@@ -201,11 +261,16 @@ def build_library(src_paths, build_path, target, toolchain_name,
201
261
src_paths = [src_paths ]
202
262
203
263
# The first path will give the name to the library
204
- name = basename (src_paths [0 ])
264
+ project_name = basename (src_paths [0 ] if src_paths [0 ] != "." and src_paths [0 ] != "./" else getcwd ())
265
+ if name is None :
266
+ # We will use default project name based on project folder name
267
+ name = project_name
205
268
206
269
if report != None :
207
270
start = time ()
208
- id_name = name .upper ()
271
+
272
+ # If project_id is specified, use that over the default name
273
+ id_name = project_id .upper () if project_id else name .upper ()
209
274
description = name
210
275
vendor_label = target .extra_labels [0 ]
211
276
cur_result = None
@@ -233,47 +298,71 @@ def build_library(src_paths, build_path, target, toolchain_name,
233
298
toolchain .jobs = jobs
234
299
toolchain .build_all = clean
235
300
236
- toolchain .info ("Building library %s (%s, %s)" % (name . upper () , target .name , toolchain_name ))
301
+ toolchain .info ("Building library %s (%s, %s)" % (name , target .name , toolchain_name ))
237
302
238
303
# Scan Resources
239
- resources = []
240
- for src_path in src_paths :
241
- resources .append (toolchain .scan_resources (src_path ))
304
+ resources = None
305
+ for path in src_paths :
306
+ # Scan resources
307
+ resource = toolchain .scan_resources (path )
308
+
309
+ # Copy headers, objects and static libraries - all files needed for static lib
310
+ toolchain .copy_files (resource .headers , build_path , rel_path = resource .base_path )
311
+ toolchain .copy_files (resource .objects , build_path , rel_path = resource .base_path )
312
+ toolchain .copy_files (resource .libraries , build_path , rel_path = resource .base_path )
313
+ if resource .linker_script :
314
+ toolchain .copy_files (resource .linker_script , build_path , rel_path = resource .base_path )
315
+
316
+ # Extend resources collection
317
+ if not resources :
318
+ resources = resource
319
+ else :
320
+ resources .add (resource )
321
+
322
+ # We need to add if necessary additional include directories
323
+ if inc_dirs :
324
+ if type (inc_dirs ) == ListType :
325
+ resources .inc_dirs .extend (inc_dirs )
326
+ else :
327
+ resources .inc_dirs .append (inc_dirs )
242
328
243
329
# Add extra include directories / files which are required by library
244
330
# This files usually are not in the same directory as source files so
245
331
# previous scan will not include them
246
332
if inc_dirs_ext is not None :
247
333
for inc_ext in inc_dirs_ext :
248
- resources .append (toolchain .scan_resources (inc_ext ))
334
+ resources .add (toolchain .scan_resources (inc_ext ))
249
335
250
336
# Dependencies Include Paths
251
- dependencies_include_dir = []
252
337
if dependencies_paths is not None :
253
338
for path in dependencies_paths :
254
339
lib_resources = toolchain .scan_resources (path )
255
- dependencies_include_dir .extend (lib_resources .inc_dirs )
340
+ resources . inc_dirs .extend (lib_resources .inc_dirs )
256
341
257
- if inc_dirs :
258
- dependencies_include_dir .extend (inc_dirs )
259
-
260
- # Create the desired build directory structure
261
- bin_path = join (build_path , toolchain .obj_path )
262
- mkdir (bin_path )
263
- tmp_path = join (build_path , '.temp' , toolchain .obj_path )
264
- mkdir (tmp_path )
342
+ if archive :
343
+ # Use temp path when building archive
344
+ tmp_path = join (build_path , '.temp' )
345
+ mkdir (tmp_path )
346
+ else :
347
+ tmp_path = build_path
265
348
266
- # Copy Headers
267
- for resource in resources :
268
- toolchain .copy_files (resource .headers , build_path , rel_path = resource .base_path )
269
- dependencies_include_dir .extend (toolchain .scan_resources (build_path ).inc_dirs )
349
+ # Handle configuration
350
+ config = Config (target )
351
+ # Update the configuration with any .json files found while scanning
352
+ config .add_config_files (resources .json_files )
353
+ # And add the configuration macros to the toolchain
354
+ toolchain .add_macros (config .get_config_data_macros ())
270
355
271
356
# Compile Sources
272
- objects = []
273
- for resource in resources :
274
- objects .extend (toolchain .compile_sources (resource , tmp_path , dependencies_include_dir ))
357
+ for path in src_paths :
358
+ src = toolchain .scan_resources (path )
359
+ objects = toolchain .compile_sources (src , abspath (tmp_path ), resources .inc_dirs )
360
+ resources .objects .extend (objects )
275
361
276
- needed_update = toolchain .build_library (objects , bin_path , name )
362
+ if archive :
363
+ needed_update = toolchain .build_library (resources .objects , build_path , name )
364
+ else :
365
+ needed_update = True
277
366
278
367
if report != None and needed_update :
279
368
end = time ()
@@ -286,7 +375,12 @@ def build_library(src_paths, build_path, target, toolchain_name,
286
375
except Exception , e :
287
376
if report != None :
288
377
end = time ()
289
- cur_result ["result" ] = "FAIL"
378
+
379
+ if isinstance (e , ToolException ):
380
+ cur_result ["result" ] = "FAIL"
381
+ elif isinstance (e , NotSupportedException ):
382
+ cur_result ["result" ] = "NOT_SUPPORTED"
383
+
290
384
cur_result ["elapsed_time" ] = end - start
291
385
292
386
toolchain_output = toolchain .get_output ()
@@ -734,3 +828,63 @@ def write_build_report(build_report, template_filename, filename):
734
828
735
829
with open (filename , 'w+' ) as f :
736
830
f .write (template .render (failing_builds = build_report_failing , passing_builds = build_report_passing ))
831
+
832
+
833
+ def scan_for_source_paths (path , exclude_paths = None ):
834
+ ignorepatterns = []
835
+ paths = []
836
+
837
+ def is_ignored (file_path ):
838
+ for pattern in ignorepatterns :
839
+ if fnmatch .fnmatch (file_path , pattern ):
840
+ return True
841
+ return False
842
+
843
+
844
+ """ os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]])
845
+ When topdown is True, the caller can modify the dirnames list in-place
846
+ (perhaps using del or slice assignment), and walk() will only recurse into
847
+ the subdirectories whose names remain in dirnames; this can be used to prune
848
+ the search, impose a specific order of visiting, or even to inform walk()
849
+ about directories the caller creates or renames before it resumes walk()
850
+ again. Modifying dirnames when topdown is False is ineffective, because in
851
+ bottom-up mode the directories in dirnames are generated before dirpath
852
+ itself is generated.
853
+ """
854
+ for root , dirs , files in walk (path , followlinks = True ):
855
+ # Remove ignored directories
856
+ # Check if folder contains .mbedignore
857
+ if ".mbedignore" in files :
858
+ with open (join (root ,".mbedignore" ), "r" ) as f :
859
+ lines = f .readlines ()
860
+ lines = [l .strip () for l in lines ] # Strip whitespaces
861
+ lines = [l for l in lines if l != "" ] # Strip empty lines
862
+ lines = [l for l in lines if not re .match ("^#" ,l )] # Strip comment lines
863
+ # Append root path to glob patterns
864
+ # and append patterns to ignorepatterns
865
+ ignorepatterns .extend ([join (root ,line .strip ()) for line in lines ])
866
+
867
+ for d in copy (dirs ):
868
+ dir_path = join (root , d )
869
+
870
+ # Always ignore hidden directories
871
+ if d .startswith ('.' ):
872
+ dirs .remove (d )
873
+
874
+ # Remove dirs that already match the ignorepatterns
875
+ # to avoid travelling into them and to prevent them
876
+ # on appearing in include path.
877
+ if is_ignored (join (dir_path ,"" )):
878
+ dirs .remove (d )
879
+
880
+ if exclude_paths :
881
+ for exclude_path in exclude_paths :
882
+ rel_path = relpath (dir_path , exclude_path )
883
+ if not (rel_path .startswith ('..' )):
884
+ dirs .remove (d )
885
+ break
886
+
887
+ # Add root to include paths
888
+ paths .append (root )
889
+
890
+ return paths
0 commit comments