Add sub-project (senf/Ext) support to test_changes target
[senf.git] / site_scons / senfutil.py
1 import os.path, glob, site_tools.Yaptu
2 from SCons.Script import *
3
4 senfutildir = os.path.dirname(__file__)
5
6 # Fix for SCons 0.97 compatibility
7 try:
8     Variables
9 except NameError: 
10     Variables = Options
11     BoolVariable = BoolOption
12
13 def parseLogOption(value):
14     stream, area, level = ( x.strip() for x in value.strip().split('|') )
15     stream = ''.join('(%s)' % x for x in stream.split('::') )
16     if area : area = ''.join( '(%s)' % x for x in area.split('::') )
17     else    : area = '(_)'
18     return '((%s,%s,%s))' % (stream,area,level)
19
20 def expandLogOption(target, source, env, for_signature):
21     if env.get('LOGLEVELS'):
22         return [ 'SENF_LOG_CONF="' + ''.join( parseLogOption(x) for x in env.subst('$LOGLEVELS').split() )+'"']
23     else:
24         return []
25
26 class BuildTypeOptions:
27     def __init__(self, var):
28         self._var = var
29
30     def __call__(self, target, source, env, for_signature):
31         type = env['final'] and "final" or env['debug'] and "debug" or "normal"
32         return env[self._var + "_" + type]
33
34 def loadTools(env):
35     global senfutildir
36     tooldir = os.path.join(senfutildir, 'site_tools')
37     for tool in os.listdir(tooldir):
38         name, ext = os.path.splitext(tool)
39         if ext == '.py' and name != "__init__" : env.Tool(name, [ tooldir ])
40
41 def parseArguments(env, *defs):
42     vars = Variables(args=ARGUMENTS)
43     for d in defs : vars.Add(d)
44     vars.Update(env)
45     env.Help("""
46 Any construction environment variable may be set from the scons
47 command line (see SConstruct file and SCons documentation for a list
48 of variables) using
49
50    VARNAME=value    Assign new value  
51    VARNAME+=value   Append value at end
52
53 Special command line parameters:
54 """)
55     env.Help(vars.GenerateHelpText(env))
56     try                  : unknv = vars.UnknownVariables()
57     except AttributeError: unknv = vars.UnknownOptions()
58     for k,v in unknv.iteritems():
59         if k.endswith('+'):
60             env.Append(**{k[:-1]: v})
61         else:
62             env.Replace(**{k: v})
63
64
65 ###########################################################################
66 # This looks much more complicated than it is: We do three things here:
67 # a) switch between final or debug options
68 # b) parse the LOGLEVELS parameter into the correct SENF_LOG_CONF syntax
69 # c) check for a local SENF, set options accordingly and update that SENF if needed
70
71 def SetupForSENF(env, senf_path = []):
72     global senfutildir
73     senf_path.extend(('senf', os.path.dirname(senfutildir), '/usr/local', '/usr'))
74
75     loadTools(env)
76
77     env.Append(
78         LIBS              = [ 'senf', 'rt', '$BOOSTREGEXLIB',
79                               '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB',
80                               '$BOOSTFSLIB' ],
81         
82         CXXFLAGS          = [ '-Wno-long-long', '$CXXFLAGS_' ],
83         CXXFLAGS_         = BuildTypeOptions('CXXFLAGS'),
84         
85         CPPDEFINES        = [ '$expandLogOption', '$CPPDEFINES_' ],
86         expandLogOption   = expandLogOption,
87         CPPDEFINES_       = BuildTypeOptions('CPPDEFINES'),
88         
89         LINKFLAGS         = [ '-rdynamic', '$LINKFLAGS_' ],
90         LINKFLAGS_        = BuildTypeOptions('LINKFLAGS'),
91
92         LOGLEVELS         = [ '$LOGLEVELS_' ],
93         LOGLEVELS_        = BuildTypeOptions('LOGLEVELS'),
94         )
95
96     env.SetDefault( 
97         CXXFLAGS_final    = [],
98         CXXFLAGS_normal   = [],
99         CXXFLAGS_debug    = [],
100
101         CPPDEFINES_final  = [],
102         CPPDEFINES_normal = [],
103         CPPDEFINES_debug  = [],
104
105         LINKFLAGS_final   = [],
106         LINKFLAGS_normal  = [],
107         LINKFLAGS_debug   = [],
108
109         LOGLEVELS_final   = [],
110         LOGLEVELS_normal  = [],
111         LOGLEVELS_debug   = [],
112
113         PROJECTNAME       = "Unnamed project",
114         DOCLINKS          = [],
115         PROJECTEMAIL      = "nobody@nowhere.org",
116         COPYRIGHT         = "nobody",
117         REVISION          = "unknown",
118         )
119
120     # Interpret command line options
121     parseArguments(
122         env, 
123         BoolVariable('final', 'Build final (optimized) build', False),
124         BoolVariable('debug', 'Link in debug symbols', False),
125     )
126
127     # If we have a symbolic link (or directory) 'senf', we use it as our
128     # senf repository
129     for path in senf_path:
130         if not path.startswith('/') : sconspath = '#/%s' % path
131         else                        : sconspath = path
132         if os.path.exists(os.path.join(path,"senf/config.hh")):
133             if not env.GetOption('no_progress'):
134                 print "\nUsing SENF in '%s'\n" \
135                     % ('/..' in sconspath and os.path.abspath(path) or sconspath)
136             env.Append( LIBPATH = [ sconspath ],
137                         CPPPATH = [ sconspath ],
138                         BUNDLEDIR = sconspath,
139                         SENFDIR = sconspath,
140                         SENFSYSLAYOUT = False)
141             try:
142                 env.MergeFlags(file(os.path.join(path,"senf.conf")).read())
143             except IOError:
144                 if not env.GetOption('no_progress'):
145                     print "(SENF configuration file 'senf.conf' not found, assuming non-final SENF)"
146                 env.Append(CPPDEFINES = [ 'SENF_DEBUG' ])
147             break
148         elif os.path.exists(os.path.join(path,"include/senf/config.hh")):
149             if not env.GetOption('no_progress'):
150                 print "\nUsing system SENF in '%s/'\n" % sconspath
151             env.Append(BUNDLEDIR = os.path.join(sconspath,"lib/senf"),
152                        SENFDIR = sconspath,
153                        SENFSYSLAYOUT = True)
154             break
155     else:
156         if not env.GetOption('no_progress'):
157             print "\nSENF library not found .. trying build anyway !!\n"
158
159     env.Alias('all', '#')
160
161
162 def DefaultOptions(env):
163     env.Append(
164         CXXFLAGS         = [ '-Wall', '-Woverloaded-virtual' ],
165         CXXFLAGS_final   = [ '-O2' ],
166         CXXFLAGS_normal  = [ '-O0', '-g' ],
167         CXXFLAGS_debug   = [ '$CXXFLAGS_normal' ],
168
169         LINKFLAGS_normal = [ '-Wl,-S' ],
170         LINKFLAGS_debug  = [ '-g' ],
171     )
172
173 def Glob(env, exclude=[], subdirs=[]):
174     testSources = env.Glob("*.test.cc", strings=True)
175     sources = [ x 
176                 for x in env.Glob("*.cc", strings=True) 
177                 if x not in testSources and x not in exclude ]
178     for subdir in subdirs:
179         testSources += env.Glob(os.path.join(subdir,"*.test.cc"), strings=True)
180         sources += [ x 
181                      for x in env.Glob(os.path.join(subdir,"*.cc"), strings=True)
182                      if x not in testSources and x not in exclude ]
183     sources.sort()
184     testSources.sort()
185     return (sources, testSources)
186
187 tagfiles = None
188
189 def Doxygen(env, doxyheader=None, doxyfooter=None, doxycss=None, mydoxyfile=False, senfdoc_path=[],
190             **kw):
191     # Additional interesting keyword arguments or environment variables:
192     #    PROJECTNAME, DOCLINKS, PROJECTEMAIL, COPYRIGHT, REVISION
193
194     global senfutildir
195     global tagfiles
196     libdir=os.path.join(senfutildir, 'lib')
197     
198     if tagfiles is None:
199         senfdocdir = None
200         senfdoc_path.extend(('senfdoc', '$SENFDIR', '$SENFDIR/manual',
201                              '$SENFDIR/share/doc/senf', '$SENFDIR/share/doc/libsenf-doc/html'))
202         for path in senfdoc_path:
203             path = env.Dir(path).get_path()
204             if os.path.exists(os.path.join(path, "doc/doclib.tag")):
205                 senfdocdir = path
206                 break
207         tagfiles = []
208         if senfdocdir is None:
209             if not env.GetOption('no_progress'):
210                 print "(SENF documentation not found)"
211         else:
212             for dir, dirs, files in os.walk(senfdocdir):
213                 tagfiles.extend([ os.path.join(dir,f) for f in files if f.endswith('.tag') ])
214                 if dir.endswith('/doc') : 
215                     try: dirs.remove('html')
216                     except ValueError: pass
217                 for d in dirs: 
218                     if d.startswith('.') : dirs.remove(d)
219     
220     if env.GetOption('clean'):
221         env.Clean('doc', env.Dir('doc'))
222         if not mydoxyfile:
223             env.Clean('doc', "Doxyfile")
224
225     if not mydoxyfile:
226         # Create Doxyfile NOW
227         site_tools.Yaptu.yaptuAction("Doxyfile", 
228                                      os.path.join(libdir, "Doxyfile.yap"),
229                                      env)
230
231     envvalues = [ env.Value('$PROJECTNAME'),
232                   env.Value('$DOCLINKS'),
233                   env.Value('$PROJECTEMAIL'),
234                   env.Value('$COPYRIGHT'),
235                   env.Value('$REVISION') ]
236
237     # The other files are created using dependencies
238     if doxyheader: 
239         doxyheader = env.CopyToDir(env.Dir("doc"), doxyheader)
240     else:
241         doxyheader = env.Yaptu("doc/doxyheader.html", os.path.join(libdir, "doxyheader.yap"), **kw)
242         env.Depends(doxyheader, envvalues)
243     if doxyfooter:
244         doxyfooter = env.CopyToDir(env.Dir("doc"), doxyfooter)
245     else:
246         doxyfooter = env.Yaptu("doc/doxyfooter.html", os.path.join(libdir, "doxyfooter.yap"), **kw)
247         env.Depends(doxyfooter, envvalues)
248     if doxycss:
249         doxycss = env.CopyToDir(env.Dir("doc"), doxycss)
250     else:
251         doxycss    = env.CopyToDir(env.Dir("doc"), os.path.join(libdir, "doxy.css"))
252
253     doc = env.Doxygen("Doxyfile",
254                       DOXYOPTS   = [ '--html', '--tagfiles', '"$TAGFILES"' ],
255                       DOXYENV    = { 'TOPDIR'     : env.Dir('#').abspath,
256                                      'LIBDIR'     : libdir,
257                                      'REVISION'   : '$REVISION',
258                                      'tagfiles'   : '$TAGFILES',
259                                      'output_dir' : 'doc',
260                                      'html_dir'   : 'html',
261                                      'html'       : 'YES',
262                                      'DOXYGEN'    : '$DOXYGEN' },
263                       TAGFILES   = tagfiles, 
264                       DOCLIBDIR  = libdir,
265                       DOXYGENCOM = "$DOCLIBDIR/doxygen.sh $DOXYOPTS $SOURCE")
266
267     env.Depends(doc, [ doxyheader, doxyfooter, doxycss ])
268
269     return doc