root/trunk/libffado/SConstruct

Revision 2722, 37.6 kB (checked in by jwoithe, 6 years ago)

SConstruct: bump trunk version to 2.4.9999 for the next development round.

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