root/trunk/libffado/SConstruct

Revision 2767, 38.6 kB (checked in by jwoithe, 6 years ago)

SConstruct: allow for the absence of svnversion during build.

Contrary to earlier assumptions, check_output() does not deal gracefully
with the absence of the target binary. Trap exceptions raised by
check_output() when calling svnversion so FFADO can still be built on
systems where subversion is not installed.

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