summaryrefslogtreecommitdiff
path: root/buildtools/wafsamba/samba_optimisation.py
blob: 9d4fad1ed16f572327a4c88bef74763e2a825f99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# This file contains waf optimisations for Samba

# most of these optimisations are possible because of the restricted build environment
# that Samba has. For example, Samba doesn't attempt to cope with Win32 paths during the
# build, and Samba doesn't need build varients

# overall this makes some build tasks quite a bit faster

import os
import Build, Utils, Node
from TaskGen import feature, after, before
import preproc

@feature('c', 'cc', 'cxx')
@after('apply_type_vars', 'apply_lib_vars', 'apply_core')
def apply_incpaths(self):
    lst = []

    try:
        kak = self.bld.kak
    except AttributeError:
        kak = self.bld.kak = {}

    # TODO move the uselib processing out of here
    for lib in self.to_list(self.uselib):
        for path in self.env['CPPPATH_' + lib]:
            if not path in lst:
                lst.append(path)
    if preproc.go_absolute:
        for path in preproc.standard_includes:
            if not path in lst:
                lst.append(path)

    for path in self.to_list(self.includes):
        if not path in lst:
            if preproc.go_absolute or path[0] != '/':  # os.path.isabs(path):
                lst.append(path)
            else:
                self.env.prepend_value('CPPPATH', path)

    for path in lst:
        node = None
        if path[0] == '/': # os.path.isabs(path):
            if preproc.go_absolute:
                node = self.bld.root.find_dir(path)
        elif path[0] == '#':
            node = self.bld.srcnode
            if len(path) > 1:
                try:
                    node = kak[path]
                except KeyError:
                    kak[path] = node = node.find_dir(path[1:])
        else:
            try:
                node = kak[(self.path.id, path)]
            except KeyError:
                kak[(self.path.id, path)] = node = self.path.find_dir(path)

        if node:
            self.env.append_value('INC_PATHS', node)

@feature('c', 'cc')
@after('apply_incpaths')
def apply_obj_vars_cc(self):
    """after apply_incpaths for INC_PATHS"""
    env = self.env
    app = env.append_unique
    cpppath_st = env['CPPPATH_ST']

    lss = env['_CCINCFLAGS']

    try:
         cac = self.bld.cac
    except AttributeError:
         cac = self.bld.cac = {}

    # local flags come first
    # set the user-defined includes paths
    for i in env['INC_PATHS']:

        try:
            lss.extend(cac[i.id])
        except KeyError:

            cac[i.id] = [cpppath_st % i.bldpath(env), cpppath_st % i.srcpath(env)]
            lss.extend(cac[i.id])

    env['_CCINCFLAGS'] = lss
    # set the library include paths
    for i in env['CPPPATH']:
        app('_CCINCFLAGS', cpppath_st % i)

import Node, Environment

def vari(self):
    return "default"
Environment.Environment.variant = vari

def variant(self, env):
    if not env: return 0
    elif self.id & 3 == Node.FILE: return 0
    else: return "default"
Node.Node.variant = variant


import TaskGen, Task

def create_task(self, name, src=None, tgt=None):
    task = Task.TaskBase.classes[name](self.env, generator=self)
    if src:
        task.set_inputs(src)
    if tgt:
        task.set_outputs(tgt)
    return task
TaskGen.task_gen.create_task = create_task

def hash_constraints(self):
    a = self.attr
    sum = hash((str(a('before', '')),
            str(a('after', '')),
            str(a('ext_in', '')),
            str(a('ext_out', '')),
            self.__class__.maxjobs))
    return sum
Task.TaskBase.hash_constraints = hash_constraints

def hash_env_vars(self, env, vars_lst):
    idx = str(id(env)) + str(vars_lst)
    try:
        return self.cache_sig_vars[idx]
    except KeyError:
        pass

    m = Utils.md5()
    m.update(''.join([str(env[a]) for a in vars_lst]))

    ret = self.cache_sig_vars[idx] = m.digest()
    return ret
Build.BuildContext.hash_env_vars = hash_env_vars


def store_fast(self, filename):
    file = open(filename, 'wb')
    data = self.get_merged_dict()
    try:
        Build.cPickle.dump(data, file, -1)
    finally:
        file.close()
Environment.Environment.store_fast = store_fast

def load_fast(self, filename):
    file = open(filename, 'rb')
    try:
        data = Build.cPickle.load(file)
    finally:
        file.close()
    self.table.update(data)
Environment.Environment.load_fast = load_fast

def is_this_a_static_lib(self, name):
    try:
        cache = self.cache_is_this_a_static_lib
    except AttributeError:
        cache = self.cache_is_this_a_static_lib = {}
    try:
        return cache[name]
    except KeyError:
        ret = cache[name] = 'cstaticlib' in self.bld.get_tgen_by_name(name).features
        return ret
TaskGen.task_gen.is_this_a_static_lib = is_this_a_static_lib

def shared_ancestors(self):
    try:
        cache = self.cache_is_this_a_static_lib
    except AttributeError:
        cache = self.cache_is_this_a_static_lib = {}
    try:
        return cache[id(self)]
    except KeyError:

        ret = []
        if 'cshlib' in self.features: # or 'cprogram' in self.features:
            if getattr(self, 'uselib_local', None):
                lst = self.to_list(self.uselib_local)
                ret = [x for x in lst if not self.is_this_a_static_lib(x)]
        cache[id(self)] = ret
        return ret
TaskGen.task_gen.shared_ancestors = shared_ancestors

@feature('c', 'cc', 'cxx')
@after('apply_link', 'init_cc', 'init_cxx', 'apply_core')
def apply_lib_vars(self):
    """after apply_link because of 'link_task'
    after default_cc because of the attribute 'uselib'"""

    # after 'apply_core' in case if 'cc' if there is no link

    env = self.env
    app = env.append_value
    seen_libpaths = set([])

    # OPTIMIZATION 1: skip uselib variables already added (700ms)
    seen_uselib = set([])

    # 1. the case of the libs defined in the project (visit ancestors first)
    # the ancestors external libraries (uselib) will be prepended
    self.uselib = self.to_list(self.uselib)
    names = self.to_list(self.uselib_local)

    seen = set([])
    tmp = Utils.deque(names) # consume a copy of the list of names
    while tmp:
        lib_name = tmp.popleft()
        # visit dependencies only once
        if lib_name in seen:
            continue

        y = self.get_tgen_by_name(lib_name)
        if not y:
            raise Utils.WafError('object %r was not found in uselib_local (required by %r)' % (lib_name, self.name))
        y.post()
        seen.add(lib_name)

        # OPTIMIZATION 2: pre-compute ancestors shared libraries (100ms)
        tmp.extend(y.shared_ancestors())

        # link task and flags
        if getattr(y, 'link_task', None):

            link_name = y.target[y.target.rfind('/') + 1:]
            if 'cstaticlib' in y.features:
                app('STATICLIB', link_name)
            elif 'cshlib' in y.features or 'cprogram' in y.features:
                # WARNING some linkers can link against programs
                app('LIB', link_name)

            # the order
            self.link_task.set_run_after(y.link_task)

            # for the recompilation
            dep_nodes = getattr(self.link_task, 'dep_nodes', [])
            self.link_task.dep_nodes = dep_nodes + y.link_task.outputs

            # OPTIMIZATION 3: reduce the amount of function calls
            # add the link path too
            par = y.link_task.outputs[0].parent
            if id(par) not in seen_libpaths:
                seen_libpaths.add(id(par))
                tmp_path = par.bldpath(self.env)
                if not tmp_path in env['LIBPATH']:
                    env.prepend_value('LIBPATH', tmp_path)


        # add ancestors uselib too - but only propagate those that have no staticlib
        for v in self.to_list(y.uselib):
            if v not in seen_uselib:
                seen_uselib.add(v)
                if not env['STATICLIB_' + v]:
                    if not v in self.uselib:
                        self.uselib.insert(0, v)

    # 2. the case of the libs defined outside
    for x in self.uselib:
        for v in self.p_flag_vars:
            val = self.env[v + '_' + x]
            if val:
                self.env.append_value(v, val)

@feature('cprogram', 'cshlib', 'cstaticlib')
@after('apply_lib_vars')
@before('apply_obj_vars')
def samba_before_apply_obj_vars(self):
    """before apply_obj_vars for uselib, this removes the standard pathes"""

    def is_standard_libpath(env, path):
        for _path in env.STANDARD_LIBPATH:
            if _path == os.path.normpath(path):
                return True
        return False

    v = self.env

    for i in v['RPATH']:
        if is_standard_libpath(v, i):
            v['RPATH'].remove(i)

    for i in v['LIBPATH']:
        if is_standard_libpath(v, i):
            v['LIBPATH'].remove(i)