source: rtems/gccdeps.py

Last change on this file was f3f0370f, checked in by Sebastian Huber <sebastian.huber@…>, on Jul 19, 2019 at 11:09:43 AM

build: Alternative build system based on waf

Update #3818.

  • Property mode set to 100644
File size: 6.9 KB
Line 
1#!/usr/bin/env python
2# encoding: utf-8
3# Thomas Nagy, 2008-2010 (ita)
4
5"""
6Execute the tasks with gcc -MD, read the dependencies from the .d file
7and prepare the dependency calculation for the next run.
8This affects the cxx class, so make sure to load Qt5 after this tool.
9
10Usage::
11
12        def options(opt):
13                opt.load('compiler_cxx')
14        def configure(conf):
15                conf.load('compiler_cxx gccdeps')
16"""
17
18import os, re, threading
19from waflib import Task, Logs, Utils, Errors
20from waflib.Tools import c_preproc
21from waflib.TaskGen import before_method, feature
22
23lock = threading.Lock()
24
25gccdeps_flags = ['-MD']
26if not c_preproc.go_absolute:
27        gccdeps_flags = ['-MMD']
28
29# Third-party tools are allowed to add extra names in here with append()
30supported_compilers = ['gas', 'gcc', 'icc', 'clang']
31
32def scan(self):
33        if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
34                return super(self.derived_gccdeps, self).scan()
35        nodes = self.generator.bld.node_deps.get(self.uid(), [])
36        names = []
37        return (nodes, names)
38
39re_o = re.compile(r"\.o$")
40re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
41
42def remove_makefile_rule_lhs(line):
43        # Splitting on a plain colon would accidentally match inside a
44        # Windows absolute-path filename, so we must search for a colon
45        # followed by whitespace to find the divider between LHS and RHS
46        # of the Makefile rule.
47        rulesep = ': '
48
49        sep_idx = line.find(rulesep)
50        if sep_idx >= 0:
51                return line[sep_idx + 2:]
52        else:
53                return line
54
55def path_to_node(base_node, path, cached_nodes):
56        # Take the base node and the path and return a node
57        # Results are cached because searching the node tree is expensive
58        # The following code is executed by threads, it is not safe, so a lock is needed...
59        if getattr(path, '__hash__'):
60                node_lookup_key = (base_node, path)
61        else:
62                # Not hashable, assume it is a list and join into a string
63                node_lookup_key = (base_node, os.path.sep.join(path))
64        try:
65                lock.acquire()
66                node = cached_nodes[node_lookup_key]
67        except KeyError:
68                node = base_node.find_resource(path)
69                cached_nodes[node_lookup_key] = node
70        finally:
71                lock.release()
72        return node
73
74def post_run(self):
75        if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
76                return super(self.derived_gccdeps, self).post_run()
77
78        name = self.outputs[0].abspath()
79        name = re_o.sub('.d', name)
80        try:
81                txt = Utils.readf(name)
82        except EnvironmentError:
83                Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
84                raise
85        #os.remove(name)
86
87        # Compilers have the choice to either output the file's dependencies
88        # as one large Makefile rule:
89        #
90        #   /path/to/file.o: /path/to/dep1.h \
91        #                    /path/to/dep2.h \
92        #                    /path/to/dep3.h \
93        #                    ...
94        #
95        # or as many individual rules:
96        #
97        #   /path/to/file.o: /path/to/dep1.h
98        #   /path/to/file.o: /path/to/dep2.h
99        #   /path/to/file.o: /path/to/dep3.h
100        #   ...
101        #
102        # So the first step is to sanitize the input by stripping out the left-
103        # hand side of all these lines. After that, whatever remains are the
104        # implicit dependencies of task.outputs[0]
105        txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()])
106
107        # Now join all the lines together
108        txt = txt.replace('\\\n', '')
109
110        val = txt.strip()
111        val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
112
113        nodes = []
114        bld = self.generator.bld
115
116        # Dynamically bind to the cache
117        try:
118                cached_nodes = bld.cached_nodes
119        except AttributeError:
120                cached_nodes = bld.cached_nodes = {}
121
122        for x in val:
123
124                node = None
125                if os.path.isabs(x):
126                        node = path_to_node(bld.root, x, cached_nodes)
127                else:
128                        # TODO waf 1.9 - single cwd value
129                        path = getattr(bld, 'cwdx', bld.bldnode)
130                        # when calling find_resource, make sure the path does not contain '..'
131                        x = [k for k in Utils.split_path(x) if k and k != '.']
132                        while '..' in x:
133                                idx = x.index('..')
134                                if idx == 0:
135                                        x = x[1:]
136                                        path = path.parent
137                                else:
138                                        del x[idx]
139                                        del x[idx-1]
140
141                        node = path_to_node(path, x, cached_nodes)
142
143                if not node:
144                        raise ValueError('could not find %r for %r' % (x, self))
145                if id(node) == id(self.inputs[0]):
146                        # ignore the source file, it is already in the dependencies
147                        # this way, successful config tests may be retrieved from the cache
148                        continue
149                nodes.append(node)
150
151        Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
152
153        bld.node_deps[self.uid()] = nodes
154        bld.raw_deps[self.uid()] = []
155
156        try:
157                del self.cache_sig
158        except AttributeError:
159                pass
160
161        Task.Task.post_run(self)
162
163def sig_implicit_deps(self):
164        if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
165                return super(self.derived_gccdeps, self).sig_implicit_deps()
166        bld = self.generator.bld
167
168        try:
169                return self.compute_sig_implicit_deps()
170        except Errors.TaskNotReady:
171                raise ValueError("Please specify the build order precisely with gccdeps (asm/c/c++ tasks)")
172        except EnvironmentError:
173                # If a file is renamed, assume the dependencies are stale and must be recalculated
174                for x in bld.node_deps.get(self.uid(), []):
175                        if not x.is_bld() and not x.exists():
176                                try:
177                                        del x.parent.children[x.name]
178                                except KeyError:
179                                        pass
180
181        key = self.uid()
182        bld.node_deps[key] = []
183        bld.raw_deps[key] = []
184        return Utils.SIG_NIL
185
186def wrap_compiled_task(classname):
187        derived_class = type(classname, (Task.classes[classname],), {})
188        derived_class.derived_gccdeps = derived_class
189        derived_class.post_run = post_run
190        derived_class.scan = scan
191        derived_class.sig_implicit_deps = sig_implicit_deps
192
193for k in ('asm', 'c', 'cxx'):
194        if k in Task.classes:
195                wrap_compiled_task(k)
196
197@before_method('process_source')
198@feature('force_gccdeps')
199def force_gccdeps(self):
200        self.env.ENABLE_GCCDEPS = ['asm', 'c', 'cxx']
201
202def configure(conf):
203        # in case someone provides a --enable-gccdeps command-line option
204        if not getattr(conf.options, 'enable_gccdeps', True):
205                return
206
207        global gccdeps_flags
208        flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
209        if conf.env.ASM_NAME in supported_compilers:
210                try:
211                        conf.check(fragment='', features='asm force_gccdeps', asflags=flags, compile_filename='test.S', msg='Checking for asm flags %r' % ''.join(flags))
212                except Errors.ConfigurationError:
213                        pass
214                else:
215                        conf.env.append_value('ASFLAGS', flags)
216                        conf.env.append_unique('ENABLE_GCCDEPS', 'asm')
217
218        if conf.env.CC_NAME in supported_compilers:
219                try:
220                        conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
221                except Errors.ConfigurationError:
222                        pass
223                else:
224                        conf.env.append_value('CFLAGS', flags)
225                        conf.env.append_unique('ENABLE_GCCDEPS', 'c')
226
227        if conf.env.CXX_NAME in supported_compilers:
228                try:
229                        conf.check(fragment='int main() { return 0; }', features='cxx force_gccdeps', cxxflags=flags, msg='Checking for cxx flags %r' % ''.join(flags))
230                except Errors.ConfigurationError:
231                        pass
232                else:
233                        conf.env.append_value('CXXFLAGS', flags)
234                        conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')
235
236def options(opt):
237        raise ValueError('Do not load gccdeps options')
238
Note: See TracBrowser for help on using the repository browser.