diff options
Diffstat (limited to 'third_party/waf/waflib/extras')
-rw-r--r-- | third_party/waf/waflib/extras/clang_compilation_database.py | 2 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/classic_runner.py | 68 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/color_gcc.py | 2 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/eclipse.py | 74 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/gccdeps.py | 82 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/msvcdeps.py | 54 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/msvs.py | 6 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/swig.py | 2 | ||||
-rw-r--r-- | third_party/waf/waflib/extras/wafcache.py | 26 |
9 files changed, 245 insertions, 71 deletions
diff --git a/third_party/waf/waflib/extras/clang_compilation_database.py b/third_party/waf/waflib/extras/clang_compilation_database.py index 17f66949376..bd29db93fd5 100644 --- a/third_party/waf/waflib/extras/clang_compilation_database.py +++ b/third_party/waf/waflib/extras/clang_compilation_database.py @@ -126,7 +126,7 @@ def patch_execute(): Invoke clangdb command before build """ if self.cmd.startswith('build'): - Scripting.run_command('clangdb') + Scripting.run_command(self.cmd.replace('build','clangdb')) old_execute_build(self) diff --git a/third_party/waf/waflib/extras/classic_runner.py b/third_party/waf/waflib/extras/classic_runner.py new file mode 100644 index 00000000000..b08c794e880 --- /dev/null +++ b/third_party/waf/waflib/extras/classic_runner.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2021 (ita) + +from waflib import Utils, Runner + +""" +Re-enable the classic threading system from waf 1.x + +def configure(conf): + conf.load('classic_runner') +""" + +class TaskConsumer(Utils.threading.Thread): + """ + Task consumers belong to a pool of workers + + They wait for tasks in the queue and then use ``task.process(...)`` + """ + def __init__(self, spawner): + Utils.threading.Thread.__init__(self) + """ + Obtain :py:class:`waflib.Task.TaskBase` instances from this queue. + """ + self.spawner = spawner + self.daemon = True + self.start() + + def run(self): + """ + Loop over the tasks to execute + """ + try: + self.loop() + except Exception: + pass + + def loop(self): + """ + Obtain tasks from :py:attr:`waflib.Runner.TaskConsumer.ready` and call + :py:meth:`waflib.Task.TaskBase.process`. If the object is a function, execute it. + """ + master = self.spawner.master + while 1: + if not master.stop: + try: + tsk = master.ready.get() + if tsk: + tsk.log_display(tsk.generator.bld) + master.process_task(tsk) + else: + break + finally: + master.out.put(tsk) + +class Spawner(object): + """ + Daemon thread that consumes tasks from :py:class:`waflib.Runner.Parallel` producer and + spawns a consuming thread :py:class:`waflib.Runner.Consumer` for each + :py:class:`waflib.Task.Task` instance. + """ + def __init__(self, master): + self.master = master + """:py:class:`waflib.Runner.Parallel` producer instance""" + + self.pool = [TaskConsumer(self) for i in range(master.numjobs)] + +Runner.Spawner = Spawner diff --git a/third_party/waf/waflib/extras/color_gcc.py b/third_party/waf/waflib/extras/color_gcc.py index b68c5ebf2df..09729035fec 100644 --- a/third_party/waf/waflib/extras/color_gcc.py +++ b/third_party/waf/waflib/extras/color_gcc.py @@ -19,7 +19,7 @@ class ColorGCCFormatter(Logs.formatter): func = frame.f_code.co_name if func == 'exec_command': cmd = frame.f_locals.get('cmd') - if isinstance(cmd, list) and ('gcc' in cmd[0] or 'g++' in cmd[0]): + if isinstance(cmd, list) and (len(cmd) > 0) and ('gcc' in cmd[0] or 'g++' in cmd[0]): lines = [] for line in rec.msg.splitlines(): if 'warning: ' in line: diff --git a/third_party/waf/waflib/extras/eclipse.py b/third_party/waf/waflib/extras/eclipse.py index bb787416e9f..49ca9686b7b 100644 --- a/third_party/waf/waflib/extras/eclipse.py +++ b/third_party/waf/waflib/extras/eclipse.py @@ -10,6 +10,9 @@ Usage: def options(opt): opt.load('eclipse') +To add additional targets beside standard ones (configure, dist, install, check) +the environment ECLIPSE_EXTRA_TARGETS can be set (ie. to ['test', 'lint', 'docs']) + $ waf configure eclipse """ @@ -25,6 +28,8 @@ cdt_core = oe_cdt + '.core' cdt_bld = oe_cdt + '.build.core' extbuilder_dir = '.externalToolBuilders' extbuilder_name = 'Waf_Builder.launch' +settings_dir = '.settings' +settings_name = 'language.settings.xml' class eclipse(Build.BuildContext): cmd = 'eclipse' @@ -131,9 +136,11 @@ class eclipse(Build.BuildContext): path = p.path_from(self.srcnode) if (path.startswith("/")): - cpppath.append(path) + if path not in cpppath: + cpppath.append(path) else: - workspace_includes.append(path) + if path not in workspace_includes: + workspace_includes.append(path) if is_cc and path not in source_dirs: source_dirs.append(path) @@ -156,6 +163,61 @@ class eclipse(Build.BuildContext): project = self.impl_create_javaproject(javasrcpath, javalibpath) self.write_conf_to_xml('.classpath', project) + # Create editor language settings to have correct standards applied in IDE, as per project configuration + try: + os.mkdir(settings_dir) + except OSError: + pass # Ignore if dir already exists + + lang_settings = Document() + project = lang_settings.createElement('project') + + # Language configurations for C and C++ via cdt + if hasc: + configuration = self.add(lang_settings, project, 'configuration', + {'id' : 'org.eclipse.cdt.core.default.config.1', 'name': 'Default'}) + + extension = self.add(lang_settings, configuration, 'extension', {'point': 'org.eclipse.cdt.core.LanguageSettingsProvider'}) + + provider = self.add(lang_settings, extension, 'provider', + { 'copy-of': 'extension', + 'id': 'org.eclipse.cdt.ui.UserLanguageSettingsProvider'}) + + provider = self.add(lang_settings, extension, 'provider-reference', + { 'id': 'org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider', + 'ref': 'shared-provider'}) + + provider = self.add(lang_settings, extension, 'provider-reference', + { 'id': 'org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider', + 'ref': 'shared-provider'}) + + # C and C++ are kept as separated providers so appropriate flags are used also in mixed projects + if self.env.CC: + provider = self.add(lang_settings, extension, 'provider', + { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector', + 'console': 'false', + 'id': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector.1', + 'keep-relative-paths' : 'false', + 'name': 'CDT GCC Built-in Compiler Settings', + 'parameter': '%s %s ${FLAGS} -E -P -v -dD "${INPUTS}"'%(self.env.CC[0],' '.join(self.env['CFLAGS'])), + 'prefer-non-shared': 'true' }) + + self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.gcc'}) + + if self.env.CXX: + provider = self.add(lang_settings, extension, 'provider', + { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector', + 'console': 'false', + 'id': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector.2', + 'keep-relative-paths' : 'false', + 'name': 'CDT GCC Built-in Compiler Settings', + 'parameter': '%s %s ${FLAGS} -E -P -v -dD "${INPUTS}"'%(self.env.CXX[0],' '.join(self.env['CXXFLAGS'])), + 'prefer-non-shared': 'true' }) + self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.g++'}) + + lang_settings.appendChild(project) + self.write_conf_to_xml('%s%s%s'%(settings_dir, os.path.sep, settings_name), lang_settings) + def impl_create_project(self, executable, appname, hasc, hasjava, haspython, waf_executable): doc = Document() projectDescription = doc.createElement('projectDescription') @@ -341,6 +403,8 @@ class eclipse(Build.BuildContext): addTargetWrap('dist', False) addTargetWrap('install', False) addTargetWrap('check', False) + for addTgt in self.env.ECLIPSE_EXTRA_TARGETS or []: + addTargetWrap(addTgt, False) storageModule = self.add(doc, cproject, 'storageModule', {'moduleId': 'cdtBuildSystem', @@ -348,6 +412,12 @@ class eclipse(Build.BuildContext): self.add(doc, storageModule, 'project', {'id': '%s.null.1'%appname, 'name': appname}) + storageModule = self.add(doc, cproject, 'storageModule', + {'moduleId': 'org.eclipse.cdt.core.LanguageSettingsProviders'}) + + storageModule = self.add(doc, cproject, 'storageModule', + {'moduleId': 'scannerConfiguration'}) + doc.appendChild(cproject) return doc diff --git a/third_party/waf/waflib/extras/gccdeps.py b/third_party/waf/waflib/extras/gccdeps.py index 1fc9373489a..9e9952f2f7d 100644 --- a/third_party/waf/waflib/extras/gccdeps.py +++ b/third_party/waf/waflib/extras/gccdeps.py @@ -29,13 +29,6 @@ if not c_preproc.go_absolute: # Third-party tools are allowed to add extra names in here with append() supported_compilers = ['gas', 'gcc', 'icc', 'clang'] -def scan(self): - if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS: - return super(self.derived_gccdeps, self).scan() - nodes = self.generator.bld.node_deps.get(self.uid(), []) - names = [] - return (nodes, names) - re_o = re.compile(r"\.o$") re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped @@ -61,28 +54,30 @@ def path_to_node(base_node, path, cached_nodes): else: # Not hashable, assume it is a list and join into a string node_lookup_key = (base_node, os.path.sep.join(path)) + try: - lock.acquire() node = cached_nodes[node_lookup_key] except KeyError: - node = base_node.find_resource(path) - cached_nodes[node_lookup_key] = node - finally: - lock.release() + # retry with lock on cache miss + with lock: + try: + node = cached_nodes[node_lookup_key] + except KeyError: + node = cached_nodes[node_lookup_key] = base_node.find_resource(path) + return node def post_run(self): if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS: return super(self.derived_gccdeps, self).post_run() - name = self.outputs[0].abspath() - name = re_o.sub('.d', name) + deps_filename = self.outputs[0].abspath() + deps_filename = re_o.sub('.d', deps_filename) try: - txt = Utils.readf(name) + deps_txt = Utils.readf(deps_filename) except EnvironmentError: Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?') raise - #os.remove(name) # Compilers have the choice to either output the file's dependencies # as one large Makefile rule: @@ -102,15 +97,16 @@ def post_run(self): # So the first step is to sanitize the input by stripping out the left- # hand side of all these lines. After that, whatever remains are the # implicit dependencies of task.outputs[0] - txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()]) + deps_txt = '\n'.join([remove_makefile_rule_lhs(line) for line in deps_txt.splitlines()]) # Now join all the lines together - txt = txt.replace('\\\n', '') + deps_txt = deps_txt.replace('\\\n', '') - val = txt.strip() - val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x] + dep_paths = deps_txt.strip() + dep_paths = [x.replace('\\ ', ' ') for x in re_splitter.split(dep_paths) if x] - nodes = [] + resolved_nodes = [] + unresolved_names = [] bld = self.generator.bld # Dynamically bind to the cache @@ -119,39 +115,41 @@ def post_run(self): except AttributeError: cached_nodes = bld.cached_nodes = {} - for x in val: + for path in dep_paths: node = None - if os.path.isabs(x): - node = path_to_node(bld.root, x, cached_nodes) + if os.path.isabs(path): + node = path_to_node(bld.root, path, cached_nodes) else: # TODO waf 1.9 - single cwd value - path = getattr(bld, 'cwdx', bld.bldnode) + base_node = getattr(bld, 'cwdx', bld.bldnode) # when calling find_resource, make sure the path does not contain '..' - x = [k for k in Utils.split_path(x) if k and k != '.'] - while '..' in x: - idx = x.index('..') + path = [k for k in Utils.split_path(path) if k and k != '.'] + while '..' in path: + idx = path.index('..') if idx == 0: - x = x[1:] - path = path.parent + path = path[1:] + base_node = base_node.parent else: - del x[idx] - del x[idx-1] + del path[idx] + del path[idx-1] - node = path_to_node(path, x, cached_nodes) + node = path_to_node(base_node, path, cached_nodes) if not node: - raise ValueError('could not find %r for %r' % (x, self)) + raise ValueError('could not find %r for %r' % (path, self)) + if id(node) == id(self.inputs[0]): # ignore the source file, it is already in the dependencies # this way, successful config tests may be retrieved from the cache continue - nodes.append(node) - Logs.debug('deps: gccdeps for %s returned %s', self, nodes) + resolved_nodes.append(node) - bld.node_deps[self.uid()] = nodes - bld.raw_deps[self.uid()] = [] + Logs.debug('deps: gccdeps for %s returned %s', self, resolved_nodes) + + bld.node_deps[self.uid()] = resolved_nodes + bld.raw_deps[self.uid()] = unresolved_names try: del self.cache_sig @@ -160,6 +158,14 @@ def post_run(self): Task.Task.post_run(self) +def scan(self): + if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS: + return super(self.derived_gccdeps, self).scan() + + resolved_nodes = self.generator.bld.node_deps.get(self.uid(), []) + unresolved_names = [] + return (resolved_nodes, unresolved_names) + def sig_implicit_deps(self): if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS: return super(self.derived_gccdeps, self).sig_implicit_deps() diff --git a/third_party/waf/waflib/extras/msvcdeps.py b/third_party/waf/waflib/extras/msvcdeps.py index 52985dce058..e8985bde7c7 100644 --- a/third_party/waf/waflib/extras/msvcdeps.py +++ b/third_party/waf/waflib/extras/msvcdeps.py @@ -32,7 +32,6 @@ from waflib.Tools import c_preproc, c, cxx, msvc from waflib.TaskGen import feature, before_method lock = threading.Lock() -nodes = {} # Cache the path -> Node lookup PREPROCESSOR_FLAG = '/showIncludes' INCLUDE_PATTERN = 'Note: including file:' @@ -50,23 +49,47 @@ def apply_msvcdeps_flags(taskgen): if taskgen.env.get_flat(flag).find(PREPROCESSOR_FLAG) < 0: taskgen.env.append_value(flag, PREPROCESSOR_FLAG) + +def get_correct_path_case(base_path, path): + ''' + Return a case-corrected version of ``path`` by searching the filesystem for + ``path``, relative to ``base_path``, using the case returned by the filesystem. + ''' + components = Utils.split_path(path) + + corrected_path = '' + if os.path.isabs(path): + corrected_path = components.pop(0).upper() + os.sep + + for part in components: + part = part.lower() + search_path = os.path.join(base_path, corrected_path) + if part == '..': + corrected_path = os.path.join(corrected_path, part) + search_path = os.path.normpath(search_path) + continue + + for item in sorted(os.listdir(search_path)): + if item.lower() == part: + corrected_path = os.path.join(corrected_path, item) + break + else: + raise ValueError("Can't find %r in %r" % (part, search_path)) + + return corrected_path + + def path_to_node(base_node, path, cached_nodes): ''' Take the base node and the path and return a node Results are cached because searching the node tree is expensive The following code is executed by threads, it is not safe, so a lock is needed... ''' - # normalize the path because ant_glob() does not understand - # parent path components (..) + # normalize the path to remove parent path components (..) path = os.path.normpath(path) # normalize the path case to increase likelihood of a cache hit - path = os.path.normcase(path) - - # ant_glob interprets [] and () characters, so those must be replaced - path = path.replace('[', '?').replace(']', '?').replace('(', '[(]').replace(')', '[)]') - - node_lookup_key = (base_node, path) + node_lookup_key = (base_node, os.path.normcase(path)) try: node = cached_nodes[node_lookup_key] @@ -76,8 +99,8 @@ def path_to_node(base_node, path, cached_nodes): try: node = cached_nodes[node_lookup_key] except KeyError: - node_list = base_node.ant_glob([path], ignorecase=True, remove=False, quiet=True, regex=False) - node = cached_nodes[node_lookup_key] = node_list[0] if node_list else None + path = get_correct_path_case(base_node.abspath(), path) + node = cached_nodes[node_lookup_key] = base_node.find_node(path) return node @@ -89,9 +112,9 @@ def post_run(self): if getattr(self, 'cached', None): return Task.Task.post_run(self) - bld = self.generator.bld - unresolved_names = [] resolved_nodes = [] + unresolved_names = [] + bld = self.generator.bld # Dynamically bind to the cache try: @@ -124,11 +147,14 @@ def post_run(self): continue if id(node) == id(self.inputs[0]): - # Self-dependency + # ignore the source file, it is already in the dependencies + # this way, successful config tests may be retrieved from the cache continue resolved_nodes.append(node) + Logs.debug('deps: msvcdeps for %s returned %s', self, resolved_nodes) + bld.node_deps[self.uid()] = resolved_nodes bld.raw_deps[self.uid()] = unresolved_names diff --git a/third_party/waf/waflib/extras/msvs.py b/third_party/waf/waflib/extras/msvs.py index 8aa2db0b751..03b739f849c 100644 --- a/third_party/waf/waflib/extras/msvs.py +++ b/third_party/waf/waflib/extras/msvs.py @@ -787,8 +787,12 @@ class msvs_generator(BuildContext): self.collect_dirs() default_project = getattr(self, 'default_project', None) def sortfun(x): - if x.name == default_project: + # folders should sort to the top + if getattr(x, 'VS_GUID_SOLUTIONFOLDER', None): return '' + # followed by the default project + elif x.name == default_project: + return ' ' return getattr(x, 'path', None) and x.path.win32path() or x.name self.all_projects.sort(key=sortfun) diff --git a/third_party/waf/waflib/extras/swig.py b/third_party/waf/waflib/extras/swig.py index 740ab46d963..967caeb5a82 100644 --- a/third_party/waf/waflib/extras/swig.py +++ b/third_party/waf/waflib/extras/swig.py @@ -17,7 +17,7 @@ tasks have to be added dynamically: SWIG_EXTS = ['.swig', '.i'] -re_module = re.compile(r'%module(?:\s*\(.*\))?\s+(.+)', re.M) +re_module = re.compile(r'%module(?:\s*\(.*\))?\s+([^\r\n]+)', re.M) re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M) re_2 = re.compile(r'[#%](?:include|import(?:\(module=".*"\))+|python(?:begin|code)) [<"](.*)[">]', re.M) diff --git a/third_party/waf/waflib/extras/wafcache.py b/third_party/waf/waflib/extras/wafcache.py index cc23fcd6673..2cef46c0e1c 100644 --- a/third_party/waf/waflib/extras/wafcache.py +++ b/third_party/waf/waflib/extras/wafcache.py @@ -258,6 +258,19 @@ def build(bld): """ Called during the build process to enable file caching """ + if WAFCACHE_STATS: + # Init counter for statistics and hook to print results at the end + bld.cache_reqs = bld.cache_hits = bld.cache_puts = 0 + + def printstats(bld): + hit_ratio = 0 + if bld.cache_reqs > 0: + hit_ratio = (bld.cache_hits / bld.cache_reqs) * 100 + Logs.pprint('CYAN', ' wafcache stats: requests: %s, hits, %s, ratio: %.2f%%, writes %s' % + (bld.cache_reqs, bld.cache_hits, hit_ratio, bld.cache_puts) ) + + bld.add_post_fun(printstats) + if process_pool: # already called once return @@ -273,19 +286,6 @@ def build(bld): for x in reversed(list(Task.classes.values())): make_cached(x) - if WAFCACHE_STATS: - # Init counter for statistics and hook to print results at the end - bld.cache_reqs = bld.cache_hits = bld.cache_puts = 0 - - def printstats(bld): - hit_ratio = 0 - if bld.cache_reqs > 0: - hit_ratio = (bld.cache_hits / bld.cache_reqs) * 100 - Logs.pprint('CYAN', ' wafcache stats: requests: %s, hits, %s, ratio: %.2f%%, writes %s' % - (bld.cache_reqs, bld.cache_hits, hit_ratio, bld.cache_puts) ) - - bld.add_post_fun(printstats) - def cache_command(sig, files_from, files_to): """ Create a command for cache worker processes, returns a pickled |