build: python modules need the libs from broken lib loops too
[Samba/gebeck_regimport.git] / buildtools / wafsamba / samba_deps.py
bloba3910b0d284dd6edd389e03a305bf128d240e8f6
1 # Samba automatic dependency handling and project rules
3 import Build, os, re, Environment
4 from samba_utils import *
5 from samba_autoconf import *
7 @conf
8 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
9 '''add a dependency for all binaries and libraries'''
10 if not 'GLOBAL_DEPENDENCIES' in ctx.env:
11 ctx.env.GLOBAL_DEPENDENCIES = []
12 ctx.env.GLOBAL_DEPENDENCIES.append(dep)
15 def TARGET_ALIAS(bld, target, alias):
16 '''define an alias for a target name'''
17 cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
18 if alias in cache:
19 print("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
20 raise
21 cache[alias] = target
22 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
25 def EXPAND_ALIAS(bld, target):
26 '''expand a target name via an alias'''
27 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
28 if target in aliases:
29 return aliases[target]
30 return target
31 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
34 def expand_subsystem_deps(bld):
35 '''expand the reverse dependencies resulting from subsystem
36 attributes of modules'''
37 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
38 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
39 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
41 for s in subsystems:
42 if s in aliases:
43 s = aliases[s]
44 bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
45 type = targets[s]
46 if type == 'DISABLED' or type == 'EMPTY':
47 continue
49 t = bld.name_to_obj(s, bld.env)
50 bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
51 for d in subsystems[s]:
52 type = targets[d['TARGET']]
53 if type != 'DISABLED' and type != 'EMPTY':
54 t.samba_deps_extended.append(d['TARGET'])
55 t2 = bld.name_to_obj(d['TARGET'], bld.env)
56 t2.samba_includes_extended.extend(t.samba_includes_extended)
57 t2.samba_deps_extended.extend(t.samba_deps_extended)
58 t.samba_deps_extended = unique_list(t.samba_deps_extended)
62 def build_dependencies(self):
63 '''This builds the dependency list for a target. It runs after all the targets are declared
65 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
66 the full dependency list for a target until we have all of the targets declared.
67 '''
69 # we only should add extra library and object deps on libraries and binaries
70 if not self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
71 return
73 # we need to link against:
75 # 1) any direct system libs
76 # 2) any indirect system libs that come from subsystem dependencies
77 # 3) any direct local libs
78 # 4) any indirect local libs that come from subsystem dependencies
79 # 5) any direct objects
80 # 6) any indirect objects that come from subsystem dependencies
82 self.uselib = list(self.final_syslibs)
83 self.uselib_local = list(self.final_libs)
84 self.add_objects = list(self.final_objects)
86 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
87 self.sname, self.uselib, self.uselib_local, self.add_objects)
91 def build_includes(self):
92 '''This builds the right set of includes for a target.
94 One tricky part of this is that the includes= attribute for a
95 target needs to use paths which are relative to that targets
96 declaration directory (which we can get at via t.path).
98 The way this works is the includes list gets added as
99 samba_includes in the main build task declaration. Then this
100 function runs after all of the tasks are declared, and it
101 processes the samba_includes attribute to produce a includes=
102 attribute
105 if getattr(self, 'samba_includes', None) is None:
106 return
108 bld = self.bld
110 inc_deps = self.includes_objects
112 includes = []
114 # maybe add local includes
115 if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
116 includes.append('.')
118 includes.extend(self.samba_includes_extended)
120 if 'EXTRA_INCLUDES' in bld.env:
121 includes.extend(bld.env['EXTRA_INCLUDES'])
123 includes.append('#')
125 inc_set = set()
126 inc_abs = []
128 for d in inc_deps:
129 t = bld.name_to_obj(d, bld.env)
130 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
131 inclist = getattr(t, 'samba_includes_extended', [])
132 if getattr(t, 'local_include', True) == True:
133 inclist.append('.')
134 if inclist == []:
135 continue
136 tpath = t.samba_abspath
137 for inc in inclist:
138 npath = tpath + '/' + inc
139 if not npath in inc_set:
140 inc_abs.append(npath)
141 inc_set.add(npath)
143 mypath = self.path.abspath(bld.env)
144 for inc in inc_abs:
145 relpath = os_path_relpath(inc, mypath)
146 includes.append(relpath)
148 if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
149 includes.append('.')
151 # now transform the includes list to be relative to the top directory
152 # which is represented by '#' in waf. This allows waf to cache the
153 # includes lists more efficiently
154 includes_top = []
155 for i in includes:
156 if i[0] == '#':
157 # some are already top based
158 includes_top.append(i)
159 continue
160 absinc = os.path.join(self.path.abspath(), i)
161 relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
162 includes_top.append('#' + relinc)
164 self.includes = unique_list(includes_top)
165 debug('deps: includes for target %s: includes=%s',
166 self.sname, self.includes)
171 def add_init_functions(self):
172 '''This builds the right set of init functions'''
174 bld = self.bld
176 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
178 # cope with the separated object lists from BINARY and LIBRARY targets
179 sname = self.sname
180 if sname.endswith('.objlist'):
181 sname = sname[0:-8]
183 modules = []
184 if sname in subsystems:
185 modules.append(sname)
187 m = getattr(self, 'samba_modules', None)
188 if m is not None:
189 modules.extend(TO_LIST(m))
191 m = getattr(self, 'samba_subsystem', None)
192 if m is not None:
193 modules.append(m)
195 if modules == []:
196 return
198 sentinal = getattr(self, 'init_function_sentinal', 'NULL')
200 cflags = getattr(self, 'samba_cflags', [])[:]
201 for m in modules:
202 bld.ASSERT(m in subsystems,
203 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
204 init_fn_list = []
205 for d in subsystems[m]:
206 init_fn_list.append(d['INIT_FUNCTION'])
207 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
208 self.ccflags = cflags
212 def check_duplicate_sources(bld, tgt_list):
213 '''see if we are compiling the same source file into multiple
214 subsystem targets for the same library or binary'''
216 debug('deps: checking for duplicate sources')
218 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
220 for t in tgt_list:
221 if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
222 continue
224 sources = []
225 for obj in t.add_objects:
226 t2 = t.bld.name_to_obj(obj, bld.env)
227 obj_sources = getattr(t2, 'source', '')
228 if obj_sources == '': continue
229 tpath = os_path_relpath(t2.path.abspath(bld.env), t.env['BUILD_DIRECTORY'] + '/default')
230 obj_sources = bld.SUBDIR(tpath, obj_sources)
231 sources.append( { 'dep':obj, 'src':set(TO_LIST(obj_sources)) } )
232 #debug('deps: dependency expansion for target %s add_object %s: %s',
233 # t.sname, obj, obj_sources)
234 for s in sources:
235 for s2 in sources:
236 if s['dep'] == s2['dep']: continue
237 common = s['src'].intersection(s2['src'])
238 if common:
239 bld.ASSERT(False,
240 "Target %s has duplicate source files in %s and %s : %s" % (t.sname,
241 s['dep'], s2['dep'],
242 common))
244 def check_orpaned_targets(bld, tgt_list):
245 '''check if any build targets are orphaned'''
247 target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
249 debug('deps: checking for orphaned targets')
251 for t in tgt_list:
252 if getattr(t, 'samba_used', False) == True:
253 continue
254 type = target_dict[t.sname]
255 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
256 if re.search('^PIDL_', t.sname) is None:
257 print "Target %s of type %s is unused by any other target" % (t.sname, type)
260 def show_final_deps(bld, tgt_list):
261 '''show the final dependencies for all targets'''
263 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
265 for t in tgt_list:
266 if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
267 continue
268 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
269 t.sname, t.uselib, t.uselib_local, t.add_objects)
272 def add_samba_attributes(bld, tgt_list):
273 '''ensure a target has a the required samba attributes'''
275 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
277 for t in tgt_list:
278 if t.name != '':
279 t.sname = t.name
280 else:
281 t.sname = t.target
282 t.samba_type = targets[t.sname]
283 t.samba_abspath = t.path.abspath(bld.env)
284 t.samba_deps_extended = t.samba_deps[:]
285 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
286 t.ccflags = getattr(t, 'samba_cflags', '')
288 def build_direct_deps(bld, tgt_list):
289 '''build the direct_objects and direct_libs sets for each target'''
291 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
292 global_deps = bld.env.GLOBAL_DEPENDENCIES
294 for t in tgt_list:
295 t.direct_objects = set()
296 t.direct_libs = set()
297 t.direct_syslibs = set()
298 deps = t.samba_deps_extended
299 deps.extend(global_deps)
300 for d in deps:
301 d = EXPAND_ALIAS(bld, d)
302 if not d in targets:
303 print "Unknown dependency %s in %s" % (d, t.sname)
304 raise
305 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
306 continue
307 if targets[d] == 'SYSLIB':
308 t.direct_syslibs.add(d)
309 continue
310 t2 = bld.name_to_obj(d, bld.env)
311 if t2 is None:
312 print "no task %s type %s" % (d, targets[d])
313 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
314 t.direct_libs.add(d)
315 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
316 t.direct_objects.add(d)
317 debug('deps: built direct dependencies')
321 def indirect_libs(bld, t, chain):
322 '''recursively calculate the indirect library dependencies for a target
324 An indirect library is a library that results from a dependency on
325 a subsystem
328 ret = getattr(t, 'indirect_libs', None)
329 if ret is not None:
330 return ret
332 ret = set()
333 for obj in t.direct_objects:
334 if obj in chain:
335 continue
336 chain.add(obj)
337 t2 = bld.name_to_obj(obj, bld.env)
338 r2 = indirect_libs(bld, t2, chain)
339 chain.remove(obj)
340 ret = ret.union(t2.direct_libs)
341 ret = ret.union(r2)
343 for obj in t.indirect_objects:
344 if obj in chain:
345 continue
346 chain.add(obj)
347 t2 = bld.name_to_obj(obj, bld.env)
348 r2 = indirect_libs(bld, t2, chain)
349 chain.remove(obj)
350 ret = ret.union(t2.direct_libs)
351 ret = ret.union(r2)
353 t.indirect_libs = ret
355 return ret
358 def indirect_syslibs(bld, t, chain):
359 '''recursively calculate the indirect system library dependencies for a target
361 An indirect syslib results from a subsystem dependency
364 ret = getattr(t, 'indirect_syslibs', None)
365 if ret is not None:
366 return ret
367 ret = set()
368 for obj in t.direct_objects:
369 if obj in chain:
370 continue
371 chain.add(obj)
372 t2 = bld.name_to_obj(obj, bld.env)
373 r2 = indirect_syslibs(bld, t2, chain)
374 chain.remove(obj)
375 ret = ret.union(t2.direct_syslibs)
376 ret = ret.union(r2)
378 t.indirect_syslibs = ret
379 return ret
382 def indirect_objects(bld, t, chain):
383 '''recursively calculate the indirect object dependencies for a target
385 indirect objects are the set of objects from expanding the
386 subsystem dependencies
389 ret = getattr(t, 'indirect_objects', None)
390 if ret is not None: return ret
392 ret = set()
393 for lib in t.direct_objects:
394 if lib in chain:
395 continue
396 chain.add(lib)
397 t2 = bld.name_to_obj(lib, bld.env)
398 r2 = indirect_objects(bld, t2, chain)
399 chain.remove(lib)
400 ret = ret.union(t2.direct_objects)
401 ret = ret.union(r2)
403 t.indirect_objects = ret
404 return ret
407 def expanded_targets(bld, t, chain):
408 '''recursively calculate the expanded targets for a target
410 expanded objects are the set of objects, libraries and syslibs
411 from expanding the subsystem dependencies, library dependencies
412 and syslib dependencies
415 ret = getattr(t, 'expanded_targets', None)
416 if ret is not None: return ret
418 ret = t.direct_objects.copy()
419 ret = ret.union(t.direct_libs)
420 ret = ret.union(t.direct_syslibs)
422 direct = ret.copy()
424 for d in direct:
425 if d in chain: continue
426 chain.add(d)
427 t2 = bld.name_to_obj(d, bld.env)
428 if t2 is None: continue
429 r2 = expanded_targets(bld, t2, chain)
430 chain.remove(d)
431 ret = ret.union(r2)
433 if t.sname in ret:
434 ret.remove(t.sname)
436 t.expanded_targets = ret
437 return ret
440 def expanded_targets2(bld, t, chain):
441 '''recursively calculate the expanded targets for a target
443 expanded objects are the set of objects from expanding the
444 subsystem dependencies and library dependencies
447 ret = getattr(t, 'expanded_targets2', None)
448 if ret is not None: return ret
450 ret = t.final_objects.copy()
452 for attr in [ 'final_objects', 'final_libs' ]:
453 f = getattr(t, attr, set())
454 for d in f.copy():
455 if d in chain:
456 continue
457 chain.add(d)
458 t2 = bld.name_to_obj(d, bld.env)
459 if t2 is None: continue
460 r2 = expanded_targets2(bld, t2, chain)
461 chain.remove(d)
462 ret = ret.union(r2)
464 if t.sname in ret:
465 ret.remove(t.sname)
467 t.expanded_targets2 = ret
468 return ret
471 def includes_objects(bld, t, chain):
472 '''recursively calculate the includes object dependencies for a target
474 includes dependencies come from either library or object dependencies
476 ret = getattr(t, 'includes_objects', None)
477 if ret is not None:
478 return ret
480 ret = t.direct_objects.copy()
481 ret = ret.union(t.direct_libs)
483 for obj in t.direct_objects:
484 if obj in chain:
485 continue
486 chain.add(obj)
487 t2 = bld.name_to_obj(obj, bld.env)
488 r2 = includes_objects(bld, t2, chain)
489 chain.remove(obj)
490 ret = ret.union(t2.direct_objects)
491 ret = ret.union(r2)
493 for lib in t.direct_libs:
494 if lib in chain:
495 continue
496 chain.add(lib)
497 t2 = bld.name_to_obj(lib, bld.env)
498 r2 = includes_objects(bld, t2, chain)
499 chain.remove(lib)
500 ret = ret.union(t2.direct_objects)
501 ret = ret.union(r2)
503 t.includes_objects = ret
504 return ret
507 def build_indirect_deps(bld, tgt_list):
508 '''build the indirect_objects and indirect_libs sets for each target'''
509 for t in tgt_list:
510 indirect_objects(bld, t, set())
511 indirect_libs(bld, t, set())
512 indirect_syslibs(bld, t, set())
513 includes_objects(bld, t, set())
514 expanded_targets(bld, t, set())
515 debug('deps: built indirect dependencies')
518 def re_expand2(bld, tgt_list):
519 for t in tgt_list:
520 t.expanded_targets2 = None
521 for type in ['BINARY','LIBRARY','PYTHON']:
522 for t in tgt_list:
523 if t.samba_type == type:
524 expanded_targets2(bld, t, set())
525 for t in tgt_list:
526 expanded_targets2(bld, t, set())
529 def calculate_final_deps(bld, tgt_list):
530 '''calculate the final library and object dependencies'''
531 for t in tgt_list:
532 # start with the maximum possible list
533 t.final_syslibs = t.direct_syslibs.union(t.indirect_syslibs)
534 t.final_libs = t.direct_libs.union(t.indirect_libs)
535 t.final_objects = t.direct_objects.union(t.indirect_objects)
537 for t in tgt_list:
538 # don't depend on ourselves
539 if t.sname in t.final_libs:
540 t.final_libs.remove(t.sname)
541 if t.sname in t.final_objects:
542 t.final_objects.remove(t.sname)
544 re_expand2(bld, tgt_list)
546 loops = {}
548 # find any library loops
549 for t in tgt_list:
550 if t.samba_type in ['LIBRARY', 'PYTHON']:
551 for l in t.final_libs.copy():
552 t2 = bld.name_to_obj(l, bld.env)
553 if t.sname in t2.final_libs:
554 # we could break this in either direction. If one of the libraries
555 # has a version number, and will this be distributed publicly, then
556 # we should make it the lower level library in the DAG
557 debug('deps: removing library loop %s<->%s', t.sname, l)
558 t2.final_libs.remove(t.sname)
559 loops[t2.sname] = t.sname;
561 re_expand2(bld, tgt_list)
563 for type in ['BINARY']:
564 while True:
565 changed = False
566 for t in tgt_list:
567 if t.samba_type != type: continue
568 # if we will indirectly link to a target then we don't need it
569 new = t.final_objects.copy()
570 for l in t.final_libs:
571 t2 = bld.name_to_obj(l, bld.env)
572 dup = new.intersection(t2.expanded_targets2)
573 if dup:
574 debug('deps: removing dups from %s: %s also in %s %s',
575 t.sname, dup, t2.samba_type, l)
576 new = new.difference(dup)
577 changed = True
578 if changed:
579 t.final_objects = new
580 break
581 if not changed:
582 break
584 # we now need to make corrections for any library loops we broke up
585 # any target that depended on the target of the loop and doesn't
586 # depend on the source of the loop needs to get the loop source added
587 for type in ['BINARY','PYTHON']:
588 for t in tgt_list:
589 if t.samba_type != type: continue
590 for loop in loops:
591 if loop in t.final_libs and loops[loop] not in t.final_libs:
592 t.final_libs.add(loops[loop])
594 debug('deps: removed duplicate dependencies')
597 ######################################################################
598 # this provides a way to save our dependency calculations between runs
599 savedeps_version = 2
600 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags']
601 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
602 savedeps_outenv = ['INC_PATHS']
603 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS']
605 def save_samba_deps(bld, tgt_list):
606 '''save the dependency calculations between builds, to make
607 further builds faster'''
608 denv = Environment.Environment()
610 denv.version = savedeps_version
611 denv.savedeps_inputs = savedeps_inputs
612 denv.savedeps_outputs = savedeps_outputs
613 denv.input = {}
614 denv.output = {}
615 denv.outenv = {}
616 denv.caches = {}
618 for c in savedeps_caches:
619 denv.caches[c] = LOCAL_CACHE(bld, c)
621 for t in tgt_list:
622 # save all the input attributes for each target
623 tdeps = {}
624 for attr in savedeps_inputs:
625 v = getattr(t, attr, None)
626 if v is not None:
627 tdeps[attr] = v
628 if tdeps != {}:
629 denv.input[t.sname] = tdeps
631 # save all the output attributes for each target
632 tdeps = {}
633 for attr in savedeps_outputs:
634 v = getattr(t, attr, None)
635 if v is not None:
636 tdeps[attr] = v
637 if tdeps != {}:
638 denv.output[t.sname] = tdeps
640 tdeps = {}
641 for attr in savedeps_outenv:
642 if attr in t.env:
643 tdeps[attr] = t.env[attr]
644 if tdeps != {}:
645 denv.outenv[t.sname] = tdeps
647 depsfile = os.path.join(bld.bdir, "sambadeps")
648 denv.store(depsfile)
651 def load_samba_deps(bld, tgt_list):
652 '''load a previous set of build dependencies if possible'''
653 depsfile = os.path.join(bld.bdir, "sambadeps")
654 denv = Environment.Environment()
655 try:
656 debug('deps: checking saved dependencies')
657 denv.load(depsfile)
658 if (denv.version != savedeps_version or
659 denv.savedeps_inputs != savedeps_inputs or
660 denv.savedeps_outputs != savedeps_outputs):
661 return False
662 except:
663 return False
665 # check if caches are the same
666 for c in savedeps_caches:
667 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
668 return False
670 # check inputs are the same
671 for t in tgt_list:
672 tdeps = {}
673 for attr in savedeps_inputs:
674 v = getattr(t, attr, None)
675 if v is not None:
676 tdeps[attr] = v
677 if t.sname in denv.input:
678 olddeps = denv.input[t.sname]
679 else:
680 olddeps = {}
681 if tdeps != olddeps:
682 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
683 return False
685 # put outputs in place
686 for t in tgt_list:
687 if not t.sname in denv.output: continue
688 tdeps = denv.output[t.sname]
689 for a in tdeps:
690 setattr(t, a, tdeps[a])
692 # put output env vars in place
693 for t in tgt_list:
694 if not t.sname in denv.outenv: continue
695 tdeps = denv.outenv[t.sname]
696 for a in tdeps:
697 t.env[a] = tdeps[a]
699 debug('deps: loaded saved dependencies')
700 return True
703 def check_project_rules(bld):
704 '''check the project rules - ensuring the targets are sane'''
706 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
708 # build a list of task generators we are interested in
709 tgt_list = []
710 for tgt in targets:
711 type = targets[tgt]
712 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
713 continue
714 t = bld.name_to_obj(tgt, bld.env)
715 if t is None:
716 print "Target %s of type %s has no task generator" % (tgt, type)
717 raise
718 tgt_list.append(t)
720 add_samba_attributes(bld, tgt_list)
722 if load_samba_deps(bld, tgt_list):
723 return
725 debug('deps: project rules checking started')
727 expand_subsystem_deps(bld)
728 build_direct_deps(bld, tgt_list)
729 build_indirect_deps(bld, tgt_list)
730 calculate_final_deps(bld, tgt_list)
732 # run the various attribute generators
733 for f in [ build_dependencies, build_includes, add_init_functions ]:
734 debug('deps: project rules checking %s', f)
735 for t in tgt_list: f(t)
737 debug('deps: project rules stage1 completed')
739 #check_orpaned_targets(bld, tgt_list)
740 #check_duplicate_sources(bld, tgt_list)
741 show_final_deps(bld, tgt_list)
743 debug('deps: project rules checking completed - %u targets checked',
744 len(tgt_list))
746 save_samba_deps(bld, tgt_list)
749 def CHECK_PROJECT_RULES(bld):
750 '''enable checking of project targets for sanity'''
751 if bld.env.added_project_rules:
752 return
753 bld.env.added_project_rules = True
754 bld.add_pre_fun(check_project_rules)
755 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES