build: Without getrandom() require gnutls 3.7.2
[bbaumbach/samba-autobuild/.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import os, sys, re
4
5 from waflib import Build, Options, Logs, Utils, Errors, Scripting
6 from waflib.Logs import debug
7 from waflib.Configure import conf
8 from waflib import ConfigSet
9
10 from samba_utils import LOCAL_CACHE, TO_LIST, get_tgt_list, unique_list
11 from samba_autoconf import library_flags
12
13 @conf
14 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
15     '''add a dependency for all binaries and libraries'''
16     if not 'GLOBAL_DEPENDENCIES' in ctx.env:
17         ctx.env.GLOBAL_DEPENDENCIES = []
18     ctx.env.GLOBAL_DEPENDENCIES.append(dep)
19
20
21 @conf
22 def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx):
23     '''indicate that circular dependencies between libraries should be broken.'''
24     ctx.env.ALLOW_CIRCULAR_LIB_DEPENDENCIES = True
25
26
27 @conf
28 def SET_SYSLIB_DEPS(conf, target, deps):
29     '''setup some implied dependencies for a SYSLIB'''
30     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
31     cache[target] = deps
32
33
34 def expand_subsystem_deps(bld):
35     '''expand the reverse dependencies resulting from subsystem
36        attributes of modules. This is walking over the complete list
37        of declared subsystems, and expands the samba_deps_extended list for any
38        module<->subsystem dependencies'''
39
40     subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
41     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
42
43     for subsystem_name in subsystem_list:
44         bld.ASSERT(subsystem_name in targets, "Subsystem target %s not declared" % subsystem_name)
45         type = targets[subsystem_name]
46         if type == 'DISABLED' or type == 'EMPTY':
47             continue
48
49         # for example,
50         #    subsystem_name = dcerpc_server (a subsystem)
51         #    subsystem      = dcerpc_server (a subsystem object)
52         #    module_name    = rpc_epmapper (a module within the dcerpc_server subsystem)
53         #    module         = rpc_epmapper (a module object within the dcerpc_server subsystem)
54
55         subsystem = bld.get_tgen_by_name(subsystem_name)
56         bld.ASSERT(subsystem is not None, "Unable to find subsystem %s" % subsystem_name)
57         for d in subsystem_list[subsystem_name]:
58             module_name = d['TARGET']
59             module_type = targets[module_name]
60             if module_type in ['DISABLED', 'EMPTY']:
61                 continue
62             bld.ASSERT(subsystem is not None,
63                        "Subsystem target %s for %s (%s) not found" % (subsystem_name, module_name, module_type))
64             if module_type in ['SUBSYSTEM']:
65                 # if a module is a plain object type (not a library) then the
66                 # subsystem it is part of needs to have it as a dependency, so targets
67                 # that depend on this subsystem get the modules of that subsystem
68                 subsystem.samba_deps_extended.append(module_name)
69         subsystem.samba_deps_extended = unique_list(subsystem.samba_deps_extended)
70
71
72
73 def build_dependencies(self):
74     '''This builds the dependency list for a target. It runs after all the targets are declared
75
76     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
77     the full dependency list for a target until we have all of the targets declared.
78     '''
79
80     if self.samba_type in ['LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON']:
81         self.uselib        = list(self.final_syslibs)
82         self.uselib_local  = list(self.final_libs)
83         self.add_objects   = list(self.final_objects)
84
85         # extra link flags from pkg_config
86         libs = self.final_syslibs.copy()
87
88         (cflags, ldflags, cpppath) = library_flags(self, list(libs))
89         new_ldflags        = getattr(self, 'samba_ldflags', [])[:]
90         new_ldflags.extend(ldflags)
91         self.ldflags       = new_ldflags
92
93         if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ldflags:
94             for f in self.env.undefined_ldflags:
95                 self.ldflags.remove(f)
96
97         if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ignore_ldflags:
98             for f in self.env.undefined_ignore_ldflags:
99                 self.ldflags.append(f)
100
101         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
102               self.sname, self.uselib, self.uselib_local, self.add_objects)
103
104     if self.samba_type in ['SUBSYSTEM', 'BUILTIN']:
105         # this is needed for the cflags of libs that come from pkg_config
106         self.uselib = list(self.final_syslibs)
107         self.uselib.extend(list(self.direct_syslibs))
108         for lib in self.final_libs:
109             t = self.bld.get_tgen_by_name(lib)
110             self.uselib.extend(list(t.final_syslibs))
111         self.uselib = unique_list(self.uselib)
112
113     if getattr(self, 'uselib', None):
114         up_list = []
115         for l in self.uselib:
116            up_list.append(l.upper())
117         self.uselib = up_list
118
119
120 def build_includes(self):
121     '''This builds the right set of includes for a target.
122
123     One tricky part of this is that the includes= attribute for a
124     target needs to use paths which are relative to that targets
125     declaration directory (which we can get at via t.path).
126
127     The way this works is the includes list gets added as
128     samba_includes in the main build task declaration. Then this
129     function runs after all of the tasks are declared, and it
130     processes the samba_includes attribute to produce a includes=
131     attribute
132     '''
133
134     if getattr(self, 'samba_includes', None) is None:
135         return
136
137     bld = self.bld
138
139     inc_deps = includes_objects(bld, self, set(), {})
140
141     includes = []
142
143     # maybe add local includes
144     if getattr(self, 'local_include', True) and getattr(self, 'local_include_first', True):
145         includes.append('.')
146
147     includes.extend(self.samba_includes_extended)
148
149     if 'EXTRA_INCLUDES' in bld.env and getattr(self, 'global_include', True):
150         includes.extend(bld.env['EXTRA_INCLUDES'])
151
152     includes.append('#')
153
154     inc_set = set()
155     inc_abs = []
156
157     for d in inc_deps:
158         t = bld.get_tgen_by_name(d)
159         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
160         inclist = getattr(t, 'samba_includes_extended', [])[:]
161         if getattr(t, 'local_include', True):
162             inclist.append('.')
163         if inclist == []:
164             continue
165         tpath = t.samba_abspath
166         for inc in inclist:
167             npath = tpath + '/' + inc
168             if not npath in inc_set:
169                 inc_abs.append(npath)
170                 inc_set.add(npath)
171
172     mypath = self.path.abspath(bld.env)
173     for inc in inc_abs:
174         relpath = os.path.relpath(inc, mypath)
175         includes.append(relpath)
176
177     if getattr(self, 'local_include', True) and not getattr(self, 'local_include_first', True):
178         includes.append('.')
179
180     # now transform the includes list to be relative to the top directory
181     # which is represented by '#' in waf. This allows waf to cache the
182     # includes lists more efficiently
183     includes_top = []
184     for i in includes:
185         if i[0] == '#':
186             # some are already top based
187             includes_top.append(i)
188             continue
189         absinc = os.path.join(self.path.abspath(), i)
190         relinc = os.path.relpath(absinc, self.bld.srcnode.abspath())
191         includes_top.append('#' + relinc)
192
193     self.includes = unique_list(includes_top)
194     debug('deps: includes for target %s: includes=%s',
195           self.sname, self.includes)
196
197
198 def add_init_functions(self):
199     '''This builds the right set of init functions'''
200
201     bld = self.bld
202
203     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
204
205     # cope with the separated object lists from BINARY and LIBRARY targets
206     sname = self.sname
207     if sname.endswith('.objlist'):
208         sname = sname[0:-8]
209
210     modules = []
211     if sname in subsystems:
212         modules.append(sname)
213
214     m = getattr(self, 'samba_modules', None)
215     if m is not None:
216         modules.extend(TO_LIST(m))
217
218     m = getattr(self, 'samba_subsystem', None)
219     if m is not None:
220         modules.append(m)
221
222     if 'pyembed' in self.features:
223         return
224
225     sentinel = getattr(self, 'init_function_sentinel', 'NULL')
226
227     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
228     cflags = getattr(self, 'samba_cflags', [])[:]
229
230     if modules == []:
231         sname = sname.replace('-','_')
232         sname = sname.replace('.','_')
233         sname = sname.replace('/','_')
234         cflags.append('-DSTATIC_%s_MODULES=%s' % (sname, sentinel))
235         if sentinel == 'NULL':
236             proto = "extern void __%s_dummy_module_proto(void)" % (sname)
237             cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (sname, proto))
238         self.cflags = cflags
239         return
240
241     for m in modules:
242         bld.ASSERT(m in subsystems,
243                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
244         init_fn_list = []
245         for d in subsystems[m]:
246             if targets[d['TARGET']] != 'DISABLED':
247                 init_fn_list.append(d['INIT_FUNCTION'])
248         if init_fn_list == []:
249             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinel))
250             if sentinel == 'NULL':
251                 proto = "extern void __%s_dummy_module_proto(void)" % (m)
252                 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
253         else:
254             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinel))
255             proto = "".join('_MODULE_PROTO(%s)' % f for f in init_fn_list) +\
256                     "extern void __%s_dummy_module_proto(void)" % (m)
257             cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
258     self.cflags = cflags
259
260
261 def check_duplicate_sources(bld, tgt_list):
262     '''see if we are compiling the same source file more than once'''
263
264     debug('deps: checking for duplicate sources')
265     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
266
267     for t in tgt_list:
268         source_list = TO_LIST(getattr(t, 'source', ''))
269         tpath = os.path.normpath(os.path.relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
270         obj_sources = set()
271         for s in source_list:
272             if not isinstance(s, str):
273                 print('strange path in check_duplicate_sources %r' % s)
274                 s = s.abspath()
275             p = os.path.normpath(os.path.join(tpath, s))
276             if p in obj_sources:
277                 Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname))
278                 sys.exit(1)
279             obj_sources.add(p)
280         t.samba_source_set = obj_sources
281
282     subsystems = {}
283
284     # build a list of targets that each source file is part of
285     for t in tgt_list:
286         if not targets[t.sname] in [ 'LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON' ]:
287             continue
288         for obj in t.add_objects:
289             t2 = t.bld.get_tgen_by_name(obj)
290             source_set = getattr(t2, 'samba_source_set', set())
291             for s in source_set:
292                 if not s in subsystems:
293                     subsystems[s] = {}
294                 if not t.sname in subsystems[s]:
295                     subsystems[s][t.sname] = []
296                 subsystems[s][t.sname].append(t2.sname)
297
298     for s in subsystems:
299         if len(subsystems[s]) > 1 and Options.options.SHOW_DUPLICATES:
300             Logs.warn("WARNING: source %s is in more than one target: %s" % (s, subsystems[s].keys()))
301         for tname in subsystems[s]:
302             if len(subsystems[s][tname]) > 1:
303                 raise Errors.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s, tname, subsystems[s][tname]))
304
305     return True
306
307 def check_group_ordering(bld, tgt_list):
308     '''see if we have any dependencies that violate the group ordering
309
310     It is an error for a target to depend on a target from a later
311     build group
312     '''
313
314     def group_name(g):
315         tm = bld.task_manager
316         return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
317
318     for g in bld.task_manager.groups:
319         gname = group_name(g)
320         for t in g.tasks_gen:
321             t.samba_group = gname
322
323     grp_map = {}
324     idx = 0
325     for g in bld.task_manager.groups:
326         name = group_name(g)
327         grp_map[name] = idx
328         idx += 1
329
330     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
331
332     ret = True
333     for t in tgt_list:
334         tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
335         for d in tdeps:
336             t2 = bld.get_tgen_by_name(d)
337             if t2 is None:
338                 continue
339             map1 = grp_map[t.samba_group]
340             map2 = grp_map[t2.samba_group]
341
342             if map2 > map1:
343                 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
344                            t.sname, t.samba_group, t2.sname, t2.samba_group))
345                 ret = False
346
347     return ret
348 Build.BuildContext.check_group_ordering = check_group_ordering
349
350 def show_final_deps(bld, tgt_list):
351     '''show the final dependencies for all targets'''
352
353     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
354
355     for t in tgt_list:
356         if not targets[t.sname] in ['LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON', 'SUBSYSTEM', 'BUILTIN']:
357             continue
358         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
359               t.sname, t.uselib, getattr(t, 'uselib_local', []), getattr(t, 'add_objects', []))
360
361
362 def add_samba_attributes(bld, tgt_list):
363     '''ensure a target has a the required samba attributes'''
364
365     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
366
367     for t in tgt_list:
368         if t.name != '':
369             t.sname = t.name
370         else:
371             t.sname = t.target
372         t.samba_type = targets[t.sname]
373         t.samba_abspath = t.path.abspath(bld.env)
374         t.samba_deps_extended = t.samba_deps[:]
375         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
376         t.cflags = getattr(t, 'samba_cflags', '')
377
378 def replace_builtin_subsystem_deps(bld, tgt_list):
379     '''replace dependencies based on builtin subsystems/libraries
380
381     '''
382
383     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
384
385     # If either the target or the dependency require builtin linking
386     # we should replace the dependency
387     for t in tgt_list:
388         t_require_builtin_deps = getattr(t, 'samba_require_builtin_deps', False)
389         if t_require_builtin_deps:
390             debug("deps: target %s: requires builtin dependencies..." % (t.sname))
391         else:
392             debug("deps: target %s: does not require builtin dependencies..." % (t.sname))
393
394         replacing = {}
395
396         for dep in t.samba_deps_extended:
397             bld.ASSERT(dep in targets, "target %s: dependency target %s not declared" % (t.sname, dep))
398             dtype = targets[dep]
399             bld.ASSERT(dtype != 'BUILTIN', "target %s: dependency target %s is BUILTIN" % (t.sname, dep))
400             bld.ASSERT(dtype != 'PLUGIN', "target %s: dependency target %s is PLUGIN" % (t.sname, dep))
401             if dtype not in ['SUBSYSTEM', 'LIBRARY']:
402                 debug("deps: target %s: keep %s dependency %s" % (t.sname, dtype, dep))
403                 continue
404             dt = bld.get_tgen_by_name(dep)
405             bld.ASSERT(dt is not None, "target %s: dependency target %s not found by name" % (t.sname, dep))
406             dt_require_builtin_deps = getattr(dt, 'samba_require_builtin_deps', False)
407             if not dt_require_builtin_deps and not t_require_builtin_deps:
408                 # both target and dependency don't require builtin linking
409                 continue
410             sdt = getattr(dt, 'samba_builtin_subsystem', None)
411             if not t_require_builtin_deps:
412                 if sdt is None:
413                     debug("deps: target %s: dependency %s requires builtin deps only" % (t.sname, dep))
414                     continue
415                 debug("deps: target %s: dependency %s requires builtin linking" % (t.sname, dep))
416             bld.ASSERT(sdt is not None, "target %s: dependency target %s is missing samba_builtin_subsystem" % (t.sname, dep))
417             sdep = sdt.sname
418             bld.ASSERT(sdep in targets, "target %s: builtin dependency target %s (from %s) not declared" % (t.sname, sdep, dep))
419             sdt = targets[sdep]
420             bld.ASSERT(sdt == 'BUILTIN', "target %s: builtin dependency target %s (from %s) is not BUILTIN" % (t.sname, sdep, dep))
421             replacing[dep] = sdep
422
423         for i in range(len(t.samba_deps_extended)):
424             dep = t.samba_deps_extended[i]
425             if dep in replacing:
426                 sdep = replacing[dep]
427                 debug("deps: target %s: replacing dependency %s with builtin subsystem %s" % (t.sname, dep, sdep))
428                 t.samba_deps_extended[i] = sdep
429
430 def replace_grouping_libraries(bld, tgt_list):
431     '''replace dependencies based on grouping libraries
432
433     If a library is marked as a grouping library, then any target that
434     depends on a subsystem that is part of that grouping library gets
435     that dependency replaced with a dependency on the grouping library
436     '''
437
438     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
439
440     grouping = {}
441
442     # find our list of grouping libraries, mapped from the subsystems they depend on
443     for t in tgt_list:
444         if not getattr(t, 'grouping_library', False):
445             continue
446         for dep in t.samba_deps_extended:
447             bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname))
448             if targets[dep] == 'SUBSYSTEM':
449                 grouping[dep] = t.sname
450
451     # now replace any dependencies on elements of grouping libraries
452     for t in tgt_list:
453         for i in range(len(t.samba_deps_extended)):
454             dep = t.samba_deps_extended[i]
455             if dep in grouping:
456                 if t.sname != grouping[dep]:
457                     debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
458                     t.samba_deps_extended[i] = grouping[dep]
459
460
461
462 def build_direct_deps(bld, tgt_list):
463     '''build the direct_objects and direct_libs sets for each target'''
464
465     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
466     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
467
468     global_deps = bld.env.GLOBAL_DEPENDENCIES
469     global_deps_exclude = set()
470     for dep in global_deps:
471         t = bld.get_tgen_by_name(dep)
472         for d in t.samba_deps:
473             # prevent loops from the global dependencies list
474             global_deps_exclude.add(d)
475             global_deps_exclude.add(d + '.objlist')
476
477     for t in tgt_list:
478         t.direct_objects = set()
479         t.direct_libs = set()
480         t.direct_syslibs = set()
481         deps = t.samba_deps_extended[:]
482         if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
483             deps.extend(global_deps)
484         for d in deps:
485             if d == t.sname: continue
486             if not d in targets:
487                 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
488                 sys.exit(1)
489             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
490                 continue
491             if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
492                 # this check should be more restrictive, but for now we have pidl-generated python
493                 # code that directly depends on other python modules
494                 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
495                 sys.exit(1)
496             if targets[d] == 'SYSLIB':
497                 t.direct_syslibs.add(d)
498                 if d in syslib_deps:
499                     for implied in TO_LIST(syslib_deps[d]):
500                         if targets[implied] == 'SUBSYSTEM':
501                             it = bld.get_tgen_by_name(implied)
502                             sit = getattr(it, 'samba_builtin_subsystem', None)
503                             if sit:
504                                 implied = sit.sname
505                         if targets[implied] == 'BUILTIN':
506                             t.direct_objects.add(implied)
507                         elif targets[implied] == 'SYSLIB':
508                             t.direct_syslibs.add(implied)
509                         elif targets[implied] in ['LIBRARY', 'MODULE']:
510                             t.direct_libs.add(implied)
511                         else:
512                             Logs.error('Implied dependency %s in %s is of type %s' % (
513                                 implied, t.sname, targets[implied]))
514                             sys.exit(1)
515                 continue
516             t2 = bld.get_tgen_by_name(d)
517             if t2 is None:
518                 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
519                 sys.exit(1)
520             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
521                 t.direct_libs.add(d)
522             elif t2.samba_type in [ 'SUBSYSTEM', 'BUILTIN', 'ASN1', 'PYTHON' ]:
523                 t.direct_objects.add(d)
524             elif t2.samba_type in [ 'PLUGIN' ]:
525                 Logs.error('Implicit dependency %s in %s is of type %s' % (
526                            d, t.sname, t2.samba_type))
527                 sys.exit(1)
528
529     debug('deps: built direct dependencies')
530
531
532 def dependency_loop(loops, t, target):
533     '''add a dependency loop to the loops dictionary'''
534     if t.sname == target:
535         return
536     if not target in loops:
537         loops[target] = set()
538     if not t.sname in loops[target]:
539         loops[target].add(t.sname)
540
541
542 def indirect_libs(bld, t, chain, loops):
543     '''recursively calculate the indirect library dependencies for a target
544
545     An indirect library is a library that results from a dependency on
546     a subsystem
547     '''
548
549     ret = getattr(t, 'indirect_libs', None)
550     if ret is not None:
551         return ret
552
553     ret = set()
554     for obj in t.direct_objects:
555         if obj in chain:
556             dependency_loop(loops, t, obj)
557             continue
558         chain.add(obj)
559         t2 = bld.get_tgen_by_name(obj)
560         r2 = indirect_libs(bld, t2, chain, loops)
561         chain.remove(obj)
562         ret = ret.union(t2.direct_libs)
563         ret = ret.union(r2)
564
565     for obj in indirect_objects(bld, t, set(), loops):
566         if obj in chain:
567             dependency_loop(loops, t, obj)
568             continue
569         chain.add(obj)
570         t2 = bld.get_tgen_by_name(obj)
571         r2 = indirect_libs(bld, t2, chain, loops)
572         chain.remove(obj)
573         ret = ret.union(t2.direct_libs)
574         ret = ret.union(r2)
575
576     t.indirect_libs = ret
577
578     return ret
579
580
581 def indirect_objects(bld, t, chain, loops):
582     '''recursively calculate the indirect object dependencies for a target
583
584     indirect objects are the set of objects from expanding the
585     subsystem dependencies
586     '''
587
588     ret = getattr(t, 'indirect_objects', None)
589     if ret is not None: return ret
590
591     ret = set()
592     for lib in t.direct_objects:
593         if lib in chain:
594             dependency_loop(loops, t, lib)
595             continue
596         chain.add(lib)
597         t2 = bld.get_tgen_by_name(lib)
598         r2 = indirect_objects(bld, t2, chain, loops)
599         chain.remove(lib)
600         ret = ret.union(t2.direct_objects)
601         ret = ret.union(r2)
602
603     t.indirect_objects = ret
604     return ret
605
606
607 def extended_objects(bld, t, chain):
608     '''recursively calculate the extended object dependencies for a target
609
610     extended objects are the union of:
611        - direct objects
612        - indirect objects
613        - direct and indirect objects of all direct and indirect libraries
614     '''
615
616     ret = getattr(t, 'extended_objects', None)
617     if ret is not None: return ret
618
619     ret = set()
620     ret = ret.union(t.final_objects)
621
622     for lib in t.final_libs:
623         if lib in chain:
624             continue
625         t2 = bld.get_tgen_by_name(lib)
626         chain.add(lib)
627         r2 = extended_objects(bld, t2, chain)
628         chain.remove(lib)
629         ret = ret.union(t2.final_objects)
630         ret = ret.union(r2)
631
632     t.extended_objects = ret
633     return ret
634
635
636 def includes_objects(bld, t, chain, inc_loops):
637     '''recursively calculate the includes object dependencies for a target
638
639     includes dependencies come from either library or object dependencies
640     '''
641     ret = getattr(t, 'includes_objects', None)
642     if ret is not None:
643         return ret
644
645     ret = t.direct_objects.copy()
646     ret = ret.union(t.direct_libs)
647
648     for obj in t.direct_objects:
649         if obj in chain:
650             dependency_loop(inc_loops, t, obj)
651             continue
652         chain.add(obj)
653         t2 = bld.get_tgen_by_name(obj)
654         r2 = includes_objects(bld, t2, chain, inc_loops)
655         chain.remove(obj)
656         ret = ret.union(t2.direct_objects)
657         ret = ret.union(r2)
658
659     for lib in t.direct_libs:
660         if lib in chain:
661             dependency_loop(inc_loops, t, lib)
662             continue
663         chain.add(lib)
664         t2 = bld.get_tgen_by_name(lib)
665         if t2 is None:
666             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
667             Logs.error('Target %s of type %s not found in direct_libs for %s' % (
668                 lib, targets[lib], t.sname))
669             sys.exit(1)
670         r2 = includes_objects(bld, t2, chain, inc_loops)
671         chain.remove(lib)
672         ret = ret.union(t2.direct_objects)
673         ret = ret.union(r2)
674
675     t.includes_objects = ret
676     return ret
677
678
679 def break_dependency_loops(bld, tgt_list):
680     '''find and break dependency loops'''
681     loops = {}
682     inc_loops = {}
683
684     # build up the list of loops
685     for t in tgt_list:
686         indirect_objects(bld, t, set(), loops)
687         indirect_libs(bld, t, set(), loops)
688         includes_objects(bld, t, set(), inc_loops)
689
690     # break the loops
691     for t in tgt_list:
692         if t.sname in loops:
693             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
694                 objs = getattr(t, attr, set())
695                 setattr(t, attr, objs.difference(loops[t.sname]))
696
697     for loop in loops:
698         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
699
700     for loop in inc_loops:
701         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
702
703     # expand the loops mapping by one level
704     for loop in loops.copy():
705         for tgt in loops[loop]:
706             if tgt in loops:
707                 loops[loop] = loops[loop].union(loops[tgt])
708
709     for loop in inc_loops.copy():
710         for tgt in inc_loops[loop]:
711             if tgt in inc_loops:
712                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
713
714
715     # expand indirect subsystem and library loops
716     for loop in loops.copy():
717         t = bld.get_tgen_by_name(loop)
718         if t.samba_type in ['SUBSYSTEM', 'BUILTIN']:
719             loops[loop] = loops[loop].union(t.indirect_objects)
720             loops[loop] = loops[loop].union(t.direct_objects)
721         if t.samba_type in ['LIBRARY', 'PLUGIN', 'PYTHON']:
722             loops[loop] = loops[loop].union(t.indirect_libs)
723             loops[loop] = loops[loop].union(t.direct_libs)
724         if loop in loops[loop]:
725             loops[loop].remove(loop)
726
727     # expand indirect includes loops
728     for loop in inc_loops.copy():
729         t = bld.get_tgen_by_name(loop)
730         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
731         if loop in inc_loops[loop]:
732             inc_loops[loop].remove(loop)
733
734     # add in the replacement dependencies
735     for t in tgt_list:
736         for loop in loops:
737             for attr in ['indirect_objects', 'indirect_libs']:
738                 objs = getattr(t, attr, set())
739                 if loop in objs:
740                     diff = loops[loop].difference(objs)
741                     if t.sname in diff:
742                         diff.remove(t.sname)
743                     if diff:
744                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
745                         objs = objs.union(diff)
746                 setattr(t, attr, objs)
747
748         for loop in inc_loops:
749             objs = getattr(t, 'includes_objects', set())
750             if loop in objs:
751                 diff = inc_loops[loop].difference(objs)
752                 if t.sname in diff:
753                     diff.remove(t.sname)
754                 if diff:
755                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
756                     objs = objs.union(diff)
757             setattr(t, 'includes_objects', objs)
758
759
760 def reduce_objects(bld, tgt_list):
761     '''reduce objects by looking for indirect object dependencies'''
762     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
763
764     rely_on = {}
765
766     for t in tgt_list:
767         t.extended_objects = None
768
769     changed = False
770
771     for type in ['BINARY', 'PYTHON', 'LIBRARY', 'PLUGIN']:
772         for t in tgt_list:
773             if t.samba_type != type: continue
774             # if we will indirectly link to a target then we don't need it
775             new = t.final_objects.copy()
776             for l in t.final_libs:
777                 t2 = bld.get_tgen_by_name(l)
778                 t2_obj = extended_objects(bld, t2, set())
779                 dup = new.intersection(t2_obj)
780                 if t.sname in rely_on:
781                     dup = dup.difference(rely_on[t.sname])
782                 if dup:
783                     # Do not remove duplicates of BUILTINS
784                     for d in iter(dup.copy()):
785                         dtype = targets[d]
786                         if dtype == 'BUILTIN':
787                             debug('deps: BUILTIN SKIP: removing dups from %s of type %s: %s also in %s %s',
788                                   t.sname, t.samba_type, d, t2.samba_type, l)
789                             dup.remove(d)
790                     if len(dup) == 0:
791                         continue
792
793                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
794                           t.sname, t.samba_type, dup, t2.samba_type, l)
795                     new = new.difference(dup)
796                     changed = True
797                     if not l in rely_on:
798                         rely_on[l] = set()
799                     rely_on[l] = rely_on[l].union(dup)
800             for n in iter(new.copy()):
801                 # if we got the builtin version as well
802                 # as the native one, we keep using the
803                 # builtin one and remove the rest.
804                 # Otherwise our check_duplicate_sources()
805                 # checks would trigger!
806                 if n.endswith('.builtin.objlist'):
807                     unused = n.replace('.builtin.objlist', '.objlist')
808                     if unused in new:
809                         new.remove(unused)
810                     unused = n.replace('.builtin.objlist', '')
811                     if unused in new:
812                         new.remove(unused)
813             t.final_objects = new
814
815     if not changed:
816         return False
817
818     # add back in any objects that were relied upon by the reduction rules
819     for r in rely_on:
820         t = bld.get_tgen_by_name(r)
821         t.final_objects = t.final_objects.union(rely_on[r])
822
823     return True
824
825
826 def show_library_loop(bld, lib1, lib2, path, seen):
827     '''show the detailed path of a library loop between lib1 and lib2'''
828
829     t = bld.get_tgen_by_name(lib1)
830     if not lib2 in getattr(t, 'final_libs', set()):
831         return
832
833     for d in t.samba_deps_extended:
834         if d in seen:
835             continue
836         seen.add(d)
837         path2 = path + '=>' + d
838         if d == lib2:
839             Logs.warn('library loop path: ' + path2)
840             return
841         show_library_loop(bld, d, lib2, path2, seen)
842         seen.remove(d)
843
844
845 def calculate_final_deps(bld, tgt_list, loops):
846     '''calculate the final library and object dependencies'''
847     for t in tgt_list:
848         # start with the maximum possible list
849         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
850         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
851
852     for t in tgt_list:
853         # don't depend on ourselves
854         if t.sname in t.final_libs:
855             t.final_libs.remove(t.sname)
856         if t.sname in t.final_objects:
857             t.final_objects.remove(t.sname)
858
859     # handle any non-shared binaries
860     for t in tgt_list:
861         if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
862             subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
863             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
864
865             # replace lib deps with objlist deps
866             for l in t.final_libs:
867                 objname = l + '.objlist'
868                 t2 = bld.get_tgen_by_name(objname)
869                 if t2 is None:
870                     Logs.error('ERROR: subsystem %s not found' % objname)
871                     sys.exit(1)
872                 t.final_objects.add(objname)
873                 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
874                 if l in subsystem_list:
875                     # its a subsystem - we also need the contents of any modules
876                     for d in subsystem_list[l]:
877                         module_name = d['TARGET']
878                         if targets[module_name] == 'LIBRARY':
879                             objname = module_name + '.objlist'
880                         elif targets[module_name] == 'SUBSYSTEM':
881                             objname = module_name
882                         else:
883                             continue
884                         t2 = bld.get_tgen_by_name(objname)
885                         if t2 is None:
886                             Logs.error('ERROR: subsystem %s not found' % objname)
887                             sys.exit(1)
888                         t.final_objects.add(objname)
889                         t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
890             t.final_libs = set()
891
892     # find any library loops
893     for t in tgt_list:
894         if t.samba_type in ['LIBRARY', 'PYTHON']:
895             for l in t.final_libs.copy():
896                 t2 = bld.get_tgen_by_name(l)
897                 if t.sname in t2.final_libs:
898                     if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
899                         # we could break this in either direction. If one of the libraries
900                         # has a version number, and will this be distributed publicly, then
901                         # we should make it the lower level library in the DAG
902                         Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
903                         dependency_loop(loops, t, t2.sname)
904                         t2.final_libs.remove(t.sname)
905                     else:
906                         Logs.error('ERROR: circular library dependency between %s and %s'
907                             % (t.sname, t2.sname))
908                         show_library_loop(bld, t.sname, t2.sname, t.sname, set())
909                         show_library_loop(bld, t2.sname, t.sname, t2.sname, set())
910                         sys.exit(1)
911
912     for loop in loops:
913         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
914
915     # we now need to make corrections for any library loops we broke up
916     # any target that depended on the target of the loop and doesn't
917     # depend on the source of the loop needs to get the loop source added
918     for type in ['BINARY','PYTHON','LIBRARY','PLUGIN','BINARY']:
919         for t in tgt_list:
920             if t.samba_type != type: continue
921             for loop in loops:
922                 if loop in t.final_libs:
923                     diff = loops[loop].difference(t.final_libs)
924                     if t.sname in diff:
925                         diff.remove(t.sname)
926                     if t.sname in diff:
927                         diff.remove(t.sname)
928                     # make sure we don't recreate the loop again!
929                     for d in diff.copy():
930                         t2 = bld.get_tgen_by_name(d)
931                         if t2.samba_type == 'LIBRARY':
932                             if t.sname in t2.final_libs:
933                                 debug('deps: removing expansion %s from %s', d, t.sname)
934                                 diff.remove(d)
935                     if diff:
936                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
937                               loops[loop], diff)
938                         t.final_libs = t.final_libs.union(diff)
939
940     # remove objects that are also available in linked libs
941     count = 0
942     while reduce_objects(bld, tgt_list):
943         count += 1
944         if count > 100:
945             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
946             break
947     debug('deps: Object reduction took %u iterations', count)
948
949     # add in any syslib dependencies
950     for t in tgt_list:
951         if not t.samba_type in ['BINARY','PYTHON','LIBRARY','PLUGIN','SUBSYSTEM','BUILTIN']:
952             continue
953         syslibs = set()
954         for d in t.final_objects:
955             t2 = bld.get_tgen_by_name(d)
956             syslibs = syslibs.union(t2.direct_syslibs)
957         # this adds the indirect syslibs as well, which may not be needed
958         # depending on the linker flags
959         for d in t.final_libs:
960             t2 = bld.get_tgen_by_name(d)
961             syslibs = syslibs.union(t2.direct_syslibs)
962         t.final_syslibs = syslibs
963
964
965     # find any unresolved library loops
966     lib_loop_error = False
967     for t in tgt_list:
968         if t.samba_type in ['LIBRARY', 'PLUGIN', 'PYTHON']:
969             for l in t.final_libs.copy():
970                 t2 = bld.get_tgen_by_name(l)
971                 if t.sname in t2.final_libs:
972                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
973                     lib_loop_error = True
974     if lib_loop_error:
975         sys.exit(1)
976
977     debug('deps: removed duplicate dependencies')
978
979
980 def show_dependencies(bld, target, seen):
981     '''recursively show the dependencies of target'''
982
983     if target in seen:
984         return
985
986     t = bld.get_tgen_by_name(target)
987     if t is None:
988         Logs.error("ERROR: Unable to find target '%s'" % target)
989         sys.exit(1)
990
991     Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
992     Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
993     Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
994
995     seen.add(target)
996
997     for t2 in t.direct_objects:
998         show_dependencies(bld, t2, seen)
999
1000
1001 def show_object_duplicates(bld, tgt_list):
1002     '''show a list of object files that are included in more than
1003     one library or binary'''
1004
1005     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1006
1007     used_by = {}
1008
1009     Logs.info("showing duplicate objects")
1010
1011     for t in tgt_list:
1012         if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
1013             continue
1014         for n in getattr(t, 'final_objects', set()):
1015             t2 = bld.get_tgen_by_name(n)
1016             if not n in used_by:
1017                 used_by[n] = set()
1018             used_by[n].add(t.sname)
1019
1020     for n in used_by:
1021         if len(used_by[n]) > 1:
1022             Logs.info("target '%s' is used by %s" % (n, used_by[n]))
1023
1024     Logs.info("showing indirect dependency counts (sorted by count)")
1025
1026     def indirect_count(t1, t2):
1027         return len(t2.indirect_objects) - len(t1.indirect_objects)
1028
1029     sorted_list = sorted(tgt_list, cmp=indirect_count)
1030     for t in sorted_list:
1031         if len(t.indirect_objects) > 1:
1032             Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
1033
1034
1035 ######################################################################
1036 # this provides a way to save our dependency calculations between runs
1037 savedeps_version = 3
1038 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
1039                     'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
1040                     'use_global_deps', 'global_include' ]
1041 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes',
1042                     'cflags', 'ldflags', 'samba_deps_extended', 'final_libs']
1043 savedeps_outenv  = ['INC_PATHS']
1044 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
1045 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
1046 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
1047
1048 def save_samba_deps(bld, tgt_list):
1049     '''save the dependency calculations between builds, to make
1050        further builds faster'''
1051     denv = ConfigSet.ConfigSet()
1052
1053     denv.version = savedeps_version
1054     denv.savedeps_inputs = savedeps_inputs
1055     denv.savedeps_outputs = savedeps_outputs
1056     denv.input = {}
1057     denv.output = {}
1058     denv.outenv = {}
1059     denv.caches = {}
1060     denv.envvar = {}
1061     denv.files  = {}
1062
1063     for f in savedeps_files:
1064         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
1065
1066     for c in savedeps_caches:
1067         denv.caches[c] = LOCAL_CACHE(bld, c)
1068
1069     for e in savedeps_envvars:
1070         denv.envvar[e] = bld.env[e]
1071
1072     for t in tgt_list:
1073         # save all the input attributes for each target
1074         tdeps = {}
1075         for attr in savedeps_inputs:
1076             v = getattr(t, attr, None)
1077             if v is not None:
1078                 tdeps[attr] = v
1079         if tdeps != {}:
1080             denv.input[t.sname] = tdeps
1081
1082         # save all the output attributes for each target
1083         tdeps = {}
1084         for attr in savedeps_outputs:
1085             v = getattr(t, attr, None)
1086             if v is not None:
1087                 tdeps[attr] = v
1088         if tdeps != {}:
1089             denv.output[t.sname] = tdeps
1090
1091         tdeps = {}
1092         for attr in savedeps_outenv:
1093             if attr in t.env:
1094                 tdeps[attr] = t.env[attr]
1095         if tdeps != {}:
1096             denv.outenv[t.sname] = tdeps
1097
1098     depsfile = os.path.join(bld.cache_dir, "sambadeps")
1099     denv.store_fast(depsfile)
1100
1101
1102
1103 def load_samba_deps(bld, tgt_list):
1104     '''load a previous set of build dependencies if possible'''
1105     depsfile = os.path.join(bld.cache_dir, "sambadeps")
1106     denv = ConfigSet.ConfigSet()
1107     try:
1108         debug('deps: checking saved dependencies')
1109         denv.load_fast(depsfile)
1110         if (denv.version != savedeps_version or
1111             denv.savedeps_inputs != savedeps_inputs or
1112             denv.savedeps_outputs != savedeps_outputs):
1113             return False
1114     except Exception:
1115         return False
1116
1117     # check if critical files have changed
1118     for f in savedeps_files:
1119         if f not in denv.files:
1120             return False
1121         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
1122             return False
1123
1124     # check if caches are the same
1125     for c in savedeps_caches:
1126         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1127             return False
1128
1129     # check if caches are the same
1130     for e in savedeps_envvars:
1131         if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1132             return False
1133
1134     # check inputs are the same
1135     for t in tgt_list:
1136         tdeps = {}
1137         for attr in savedeps_inputs:
1138             v = getattr(t, attr, None)
1139             if v is not None:
1140                 tdeps[attr] = v
1141         if t.sname in denv.input:
1142             olddeps = denv.input[t.sname]
1143         else:
1144             olddeps = {}
1145         if tdeps != olddeps:
1146             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1147             return False
1148
1149     # put outputs in place
1150     for t in tgt_list:
1151         if not t.sname in denv.output: continue
1152         tdeps = denv.output[t.sname]
1153         for a in tdeps:
1154             setattr(t, a, tdeps[a])
1155
1156     # put output env vars in place
1157     for t in tgt_list:
1158         if not t.sname in denv.outenv: continue
1159         tdeps = denv.outenv[t.sname]
1160         for a in tdeps:
1161             t.env[a] = tdeps[a]
1162
1163     debug('deps: loaded saved dependencies')
1164     return True
1165
1166
1167
1168 def check_project_rules(bld):
1169     '''check the project rules - ensuring the targets are sane'''
1170
1171     if bld.__class__.__name__ == "ClangDbContext":
1172         return
1173
1174     loops = {}
1175     inc_loops = {}
1176
1177     tgt_list = get_tgt_list(bld)
1178
1179     add_samba_attributes(bld, tgt_list)
1180
1181     force_project_rules = (Options.options.SHOWDEPS or
1182                            Options.options.SHOW_DUPLICATES)
1183
1184     if not force_project_rules and load_samba_deps(bld, tgt_list):
1185         return
1186
1187     timer = Utils.Timer()
1188
1189     bld.new_rules = True
1190     Logs.info("Checking project rules ...")
1191
1192     debug('deps: project rules checking started')
1193
1194     replace_builtin_subsystem_deps(bld, tgt_list)
1195
1196     debug("deps: replace_builtin_subsystem_deps: %s" % str(timer))
1197
1198     expand_subsystem_deps(bld)
1199
1200     debug("deps: expand_subsystem_deps: %s" % str(timer))
1201
1202     replace_grouping_libraries(bld, tgt_list)
1203
1204     debug("deps: replace_grouping_libraries: %s" % str(timer))
1205
1206     build_direct_deps(bld, tgt_list)
1207
1208     debug("deps: build_direct_deps: %s" % str(timer))
1209
1210     break_dependency_loops(bld, tgt_list)
1211
1212     debug("deps: break_dependency_loops: %s" % str(timer))
1213
1214     if Options.options.SHOWDEPS:
1215             show_dependencies(bld, Options.options.SHOWDEPS, set())
1216
1217     calculate_final_deps(bld, tgt_list, loops)
1218
1219     debug("deps: calculate_final_deps: %s" % str(timer))
1220
1221     if Options.options.SHOW_DUPLICATES:
1222             show_object_duplicates(bld, tgt_list)
1223
1224     # run the various attribute generators
1225     for f in [ build_dependencies, build_includes, add_init_functions ]:
1226         debug('deps: project rules checking %s', f)
1227         for t in tgt_list: f(t)
1228         debug("deps: %s: %s" % (f, str(timer)))
1229
1230     debug('deps: project rules stage1 completed')
1231
1232     if not check_duplicate_sources(bld, tgt_list):
1233         Logs.error("Duplicate sources present - aborting")
1234         sys.exit(1)
1235
1236     debug("deps: check_duplicate_sources: %s" % str(timer))
1237
1238     if not bld.check_group_ordering(tgt_list):
1239         Logs.error("Bad group ordering - aborting")
1240         sys.exit(1)
1241
1242     debug("deps: check_group_ordering: %s" % str(timer))
1243
1244     show_final_deps(bld, tgt_list)
1245
1246     debug("deps: show_final_deps: %s" % str(timer))
1247
1248     debug('deps: project rules checking completed - %u targets checked',
1249           len(tgt_list))
1250
1251     if not bld.is_install:
1252         save_samba_deps(bld, tgt_list)
1253
1254     debug("deps: save_samba_deps: %s" % str(timer))
1255
1256     Logs.info("Project rules pass")
1257
1258     timer = Utils.Timer()
1259
1260     bld.load('clang_compilation_database')
1261     Scripting.run_command('clangdb')
1262
1263     debug("deps: clang_compilation_database: %s" % str(timer))
1264
1265
1266 def CHECK_PROJECT_RULES(bld):
1267     '''enable checking of project targets for sanity'''
1268     if bld.env.added_project_rules:
1269         return
1270     bld.env.added_project_rules = True
1271     bld.add_pre_fun(check_project_rules)
1272 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
1273
1274