root/trunk/libffado/SConstruct

Revision 2715, 37.5 kB (checked in by jwoithe, 6 years ago)

SConstruct: add byte-string prefix to prevent TypeErrors? in Python3/scons3.

Patch from Orcan Ogetbil.

Line 
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007, 2008, 2010 Arnold Krille
4 # Copyright (C) 2007, 2008 Pieter Palmers
5 # Copyright (C) 2008, 2012 Jonathan Woithe
6 #
7 # This file is part of FFADO
8 # FFADO = Free Firewire (pro-)audio drivers for linux
9 #
10 # FFADO is based upon FreeBoB.
11 #
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 2 of the License, or
15 # (at your option) version 3 of the License.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #
25
26 FFADO_API_VERSION = "9"
27 FFADO_VERSION="2.3.9999"
28
29 from subprocess import Popen, PIPE
30 import os
31 import re
32 from string import Template
33 import imp
34 import distutils.sysconfig
35
36 if not os.path.isdir( "cache" ):
37         os.makedirs( "cache" )
38
39 opts = Variables( "cache/options.cache" )
40
41 opts.AddVariables(
42     BoolVariable( "DEBUG", """\
43 Build with \"-g -Wall\" rather than \"-O2\", and include extra debugging
44 checks in the code.""", True ),
45     BoolVariable( "DEBUG_MESSAGES", "Enable support for debug messages", True ),
46     BoolVariable( "PROFILE", "Build with symbols, warnings and other profiling info", False ),
47     PathVariable( "PREFIX", "The prefix where ffado will be installed to.", "/usr/local", PathVariable.PathAccept ),
48     PathVariable( "BINDIR", "Overwrite the directory where apps are installed to.", "$PREFIX/bin", PathVariable.PathAccept ),
49     PathVariable( "LIBDIR", "Overwrite the directory where libs are installed to.", "$PREFIX/lib", PathVariable.PathAccept ),
50     PathVariable( "INCLUDEDIR", "Overwrite the directory where headers are installed to.", "$PREFIX/include", PathVariable.PathAccept ),
51     PathVariable( "SHAREDIR", "Overwrite the directory where misc shared files are installed to.", "$PREFIX/share/libffado", PathVariable.PathAccept ),
52     PathVariable( "MANDIR", "Overwrite the directory where manpages are installed", "$PREFIX/man", PathVariable.PathAccept ),
53     PathVariable( "PYPKGDIR", "The directory where the python modules get installed.",
54         distutils.sysconfig.get_python_lib( prefix="$PREFIX" ), PathVariable.PathAccept ),
55     PathVariable( "UDEVDIR", "Overwrite the directory where udev rules are installed to.", "/lib/udev/rules.d/", PathVariable.PathAccept ),
56     BoolVariable( "ENABLE_BEBOB", "Enable/Disable support for the BeBoB platform.", True ),
57     BoolVariable( "ENABLE_FIREWORKS", "Enable/Disable support for the ECHO Audio FireWorks platform.", True ),
58     BoolVariable( "ENABLE_OXFORD", "Enable/Disable support for the Oxford Semiconductor FW platform.", True ),
59     BoolVariable( "ENABLE_MOTU", "Enable/Disable support for the MOTU platform.", True ),
60     BoolVariable( "ENABLE_DICE", "Enable/Disable support for the TCAT DICE platform.", True ),
61     BoolVariable( "ENABLE_METRIC_HALO", "Enable/Disable support for the Metric Halo platform.", False ),
62     BoolVariable( "ENABLE_RME", "Enable/Disable support for the RME platform.", True ),
63     BoolVariable( "ENABLE_DIGIDESIGN", "Enable/Disable support for Digidesign interfaces.", False ),
64     BoolVariable( "ENABLE_BOUNCE", "Enable/Disable the BOUNCE device.", False ),
65     BoolVariable( "ENABLE_GENERICAVC", """\
66 Enable/Disable the the generic avc part (mainly used by apple).
67   Note that disabling this option might be overwritten by other devices needing
68   this code.""", False ),
69     BoolVariable( "ENABLE_ALL", "Enable/Disable support for all devices.", False ),
70     BoolVariable( "SERIALIZE_USE_EXPAT", "Use libexpat for XML serialization.", False ),
71     EnumVariable( "BUILD_MIXER", "Build the ffado-mixer", 'auto', allowed_values=('auto', 'true', 'false'), ignorecase=2),
72     BoolVariable( "BUILD_TESTS", """\
73 Build the tests in their directory. As some contain quite some functionality,
74   this is on by default.
75   If you just want to use ffado with jack without the tools, you can disable this.\
76 """, True ),
77     BoolVariable( "BUILD_STATIC_TOOLS", "Build a statically linked version of the FFADO tools.", False ),
78     EnumVariable('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'powerpc64', 'none' ), ignorecase=2),
79     BoolVariable( "ENABLE_OPTIMIZATIONS", "Enable optimizations and the use of processor specific extentions (MMX/SSE/...).", False ),
80     BoolVariable( "DETECT_USERSPACE_ENV", "Try to detect the user space environment and add necessary 32/64 bit machine flags.", True ),
81     BoolVariable( "PEDANTIC", "Enable -Werror and more pedantic options during compile.", False ),
82     BoolVariable( "CUSTOM_ENV", "Respect CC, CXX, CFLAGS, CXXFLAGS and LDFLAGS.\nOnly meant for distributors and gentoo-users who want to over-optimize their build.\n Using this is not supported by the ffado-devs!", False ),
83     ( "COMPILE_FLAGS", "Deprecated (use CFLAGS and CXXFLAGS with CUSTOM_ENV=True instead).  Add additional flags to the environment.\nOnly meant for distributors and gentoo-users who want to over-optimize their build.\n Using this is not supported by the ffado-devs!" ),
84     EnumVariable( "ENABLE_SETBUFFERSIZE_API_VER", "Report API version at runtime which includes support for dynamic buffer resizing (requires recent jack).", 'auto', allowed_values=('auto', 'true', 'false', 'force'), ignorecase=2),
85
86     )
87
88 ## Load the builders in config
89 buildenv=os.environ
90
91 env = Environment( tools=['default','scanreplace','pyuic','pyuic4','pyuic5','dbus','doxygen','pkgconfig'], toolpath=['admin'], ENV = buildenv, options=opts )
92
93 custom_flags = False
94
95 if 'COMPILE_FLAGS' in env and len(env['COMPILE_FLAGS']) > 0:
96     print("The COMPILE_FLAGS option is deprecated. Use CFLAGS and CXXFLAGS with CUSTOM_ENV=True instead")
97     custom_flags = True
98     env.MergeFlags(env['COMPILE_FLAGS'])
99
100 if env['CUSTOM_ENV']:
101     custom_flags = True
102
103     # Honour the user choice of compiler (if any).
104     if 'CC' in os.environ and len(os.environ['CC']) > 0:
105         env['CC'] = os.environ['CC']
106     if 'CXX' in os.environ and len(os.environ['CXX']) > 0:
107         env['CXX'] = os.environ['CXX']
108
109     # Honour the user supplied flags (if any), but notify the user that this is not supported.
110     if 'CFLAGS' in os.environ and len(os.environ['CFLAGS']) > 0:
111         env.Append(CFLAGS = str(os.environ['CFLAGS'].replace('\"', '')))
112     if 'CXXFLAGS' in os.environ and len(os.environ['CXXFLAGS']) > 0:
113         env.Append(CXXFLAGS = str(os.environ['CXXFLAGS'].replace('\"', '')))
114     if 'LDFLAGS' in os.environ and len(os.environ['LDFLAGS']) > 0:
115         env.Append(LINKFLAGS = str(os.environ['LDFLAGS'].replace('\"', '')))
116
117 if custom_flags:
118     print('''
119  * Usage of additional flags is not supported by the ffado-devs.
120  * Use at own risk!
121  *
122  * Flags in use:
123  *   CC = %s
124  *   CXX = %s
125  *   CFLAGS = %s
126  *   CXXFLAGS = %s
127  *   LDFLAGS = %s
128 ''' % (env['CC'], env['CXX'], env['CFLAGS'], env['CXXFLAGS'], env['LINKFLAGS']))
129
130 Help( """
131 For building ffado you can set different options as listed below. You have to
132 specify them only once, scons will save the last value you used and re-use
133 that.
134 To really undo your settings and return to the factory defaults, remove the
135 "cache"-folder and the file ".sconsign.dblite" from this directory.
136 For example with: "rm -Rf .sconsign.dblite cache"
137
138 Note that this is a development version! Don't complain if its not working!
139 See www.ffado.org for stable releases.
140 """ )
141 Help( opts.GenerateHelpText( env ) )
142
143 # make sure the necessary dirs exist
144 if not os.path.isdir( "cache" ):
145         os.makedirs( "cache" )
146 if not os.path.isdir( 'cache/objects' ):
147     os.makedirs( 'cache/objects' )
148
149 CacheDir( 'cache/objects' )
150
151 opts.Save( 'cache/options.cache', env )
152
153 def ConfigGuess( context ):
154     context.Message( "Trying to find the system triple: " )
155     ret = os.popen( "/bin/sh admin/config.guess" ).read()[:-1]
156     context.Result( ret )
157     return ret
158
159 def CheckForApp( context, app ):
160     context.Message( "Checking whether '" + app + "' executes " )
161     ret = context.TryAction( app )
162     context.Result( ret[0] )
163     return ret[0]
164
165 def CheckForPyModule( context, module ):
166     context.Message( "Checking for the python module '" + module + "' " )
167     ret = context.TryAction( "python $SOURCE", "import %s" % module, ".py" )
168     context.Result( ret[0] )
169     return ret[0]
170
171 def CompilerCheck( context ):
172     context.Message( "Checking for a working C-compiler " )
173     ret = context.TryRun( """
174 #include <stdio.h>
175
176 int main() {
177     printf( "Hello World!" );
178     return 0;
179 }""", '.c' )[0]
180     context.Result( ret )
181     if ret == 0:
182         return False;
183     context.Message( "Checking for a working C++-compiler " )
184     ret = context.TryRun( """
185 #include <iostream>
186
187 int main() {
188     std::cout << "Hello World!" << std::endl;
189     return 0;
190 }""", ".cpp" )[0]
191     context.Result( ret )
192     return ret
193
194 def CheckPKG(context, name):
195     context.Message( 'Checking for %s... ' % name )
196     ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
197     context.Result( ret )
198     return ret
199
200 tests = {
201     "ConfigGuess" : ConfigGuess,
202     "CheckForApp" : CheckForApp,
203     "CheckForPyModule": CheckForPyModule,
204     "CompilerCheck" : CompilerCheck,
205     "CheckPKG" : CheckPKG,
206 }
207 tests.update( env['PKGCONFIG_TESTS'] )
208 tests.update( env['PYUIC_TESTS'] )
209 tests.update( env['PYUIC4_TESTS'] )
210
211 conf = Configure( env,
212     custom_tests = tests,
213     conf_dir = "cache/",
214     log_file = 'cache/config.log' )
215
216 version_re = re.compile(r'^(\d+)\.(\d+)\.(\d+)')
217
218 def VersionInt(vers):
219     match = version_re.match(vers)
220     if not match:
221         return -1
222     (maj, min, patch) = match.group(1, 2, 3)
223     # For now allow "min" to run up to 65535.  "maj" and "patch" are
224     # restricted to 0-255.
225     return (int(maj) << 24) | (int(min) << 8) | int(patch)
226
227 def CheckJackdVer():
228     # Suppress newline in python 2 and 3
229     sys.stdout.write('Checking jackd version...')
230     sys.stdout.flush()
231     ret = Popen("which jackd >/dev/null 2>&1 && jackd --version | tail -n 1 | cut -d ' ' -f 3", shell=True, stdout=PIPE).stdout.read()[:-1]
232     if (ret == ""):
233         print("not installed")
234         return -1
235     else:
236         print(ret)
237     return VersionInt(ret)
238
239 if env['SERIALIZE_USE_EXPAT']:
240     env['SERIALIZE_USE_EXPAT']=1
241 else:
242     env['SERIALIZE_USE_EXPAT']=0
243
244 if env['ENABLE_BOUNCE'] or env['ENABLE_ALL']:
245     env['REQUIRE_LIBAVC']=1
246 else:
247     env['REQUIRE_LIBAVC']=0
248
249 if not env.GetOption('clean'):
250     #
251     # Check for working gcc and g++ compilers and their environment.
252     #
253     if not conf.CompilerCheck():
254         print("\nIt seems as if your system isn't even able to compile any C-/C++-programs. Probably you don't have gcc and g++ installed. Compiling a package from source without a working compiler is very hard to do, please install the needed packages.\nHint: on *ubuntu you need both gcc- and g++-packages installed, easiest solution is to install build-essential which depends on gcc and g++.")
255         Exit( 1 )
256
257     # Check for pkg-config before using pkg-config to check for other dependencies.
258     if not conf.CheckForPKGConfig():
259         print("\nThe program 'pkg-config' could not be found.\nEither you have to install the corresponding package first or make sure that PATH points to the right directions.")
260         Exit( 1 )
261
262     #
263     # The following checks are for headers and libs and packages we need.
264     #
265     allpresent = 1;
266     # for cache-serialization.
267     if env['SERIALIZE_USE_EXPAT']:
268         allpresent &= conf.CheckHeader( "expat.h" )
269         allpresent &= conf.CheckLib( 'expat', 'XML_ExpatVersion', '#include <expat.h>' )
270
271     pkgs = {
272         'libraw1394' : '2.0.5',
273         'libiec61883' : '1.1.0',
274         'libconfig++' : '0'
275         }
276
277     if env['REQUIRE_LIBAVC']:
278         pkgs['libavc1394'] = '0.5.3'
279
280     if not env['SERIALIZE_USE_EXPAT']:
281         if conf.CheckPKG('libxml++-3.0'):
282             pkgs['libxml++-3.0'] = '3.0.0'
283         if not('libxml++-3.0' in pkgs):
284             pkgs['libxml++-2.6'] = '2.13.0'
285
286     # Provide a way for users to compile newer libffado which will work
287     # against older jack installations which will not accept the new API
288     # version reported at runtime.
289     have_jack = conf.CheckPKG('jack')
290     if have_jack:
291         good_jack1 = conf.CheckPKG('jack < 1.9.0') and conf.CheckPKG('jack >= 0.121.4')
292         good_jack2 = conf.CheckPKG('jack >= 1.9.9')
293     else:
294         jackd_ver = CheckJackdVer()
295         if (jackd_ver != -1):
296             # If jackd is unknown to pkg-config but is never-the-less
297             # runnable, use the version number reported by it.  This means
298             # users don't have to have jack development files present on
299             # their system for this to work.
300             have_jack = (jackd_ver >= VersionInt('0.0.0'))
301             good_jack1 = (jackd_ver < VersionInt('1.9.0')) and (jackd_ver >= VersionInt('0.121.4'))
302             good_jack2 = (jackd_ver >= VersionInt('1.9.9'))
303
304     if env['ENABLE_SETBUFFERSIZE_API_VER'] == 'auto':
305         if not(have_jack):
306             print("""
307 No Jack Audio Connection Kit (JACK) installed: assuming a FFADO
308 setbuffersize-compatible version will be used.
309 """)
310         elif not(good_jack1 or good_jack2):
311             FFADO_API_VERSION="8"
312             print("""
313 Installed Jack Audio Connection Kit (JACK) jack does not support FFADO
314 setbuffersize API: will report earlier API version at runtime.  Consider
315 upgrading to jack1 >=0.122.0 or jack2 >=1.9.9 at some point, and then
316 recompile ffado to gain access to this added feature.
317 """)
318         else:
319             print("Installed Jack Audio Connection Kit (JACK) supports FFADO setbuffersize API")
320     elif env['ENABLE_SETBUFFERSIZE_API_VER'] == 'true':
321         if (have_jack and not(good_jack1) and not(good_jack2)):
322             print("""
323 SetBufferSize API version is enabled but no suitable version of Jack Audio
324 Connection Kit (JACK) has been found.  The resulting FFADO would cause your
325 jackd to abort with "incompatible FFADO version".  Please upgrade to
326 jack1 >=0.122.0 or jack2 >=1.9.9, or set ENABLE_SETBUFFERSIZE_API_VER to "auto"
327 or "false".
328 """)
329             # Although it's not strictly an error, in almost every case that
330             # this occurs the user will want to know about it and fix the
331             # problem, so we exit so they're guaranteed of seeing the above
332             # message.
333             Exit( 1 )
334         else:
335             print("Will report SetBufferSize API version at runtime")
336     elif env['ENABLE_SETBUFFERSIZE_API_VER'] == 'force':
337         print("Will report SetBufferSize API version at runtime")
338     else:
339         FFADO_API_VERSION="8"
340         print("Will not report SetBufferSize API version at runtime")
341
342     for pkg in pkgs:
343         name2 = pkg.replace("+","").replace(".","").replace("-","").upper()
344         env['%s_FLAGS' % name2] = conf.GetPKGFlags( pkg, pkgs[pkg] )
345         #print('%s_FLAGS' % name2)
346         if env['%s_FLAGS'%name2] == 0:
347             allpresent &= 0
348
349     if not allpresent:
350         print("""
351 (At least) One of the dependencies is missing. I can't go on without it, please
352 install the needed packages for each of the lines saying "no".
353 (Remember to also install the *-devel packages!)
354
355 And remember to remove the cache with "rm -Rf .sconsign.dblite cache" so the
356 results above get rechecked.
357 """)
358         Exit( 1 )
359
360     # libxml++-2.6 requires a c++11 compiler as of version 2.39.1.  The
361     # gnu++11 standard seems to work both with these later libxml++ versions
362     # and ffado itself, although a significant number of warnings are
363     # produced.  Add the necessary option to CXXFLAGS if required.
364     if conf.CheckPKG('libxml++-2.6 >= 2.39.1'):
365         env.Append(CXXFLAGS = '-std=gnu++11')
366     if conf.CheckPKG('libxml++-3.0 >= 3.0.0'):
367         env.Append(CXXFLAGS = '-std=gnu++11')
368
369     # Check for C99 lrint() and lrintf() functions used to convert from
370     # float to integer more efficiently via float_cast.h.  If not
371     # present the standard slower methods will be used instead.  This
372     # might not be the best way of testing for these but it's the only
373     # way which seems to work properly.  CheckFunc() fails due to
374     # argument count problems.
375     if 'CFLAGS' in env:
376         oldcf = env['CFLAGS']
377     else:
378         oldcf = ""
379     env.Append(CFLAGS = '-std=c99')
380     if conf.CheckLibWithHeader( "m", "math.h", "c", "lrint(3.2);" ):
381         HAVE_LRINT = 1
382     else:
383         HAVE_LRINT = 0
384     if conf.CheckLibWithHeader( "m", "math.h", "c", "lrintf(3.2);" ):
385         HAVE_LRINTF = 1
386     else:
387         HAVE_LRINTF = 0
388     env['HAVE_LRINT'] = HAVE_LRINT;
389     env['HAVE_LRINTF'] = HAVE_LRINTF;
390     env.Replace(CFLAGS=oldcf)
391
392 #
393 # Optional checks follow:
394 #
395
396 # PyQT checks
397 if env['BUILD_MIXER'] != 'false':
398     have_dbus = (conf.CheckForApp( 'which pyuic4' ) and conf.CheckForPyModule( 'dbus.mainloop.qt' ))
399     have_pyqt4 = (conf.CheckForApp( 'which pyuic4' ) and conf.CheckForPyModule( 'PyQt4' ))
400     have_pyqt5 = (conf.CheckForApp( 'which pyuic5' ) and conf.CheckForPyModule( 'PyQt5' ))
401     if ((have_pyqt4 or have_pyqt5) and have_dbus):
402         env['BUILD_MIXER'] = 'true'
403     elif not env.GetOption('clean'):
404         if env['BUILD_MIXER'] == 'auto':
405             env['BUILD_MIXER'] = 'false'
406             print("""
407 The prerequisites ('pyuic4'/'pyuic5' and the python-modules 'dbus' and
408 'PyQt4'/'PyQt5', the packages could be named like dbus-python and PyQt) to
409 build the mixer were not found. Therefore the qt mixer will not be installed.""")
410         else: # env['BUILD_MIXER'] == 'true'
411             print("""
412 The prerequisites ('pyuic4'/'pyuic5' and the python-modules 'dbus' and
413 'PyQt4'/'PyQt5', the packages could be named like dbus-python and PyQt) to
414 build the mixer were not found, but BUILD_MIXER was requested.""")
415             Exit( 1 )
416
417 env['XDG_TOOLS'] = False
418 if env['BUILD_MIXER'] == 'true':
419     if conf.CheckForApp( 'xdg-desktop-menu --help' ) and conf.CheckForApp( 'xdg-icon-resource --help' ):
420         env['XDG_TOOLS'] = True
421     else:
422         print("""
423 I couldn't find the 'xdg-desktop-menu' and 'xdg-icon-resource' programs. These
424 are needed to add the fancy entry for the mixer to your menu, but you can still
425 start it by executing "ffado-mixer".""")
426
427 #
428 # Optional pkg-config
429 #
430 pkgs = {
431     'alsa': '0',
432     'dbus-1': '1.0',
433     'dbus-c++-1' : '0',
434     }
435 for pkg in pkgs:
436     name2 = pkg.replace("+","").replace(".","").replace("-","").upper()
437     env['%s_FLAGS' % name2] = conf.GetPKGFlags( pkg, pkgs[pkg] )
438
439 if not env['DBUS1_FLAGS'] or not env['DBUSC1_FLAGS'] or not conf.CheckForApp('which dbusxx-xml2cpp'):
440     env['DBUS1_FLAGS'] = b""
441     env['DBUSC1_FLAGS'] = b""
442     print("""
443 One of the dbus-headers, the dbus-c++-headers and/or the application
444 'dbusxx-xml2cpp' where not found. The dbus-server for ffado will therefore not
445 be built.
446 """)
447 else:
448     # Get the directory where dbus stores the service-files
449     env['dbus_service_dir'] = conf.GetPKGVariable( 'dbus-1', 'session_bus_services_dir' ).strip()
450     # this is required to indicate that the DBUS version we use has support
451     # for platform dependent threading init functions
452     # this is true for DBUS >= 0.96 or so. Since we require >= 1.0 it is
453     # always true
454     env['DBUS1_FLAGS'] += b" -DDBUS_HAS_THREADS_INIT_DEFAULT"
455
456     # The controlserver-glue.h file generated by dbusxx-xml2cpp generates
457     # a large number of instances where call.reader()'s return value is
458     # stored (in ri) but not used.  This generates a compiler warning which
459     # we can do nothing about.  Therefore when compiling dbus-related
460     # code, suppress the "set but not used" warning.
461     env['DBUS1_FLAGS'] += b" -Wno-unused-but-set-variable"
462
463 config_guess = conf.ConfigGuess()
464
465 env = conf.Finish()
466
467 if env['DEBUG']:
468     print("Doing a debug build")
469     env.MergeFlags( "-Wall -g -DDEBUG" )
470     env['DEBUG_MESSAGES'] = True
471 elif not custom_flags:
472     # Only merge -O2 to flags if the user has not specified custom flags.
473     env.MergeFlags( "-O2" )
474
475 if env['DEBUG_MESSAGES']:
476     env.MergeFlags( "-DDEBUG_MESSAGES" )
477
478 if env['PROFILE']:
479     print("Doing a PROFILE build")
480     env.MergeFlags( "-Wall -g" )
481
482 if env['PEDANTIC']:
483     env.MergeFlags( "-Werror" )
484
485
486 if env['ENABLE_ALL']:
487     env['ENABLE_BEBOB'] = True
488     env['ENABLE_FIREWORKS'] = True
489     env['ENABLE_OXFORD'] = True
490     env['ENABLE_MOTU'] = True
491     env['ENABLE_DICE'] = True
492     env['ENABLE_METRIC_HALO'] = True
493     env['ENABLE_RME'] = True
494     env['ENABLE_DIGIDESIGN'] = True
495     env['ENABLE_BOUNCE'] = True
496
497
498 env['BUILD_STATIC_LIB'] = False
499 if env['BUILD_STATIC_TOOLS']:
500     print("Building static versions of the tools...")
501     env['BUILD_STATIC_LIB'] = True
502
503 env['build_base']="#/"
504
505 #
506 # Get the DESTDIR (if wanted) from the commandline
507 #
508 env.destdir = ARGUMENTS.get( 'DESTDIR', "" )
509
510 #
511 # Uppercase variables are for usage in code, lowercase versions for usage in
512 # scons-files for installing.
513 #
514 env['BINDIR'] = Template( env['BINDIR'] ).safe_substitute( env )
515 env['LIBDIR'] = Template( env['LIBDIR'] ).safe_substitute( env )
516 env['INCLUDEDIR'] = Template( env['INCLUDEDIR'] ).safe_substitute( env )
517 env['SHAREDIR'] = Template( env['SHAREDIR'] ).safe_substitute( env )
518 env['UDEVDIR'] = Template( env['UDEVDIR'] ).safe_substitute( env )
519 env['prefix'] = Template( env.destdir + env['PREFIX'] ).safe_substitute( env )
520 env['bindir'] = Template( env.destdir + env['BINDIR'] ).safe_substitute( env )
521 env['libdir'] = Template( env.destdir + env['LIBDIR'] ).safe_substitute( env )
522 env['includedir'] = Template( env.destdir + env['INCLUDEDIR'] ).safe_substitute( env )
523 env['sharedir'] = Template( env.destdir + env['SHAREDIR'] ).safe_substitute( env )
524 env['mandir'] = Template( env.destdir + env['MANDIR'] ).safe_substitute( env )
525 env['pypkgdir'] = Template( env.destdir + env['PYPKGDIR'] ).safe_substitute( env )
526 env['udevdir'] = Template( env.destdir + env['UDEVDIR'] ).safe_substitute( env )
527 env['PYPKGDIR'] = Template( env['PYPKGDIR'] ).safe_substitute( env )
528
529 env.Command( target=env['sharedir'], source="", action=Mkdir( env['sharedir'] ) )
530
531 env.Alias( "install", env['libdir'] )
532 env.Alias( "install", env['includedir'] )
533 env.Alias( "install", env['sharedir'] )
534 env.Alias( "install", env['bindir'] )
535 env.Alias( "install", env['mandir'] )
536 if env['BUILD_MIXER'] == 'true':
537     env.Alias( "install", env['pypkgdir'] )
538
539 #
540 # shamelessly copied from the Ardour scons file
541 #
542
543 opt_flags = []
544 env['USE_SSE'] = 0
545
546 # guess at the platform, used to define compiler flags
547
548 config_cpu = 0
549 config_arch = 1
550 config_kernel = 2
551 config_os = 3
552 config = config_guess.split ("-")
553
554 needs_fPIC = False
555
556 #=== Begin Revised CXXFLAGS =========================================
557 def outputof(*cmd):
558     """Run a command without running a shell, return cmd's stdout
559     """
560     p = Popen(cmd, stdout=PIPE)
561     return p.communicate()[0]
562
563 def cpuinfo_kv():
564     """generator which reads lines from Linux /proc/cpuinfo and splits them
565     into key:value tokens and yields (key, value) tuple.
566     """
567     f = open('/proc/cpuinfo', 'r')
568     for line in f:
569         line = line.strip()
570         if line:
571             k,v = line.split(':', 1)
572             yield (k.strip(), v.strip())
573     f.close()
574
575
576 class CpuInfo (object):
577     """Collects information about the CPU, mainly from /proc/cpuinfo
578     """
579     def __init__(self):
580         self.sysname, self.hostname, self.release, self.version, self.machine = os.uname()
581         # general CPU architecture
582         self.is_x86 = self.machine in ('i686', 'x86_64') or \
583                       re.match("i[3-5]86", self.machine) or False
584         self.is_powerpc = self.machine in ('ppc64', 'ppc', 'powerpc', 'powerpc64')
585         #!!! probably not comprehensive
586         self.is_mips = self.machine == 'mips'
587         #!!! not a comprehensive list. uname -m on one android phone reports 'armv71'
588         # I have no other arm devices I can check
589         self.is_arm = self.machine in ('armv71', )
590
591         self.cpu_count = 0
592         if self.is_x86:
593             self.cpu_info_x86()
594         elif self.is_powerpc:
595             self.cpu_info_ppc()
596         elif self.is_mips:
597             self.cpu_info_mips()
598
599         # 64-bit (x86_64/AMD64/Intel64)
600         # Long Mode (x86-64: amd64, also known as Intel 64, i.e. 64-bit capable)
601         self.is_64bit = (self.is_x86 and 'lm' in self.x86_flags) or \
602                         (self.is_powerpc and '970' in self.ppc_type)
603
604         # Hardware virtualization capable: vmx (Intel), svm (AMD)
605         self.has_hwvirt = self.is_x86 and (
606                             (self.is_amd and 'svm' in self.x86_flags) or
607                             (self.is_intel and 'vmx' in self.x86_flags))
608
609         # Physical Address Extensions (support for more than 4GB of RAM)
610         self.has_pae = self.is_x86 and 'pae' in self.x86_flags
611
612
613     def cpu_info_x86(self):
614         "parse /proc/cpuinfo for x86 kernels"
615         for k,v in cpuinfo_kv():
616             if k == 'processor':
617                 self.cpu_count += 1
618                 if self.cpu_count > 1:
619                     # assume all CPUs are identical features, no need to
620                     # parse all of them
621                     continue
622             elif k == 'vendor_id': # AuthenticAMD, GenuineIntel
623                 self.vendor_id = v
624                 self.is_amd = v == 'AuthenticAMD'
625                 self.is_intel = v == 'GenuineIntel'
626             elif k == 'flags':
627                 self.x86_flags = v.split()
628             elif k == 'model name':
629                 self.model_name = v
630             elif k == 'cpu family':
631                 self.cpu_family = v
632             elif k == 'model':
633                 self.model = v
634
635     def cpu_info_ppc(self):
636         "parse /proc/cpuinfo for PowerPC kernels"
637         # http://en.wikipedia.org/wiki/List_of_PowerPC_processors
638         # PowerPC 7xx family
639         # PowerPC 740 and 750, 233-366 MHz
640         # 745/755, 300–466 MHz
641
642         # PowerPC G4 series
643         # 7400/7410 350 - 550 MHz, uses AltiVec, a SIMD extension of the original PPC specs
644         # 7450 micro-architecture family up to 1.5 GHz and 256 kB on-chip L2 cache and improved Altivec
645         # 7447/7457 micro-architecture family up to 1.8 GHz with 512 kB on-chip L2 cache
646         # 7448 micro-architecture family (1.5 GHz) in 90 nm with 1MB L2 cache and slightly
647         #  improved AltiVec (out of order instructions).
648         # 8640/8641/8640D/8641D with one or two e600 (Formerly known as G4) cores, 1MB L2 cache
649
650         # PowerPC G5 series
651         # 970 (2003), 64-bit, derived from POWER4, enhanced with VMX, 512 kB L2 cache, 1.4 – 2 GHz
652         # 970FX (2004), manufactured at 90 nm, 1.8 - 2.7 GHz
653         # 970GX (2006), manufactured at 90 nm, 1MB L2 cache/core, 1.2 - 2.5 GHz
654         # 970MP (2005), dual core, 1 MB L2 cache/core, 1.6 - 2.5 GHz
655         for k,v in cpuinfo_kv():
656             if k == 'processor':
657                 self.cpu_count += 1
658             elif k == 'cpu':
659                 self.is_altivec_supported = 'altivec' in v
660                 if ',' in v:
661                     ppc_type, x = v.split(',')
662                 else:
663                     ppc_type = v
664                 self.ppc_type = ppc_type.strip()
665         # older kernels might not have a 'processor' line
666         if self.cpu_count == 0:
667             self.cpu_count += 1
668
669
670     def cpu_info_mips(self):
671         "parse /proc/cpuinfo for MIPS kernels"
672         for k,v in cpuinfo_kv():
673             if k == 'processor':
674                 self.cpu_count += 1
675             elif k == 'cpu model':
676                 self.mips_cpu_model = v
677
678
679 def is_userspace_32bit(cpuinfo):
680     """Even if `uname -m` reports a 64-bit architecture, userspace could still
681     be 32-bit, such as Debian on powerpc64. This function tries to figure out
682     if userspace is 32-bit, i.e. we might need to pass '-m32' or '-m64' to gcc.
683     """
684     if not cpuinfo.is_64bit:
685         return True
686     # note that having a 64-bit CPU means nothing for these purposes. You could
687     # run a completely 32-bit system on a 64-bit capable CPU.
688     answer = None
689
690     # If setting DIST_TARGET to i686 on a 64-bit CPU to facilitate
691     # compilation of a multilib environment, force 32-bit.
692     if env['DIST_TARGET'] == 'i686':
693         return True
694
695     # Debian ppc64 returns machine 'ppc64', but userspace might be 32-bit
696     # We'll make an educated guess by examining a known executable
697     exe = '/bin/mount'
698     if os.path.isfile(exe):
699         #print('Found %s' % exe)
700         if os.path.islink(exe):
701             real_exe = os.path.join(os.path.dirname(exe), os.readlink(exe))
702             #print('%s is a symlink to %s' % (exe, real_exe))
703         else:
704             real_exe = exe
705         # presumably if a person is running this script, they should have
706         # a gcc toolchain installed...
707         x = outputof('objdump', '-Wi', real_exe)
708         # should emit a line that looks like this:
709         # /bin/mount:     file format elf32-i386
710         # or like this:
711         # /bin/mount:     file format elf64-x86-64
712         # or like this:
713         # /bin/mount:     file format elf32-powerpc
714         for line in x.split('\n'):
715             line = line.strip()
716             if line.startswith(real_exe):
717                 x, fmt = line.rsplit(None, 1)
718                 answer = 'elf32' in fmt
719                 break
720     else:
721         print('!!! Not found %s' % exe)
722     return answer
723
724
725 def cc_flags_x86(cpuinfo, enable_optimizations):
726     """add certain gcc -m flags based on CPU features
727     """
728     # See http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/i386-and-x86_002d64-Options.html
729     cc_opts = []
730     if cpuinfo.machine == 'i586':
731         cc_opts.append('-march=i586')
732     elif cpuinfo.machine == 'i686':
733         cc_opts.append('-march=i686')
734
735     if 'mmx' in cpuinfo.x86_flags:
736         cc_opts.append('-mmmx')
737
738     # map from proc/cpuinfo flags to gcc options
739     opt_flags = [
740             ('sse', ('-mfpmath=sse', '-msse')),
741             ('sse2', '-msse2'),
742             ('ssse3', '-mssse3'),
743             ('sse4', '-msse4'),
744             ('sse4_1', '-msse4.1'),
745             ('sse4_2', '-msse4.2'),
746             ('sse4a', '-msse4a'),
747             ('3dnow', '-m3dnow'),
748     ]
749     if enable_optimizations:
750         for flag, gccopt in opt_flags:
751             if flag in cpuinfo.x86_flags:
752                 if isinstance(gccopt, (tuple, list)):
753                     cc_opts.extend(gccopt)
754                 else:
755                     cc_opts.append(gccopt)
756     return cc_opts
757
758
759 def cc_flags_powerpc(cpuinfo, enable_optimizations):
760     """add certain gcc -m flags based on CPU model
761     """
762     cc_opts = []
763     if cpuinfo.is_altivec_supported:
764         cc_opts.append ('-maltivec')
765         cc_opts.append ('-mabi=altivec')
766
767     if re.match('74[0145][0578]A?', cpuinfo.ppc_type) is not None:
768         cc_opts.append ('-mcpu=7400')
769         cc_opts.append ('-mtune=7400')
770     elif re.match('750', cpuinfo.ppc_type) is not None:
771         cc_opts.append ('-mcpu=750')
772         cc_opts.append ('-mtune=750')
773     elif re.match('PPC970', cpuinfo.ppc_type) is not None:
774         cc_opts.append ('-mcpu=970')
775         cc_opts.append ('-mtune=970')
776     elif re.match('Cell Broadband Engine', cpuinfo.ppc_type) is not None:
777         cc_opts.append('-mcpu=cell')
778         cc_opts.append('-mtune=cell')
779     return cc_opts
780 #=== End Revised CXXFLAGS =========================================
781
782 # Autodetect
783 if env['DIST_TARGET'] == 'auto':
784     if re.search ("x86_64", config[config_cpu]) is not None:
785         env['DIST_TARGET'] = 'x86_64'
786     elif re.search("i[0-5]86", config[config_cpu]) is not None:
787         env['DIST_TARGET'] = 'i386'
788     elif re.search("i686", config[config_cpu]) is not None:
789         env['DIST_TARGET'] = 'i686'
790     elif re.search("powerpc64", config[config_cpu]) is not None:
791         env['DIST_TARGET'] = 'powerpc64'
792     elif re.search("powerpc", config[config_cpu]) is not None:
793         env['DIST_TARGET'] = 'powerpc'
794     else:
795         env['DIST_TARGET'] = config[config_cpu]
796     print("Detected DIST_TARGET = " + env['DIST_TARGET'])
797
798 #=== Begin Revised CXXFLAGS =========================================
799 # comment on DIST_TARGET up top implies it can be used for cross-compiling
800 # but that's not true because even if it is not 'auto' the original
801 # script still reads /proc/cpuinfo to determine gcc arch flags.
802 # This script does the same as the original. Needs to be fixed someday.
803 cpuinfo = CpuInfo()
804 if cpuinfo.is_x86:
805     opt_flags.extend(cc_flags_x86(cpuinfo, env['ENABLE_OPTIMIZATIONS']))
806 if cpuinfo.is_powerpc:
807     opt_flags.extend(cc_flags_powerpc(cpuinfo, env['ENABLE_OPTIMIZATIONS']))
808 if '-msse' in opt_flags:
809     env['USE_SSE'] = 1
810 if '-msse2' in opt_flags:
811     env['USE_SSE2'] = 1
812
813 if env['DETECT_USERSPACE_ENV']:
814     m32 = is_userspace_32bit(cpuinfo)
815     print('User space is %s' % (m32 and '32-bit' or '64-bit'))
816     if cpuinfo.is_powerpc:
817         if m32:
818             print("Doing a 32-bit PowerPC build for %s CPU" % cpuinfo.ppc_type)
819             machineflags = { 'CXXFLAGS' : ['-m32'] }
820         else:
821             print("Doing a 64-bit PowerPC build for %s CPU" % cpuinfo.ppc_type)
822             machineflags = { 'CXXFLAGS' : ['-m64'] }
823         env.MergeFlags( machineflags )
824     elif cpuinfo.is_x86:
825         if m32:
826             print("Doing a 32-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name))
827             if cpuinfo.machine == 'x86_64':
828                 machineflags = { 'CXXFLAGS' : ['-mx32'] }
829             else:
830                 machineflags = { 'CXXFLAGS' : ['-m32'] }
831         else:
832             print("Doing a 64-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name))
833             machineflags = { 'CXXFLAGS' : ['-m64'] }
834             needs_fPIC = True
835         env.MergeFlags( machineflags )
836 #=== End Revised CXXFLAGS =========================================
837
838
839 if needs_fPIC or ( 'COMPILE_FLAGS' in env and '-fPIC' in env['COMPILE_FLAGS'] ):
840     env.MergeFlags( "-fPIC" )
841
842 # end of processor-specific section
843 if env['ENABLE_OPTIMIZATIONS']:
844     opt_flags.extend (["-fomit-frame-pointer","-ffast-math","-funroll-loops"])
845     env.MergeFlags( opt_flags )
846     print("Doing an optimized build...")
847
848 env['REVISION'] = os.popen('svnversion .').read()[:-1]
849 # This may be as simple as '89' or as complex as '4123:4184M'.
850 # We'll just use the last bit.
851 env['REVISION'] = env['REVISION'].split(':')[-1]
852
853 # try to circumvent localized versions
854 if len(env['REVISION']) >= 5 and env['REVISION'][0:6] == 'export':
855     env['REVISION'] = ''
856
857 # avoid the 1.999.41- type of version for exported versions
858 if env['REVISION'] != '':
859         env['REVISIONSTRING'] = '-' + env['REVISION']
860 else:
861         env['REVISIONSTRING'] = ''
862
863 env['FFADO_API_VERSION'] = FFADO_API_VERSION
864
865 env['PACKAGE'] = "libffado"
866 env['VERSION'] = FFADO_VERSION
867 env['LIBVERSION'] = "1.0.0"
868
869 env['CONFIGDIR'] = "~/.ffado"
870 env['CACHEDIR'] = "~/.ffado"
871
872 env['USER_CONFIG_FILE'] = env['CONFIGDIR'] + "/configuration"
873 env['SYSTEM_CONFIG_FILE'] = env['SHAREDIR'] + "/configuration"
874
875 env['REGISTRATION_URL'] = "http://ffado.org/deviceregistration/register.php?action=register"
876
877 #
878 # To have the top_srcdir as the doxygen-script is used from auto*
879 #
880 env['top_srcdir'] = env.Dir( "." ).abspath
881
882 #
883 # Start building
884 #
885 env.ScanReplace( "config.h.in" )
886 env.ScanReplace( "config_debug.h.in" )
887 env.ScanReplace( "version.h.in" )
888
889 # ensure that the config.h is updated
890 env.Depends( "config.h", "SConstruct" )
891 env.Depends( "config.h", 'cache/options.cache' )
892
893 # update version.h whenever the version or SVN revision changes
894 env.Depends( "version.h", env.Value(env['REVISION']))
895 env.Depends( "version.h", env.Value(env['VERSION']))
896
897 env.Depends( "libffado.pc", "SConstruct" )
898 pkgconfig = env.ScanReplace( "libffado.pc.in" )
899 env.Install( env['libdir'] + '/pkgconfig', pkgconfig )
900
901 env.Install( env['sharedir'], 'configuration' )
902
903 subdirs=['src','libffado','support','doc']
904 if env['BUILD_TESTS']:
905     subdirs.append('tests')
906
907 env.SConscript( dirs=subdirs, exports="env" )
908
909 if 'debian' in COMMAND_LINE_TARGETS:
910     env.SConscript("deb/SConscript", exports="env")
911
912 # By default only src is built but all is cleaned
913 if not env.GetOption('clean'):
914     Default( 'src' )
915     Default( 'support' )
916     if env['BUILD_TESTS']:
917         Default( 'tests' )
918
919 #
920 # Deal with the DESTDIR vs. xdg-tools conflict (which is basicely that the
921 # xdg-tools can't deal with DESTDIR, so the packagers have to deal with this
922 # their own :-/
923 #
924 if len(env.destdir) > 0:
925     if not len( ARGUMENTS.get( "WILL_DEAL_WITH_XDG_MYSELF", "" ) ) > 0:
926         print("""
927 WARNING!
928 You are using the (packagers) option DESTDIR to install this package to a
929 different place than the real prefix. As the xdg-tools can't cope with
930 that, the .desktop-files are not installed by this build, you have to
931 deal with them your own.
932 (And you have to look into the SConstruct to learn how to disable this
933 message.)
934 """)
935 else:
936
937     def CleanAction( action ):
938         if env.GetOption( "clean" ):
939             env.Execute( action )
940
941     if env['BUILD_MIXER'] == 'true' and env['XDG_TOOLS']:
942         if not env.GetOption("clean"):
943             action = "install"
944         else:
945             action = "uninstall"
946         mixerdesktopaction = env.Action( "-xdg-desktop-menu %s support/xdg/ffado.org-ffadomixer.desktop" % action )
947         mixericonaction = env.Action( "-xdg-icon-resource %s --size 64 --novendor --context apps support/xdg/hi64-apps-ffado.png ffado" % action )
948         env.Command( "__xdgstuff1", None, mixerdesktopaction )
949         env.Command( "__xdgstuff2", None, mixericonaction )
950         env.Alias( "install", ["__xdgstuff1", "__xdgstuff2" ] )
951         CleanAction( mixerdesktopaction )
952         CleanAction( mixericonaction )
953
954 #
955 # Create a tags-file for easier emacs/vim-source-browsing
956 #  I don't know if the dependency is right...
957 #
958 findcommand = "find . \( -path \"*.h\" -o -path \"*.cpp\" -o -path \"*.c\" \) \! -path \"*.svn*\" \! -path \"./doc*\" \! -path \"./cache*\""
959 env.Command( "tags", "", findcommand + " |xargs ctags" )
960 env.Command( "TAGS", "", findcommand + " |xargs etags" )
961 env.AlwaysBuild( "tags", "TAGS" )
962 if 'NoCache' in dir(env):
963     env.NoCache( "tags", "TAGS" )
964
965 # Another convinience target
966 if env.GetOption( "clean" ):
967     env.Execute( "rm cache/objects -Rf" )
968
969 #
970 # vim: ts=4 sw=4 et
Note: See TracBrowser for help on using the browser.