771d4fadad223a6172cf51bcedbd21d2061db273
[senf.git] / senfscons / SENFSCons.py
1 import os.path, glob
2 import  SCons.Options, SCons.Environment, SCons.Script.SConscript, SCons.Node.FS, SCons.Defaults
3
4 SCONS_TOOLS = [
5     "Doxygen",
6     "Dia2Png",
7 ]
8
9 opts = None
10 finalizers = []
11
12 basedir = os.path.abspath(os.path.split(__file__)[0])
13
14 def InitOpts():
15     global opts
16     if opts is not None: return
17     opts = SCons.Options.Options('SConfig')
18     opts.Add('CXX', 'C++ compiler to use', 'g++')
19     opts.Add('EXTRA_DEFINES', 'Additional preprocessor defines', '')
20     opts.Add('EXTRA_LIBS', 'Additional libraries to link against', '')
21     opts.Add(SCons.Options.BoolOption('final','Enable optimization',0))
22
23 def Finalizer(f):
24     global finalizers
25     finalizers.append(f)
26
27 def UseBoost():
28     global opts
29     InitOpts()
30     opts.Add('BOOST_INCLUDES', 'Boost include directory', '')
31     opts.Add('BOOST_VARIANT', 'The boost variant to use', '')
32     opts.Add('BOOST_TOOLSET', 'The boost toolset to use', '')
33     opts.Add('BOOST_RUNTIME', 'The boost runtime to use', '')
34     opts.Add('BOOST_DEBUG_RUNTIME', 'The boost debug runtime to use', '')
35     opts.Add('BOOST_LIBDIR', 'The directory of the boost libraries', '')
36     Finalizer(FinalizeBoost)
37
38 def FinalizeBoost(env):
39     env.Tool('BoostUnitTests', [basedir])
40
41     if env['BOOST_TOOLSET']:
42         runtime = ""
43         if env['final'] : runtime += env.get('BOOST_RUNTIME','')
44         else            : runtime += env.get('BOOST_DEBUG_RUNTIME','gd')
45         if env['STLPORT_LIB'] : runtime += "p"
46         if runtime: runtime = "-" + runtime
47         env['BOOST_VARIANT'] = "-" + env['BOOST_TOOLSET'] + runtime
48
49     env['BOOSTTESTLIB'] = 'libboost_unit_test_framework' + env['BOOST_VARIANT']
50
51     env.Append(LIBPATH = [ '$BOOST_LIBDIR' ],
52                CPPPATH = [ '$BOOST_INCLUDES' ])
53
54 def UseSTLPort():
55     global opts
56     InitOpts()
57     opts.Add('STLPORT_INCLUDES', 'STLport include directory', '')
58     opts.Add('STLPORT_LIB', 'Name of the stlport library or empty to not use stlport', '')
59     opts.Add('STLPORT_DEBUGLIB', 'Name of the stlport debug library','')
60     opts.Add('STLPORT_LIBDIR', 'The directory of the stlport libraries','')
61     Finalizer(FinalizeSTLPort)
62
63 def FinalizeSTLPort(env):
64     if env['STLPORT_LIB']:
65         if not env['STLPORT_DEBUGLIB']:
66             env['STLPORT_DEBUGLIB'] = env['STLPORT_LIB'] + '_stldebug'
67         env.Append(LIBPATH = [ '$STLPORT_LIBDIR' ],
68                    CPPPATH = [ '$STLPORT_INCLUDES' ])
69         if env['final']:
70             env.Append(LIBS = [ '$STLPORT_LIB' ])
71         else:
72             env.Append(LIBS = [ '$STLPORT_DEBUGLIB' ],
73                        CPPDEFINES = [ '_STLP_DEBUG' ])
74
75 def MakeEnvironment():
76     global opts, finalizers
77     InitOpts()
78     env = SCons.Environment.Environment(options=opts)
79     if SCons.Script.SConscript.Arguments.get('final'):
80         env['final'] = 1
81     env.Help(opts.GenerateHelpText(env))
82     #conf = env.Configure()
83     #env = conf.env
84     if os.environ.has_key('SSH_AUTH_SOCK'):
85         env.Append( ENV = { 'SSH_AUTH_SOCK': os.environ['SSH_AUTH_SOCK'] } )
86
87     for finalizer in finalizers:
88         finalizer(env)
89
90     for tool in SCONS_TOOLS:
91         env.Tool(tool, [basedir])
92
93     env.Append(CXXFLAGS = [ '-Wall', '-Woverloaded-virtual', '-Wno-long-long' ],
94                LOCALLIBDIR = [ '#' ],
95                LIBPATH = [ '$LOCALLIBDIR' ])
96
97     if env['final']:
98         env.Append(CXXFLAGS = [ '-O3' ],
99                    CPPDEFINES = [ 'NDEBUG' ])
100     else:
101         env.Append(CXXFLAGS = [ '-O0', '-g', '-fno-inline' ],
102                    LINKFLAGS = [ '-g' ])
103
104     env.Append(CPPDEFINES = [ '$EXTRA_DEFINES' ],
105                LIBS = [ '$EXTRA_LIBS' ])
106
107     #return conf.Finish()
108     return env
109
110 def GlobSources(exclude=[]):
111     testSources = glob.glob("*.test.cc")
112     sources = [ x for x in glob.glob("*.cc") if x not in testSources and x not in exclude ]
113     return (sources, testSources)
114     
115 def StandardTargets(env):
116     all = env.Alias('all')
117     env.Clean(all, [ '.sconsign', '.sconf_temp', 'config.log' ])
118     env.Depends(all, '.')
119
120 def GlobalTargets(env):
121     pass
122
123 def LibPath(lib): return '$LOCALLIBDIR/lib%s.a' % lib
124     
125 def Objects(env, sources, testSources = None, LIBS = []):
126     if type(sources) == type(()):
127         testSources = sources[1]
128         sources = sources[0]
129
130     objects = None
131     if sources:
132         objects = env.Object(sources)
133
134     if testSources:
135         test = env.BoostUnitTests(
136             target = 'test',
137             source = sources,
138             test_source = testSources,
139             LIBS = LIBS,
140             DEPENDS = [ env.File(LibPath(x)) for x in LIBS ])
141         env.Alias('all_tests', test)
142         # Hmm ... here I'd like to use an Alias instead of a file
143         # however the alias does not seem to live in the subdirectory
144         # which breaks 'scons -u test'
145         env.Alias(env.File('test'), test)
146
147     return objects
148
149 def Doxygen(env, doxyfile = "Doxyfile", extra_sources = []):
150     # ARGHHH !!! without the [:] we are changing the target list
151     #        ||| WITHIN THE DOXYGEN BUILDER
152     docs = env.Doxygen(doxyfile)[:]
153     xmlnode = None
154     htmlnode = None
155     tagnode = None
156     for doc in docs:
157         if isinstance(doc,SCons.Node.FS.Dir): continue
158         if doc.name == 'xml.stamp' : xmlnode = doc
159         if doc.name == 'html.stamp' : htmlnode = doc
160         if os.path.splitext(doc.name)[1] == '.stamp' : continue # ignore other file stamps
161         # otherwise it must be the tag file
162         tagnode = doc
163
164     if tagnode:
165         # Postprocess the tag file to remove the (broken) namespace
166         # references
167         env.AddPostAction(
168             docs,
169             env.Action("xsltproc --nonet -o %(target)s.temp %(template)s %(target)s && mv %(target)s.temp %(target)s"
170                        % { 'target': tagnode.abspath,
171                            'template': os.path.join(basedir,"tagmunge.xsl") }))
172
173     if htmlnode and env.get('DOXY_HTML_XSL'):
174         xslfile = env.File(env['DOXY_HTML_XSL'])
175         env.AddPostAction(
176             docs,
177             env.Action(("for html in %s/*.html; do " +
178                         "    echo $$html;" +
179                         "    sed -e 's/id=\"current\"/class=\"current\"/' $${html}" +
180                         "        | tidy -ascii -q --show-warnings no --fix-uri no" +
181                         "        | xsltproc --nonet --html -o $${html}.new %s - 2>&1" +
182                         "        | grep '^-'" +
183                         "        | grep -v 'ID .* already defined';" +
184                         "    mv $${html}.new $${html}; " +
185                         "done")
186                        % (htmlnode.dir.abspath, xslfile.abspath)))
187         for doc in docs:
188             env.Depends(doc,xslfile)
189
190     if xmlnode:
191         xrefs = []
192         for type in env.get("DOXY_XREF_TYPES",[ "bug", "todo" ]):
193             xref = os.path.join(xmlnode.dir.abspath,type+".xml")
194             xref_pp = env.Command(xref+'i', [ xref, os.path.join(basedir,'xrefxtract.xslt'), xmlnode ],
195                                   [ "test -s $SOURCE && xsltproc -o $TARGET" +
196                                     " --stringparam module $MODULE" + 
197                                     " --stringparam type $TYPE" +
198                                     " ${SOURCES[1]} $SOURCE || touch $TARGET" ],
199                                   MODULE = xmlnode.dir.dir.dir.name,
200                                   TYPE = type)
201             env.SideEffect(xref, xmlnode)
202             env.AddPreAction(docs, "rm -f %s" % (xref,))
203             env.AddPostAction(docs, "test -r %s || touch %s" % (xref,xref))
204             xrefs.extend(xref_pp)
205         docs.extend(xrefs)
206
207     env.Depends(docs,extra_sources)
208     for doc in docs :
209         env.Alias('all_docs', doc)
210         env.Clean('all_docs', doc)
211         env.Clean('all', doc)
212     return docs
213
214 def DoxyXRef(env, docs=None,
215              HTML_HEADER = None, HTML_FOOTER = None,
216              TITLE = "Cross-reference of action points"):
217     if docs is None:
218         docs = env.Alias('all_docs')[0].sources
219     xrefs = [ doc for doc in docs if os.path.splitext(doc.name)[1] == ".xmli" ]
220     xref = env.Command("doc/html/xref.xml", xrefs,
221                        [ "echo -e '<?xml version=\"1.0\"?>\\n<xref>' >$TARGET",
222                          "cat $SOURCES >> $TARGET",
223                          "echo '</xref>' >>$TARGET" ])
224
225     # Lastly we create the html file
226     sources = [ xref, "%s/xrefhtml.xslt" % basedir ]
227     if HTML_HEADER : sources.append(HTML_HEADER)
228     if HTML_FOOTER : sources.append(HTML_FOOTER)
229
230     commands = []
231     if HTML_HEADER:
232         commands.append(
233             "sed -e 's/\\$$title/$TITLE/g' -e 's/\\$$projectname/Overview/g' ${SOURCES[2]} > $TARGET")
234     commands.append("xsltproc --stringparam title '$TITLE' ${SOURCES[1]} $SOURCE >> $TARGET")
235     if HTML_FOOTER:
236         commands.append(
237             "sed -e 's/\\$$title/$TITLE/g' -e 's/\\$$projectname/Overview/g' ${SOURCES[%d]} >> $TARGET"
238             % (HTML_HEADER and 3 or 2))
239     
240     xref = env.Command("doc/html/xref.html", sources, commands,
241                        TITLE = TITLE)
242
243     env.Alias('all_docs',xref)
244     return xref
245
246 def Lib(env, library, sources, testSources = None, LIBS = []):
247     objects = Objects(env,sources,testSources,LIBS=LIBS)
248     lib = None
249     if objects:
250         lib = env.Library(env.File(LibPath(library)),objects)
251         env.Default(lib)
252         env.Append(ALLLIBS = library)
253     return lib
254
255 def Binary(env, binary, sources, testSources = None, LIBS = []):
256     objects = Objects(env,sources,testSources,LIBS=LIBS)
257     program = None
258     if objects:
259         progEnv = env.Copy()
260         progEnv.Prepend(LIBS = LIBS)
261         program = progEnv.Program(target=binary,source=objects)
262         env.Default(program)
263         env.Depends(program, [ env.File(LibPath(x)) for x in LIBS ])
264     return program