]> git.gir.st - tmk_keyboard.git/blob - tool/mbed/mbed-sdk/workspace_tools/build_api.py
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[tmk_keyboard.git] / tool / mbed / mbed-sdk / workspace_tools / build_api.py
1 """
2 mbed SDK
3 Copyright (c) 2011-2013 ARM Limited
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16 """
17
18 import re
19 import tempfile
20 import colorama
21
22
23 from types import ListType
24 from shutil import rmtree
25 from os.path import join, exists, basename
26
27 from workspace_tools.utils import mkdir, run_cmd, run_cmd_ext
28 from workspace_tools.paths import MBED_TARGETS_PATH, MBED_LIBRARIES, MBED_API, MBED_HAL, MBED_COMMON
29 from workspace_tools.targets import TARGET_NAMES, TARGET_MAP
30 from workspace_tools.libraries import Library
31 from workspace_tools.toolchains import TOOLCHAIN_CLASSES
32 from jinja2 import FileSystemLoader
33 from jinja2.environment import Environment
34
35
36 def build_project(src_path, build_path, target, toolchain_name,
37 libraries_paths=None, options=None, linker_script=None,
38 clean=False, notify=None, verbose=False, name=None, macros=None, inc_dirs=None, jobs=1, silent=False):
39 """ This function builds project. Project can be for example one test / UT
40 """
41 # Toolchain instance
42 toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, notify, macros, silent)
43 toolchain.VERBOSE = verbose
44 toolchain.jobs = jobs
45 toolchain.build_all = clean
46 src_paths = [src_path] if type(src_path) != ListType else src_path
47
48 # We need to remove all paths which are repeated to avoid
49 # multiple compilations and linking with the same objects
50 src_paths = [src_paths[0]] + list(set(src_paths[1:]))
51
52 PROJECT_BASENAME = basename(src_paths[0])
53
54 if name is None:
55 # We will use default project name based on project folder name
56 name = PROJECT_BASENAME
57 toolchain.info("Building project %s (%s, %s)" % (PROJECT_BASENAME.upper(), target.name, toolchain_name))
58 else:
59 # User used custom global project name to have the same name for the
60 toolchain.info("Building project %s to %s (%s, %s)" % (PROJECT_BASENAME.upper(), name, target.name, toolchain_name))
61
62 # Scan src_path and libraries_paths for resources
63 resources = toolchain.scan_resources(src_paths[0])
64 for path in src_paths[1:]:
65 resources.add(toolchain.scan_resources(path))
66 if libraries_paths is not None:
67 src_paths.extend(libraries_paths)
68 for path in libraries_paths:
69 resources.add(toolchain.scan_resources(path))
70
71 if linker_script is not None:
72 resources.linker_script = linker_script
73
74 # Build Directory
75 if clean:
76 if exists(build_path):
77 rmtree(build_path)
78 mkdir(build_path)
79
80 # We need to add if necessary additional include directories
81 if inc_dirs:
82 if type(inc_dirs) == ListType:
83 resources.inc_dirs.extend(inc_dirs)
84 else:
85 resources.inc_dirs.append(inc_dirs)
86
87 # Compile Sources
88 for path in src_paths:
89 src = toolchain.scan_resources(path)
90 objects = toolchain.compile_sources(src, build_path, resources.inc_dirs)
91 resources.objects.extend(objects)
92
93 # Link Program
94 return toolchain.link_program(resources, build_path, name)
95
96
97 def build_library(src_paths, build_path, target, toolchain_name,
98 dependencies_paths=None, options=None, name=None, clean=False,
99 notify=None, verbose=False, macros=None, inc_dirs=None, inc_dirs_ext=None, jobs=1, silent=False):
100 """ src_path: the path of the source directory
101 build_path: the path of the build directory
102 target: ['LPC1768', 'LPC11U24', 'LPC2368']
103 toolchain: ['ARM', 'uARM', 'GCC_ARM', 'GCC_CS', 'GCC_CR']
104 library_paths: List of paths to additional libraries
105 clean: Rebuild everything if True
106 notify: Notify function for logs
107 verbose: Write the actual tools command lines if True
108 inc_dirs: additional include directories which should be included in build
109 inc_dirs_ext: additional include directories which should be copied to library directory
110 """
111 if type(src_paths) != ListType:
112 src_paths = [src_paths]
113
114 for src_path in src_paths:
115 if not exists(src_path):
116 raise Exception("The library source folder does not exist: %s", src_path)
117
118 # Toolchain instance
119 toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, silent=silent)
120 toolchain.VERBOSE = verbose
121 toolchain.jobs = jobs
122 toolchain.build_all = clean
123
124 # The first path will give the name to the library
125 name = basename(src_paths[0])
126 toolchain.info("Building library %s (%s, %s)" % (name.upper(), target.name, toolchain_name))
127
128 # Scan Resources
129 resources = []
130 for src_path in src_paths:
131 resources.append(toolchain.scan_resources(src_path))
132
133 # Add extra include directories / files which are required by library
134 # This files usually are not in the same directory as source files so
135 # previous scan will not include them
136 if inc_dirs_ext is not None:
137 for inc_ext in inc_dirs_ext:
138 resources.append(toolchain.scan_resources(inc_ext))
139
140 # Dependencies Include Paths
141 dependencies_include_dir = []
142 if dependencies_paths is not None:
143 for path in dependencies_paths:
144 lib_resources = toolchain.scan_resources(path)
145 dependencies_include_dir.extend(lib_resources.inc_dirs)
146
147 if inc_dirs:
148 dependencies_include_dir.extend(inc_dirs)
149
150 # Create the desired build directory structure
151 bin_path = join(build_path, toolchain.obj_path)
152 mkdir(bin_path)
153 tmp_path = join(build_path, '.temp', toolchain.obj_path)
154 mkdir(tmp_path)
155
156 # Copy Headers
157 for resource in resources:
158 toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
159 dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
160
161 # Compile Sources
162 objects = []
163 for resource in resources:
164 objects.extend(toolchain.compile_sources(resource, tmp_path, dependencies_include_dir))
165
166 toolchain.build_library(objects, bin_path, name)
167
168
169 def build_lib(lib_id, target, toolchain, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, silent=False):
170 """ Wrapper for build_library function.
171 Function builds library in proper directory using all dependencies and macros defined by user.
172 """
173 lib = Library(lib_id)
174 if lib.is_supported(target, toolchain):
175 # We need to combine macros from parameter list with macros from library definition
176 MACROS = lib.macros if lib.macros else []
177 if macros:
178 MACROS.extend(macros)
179
180 build_library(lib.source_dir, lib.build_dir, target, toolchain, lib.dependencies, options,
181 verbose=verbose,
182 silent=silent,
183 clean=clean,
184 macros=MACROS,
185 notify=notify,
186 inc_dirs=lib.inc_dirs,
187 inc_dirs_ext=lib.inc_dirs_ext,
188 jobs=jobs)
189 else:
190 print 'Library "%s" is not yet supported on target %s with toolchain %s' % (lib_id, target.name, toolchain)
191
192
193 # We do have unique legacy conventions about how we build and package the mbed library
194 def build_mbed_libs(target, toolchain_name, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1, silent=False):
195 """ Function returns True is library was built and false if building was skipped """
196 # Check toolchain support
197 if toolchain_name not in target.supported_toolchains:
198 supported_toolchains_text = ", ".join(target.supported_toolchains)
199 print '%s target is not yet supported by toolchain %s' % (target.name, toolchain_name)
200 print '%s target supports %s toolchain%s' % (target.name, supported_toolchains_text, 's' if len(target.supported_toolchains) > 1 else '')
201 return False
202
203 # Toolchain
204 toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify, silent=silent)
205 toolchain.VERBOSE = verbose
206 toolchain.jobs = jobs
207 toolchain.build_all = clean
208
209 # Source and Build Paths
210 BUILD_TARGET = join(MBED_LIBRARIES, "TARGET_" + target.name)
211 BUILD_TOOLCHAIN = join(BUILD_TARGET, "TOOLCHAIN_" + toolchain.name)
212 mkdir(BUILD_TOOLCHAIN)
213
214 TMP_PATH = join(MBED_LIBRARIES, '.temp', toolchain.obj_path)
215 mkdir(TMP_PATH)
216
217 # CMSIS
218 toolchain.info("Building library %s (%s, %s)"% ('CMSIS', target.name, toolchain_name))
219 cmsis_src = join(MBED_TARGETS_PATH, "cmsis")
220 resources = toolchain.scan_resources(cmsis_src)
221
222 toolchain.copy_files(resources.headers, BUILD_TARGET)
223 toolchain.copy_files(resources.linker_script, BUILD_TOOLCHAIN)
224 toolchain.copy_files(resources.bin_files, BUILD_TOOLCHAIN)
225
226 objects = toolchain.compile_sources(resources, TMP_PATH)
227 toolchain.copy_files(objects, BUILD_TOOLCHAIN)
228
229 # mbed
230 toolchain.info("Building library %s (%s, %s)" % ('MBED', target.name, toolchain_name))
231
232 # Common Headers
233 toolchain.copy_files(toolchain.scan_resources(MBED_API).headers, MBED_LIBRARIES)
234 toolchain.copy_files(toolchain.scan_resources(MBED_HAL).headers, MBED_LIBRARIES)
235
236 # Target specific sources
237 HAL_SRC = join(MBED_TARGETS_PATH, "hal")
238 hal_implementation = toolchain.scan_resources(HAL_SRC)
239 toolchain.copy_files(hal_implementation.headers + hal_implementation.hex_files + hal_implementation.libraries, BUILD_TARGET, HAL_SRC)
240 incdirs = toolchain.scan_resources(BUILD_TARGET).inc_dirs
241 objects = toolchain.compile_sources(hal_implementation, TMP_PATH, [MBED_LIBRARIES] + incdirs)
242
243 # Common Sources
244 mbed_resources = toolchain.scan_resources(MBED_COMMON)
245 objects += toolchain.compile_sources(mbed_resources, TMP_PATH, [MBED_LIBRARIES] + incdirs)
246
247 # A number of compiled files need to be copied as objects as opposed to
248 # being part of the mbed library, for reasons that have to do with the way
249 # the linker search for symbols in archives. These are:
250 # - retarget.o: to make sure that the C standard lib symbols get overridden
251 # - board.o: mbed_die is weak
252 # - mbed_overrides.o: this contains platform overrides of various weak SDK functions
253 separate_names, separate_objects = ['retarget.o', 'board.o', 'mbed_overrides.o'], []
254 for o in objects:
255 for name in separate_names:
256 if o.endswith(name):
257 separate_objects.append(o)
258 for o in separate_objects:
259 objects.remove(o)
260 toolchain.build_library(objects, BUILD_TOOLCHAIN, "mbed")
261 for o in separate_objects:
262 toolchain.copy_files(o, BUILD_TOOLCHAIN)
263 return True
264
265
266 def get_unique_supported_toolchains():
267 """ Get list of all unique toolchains supported by targets """
268 unique_supported_toolchains = []
269 for target in TARGET_NAMES:
270 for toolchain in TARGET_MAP[target].supported_toolchains:
271 if toolchain not in unique_supported_toolchains:
272 unique_supported_toolchains.append(toolchain)
273 return unique_supported_toolchains
274
275
276 def mcu_toolchain_matrix(verbose_html=False, platform_filter=None):
277 """ Shows target map using prettytable """
278 unique_supported_toolchains = get_unique_supported_toolchains()
279 from prettytable import PrettyTable # Only use it in this function so building works without extra modules
280
281 # All tests status table print
282 columns = ["Platform"] + unique_supported_toolchains
283 pt = PrettyTable(["Platform"] + unique_supported_toolchains)
284 # Align table
285 for col in columns:
286 pt.align[col] = "c"
287 pt.align["Platform"] = "l"
288
289 perm_counter = 0
290 target_counter = 0
291 for target in sorted(TARGET_NAMES):
292 if platform_filter is not None:
293 # FIlter out platforms using regex
294 if re.search(platform_filter, target) is None:
295 continue
296 target_counter += 1
297
298 row = [target] # First column is platform name
299 default_toolchain = TARGET_MAP[target].default_toolchain
300 for unique_toolchain in unique_supported_toolchains:
301 text = "-"
302 if default_toolchain == unique_toolchain:
303 text = "Default"
304 perm_counter += 1
305 elif unique_toolchain in TARGET_MAP[target].supported_toolchains:
306 text = "Supported"
307 perm_counter += 1
308 row.append(text)
309 pt.add_row(row)
310
311 result = pt.get_html_string() if verbose_html else pt.get_string()
312 result += "\n"
313 result += "*Default - default on-line compiler\n"
314 result += "*Supported - supported off-line compiler\n"
315 result += "\n"
316 result += "Total platforms: %d\n"% (target_counter)
317 result += "Total permutations: %d"% (perm_counter)
318 return result
319
320
321 def get_target_supported_toolchains(target):
322 """ Returns target supported toolchains list """
323 return TARGET_MAP[target].supported_toolchains if target in TARGET_MAP else None
324
325
326 def static_analysis_scan(target, toolchain_name, CPPCHECK_CMD, CPPCHECK_MSG_FORMAT, options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1):
327 # Toolchain
328 toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify)
329 toolchain.VERBOSE = verbose
330 toolchain.jobs = jobs
331 toolchain.build_all = clean
332
333 # Source and Build Paths
334 BUILD_TARGET = join(MBED_LIBRARIES, "TARGET_" + target.name)
335 BUILD_TOOLCHAIN = join(BUILD_TARGET, "TOOLCHAIN_" + toolchain.name)
336 mkdir(BUILD_TOOLCHAIN)
337
338 TMP_PATH = join(MBED_LIBRARIES, '.temp', toolchain.obj_path)
339 mkdir(TMP_PATH)
340
341 # CMSIS
342 toolchain.info("Static analysis for %s (%s, %s)" % ('CMSIS', target.name, toolchain_name))
343 cmsis_src = join(MBED_TARGETS_PATH, "cmsis")
344 resources = toolchain.scan_resources(cmsis_src)
345
346 # Copy files before analysis
347 toolchain.copy_files(resources.headers, BUILD_TARGET)
348 toolchain.copy_files(resources.linker_script, BUILD_TOOLCHAIN)
349
350 # Gather include paths, c, cpp sources and macros to transfer to cppcheck command line
351 includes = ["-I%s"% i for i in resources.inc_dirs]
352 includes.append("-I%s"% str(BUILD_TARGET))
353 c_sources = " ".join(resources.c_sources)
354 cpp_sources = " ".join(resources.cpp_sources)
355 macros = ["-D%s"% s for s in toolchain.get_symbols() + toolchain.macros]
356
357 includes = map(str.strip, includes)
358 macros = map(str.strip, macros)
359
360 check_cmd = CPPCHECK_CMD
361 check_cmd += CPPCHECK_MSG_FORMAT
362 check_cmd += includes
363 check_cmd += macros
364
365 # We need to pass some params via file to avoid "command line too long in some OSs"
366 tmp_file = tempfile.NamedTemporaryFile(delete=False)
367 tmp_file.writelines(line + '\n' for line in c_sources.split())
368 tmp_file.writelines(line + '\n' for line in cpp_sources.split())
369 tmp_file.close()
370 check_cmd += ["--file-list=%s"% tmp_file.name]
371
372 _stdout, _stderr, _rc = run_cmd(check_cmd)
373 if verbose:
374 print _stdout
375 print _stderr
376
377 # =========================================================================
378
379 # MBED
380 toolchain.info("Static analysis for %s (%s, %s)" % ('MBED', target.name, toolchain_name))
381
382 # Common Headers
383 toolchain.copy_files(toolchain.scan_resources(MBED_API).headers, MBED_LIBRARIES)
384 toolchain.copy_files(toolchain.scan_resources(MBED_HAL).headers, MBED_LIBRARIES)
385
386 # Target specific sources
387 HAL_SRC = join(MBED_TARGETS_PATH, "hal")
388 hal_implementation = toolchain.scan_resources(HAL_SRC)
389
390 # Copy files before analysis
391 toolchain.copy_files(hal_implementation.headers + hal_implementation.hex_files, BUILD_TARGET, HAL_SRC)
392 incdirs = toolchain.scan_resources(BUILD_TARGET)
393
394 target_includes = ["-I%s" % i for i in incdirs.inc_dirs]
395 target_includes.append("-I%s"% str(BUILD_TARGET))
396 target_includes.append("-I%s"% str(HAL_SRC))
397 target_c_sources = " ".join(incdirs.c_sources)
398 target_cpp_sources = " ".join(incdirs.cpp_sources)
399 target_macros = ["-D%s"% s for s in toolchain.get_symbols() + toolchain.macros]
400
401 # Common Sources
402 mbed_resources = toolchain.scan_resources(MBED_COMMON)
403
404 # Gather include paths, c, cpp sources and macros to transfer to cppcheck command line
405 mbed_includes = ["-I%s" % i for i in mbed_resources.inc_dirs]
406 mbed_includes.append("-I%s"% str(BUILD_TARGET))
407 mbed_includes.append("-I%s"% str(MBED_COMMON))
408 mbed_includes.append("-I%s"% str(MBED_API))
409 mbed_includes.append("-I%s"% str(MBED_HAL))
410 mbed_c_sources = " ".join(mbed_resources.c_sources)
411 mbed_cpp_sources = " ".join(mbed_resources.cpp_sources)
412
413 target_includes = map(str.strip, target_includes)
414 mbed_includes = map(str.strip, mbed_includes)
415 target_macros = map(str.strip, target_macros)
416
417 check_cmd = CPPCHECK_CMD
418 check_cmd += CPPCHECK_MSG_FORMAT
419 check_cmd += target_includes
420 check_cmd += mbed_includes
421 check_cmd += target_macros
422
423 # We need to pass some parames via file to avoid "command line too long in some OSs"
424 tmp_file = tempfile.NamedTemporaryFile(delete=False)
425 tmp_file.writelines(line + '\n' for line in target_c_sources.split())
426 tmp_file.writelines(line + '\n' for line in target_cpp_sources.split())
427 tmp_file.writelines(line + '\n' for line in mbed_c_sources.split())
428 tmp_file.writelines(line + '\n' for line in mbed_cpp_sources.split())
429 tmp_file.close()
430 check_cmd += ["--file-list=%s"% tmp_file.name]
431
432 _stdout, _stderr, _rc = run_cmd_ext(check_cmd)
433 if verbose:
434 print _stdout
435 print _stderr
436
437
438 def static_analysis_scan_lib(lib_id, target, toolchain, cppcheck_cmd, cppcheck_msg_format,
439 options=None, verbose=False, clean=False, macros=None, notify=None, jobs=1):
440 lib = Library(lib_id)
441 if lib.is_supported(target, toolchain):
442 static_analysis_scan_library(lib.source_dir, lib.build_dir, target, toolchain, cppcheck_cmd, cppcheck_msg_format,
443 lib.dependencies, options,
444 verbose=verbose, clean=clean, macros=macros, notify=notify, jobs=jobs)
445 else:
446 print 'Library "%s" is not yet supported on target %s with toolchain %s'% (lib_id, target.name, toolchain)
447
448
449 def static_analysis_scan_library(src_paths, build_path, target, toolchain_name, cppcheck_cmd, cppcheck_msg_format,
450 dependencies_paths=None, options=None, name=None, clean=False,
451 notify=None, verbose=False, macros=None, jobs=1):
452 """ Function scans library (or just some set of sources/headers) for staticly detectable defects """
453 if type(src_paths) != ListType:
454 src_paths = [src_paths]
455
456 for src_path in src_paths:
457 if not exists(src_path):
458 raise Exception("The library source folder does not exist: %s", src_path)
459
460 # Toolchain instance
461 toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, macros=macros, notify=notify)
462 toolchain.VERBOSE = verbose
463 toolchain.jobs = jobs
464
465 # The first path will give the name to the library
466 name = basename(src_paths[0])
467 toolchain.info("Static analysis for library %s (%s, %s)" % (name.upper(), target.name, toolchain_name))
468
469 # Scan Resources
470 resources = []
471 for src_path in src_paths:
472 resources.append(toolchain.scan_resources(src_path))
473
474 # Dependencies Include Paths
475 dependencies_include_dir = []
476 if dependencies_paths is not None:
477 for path in dependencies_paths:
478 lib_resources = toolchain.scan_resources(path)
479 dependencies_include_dir.extend(lib_resources.inc_dirs)
480
481 # Create the desired build directory structure
482 bin_path = join(build_path, toolchain.obj_path)
483 mkdir(bin_path)
484 tmp_path = join(build_path, '.temp', toolchain.obj_path)
485 mkdir(tmp_path)
486
487 # Gather include paths, c, cpp sources and macros to transfer to cppcheck command line
488 includes = ["-I%s" % i for i in dependencies_include_dir + src_paths]
489 c_sources = " "
490 cpp_sources = " "
491 macros = ['-D%s' % s for s in toolchain.get_symbols() + toolchain.macros]
492
493 # Copy Headers
494 for resource in resources:
495 toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
496 includes += ["-I%s" % i for i in resource.inc_dirs]
497 c_sources += " ".join(resource.c_sources) + " "
498 cpp_sources += " ".join(resource.cpp_sources) + " "
499
500 dependencies_include_dir.extend(toolchain.scan_resources(build_path).inc_dirs)
501
502 includes = map(str.strip, includes)
503 macros = map(str.strip, macros)
504
505 check_cmd = cppcheck_cmd
506 check_cmd += cppcheck_msg_format
507 check_cmd += includes
508 check_cmd += macros
509
510 # We need to pass some parameters via file to avoid "command line too long in some OSs"
511 # Temporary file is created to store e.g. cppcheck list of files for command line
512 tmp_file = tempfile.NamedTemporaryFile(delete=False)
513 tmp_file.writelines(line + '\n' for line in c_sources.split())
514 tmp_file.writelines(line + '\n' for line in cpp_sources.split())
515 tmp_file.close()
516 check_cmd += ["--file-list=%s"% tmp_file.name]
517
518 # This will allow us to grab result from both stdio and stderr outputs (so we can show them)
519 # We assume static code analysis tool is outputting defects on STDERR
520 _stdout, _stderr, _rc = run_cmd_ext(check_cmd)
521 if verbose:
522 print _stdout
523 print _stderr
524
525
526 def print_build_results(result_list, build_name):
527 """ Generate result string for build results """
528 result = ""
529 if result_list:
530 result += build_name + "\n"
531 result += "\n".join([" * %s" % f for f in result_list])
532 result += "\n"
533 return result
534
535 def write_build_report(build_report, template_filename, filename):
536 build_report_failing = []
537 build_report_passing = []
538
539 for report in build_report:
540 if len(report["failing"]) > 0:
541 build_report_failing.append(report)
542 else:
543 build_report_passing.append(report)
544
545 env = Environment(extensions=['jinja2.ext.with_'])
546 env.loader = FileSystemLoader('ci_templates')
547 template = env.get_template(template_filename)
548
549 with open(filename, 'w+') as f:
550 f.write(template.render(failing_builds=build_report_failing, passing_builds=build_report_passing))
Imprint / Impressum