root/branches/2.4.x/libffado/SConstruct

Revision 2823, 38.7 kB (checked in by jwoithe, 1 year ago)

v2.4.x branch: update version number to 2.4.5.

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.5"
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.decode()
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 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, Hygon)
613         self.has_hwvirt = self.is_x86 and (
614                             ((self.is_amd or self.is_hygon) and
615                                 'svm' in self.x86_flags) or
616                             (self.is_intel and 'vmx' in self.x86_flags))
617
618         # Physical Address Extensions (support for more than 4GB of RAM)
619         self.has_pae = self.is_x86 and 'pae' in self.x86_flags
620
621
622     def cpu_info_x86(self):
623         "parse /proc/cpuinfo for x86 kernels"
624         for k,v in cpuinfo_kv():
625             if k == 'processor':
626                 self.cpu_count += 1
627                 if self.cpu_count > 1:
628                     # assume all CPUs are identical features, no need to
629                     # parse all of them
630                     continue
631             elif k == 'vendor_id': # AuthenticAMD, HygonGenuine, GenuineIntel
632                 self.vendor_id = v
633                 self.is_amd = v == 'AuthenticAMD'
634                 self.is_hygon = v == 'HygonGenuine'
635                 self.is_intel = v == 'GenuineIntel'
636             elif k == 'flags':
637                 self.x86_flags = v.split()
638             elif k == 'model name':
639                 self.model_name = v
640             elif k == 'cpu family':
641                 self.cpu_family = v
642             elif k == 'model':
643                 self.model = v
644
645     def cpu_info_ppc(self):
646         "parse /proc/cpuinfo for PowerPC kernels"
647         # http://en.wikipedia.org/wiki/List_of_PowerPC_processors
648         # PowerPC 7xx family
649         # PowerPC 740 and 750, 233-366 MHz
650         # 745/755, 300–466 MHz
651
652         # PowerPC G4 series
653         # 7400/7410 350 - 550 MHz, uses AltiVec, a SIMD extension of the original PPC specs
654         # 7450 micro-architecture family up to 1.5 GHz and 256 kB on-chip L2 cache and improved Altivec
655         # 7447/7457 micro-architecture family up to 1.8 GHz with 512 kB on-chip L2 cache
656         # 7448 micro-architecture family (1.5 GHz) in 90 nm with 1MB L2 cache and slightly
657         #  improved AltiVec (out of order instructions).
658         # 8640/8641/8640D/8641D with one or two e600 (Formerly known as G4) cores, 1MB L2 cache
659
660         # PowerPC G5 series
661         # 970 (2003), 64-bit, derived from POWER4, enhanced with VMX, 512 kB L2 cache, 1.4 – 2 GHz
662         # 970FX (2004), manufactured at 90 nm, 1.8 - 2.7 GHz
663         # 970GX (2006), manufactured at 90 nm, 1MB L2 cache/core, 1.2 - 2.5 GHz
664         # 970MP (2005), dual core, 1 MB L2 cache/core, 1.6 - 2.5 GHz
665         for k,v in cpuinfo_kv():
666             if k == 'processor':
667                 self.cpu_count += 1
668             elif k == 'cpu':
669                 self.is_altivec_supported = 'altivec' in v
670                 if ',' in v:
671                     ppc_type, x = v.split(',')
672                 else:
673                     ppc_type = v
674                 self.ppc_type = ppc_type.strip()
675         # older kernels might not have a 'processor' line
676         if self.cpu_count == 0:
677             self.cpu_count += 1
678
679
680     def cpu_info_mips(self):
681         "parse /proc/cpuinfo for MIPS kernels"
682         for k,v in cpuinfo_kv():
683             if k == 'processor':
684                 self.cpu_count += 1
685             elif k == 'cpu model':
686                 self.mips_cpu_model = v
687
688
689 def is_userspace_32bit(cpuinfo):
690     """Even if `uname -m` reports a 64-bit architecture, userspace could still
691     be 32-bit, such as Debian on powerpc64. This function tries to figure out
692     if userspace is 32-bit, i.e. we might need to pass '-m32' or '-m64' to gcc.
693     """
694     if not cpuinfo.is_64bit:
695         return True
696     # note that having a 64-bit CPU means nothing for these purposes. You could
697     # run a completely 32-bit system on a 64-bit capable CPU.
698     answer = None
699
700     # If setting DIST_TARGET to i686 on a 64-bit CPU to facilitate
701     # compilation of a multilib environment, force 32-bit.
702     if env['DIST_TARGET'] == 'i686':
703         return True
704
705     # Debian ppc64 returns machine 'ppc64', but userspace might be 32-bit
706     # We'll make an educated guess by examining a known executable
707     exe = '/bin/mount'
708     if os.path.isfile(exe):
709         #print('Found %s' % exe)
710         if os.path.islink(exe):
711             real_exe = os.path.join(os.path.dirname(exe), os.readlink(exe))
712             #print('%s is a symlink to %s' % (exe, real_exe))
713         else:
714             real_exe = exe
715         # presumably if a person is running this script, they should have
716         # a gcc toolchain installed...
717         x = check_output(('objdump', '-Wi', real_exe)).decode()
718         # should emit a line that looks like this:
719         # /bin/mount:     file format elf32-i386
720         # or like this:
721         # /bin/mount:     file format elf64-x86-64
722         # or like this:
723         # /bin/mount:     file format elf32-powerpc
724         for line in x.split('\n'):
725             line = line.strip()
726             if line.startswith(real_exe):
727                 x, fmt = line.rsplit(None, 1)
728                 answer = 'elf32' in fmt
729                 break
730     else:
731         print('!!! Not found %s' % exe)
732     return answer
733
734
735 def cc_flags_x86(cpuinfo, enable_optimizations):
736     """add certain gcc -m flags based on CPU features
737     """
738     # See http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/i386-and-x86_002d64-Options.html
739     cc_opts = []
740     if cpuinfo.machine == 'i586':
741         cc_opts.append('-march=i586')
742     elif cpuinfo.machine == 'i686':
743         cc_opts.append('-march=i686')
744
745     if 'mmx' in cpuinfo.x86_flags:
746         cc_opts.append('-mmmx')
747
748     # map from proc/cpuinfo flags to gcc options
749     opt_flags = [
750             ('sse', ('-mfpmath=sse', '-msse')),
751             ('sse2', '-msse2'),
752             ('ssse3', '-mssse3'),
753             ('sse4', '-msse4'),
754             ('sse4_1', '-msse4.1'),
755             ('sse4_2', '-msse4.2'),
756             ('sse4a', '-msse4a'),
757             ('3dnow', '-m3dnow'),
758     ]
759     if enable_optimizations:
760         for flag, gccopt in opt_flags:
761             if flag in cpuinfo.x86_flags:
762                 if isinstance(gccopt, (tuple, list)):
763                     cc_opts.extend(gccopt)
764                 else:
765                     cc_opts.append(gccopt)
766     return cc_opts
767
768
769 def cc_flags_powerpc(cpuinfo, enable_optimizations):
770     """add certain gcc -m flags based on CPU model
771     """
772     cc_opts = []
773     if cpuinfo.is_altivec_supported:
774         cc_opts.append ('-maltivec')
775         cc_opts.append ('-mabi=altivec')
776
777     if re.match('74[0145][0578]A?', cpuinfo.ppc_type) is not None:
778         cc_opts.append ('-mcpu=7400')
779         cc_opts.append ('-mtune=7400')
780     elif re.match('750', cpuinfo.ppc_type) is not None:
781         cc_opts.append ('-mcpu=750')
782         cc_opts.append ('-mtune=750')
783     elif re.match('PPC970', cpuinfo.ppc_type) is not None:
784         cc_opts.append ('-mcpu=970')
785         cc_opts.append ('-mtune=970')
786     elif re.match('Cell Broadband Engine', cpuinfo.ppc_type) is not None:
787         cc_opts.append('-mcpu=cell')
788         cc_opts.append('-mtune=cell')
789     return cc_opts
790 #=== End Revised CXXFLAGS =========================================
791
792 # Autodetect
793 if env['DIST_TARGET'] == 'auto':
794     if re.search ("x86_64", config[config_cpu]) is not None:
795         env['DIST_TARGET'] = 'x86_64'
796     elif re.search("i[0-5]86", config[config_cpu]) is not None:
797         env['DIST_TARGET'] = 'i386'
798     elif re.search("i686", config[config_cpu]) is not None:
799         env['DIST_TARGET'] = 'i686'
800     elif re.search("powerpc64", config[config_cpu]) is not None:
801         env['DIST_TARGET'] = 'powerpc64'
802     elif re.search("powerpc", config[config_cpu]) is not None:
803         env['DIST_TARGET'] = 'powerpc'
804     else:
805         env['DIST_TARGET'] = config[config_cpu]
806     print("Detected DIST_TARGET = " + env['DIST_TARGET'])
807
808 #=== Begin Revised CXXFLAGS =========================================
809 # comment on DIST_TARGET up top implies it can be used for cross-compiling
810 # but that's not true because even if it is not 'auto' the original
811 # script still reads /proc/cpuinfo to determine gcc arch flags.
812 # This script does the same as the original. Needs to be fixed someday.
813 cpuinfo = CpuInfo()
814 if cpuinfo.is_x86:
815     opt_flags.extend(cc_flags_x86(cpuinfo, env['ENABLE_OPTIMIZATIONS']))
816 if cpuinfo.is_powerpc:
817     opt_flags.extend(cc_flags_powerpc(cpuinfo, env['ENABLE_OPTIMIZATIONS']))
818 if '-msse' in opt_flags:
819     env['USE_SSE'] = 1
820 if '-msse2' in opt_flags:
821     env['USE_SSE2'] = 1
822
823 if env['DETECT_USERSPACE_ENV']:
824     m32 = is_userspace_32bit(cpuinfo)
825     print('User space is %s' % (m32 and '32-bit' or '64-bit'))
826     if cpuinfo.is_powerpc:
827         if m32:
828             print("Doing a 32-bit PowerPC build for %s CPU" % cpuinfo.ppc_type)
829             machineflags = { 'CXXFLAGS' : ['-m32'] }
830         else:
831             print("Doing a 64-bit PowerPC build for %s CPU" % cpuinfo.ppc_type)
832             machineflags = { 'CXXFLAGS' : ['-m64'] }
833         env.MergeFlags( machineflags )
834     elif cpuinfo.is_x86:
835         if m32:
836             print("Doing a 32-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name))
837             if cpuinfo.machine == 'x86_64':
838                 machineflags = { 'CXXFLAGS' : ['-mx32'] }
839             else:
840                 machineflags = { 'CXXFLAGS' : ['-m32'] }
841         else:
842             print("Doing a 64-bit %s build for %s" % (cpuinfo.machine, cpuinfo.model_name))
843             machineflags = { 'CXXFLAGS' : ['-m64'] }
844             needs_fPIC = True
845         env.MergeFlags( machineflags )
846 #=== End Revised CXXFLAGS =========================================
847
848
849 if needs_fPIC or ( 'COMPILE_FLAGS' in env and '-fPIC' in env['COMPILE_FLAGS'] ):
850     env.MergeFlags( "-fPIC" )
851
852 # end of processor-specific section
853 if env['ENABLE_OPTIMIZATIONS']:
854     opt_flags.extend (["-fomit-frame-pointer","-ffast-math","-funroll-loops"])
855     env.MergeFlags( opt_flags )
856     print("Doing an optimized build...")
857
858 try:
859     env['REVISION'] = check_output(('svnversion', '.',)).decode().rstrip()
860 except:
861     env['REVISION'] = ''
862
863 # This may be as simple as '89' or as complex as '4123:4184M'.
864 # We'll just use the last bit.
865 env['REVISION'] = env['REVISION'].split(':')[-1]
866
867 # Assume an unversioned directory indicates a release.
868 if env['REVISION'].startswith ('Unversioned'):
869     env['REVISION'] = ''
870
871 # try to circumvent localized versions
872 if env['REVISION'].startswith ('export'):
873     env['REVISION'] = ''
874
875 # avoid the 1.999.41- type of version for exported versions
876 if env['REVISION'] != '':
877     env['REVISIONSTRING'] = '-' + env['REVISION']
878 else:
879     env['REVISIONSTRING'] = ''
880
881 env['FFADO_API_VERSION'] = FFADO_API_VERSION
882
883 env['PACKAGE'] = "libffado"
884 env['VERSION'] = FFADO_VERSION
885 env['LIBVERSION'] = "1.0.0"
886
887 env['CONFIGDIR'] = "~/.ffado"
888 env['CACHEDIR'] = "~/.ffado"
889
890 env['USER_CONFIG_FILE'] = env['CONFIGDIR'] + "/configuration"
891 env['SYSTEM_CONFIG_FILE'] = env['SHAREDIR'] + "/configuration"
892
893 env['REGISTRATION_URL'] = "http://ffado.org/deviceregistration/register.php?action=register"
894
895 #
896 # To have the top_srcdir as the doxygen-script is used from auto*
897 #
898 env['top_srcdir'] = env.Dir( "." ).abspath
899
900 #
901 # Start building
902 #
903 env.ScanReplace( "config.h.in" )
904 env.ScanReplace( "config_debug.h.in" )
905 env.ScanReplace( "version.h.in" )
906
907 # ensure that the config.h is updated
908 env.Depends( "config.h", "SConstruct" )
909 env.Depends( "config.h", 'cache/options.cache' )
910
911 # update version.h whenever the version or SVN revision changes
912 env.Depends( "version.h", env.Value(env['REVISION']))
913 env.Depends( "version.h", env.Value(env['VERSION']))
914
915 env.Depends( "libffado.pc", "SConstruct" )
916 pkgconfig = env.ScanReplace( "libffado.pc.in" )
917 env.Install( env['libdir'] + '/pkgconfig', pkgconfig )
918
919 env.Install( env['sharedir'], 'configuration' )
920
921 subdirs=['src','libffado','support','doc']
922 if env['BUILD_TESTS']:
923     subdirs.append('tests')
924
925 env.SConscript( dirs=subdirs, exports="env" )
926
927 if 'debian' in COMMAND_LINE_TARGETS:
928     env.SConscript("deb/SConscript", exports="env")
929
930 # By default only src is built but all is cleaned
931 if not env.GetOption('clean'):
932     Default( 'src' )
933     Default( 'support' )
934     if env['BUILD_TESTS']:
935         Default( 'tests' )
936     if env['BUILD_DOC'] != 'none':
937         Default( 'doc' )
938
939 env.Install( env['metainfodir'], "support/xdg/ffado-mixer.appdata.xml" )
940
941 #
942 # Deal with the DESTDIR vs. xdg-tools conflict (which is basicely that the
943 # xdg-tools can't deal with DESTDIR, so the packagers have to deal with this
944 # their own :-/
945 #
946 if len(env.destdir) > 0:
947     if not len( ARGUMENTS.get( "WILL_DEAL_WITH_XDG_MYSELF", "" ) ) > 0:
948         print("""
949 WARNING!
950 You are using the (packagers) option DESTDIR to install this package to a
951 different place than the real prefix. As the xdg-tools can't cope with
952 that, the .desktop-files are not installed by this build, you have to
953 deal with them your own.
954 (And you have to look into the SConstruct to learn how to disable this
955 message.)
956 """)
957 else:
958
959     def CleanAction( action ):
960         if env.GetOption( "clean" ):
961             env.Execute( action )
962
963     if env['BUILD_MIXER'] == 'true' and env['XDG_TOOLS']:
964         if not env.GetOption("clean"):
965             action = "install"
966         else:
967             action = "uninstall"
968         mixerdesktopaction = env.Action( "-xdg-desktop-menu %s support/xdg/ffado.org-ffadomixer.desktop" % action )
969         mixericonaction = env.Action( "-xdg-icon-resource %s --size 64 --novendor --context apps support/xdg/hi64-apps-ffado.png ffado" % action )
970         env.Command( "__xdgstuff1", None, mixerdesktopaction )
971         env.Command( "__xdgstuff2", None, mixericonaction )
972         env.Alias( "install", ["__xdgstuff1", "__xdgstuff2" ] )
973         CleanAction( mixerdesktopaction )
974         CleanAction( mixericonaction )
975
976 #
977 # Create a tags-file for easier emacs/vim-source-browsing
978 #  I don't know if the dependency is right...
979 #
980 findcommand = "find . \( -path \"*.h\" -o -path \"*.cpp\" -o -path \"*.c\" \) \! -path \"*.svn*\" \! -path \"./doc*\" \! -path \"./cache*\""
981 env.Command( "tags", "", findcommand + " |xargs ctags" )
982 env.Command( "TAGS", "", findcommand + " |xargs etags" )
983 env.AlwaysBuild( "tags", "TAGS" )
984 if 'NoCache' in dir(env):
985     env.NoCache( "tags", "TAGS" )
986
987 # Another convinience target
988 if env.GetOption( "clean" ):
989     env.Execute( "rm cache/objects -Rf" )
990
991 #
992 # vim: ts=4 sw=4 et
Note: See TracBrowser for help on using the browser.