Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/Tools/ardupilotwaf/ap_library.py
Views: 1798
# Copyright (C) 2016 Intel Corporation. All rights reserved.1#2# This file is free software: you can redistribute it and/or modify it3# under the terms of the GNU General Public License as published by the4# Free Software Foundation, either version 3 of the License, or5# (at your option) any later version.6#7# This file is distributed in the hope that it will be useful, but8# WITHOUT ANY WARRANTY; without even the implied warranty of9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.10# See the GNU General Public License for more details.11#12# You should have received a copy of the GNU General Public License along13# with this program. If not, see <http://www.gnu.org/licenses/>.14"""15Waf tool for Ardupilot libraries. The function bld.ap_library() creates the16necessary task generators for creating the objects of a library for a vehicle.17That includes the common objects, which are shared among vehicles. That18function is used by bld.ap_stlib() and shouldn't need to be called otherwise.1920The environment variable AP_LIBRARIES_OBJECTS_KW is a dictionary of keyword21arguments to be passed to bld.objects() when during the creation of the task22generators. You can use it to pass extra arguments to that function (although23some of them will be rewritten, see the implementation for details).2425This tool also checks if the headers used by the source files don't use26vehicle-related headers and fails the build if they do.27"""28import os29import re3031from waflib import Errors, Task, Utils, Logs32from waflib.Configure import conf33from waflib.TaskGen import after_method, before_method, feature34from waflib.Tools import c_preproc3536import ardupilotwaf as ap3738UTILITY_SOURCE_EXTS = ['utility/' + glob for glob in ap.SOURCE_EXTS]3940def _common_tgen_name(library):41return 'objs/%s' % library4243def _vehicle_tgen_name(library, vehicle):44return 'objs/%s/%s' % (library, vehicle)4546_vehicle_indexes = {}47def _vehicle_index(vehicle):48""" Used for the objects taskgens idx parameter """49if vehicle not in _vehicle_indexes:50_vehicle_indexes[vehicle] = len(_vehicle_indexes) + 151return _vehicle_indexes[vehicle]5253# note that AP_NavEKF3_core.h is needed for AP_NavEKF3_feature.h54_vehicle_macros = ['APM_BUILD_DIRECTORY', 'AP_BUILD_TARGET_NAME',55'APM_BUILD_TYPE', 'APM_BUILD_COPTER_OR_HELI',56'AP_NavEKF3_core.h', 'lua_generated_bindings.h',57'AP_InertialSensor_rate_config.h']58_macros_re = re.compile(r'\b(%s)\b' % '|'.join(_vehicle_macros))5960# some cpp files are not available at the time we run this check so need to be61# unilaterally added62_vehicle_cpp_need_macros = ['lua_generated_bindings.cpp']63_macros_cpp_re = re.compile(r'\b(%s)\b' % '|'.join(_vehicle_cpp_need_macros))6465def _remove_comments(s):66return c_preproc.re_cpp.sub(c_preproc.repl, s)6768_depends_on_vehicle_cache = {}69def _depends_on_vehicle(bld, source_node):70path = source_node.srcpath()7172if not bld.env.BUILDROOT:73bld.env.BUILDROOT = bld.bldnode.make_node('').abspath()7475if _macros_cpp_re.search(path) is not None:76_depends_on_vehicle_cache[path] = True7778if path not in _depends_on_vehicle_cache:79try:80s = _remove_comments(source_node.read())81_depends_on_vehicle_cache[path] = _macros_re.search(s) is not None82except Exception:83return False8485return _depends_on_vehicle_cache[path]8687@conf88def ap_library(bld, library, vehicle):89try:90common_tg = bld.get_tgen_by_name(_common_tgen_name(library))91except Errors.WafError:92common_tg = None9394try:95vehicle_tg = bld.get_tgen_by_name(_vehicle_tgen_name(library, vehicle))96except Errors.WafError:97vehicle_tg = None9899if common_tg and vehicle_tg:100return101102if library.find('*') != -1:103# allow for wildcard patterns, used for submodules without direct waf support104library_dir = bld.srcnode.find_dir('.')105wildcard = library106else:107library_dir = bld.srcnode.find_dir('libraries/%s' % library)108wildcard = ap.SOURCE_EXTS + UTILITY_SOURCE_EXTS109110if not library_dir:111bld.fatal('ap_library: %s not found' % library)112113src = library_dir.ant_glob(wildcard)114115# allow for dynamically generated sources in a library that inherit the116# dependencies and includes117if library in bld.env.AP_LIB_EXTRA_SOURCES:118for s in bld.env.AP_LIB_EXTRA_SOURCES[library]:119src.append(bld.bldnode.find_or_declare(os.path.join('libraries', library, s)))120121if not common_tg:122kw = dict(bld.env.AP_LIBRARIES_OBJECTS_KW)123kw['features'] = kw.get('features', []) + ['ap_library_object']124kw.update(125name=_common_tgen_name(library),126source=[s for s in src if not _depends_on_vehicle(bld, s)],127idx=0,128)129bld.objects(**kw)130131if not vehicle_tg:132source = [s for s in src if _depends_on_vehicle(bld, s)]133134if not source:135return136137kw = dict(bld.env.AP_LIBRARIES_OBJECTS_KW)138kw['features'] = kw.get('features', []) + ['ap_library_object']139kw.update(140name=_vehicle_tgen_name(library, vehicle),141source=source,142defines=ap.get_legacy_defines(vehicle, bld),143idx=_vehicle_index(vehicle),144)145bld.objects(**kw)146147@before_method('process_use')148@feature('cxxstlib')149def process_ap_libraries(self):150self.use = Utils.to_list(getattr(self, 'use', []))151libraries = Utils.to_list(getattr(self, 'ap_libraries', []))152vehicle = getattr(self, 'ap_vehicle', None)153154for l in libraries:155self.use.append(_common_tgen_name(l))156if vehicle:157self.use.append(_vehicle_tgen_name(l, vehicle))158159@before_method('process_source')160@feature('cxxstlib')161def dynamic_post(self):162if not getattr(self, 'dynamic_source', None):163return164self.source = Utils.to_list(self.source)165self.source.extend(self.bld.bldnode.ant_glob(self.dynamic_source))166167class ap_library_check_headers(Task.Task):168color = 'PINK'169before = 'cxx c'170dispatched_headers = set()171whitelist = (172'libraries/AP_Vehicle/AP_Vehicle_Type.h',173'libraries/AP_Common/AP_FWVersionDefine.h',174'libraries/AP_Scripting/lua_generated_bindings.h',175'libraries/AP_NavEKF3/AP_NavEKF3_feature.h',176'libraries/AP_LandingGear/AP_LandingGear_config.h',177'libraries/AP_InertialSensor/AP_InertialSensor_rate_config.h',178)179whitelist = tuple(os.path.join(*p.split('/')) for p in whitelist)180181def run(self):182for n in self.headers:183s = _remove_comments(n.read())184if _macros_re.search(s):185raise Errors.WafError('%s: library header uses vehicle-dependent macros' % n.srcpath())186187def uid(self):188try:189return self._uid190except AttributeError:191self._uid = 'check_headers-%s' % self.compiled_task.uid()192return self._uid193194def signature(self):195bld = self.generator.bld196# force scan() to be called197bld.imp_sigs[self.uid()] = None198s = super(ap_library_check_headers, self).signature()199bld.ap_persistent_task_sigs[self.uid()] = s200return s201202def scan(self):203r = []204self.headers = []205206srcnode_path = self.generator.bld.srcnode.abspath()207208# force dependency scan, if necessary209self.compiled_task.signature()210if not self.compiled_task.uid() in self.generator.bld.node_deps:211return r, []212for n in self.generator.bld.node_deps[self.compiled_task.uid()]:213# using common Node methods doesn't work here214p = n.abspath()215if not p.startswith(srcnode_path):216continue217rel_p = os.path.relpath(p, srcnode_path)218if rel_p in self.whitelist:219continue220221# check if the path ends with something in the white list222# this is required for white listing files in 'build/' (for scripting generated bindings)223found = False224for m in self.whitelist:225if rel_p.endswith(m):226found = True227break228229if found:230continue231232r.append(n)233if n not in self.dispatched_headers:234self.headers.append(n)235self.dispatched_headers.add(n)236237return r, []238239def __str__(self):240return str(self.compiled_task)241242def keyword(self):243return 'Checking included headers'244245def custom_flags_check(tgen):246'''247check for tasks marked as having custom cpp or c flags248a library can do this by setting AP_LIB_EXTRA_CXXFLAGS and AP_LIB_EXTRA_CFLAGS249250For example add this is the configure section of the library, using AP_DDS as an example:251252cfg.env.AP_LIB_EXTRA_CXXFLAGS['AP_DDS'] = ['-DSOME_CXX_FLAG']253cfg.env.AP_LIB_EXTRA_CFLAGS['AP_DDS'] = ['-DSOME_C_FLAG']254'''255if not tgen.name.startswith("objs/"):256return257libname = tgen.name[5:]258if libname in tgen.env.AP_LIB_EXTRA_CXXFLAGS:259tgen.env.CXXFLAGS.extend(tgen.env.AP_LIB_EXTRA_CXXFLAGS[libname])260if libname in tgen.env.AP_LIB_EXTRA_CFLAGS:261tgen.env.CFLAGS.extend(tgen.env.AP_LIB_EXTRA_CFLAGS[libname])262263264def double_precision_check(tasks):265'''check for tasks marked as double precision'''266267for t in tasks:268if len(t.inputs) == 1:269# get a list of tasks we need to change to be double precision270double_tasks = []271for library in t.env.DOUBLE_PRECISION_SOURCES.keys():272for s in t.env.DOUBLE_PRECISION_SOURCES[library]:273double_tasks.append([library, s])274275src = str(t.inputs[0]).split('/')[-2:]276double_library = t.env.DOUBLE_PRECISION_LIBRARIES.get(src[0],False)277278if double_library or src in double_tasks:279t.env.CXXFLAGS = ap.set_double_precision_flags(t.env.CXXFLAGS)280281282def gsoap_library_check(bld, tasks):283'''check for tasks marked as gSOAP library source'''284285for t in tasks:286if len(t.inputs) == 1:287gsoap_tasks = []288for s in t.env.AP_LIB_EXTRA_SOURCES["AP_ONVIF"]:289gsoap_tasks.append(bld.bldnode.find_or_declare(os.path.join('libraries', "AP_ONVIF", s)))290291if t.inputs[0] in gsoap_tasks:292t.env.CXXFLAGS += [293'-Wno-shadow',294]295if 'clang++' not in t.env.COMPILER_CXX:296t.env.CXXFLAGS += [297'-Wno-suggest-override',298]299300301@feature('ap_library_object')302@after_method('process_source')303def ap_library_register_for_check(self):304if not hasattr(self, 'compiled_tasks'):305return306307custom_flags_check(self)308double_precision_check(self.compiled_tasks)309if self.env.ENABLE_ONVIF:310gsoap_library_check(self.bld, self.compiled_tasks)311312if not self.env.ENABLE_HEADER_CHECKS:313return314315for t in self.compiled_tasks:316tsk = self.create_task('ap_library_check_headers')317tsk.compiled_task = t318319def write_compilation_database(bld):320"""321Write the compilation database as JSON322"""323database_file = bld.bldnode.find_or_declare('compile_commands.json')324# don't remove the file at clean325326Logs.info('Build commands will be stored in %s', database_file.path_from(bld.path))327try:328root = database_file.read_json()329except IOError:330root = []331compile_cmd_db = dict((x['file'], x) for x in root)332for task in bld.compilation_database_tasks:333try:334cmd = task.last_cmd335except AttributeError:336continue337f_node = task.inputs[0]338filename = f_node.path_from(task.get_cwd())339entry = {340"directory": task.get_cwd().abspath(),341"arguments": cmd,342"file": filename,343}344compile_cmd_db[filename] = entry345root = list(compile_cmd_db.values())346database_file.write_json(root)347348def target_list_changed(bld, targets):349"""350Check if the list of targets has changed recorded in target_list file351"""352# target_list file is in the root build directory353target_list_file = bld.bldnode.find_or_declare('target_list')354try:355with open(target_list_file.abspath(), 'r') as f:356old_targets = f.read().strip().split(',')357except IOError:358Logs.info('No target_list file found, creating')359old_targets = []360if old_targets != targets:361with open(target_list_file.abspath(), 'w') as f:362f.write(','.join(targets))363return True364return False365366@conf367def remove_target_list(cfg):368target_list_file = cfg.bldnode.make_node(cfg.options.board + '/target_list')369try:370Logs.info('Removing target_list file %s', target_list_file.abspath())371os.remove(target_list_file.abspath())372except OSError:373pass374375@feature('cxxprogram', 'cxxstlib')376@after_method('propagate_uselib_vars')377def dry_run_compilation_database(self):378if not hasattr(self, 'bld'):379return380bld = self.bld381bld.compilation_database_tasks = []382targets = bld.targets.split(',')383use = self.use384if isinstance(use, str):385use = [use]386# if targets have not changed and neither has configuration,387# we can skip compilation database generation388if not target_list_changed(bld, targets + use):389Logs.info('Targets have not changed, skipping compilation database compile_commands.json generation')390return391Logs.info('Generating compile_commands.json')392# we need only to generate last_cmd, so override393# exec_command temporarily394def exec_command(bld, *k, **kw):395return 0396397for g in bld.groups:398for tg in g:399# we only care to list targets and library objects400if not hasattr(tg, 'name'):401continue402if (tg.name not in targets) and (tg.name not in self.use):403continue404try:405f = tg.post406except AttributeError:407pass408else:409f()410411if isinstance(tg, Task.Task):412lst = [tg]413else:414lst = tg.tasks415for tsk in lst:416if tsk.__class__.__name__ == "swig":417tsk.runnable_status()418if hasattr(tsk, 'more_tasks'):419lst.extend(tsk.more_tasks)420# Not all dynamic tasks can be processed, in some cases421# one may have to call the method "run()" like this:422# elif tsk.__class__.__name__ == 'src2c':423# tsk.run()424# if hasattr(tsk, 'more_tasks'):425# lst.extend(tsk.more_tasks)426427tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)428if isinstance(tsk, tup):429bld.compilation_database_tasks.append(tsk)430tsk.nocache = True431old_exec = tsk.exec_command432tsk.exec_command = exec_command433tsk.run()434tsk.exec_command = old_exec435436write_compilation_database(bld)437438def configure(cfg):439cfg.env.AP_LIBRARIES_OBJECTS_KW = dict()440cfg.env.AP_LIB_EXTRA_SOURCES = dict()441cfg.env.AP_LIB_EXTRA_CXXFLAGS = dict()442cfg.env.AP_LIB_EXTRA_CFLAGS = dict()443cfg.env.DOUBLE_PRECISION_SOURCES = dict()444cfg.env.DOUBLE_PRECISION_LIBRARIES = dict()445446447