Merged with trunk R920.
[ardour.git] / SConstruct
1 # -*- python -*-
2
3 import os
4 import sys
5 import re
6 import shutil
7 import glob
8 import errno
9 import time
10 import platform
11 import string
12 from sets import Set
13 import SCons.Node.FS
14
15 SConsignFile()
16 EnsureSConsVersion(0, 96)
17
18 version = '2.0beta2'
19
20 subst_dict = { }
21
22 #
23 # Command-line options
24 #
25
26 opts = Options('scache.conf')
27 opts.AddOptions(
28   ('ARCH', 'Set architecture-specific compilation flags by hand (all flags as 1 argument)',''),
29     BoolOption('AUDIOUNITS', 'Compile with Apple\'s AudioUnit library. (experimental)', 0),
30     BoolOption('COREAUDIO', 'Compile with Apple\'s CoreAudio library', 0),
31     BoolOption('DEBUG', 'Set to build with debugging information and no optimizations', 0),
32     PathOption('DESTDIR', 'Set the intermediate install "prefix"', '/'),
33     EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'none' ), ignorecase=2),
34     BoolOption('DMALLOC', 'Compile and link using the dmalloc library', 0),
35     BoolOption('EXTRA_WARN', 'Compile with -Wextra, -ansi, and -pedantic.  Might break compilation.  For pedants', 0),
36     BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0),
37     BoolOption('FPU_OPTIMIZATION', 'Build runtime checked assembler code', 1),
38     BoolOption('LIBLO', 'Compile with support for liblo library', 1),
39     BoolOption('NLS', 'Set to turn on i18n support', 1),
40     PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'),
41     BoolOption('SURFACES', 'Build support for control surfaces', 0),
42     BoolOption('SYSLIBS', 'USE AT YOUR OWN RISK: CANCELS ALL SUPPORT FROM ARDOUR AUTHORS: Use existing system versions of various libraries instead of internal ones', 0),
43     BoolOption('VERSIONED', 'Add version information to ardour/gtk executable name inside the build directory', 0),
44     BoolOption('VST', 'Compile with support for VST', 0)
45 )
46
47 #----------------------------------------------------------------------
48 # a handy helper that provides a way to merge compile/link information
49 # from multiple different "environments"
50 #----------------------------------------------------------------------
51 #
52 class LibraryInfo(Environment):
53     def __init__(self,*args,**kw):
54         Environment.__init__ (self,*args,**kw)
55     
56     def Merge (self,others):
57         for other in others:
58             self.Append (LIBS = other.get ('LIBS',[]))
59             self.Append (LIBPATH = other.get ('LIBPATH', []))
60             self.Append (CPPPATH = other.get('CPPPATH', []))
61             self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
62         self.Replace(LIBPATH = list(Set(self.get('LIBPATH', []))))
63         self.Replace(CPPPATH = list(Set(self.get('CPPPATH',[]))))
64         #doing LINKFLAGS breaks -framework
65         #doing LIBS break link order dependency
66     
67     def ENV_update(self, src_ENV):
68         for k in src_ENV.keys():
69             if k in self['ENV'].keys() and k in [ 'PATH', 'LD_LIBRARY_PATH',
70                                                   'LIB', 'INCLUDE' ]:
71                 self['ENV'][k]=SCons.Util.AppendPath(self['ENV'][k], src_ENV[k])
72             else:
73                 self['ENV'][k]=src_ENV[k]
74
75 env = LibraryInfo (options = opts,
76                    CPPPATH = [ '.' ],
77                    VERSION = version,
78                    TARBALL='ardour-' + version + '.tar.bz2',
79                    DISTFILES = [ ],
80                    DISTTREE  = '#ardour-' + version,
81                    DISTCHECKDIR = '#ardour-' + version + '/check'
82                    )
83
84 env.ENV_update(os.environ)
85
86 #----------------------------------------------------------------------
87 # Builders
88 #----------------------------------------------------------------------
89
90 # Handy subst-in-file builder
91 #
92
93 def do_subst_in_file(targetfile, sourcefile, dict):
94     """Replace all instances of the keys of dict with their values.
95     For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
96     then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
97     """
98     try:
99         f = open(sourcefile, 'rb')
100         contents = f.read()
101         f.close()
102     except:
103         raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
104     for (k,v) in dict.items():
105         contents = re.sub(k, v, contents)
106     try:
107         f = open(targetfile, 'wb')
108         f.write(contents)
109         f.close()
110     except:
111         raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
112     return 0 # success
113
114 def subst_in_file(target, source, env):
115     if not env.has_key('SUBST_DICT'):
116         raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
117     d = dict(env['SUBST_DICT']) # copy it
118     for (k,v) in d.items():
119         if callable(v):
120             d[k] = env.subst(v())
121         elif SCons.Util.is_String(v):
122             d[k]=env.subst(v)
123         else:
124             raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
125     for (t,s) in zip(target, source):
126         return do_subst_in_file(str(t), str(s), d)
127
128 def subst_in_file_string(target, source, env):
129     """This is what gets printed on the console."""
130     return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
131                       for (t,s) in zip(target, source)])
132
133 def subst_emitter(target, source, env):
134     """Add dependency from substituted SUBST_DICT to target.
135     Returns original target, source tuple unchanged.
136     """
137     d = env['SUBST_DICT'].copy() # copy it
138     for (k,v) in d.items():
139         if callable(v):
140             d[k] = env.subst(v())
141         elif SCons.Util.is_String(v):
142             d[k]=env.subst(v)
143     Depends(target, SCons.Node.Python.Value(d))
144     # Depends(target, source) # this doesn't help the install-sapphire-linux.sh problem
145     return target, source
146
147 subst_action = Action (subst_in_file, subst_in_file_string)
148 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
149
150 #
151 # internationalization
152 #
153
154 # po_builder: builder function to copy po files to the parent directory while updating them
155 #
156 # first source:  .po file
157 # second source: .pot file
158 #
159
160 def po_builder(target,source,env):
161     os.spawnvp (os.P_WAIT, 'cp', ['cp', str(source[0]), str(target[0])])
162     args = [ 'msgmerge',
163              '--update',
164              str(target[0]),
165              str(source[1])
166              ]
167     print 'Updating ' + str(target[0])
168     return os.spawnvp (os.P_WAIT, 'msgmerge', args)
169
170 po_bld = Builder (action = po_builder)
171 env.Append(BUILDERS = {'PoBuild' : po_bld})
172
173 # mo_builder: builder function for (binary) message catalogs (.mo)
174 #
175 # first source:  .po file
176 #
177
178 def mo_builder(target,source,env):
179     args = [ 'msgfmt',
180              '-c',
181              '-o',
182              target[0].get_path(),
183              source[0].get_path()
184              ]
185     return os.spawnvp (os.P_WAIT, 'msgfmt', args)
186
187 mo_bld = Builder (action = mo_builder)
188 env.Append(BUILDERS = {'MoBuild' : mo_bld})
189
190 # pot_builder: builder function for message templates (.pot)
191 #
192 # source: list of C/C++ etc. files to extract messages from
193 #
194
195 def pot_builder(target,source,env):
196     args = [ 'xgettext',
197              '--keyword=_',
198              '--keyword=N_',
199              '--from-code=UTF-8',
200              '-o', target[0].get_path(),
201              "--default-domain=" + env['PACKAGE'],
202              '--copyright-holder="Paul Davis"' ]
203     args += [ src.get_path() for src in source ]
204     
205     return os.spawnvp (os.P_WAIT, 'xgettext', args)
206
207 pot_bld = Builder (action = pot_builder)
208 env.Append(BUILDERS = {'PotBuild' : pot_bld})
209
210 #
211 # utility function, not a builder
212 #
213
214 def i18n (buildenv, sources, installenv):
215     domain = buildenv['PACKAGE']
216     potfile = buildenv['POTFILE']
217     
218     installenv.Alias ('potupdate', buildenv.PotBuild (potfile, sources))
219     
220     p_oze = [ os.path.basename (po) for po in glob.glob ('po/*.po') ]
221     languages = [ po.replace ('.po', '') for po in p_oze ]
222     
223     for po_file in p_oze:
224         buildenv.PoBuild(po_file, ['po/'+po_file, potfile])
225         mo_file = po_file.replace (".po", ".mo")
226         installenv.Alias ('install', buildenv.MoBuild (mo_file, po_file))
227     
228     for lang in languages:
229         modir = (os.path.join (install_prefix, 'share/locale/' + lang + '/LC_MESSAGES/'))
230         moname = domain + '.mo'
231         installenv.Alias('install', installenv.InstallAs (os.path.join (modir, moname), lang + '.mo'))
232
233 #
234 # A generic builder for version.cc files
235 #
236 # note: requires that DOMAIN, MAJOR, MINOR, MICRO are set in the construction environment
237 # note: assumes one source files, the header that declares the version variables
238 #
239 def version_builder (target, source, env):
240    text  = "int " + env['DOMAIN'] + "_major_version = " + str (env['MAJOR']) + ";\n"
241    text += "int " + env['DOMAIN'] + "_minor_version = " + str (env['MINOR']) + ";\n"
242    text += "int " + env['DOMAIN'] + "_micro_version = " + str (env['MICRO']) + ";\n"
243    
244    try:
245       o = file (target[0].get_path(), 'w')
246       o.write (text)
247       o.close ()
248    except IOError:
249       print "Could not open", target[0].get_path(), " for writing\n"
250       sys.exit (-1)
251    
252    text  = "#ifndef __" + env['DOMAIN'] + "_version_h__\n"
253    text += "#define __" + env['DOMAIN'] + "_version_h__\n"
254    text += "extern int " + env['DOMAIN'] + "_major_version;\n"
255    text += "extern int " + env['DOMAIN'] + "_minor_version;\n"
256    text += "extern int " + env['DOMAIN'] + "_micro_version;\n"
257    text += "#endif /* __" + env['DOMAIN'] + "_version_h__ */\n"
258    
259    try:
260       o = file (target[1].get_path(), 'w')
261       o.write (text)
262       o.close ();
263    except IOError:
264       print "Could not open", target[1].get_path(), " for writing\n"
265       sys.exit (-1)
266    
267    return None
268
269 version_bld = Builder (action = version_builder)
270 env.Append (BUILDERS = {'VersionBuild' : version_bld})
271
272 #
273 # a builder that makes a hard link from the 'source' executable to a name with
274 # a "build ID" based on the most recent CVS activity that might be reasonably
275 # related to version activity. this relies on the idea that the SConscript
276 # file that builds the executable is updated with new version info and committed
277 # to the source code repository whenever things change.
278 #
279
280 def versioned_builder(target,source,env):
281     # build ID is composed of a representation of the date of the last CVS transaction
282     # for this (SConscript) file
283     
284     try:
285         o = file (source[0].get_dir().get_path() +  '/CVS/Entries', "r")
286     except IOError:
287         print "Could not CVS/Entries for reading"
288         return -1
289     
290     last_date = ""
291     lines = o.readlines()
292     for line in lines:
293         if line[0:12] == '/SConscript/':
294             parts = line.split ("/")
295             last_date = parts[3]
296             break
297     o.close ()
298     
299     if last_date == "":
300         print "No SConscript CVS update info found - versioned executable cannot be built"
301         return -1
302     
303     tag = time.strftime ('%Y%M%d%H%m', time.strptime (last_date))
304     print "The current build ID is " + tag
305     
306     tagged_executable = source[0].get_path() + '-' + tag
307     
308     if os.path.exists (tagged_executable):
309         print "Replacing existing executable with the same build tag."
310         os.unlink (tagged_executable)
311     
312     return os.link (source[0].get_path(), tagged_executable)
313
314 verbuild = Builder (action = versioned_builder)
315 env.Append (BUILDERS = {'VersionedExecutable' : verbuild})
316
317 #
318 # source tar file builder
319 #
320
321 def distcopy (target, source, env):
322     treedir = str (target[0])
323     
324     try:
325         os.mkdir (treedir)
326     except OSError, (errnum, strerror):
327         if errnum != errno.EEXIST:
328             print 'mkdir ', treedir, ':', strerror
329     
330     cmd = 'tar cf - '
331     #
332     # we don't know what characters might be in the file names
333     # so quote them all before passing them to the shell
334     #
335     all_files = ([ str(s) for s in source ])
336     cmd += " ".join ([ "'%s'" % quoted for quoted in all_files])
337     cmd += ' | (cd ' + treedir + ' && tar xf -)'
338     p = os.popen (cmd)
339     return p.close ()
340
341 def tarballer (target, source, env):
342     cmd = 'tar -jcf ' + str (target[0]) +  ' ' + str(source[0]) + "  --exclude '*~'"
343     print 'running ', cmd, ' ... '
344     p = os.popen (cmd)
345     return p.close ()
346
347 dist_bld = Builder (action = distcopy,
348                     target_factory = SCons.Node.FS.default_fs.Entry,
349                     source_factory = SCons.Node.FS.default_fs.Entry,
350                     multi = 1)
351
352 tarball_bld = Builder (action = tarballer,
353                        target_factory = SCons.Node.FS.default_fs.Entry,
354                        source_factory = SCons.Node.FS.default_fs.Entry)
355
356 env.Append (BUILDERS = {'Distribute' : dist_bld})
357 env.Append (BUILDERS = {'Tarball' : tarball_bld})
358
359 #
360 # Make sure they know what they are doing
361 #
362
363 if env['VST']:
364     sys.stdout.write ("Are you building Ardour for personal use (rather than distributiont to others)? [no]: ")
365     answer = sys.stdin.readline ()
366     answer = answer.rstrip().strip()
367     if answer != "yes" and answer != "y":
368         print 'You cannot build Ardour with VST support for distribution to others.\nIt is a violation of several different licenses. VST support disabled.'
369         env['VST'] = 0;
370     else:
371         print "OK, VST support will be enabled"
372
373
374 # ----------------------------------------------------------------------
375 # Construction environment setup
376 # ----------------------------------------------------------------------
377
378 libraries = { }
379
380 libraries['core'] = LibraryInfo (CCFLAGS = '-Ilibs')
381
382 #libraries['sndfile'] = LibraryInfo()
383 #libraries['sndfile'].ParseConfig('pkg-config --cflags --libs sndfile')
384
385 libraries['lrdf'] = LibraryInfo()
386 libraries['lrdf'].ParseConfig('pkg-config --cflags --libs lrdf')
387
388 libraries['raptor'] = LibraryInfo()
389 libraries['raptor'].ParseConfig('pkg-config --cflags --libs raptor')
390
391 libraries['samplerate'] = LibraryInfo()
392 libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate')
393
394 if env['FFT_ANALYSIS']:
395         libraries['fftw3f'] = LibraryInfo()
396         libraries['fftw3f'].ParseConfig('pkg-config --cflags --libs fftw3f')
397
398 libraries['jack'] = LibraryInfo()
399 libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
400
401 libraries['xml'] = LibraryInfo()
402 libraries['xml'].ParseConfig('pkg-config --cflags --libs libxml-2.0')
403
404 libraries['xslt'] = LibraryInfo()
405 libraries['xslt'].ParseConfig('pkg-config --cflags --libs libxslt')
406
407 libraries['glib2'] = LibraryInfo()
408 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs glib-2.0')
409 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gobject-2.0')
410 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gmodule-2.0')
411 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gthread-2.0')
412
413 libraries['gtk2'] = LibraryInfo()
414 libraries['gtk2'].ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
415
416 libraries['pango'] = LibraryInfo()
417 libraries['pango'].ParseConfig ('pkg-config --cflags --libs pango')
418
419 libraries['libgnomecanvas2'] = LibraryInfo()
420 libraries['libgnomecanvas2'].ParseConfig ('pkg-config --cflags --libs libgnomecanvas-2.0')
421
422 #libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
423
424 # The Ardour Control Protocol Library
425
426 libraries['ardour_cp'] = LibraryInfo (LIBS='ardour_cp', LIBPATH='#libs/surfaces/control_protocol',
427                                       CPPPATH='#libs/surfaces/control_protocol')
428
429 # The Ardour backend/engine
430
431 libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
432 libraries['midi++2'] = LibraryInfo (LIBS='midi++', LIBPATH='#libs/midi++2', CPPPATH='#libs/midi++2')
433 libraries['pbd']    = LibraryInfo (LIBS='pbd', LIBPATH='#libs/pbd', CPPPATH='#libs/pbd')
434 libraries['gtkmm2ext'] = LibraryInfo (LIBS='gtkmm2ext', LIBPATH='#libs/gtkmm2ext', CPPPATH='#libs/gtkmm2ext')
435
436 #
437 # Check for libusb
438
439 libraries['usb'] = LibraryInfo ()
440
441 conf = Configure (libraries['usb'])
442 if conf.CheckLib ('usb', 'usb_interrupt_write'):
443     have_libusb = True
444 else:
445     have_libusb = False
446
447 libraries['usb'] = conf.Finish ()
448
449 #
450 # Check for FLAC
451
452 libraries['flac'] = LibraryInfo ()
453
454 conf = Configure (libraries['flac'])
455 conf.CheckLib ('FLAC', 'FLAC__stream_decoder_new', language='CXX')
456 libraries['flac'] = conf.Finish ()
457
458 # or if that fails...
459 #libraries['flac']    = LibraryInfo (LIBS='FLAC')
460
461 # boost (we don't link against boost, just use some header files)
462
463 libraries['boost'] = LibraryInfo ()
464 conf = Configure (libraries['boost'])
465 if conf.CheckHeader ('boost/shared_ptr.hpp', language='CXX') == 0:
466         print "Boost header files do not appear to be installed."
467         sys.exit (1)
468     
469 libraries['boost'] = conf.Finish ()
470
471 #
472 # Check for liblo
473
474 if env['LIBLO']:
475     libraries['lo'] = LibraryInfo ()
476     
477     conf = Configure (libraries['lo'])
478     if conf.CheckLib ('lo', 'lo_server_new') == False:
479         print "liblo does not appear to be installed."
480         sys.exit (1)
481     
482     libraries['lo'] = conf.Finish ()
483
484 #
485 # Check for dmalloc
486
487 libraries['dmalloc'] = LibraryInfo ()
488
489 #
490 # look for the threaded version
491 #
492
493 conf = Configure (libraries['dmalloc'])
494 if conf.CheckLib ('dmallocth', 'dmalloc_shutdown'):
495     have_libdmalloc = True
496 else:
497     have_libdmalloc = False
498
499 libraries['dmalloc'] = conf.Finish ()
500
501 #
502 # Audio/MIDI library (needed for MIDI, since audio is all handled via JACK)
503 #
504
505 conf = Configure(env)
506 if conf.CheckCHeader('jack/midiport.h'):
507     libraries['sysmidi'] = LibraryInfo (LIBS='jack')
508     env['SYSMIDI'] = 'JACK MIDI'
509     subst_dict['%MIDITAG%'] = "control"
510     subst_dict['%MIDITYPE%'] = "jack"
511     print "Using JACK MIDI"
512 elif conf.CheckCHeader('alsa/asoundlib.h'):
513     libraries['sysmidi'] = LibraryInfo (LIBS='asound')
514     env['SYSMIDI'] = 'ALSA Sequencer'
515     subst_dict['%MIDITAG%'] = "seq"
516     subst_dict['%MIDITYPE%'] = "alsa/sequencer"
517     print "Using ALSA MIDI"
518 elif conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'):
519     # this line is needed because scons can't handle -framework in ParseConfig() yet.
520     libraries['sysmidi'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -bind_at_load')
521     env['SYSMIDI'] = 'CoreMIDI'
522     subst_dict['%MIDITAG%'] = "ardour"
523     subst_dict['%MIDITYPE%'] = "coremidi"
524     print "Using CoreMIDI"
525 else:
526     print "It appears you don't have the required MIDI libraries installed."
527     sys.exit (1)
528
529 env = conf.Finish()
530
531 if env['SYSLIBS']:
532     
533     libraries['sigc2'] = LibraryInfo()
534     libraries['sigc2'].ParseConfig('pkg-config --cflags --libs sigc++-2.0')
535     libraries['glibmm2'] = LibraryInfo()
536     libraries['glibmm2'].ParseConfig('pkg-config --cflags --libs glibmm-2.4')
537     libraries['gdkmm2'] = LibraryInfo()
538     libraries['gdkmm2'].ParseConfig ('pkg-config --cflags --libs gdkmm-2.4')
539     libraries['gtkmm2'] = LibraryInfo()
540     libraries['gtkmm2'].ParseConfig ('pkg-config --cflags --libs gtkmm-2.4')
541     libraries['atkmm'] = LibraryInfo()
542     libraries['atkmm'].ParseConfig ('pkg-config --cflags --libs atkmm-1.6')
543     libraries['pangomm'] = LibraryInfo()
544     libraries['pangomm'].ParseConfig ('pkg-config --cflags --libs pangomm-1.4')
545     libraries['libgnomecanvasmm'] = LibraryInfo()
546     libraries['libgnomecanvasmm'].ParseConfig ('pkg-config --cflags --libs libgnomecanvasmm-2.6')
547
548 #
549 # cannot use system one for the time being
550 #
551     
552     libraries['sndfile'] = LibraryInfo(LIBS='libsndfile',
553                                     LIBPATH='#libs/libsndfile',
554                                     CPPPATH=['#libs/libsndfile', '#libs/libsndfile/src'])
555
556 #    libraries['libglademm'] = LibraryInfo()
557 #    libraries['libglademm'].ParseConfig ('pkg-config --cflags --libs libglademm-2.4')
558
559 #    libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
560     libraries['soundtouch'] = LibraryInfo()
561     libraries['soundtouch'].ParseConfig ('pkg-config --cflags --libs libSoundTouch')
562
563     libraries['appleutility'] = LibraryInfo(LIBS='libappleutility',
564                                             LIBPATH='#libs/appleutility',
565                                             CPPPATH='#libs/appleutility')
566     
567     coredirs = [
568         'templates'
569     ]
570     
571     subdirs = [
572         'libs/libsndfile',
573         'libs/pbd',
574         'libs/midi++2',
575         'libs/ardour'
576         ]
577     
578     if env['VST']:
579         subdirs = ['libs/fst'] + subdirs + ['vst']
580
581     if env['COREAUDIO']:
582         subdirs = subdirs + ['libs/appleutility']
583     
584     gtk_subdirs = [
585 #        'libs/flowcanvas',
586         'libs/gtkmm2ext',
587         'gtk2_ardour'
588         ]
589
590 else:
591     libraries['sigc2'] = LibraryInfo(LIBS='sigc++2',
592                                     LIBPATH='#libs/sigc++2',
593                                     CPPPATH='#libs/sigc++2')
594     libraries['glibmm2'] = LibraryInfo(LIBS='glibmm2',
595                                     LIBPATH='#libs/glibmm2',
596                                     CPPPATH='#libs/glibmm2')
597     libraries['pangomm'] = LibraryInfo(LIBS='pangomm',
598                                     LIBPATH='#libs/gtkmm2/pango',
599                                     CPPPATH='#libs/gtkmm2/pango')
600     libraries['atkmm'] = LibraryInfo(LIBS='atkmm',
601                                      LIBPATH='#libs/gtkmm2/atk',
602                                      CPPPATH='#libs/gtkmm2/atk')
603     libraries['gdkmm2'] = LibraryInfo(LIBS='gdkmm2',
604                                       LIBPATH='#libs/gtkmm2/gdk',
605                                       CPPPATH='#libs/gtkmm2/gdk')
606     libraries['gtkmm2'] = LibraryInfo(LIBS='gtkmm2',
607                                      LIBPATH="#libs/gtkmm2/gtk",
608                                      CPPPATH='#libs/gtkmm2/gtk/')
609     libraries['libgnomecanvasmm'] = LibraryInfo(LIBS='libgnomecanvasmm',
610                                                 LIBPATH='#libs/libgnomecanvasmm',
611                                                 CPPPATH='#libs/libgnomecanvasmm')
612     
613     libraries['soundtouch'] = LibraryInfo(LIBS='soundtouch',
614                                           LIBPATH='#libs/soundtouch',
615                                           CPPPATH=['#libs', '#libs/soundtouch'])
616     libraries['sndfile'] = LibraryInfo(LIBS='libsndfile',
617                                     LIBPATH='#libs/libsndfile',
618                                     CPPPATH=['#libs/libsndfile', '#libs/libsndfile/src'])
619 #    libraries['libglademm'] = LibraryInfo(LIBS='libglademm',
620 #                                          LIBPATH='#libs/libglademm',
621 #                                          CPPPATH='#libs/libglademm')
622     libraries['appleutility'] = LibraryInfo(LIBS='libappleutility',
623                                             LIBPATH='#libs/appleutility',
624                                             CPPPATH='#libs/appleutility')
625
626     coredirs = [
627         'libs/soundtouch',
628         'templates'
629     ]
630     
631     subdirs = [
632         'libs/sigc++2',
633         'libs/libsndfile',
634         'libs/pbd',
635         'libs/midi++2',
636         'libs/ardour'
637         ]
638     
639     if env['VST']:
640         subdirs = ['libs/fst'] + subdirs + ['vst']
641
642     if env['COREAUDIO']:
643         subdirs = subdirs + ['libs/appleutility']
644     
645     gtk_subdirs = [
646         'libs/glibmm2',
647         'libs/gtkmm2/pango',
648         'libs/gtkmm2/atk',
649         'libs/gtkmm2/gdk',
650         'libs/gtkmm2/gtk',
651         'libs/libgnomecanvasmm',
652 #       'libs/flowcanvas',
653     'libs/gtkmm2ext',
654     'gtk2_ardour'
655         ]
656
657 #
658 # always build the LGPL control protocol lib, since we link against it ourselves
659 # ditto for generic MIDI
660 #
661
662 surface_subdirs = [ 'libs/surfaces/control_protocol', 'libs/surfaces/generic_midi' ]
663
664 if env['SURFACES']:
665     if have_libusb:
666         surface_subdirs += [ 'libs/surfaces/tranzport' ]
667     if os.access ('libs/surfaces/sony9pin', os.F_OK):
668         surface_subdirs += [ 'libs/surfaces/sony9pin' ]
669
670 opts.Save('scache.conf', env)
671 Help(opts.GenerateHelpText(env))
672
673 if os.environ.has_key('PATH'):
674     env.Append(PATH = os.environ['PATH'])
675
676 if os.environ.has_key('PKG_CONFIG_PATH'):
677     env.Append(PKG_CONFIG_PATH = os.environ['PKG_CONFIG_PATH'])
678
679 if os.environ.has_key('CC'):
680     env['CC'] = os.environ['CC']
681
682 if os.environ.has_key('CXX'):
683     env['CXX'] = os.environ['CXX']
684
685 if os.environ.has_key('DISTCC_HOSTS'):
686     env['ENV']['DISTCC_HOSTS'] = os.environ['DISTCC_HOSTS']
687     env['ENV']['HOME'] = os.environ['HOME']
688
689 final_prefix = '$PREFIX'
690 install_prefix = '$DESTDIR/$PREFIX'
691
692 subst_dict['INSTALL_PREFIX'] = install_prefix;
693
694 if env['PREFIX'] == '/usr':
695     final_config_prefix = '/etc'
696 else:
697     final_config_prefix = env['PREFIX'] + '/etc'
698
699 config_prefix = '$DESTDIR' + final_config_prefix
700
701 # For colorgcc ( so says the wiki, but it's still not working :/  anyone? )
702 if os.environ.has_key('PATH'):
703         env['PATH'] = os.environ['PATH']
704 if os.environ.has_key('TERM'):
705         env['TERM'] = os.environ['TERM']
706 if os.environ.has_key('HOME'):
707         env['HOME'] = os.environ['HOME']
708
709
710 # SCons should really do this for us
711
712 conf = Configure (env)
713
714 have_cxx = conf.TryAction (Action (env['CXX'] + ' --version'))
715 if have_cxx[0] != 1:
716     print "This system has no functional C++ compiler. You cannot build Ardour from source without one."
717     exit (1)
718 else:
719     print "Congratulations, you have a functioning C++ compiler."
720
721 env = conf.Finish()
722
723 #
724 # Compiler flags and other system-dependent stuff
725 #
726
727 opt_flags = []
728 debug_flags = [ '-g' ]
729
730 # guess at the platform, used to define compiler flags
731
732 config_guess = os.popen("tools/config.guess").read()[:-1]
733
734 config_cpu = 0
735 config_arch = 1
736 config_kernel = 2
737 config_os = 3
738 config = config_guess.split ("-")
739
740 print "system triple: " + config_guess
741
742 # Autodetect
743 if env['DIST_TARGET'] == 'auto':
744     if config[config_arch] == 'apple':
745         # The [.] matches to the dot after the major version, "." would match any character
746         if re.search ("darwin[0-7][.]", config[config_kernel]) != None:
747             env['DIST_TARGET'] = 'panther'
748         else:
749             env['DIST_TARGET'] = 'tiger'
750     else:
751         if re.search ("x86_64", config[config_cpu]) != None:
752             env['DIST_TARGET'] = 'x86_64'
753         elif re.search("i[0-5]86", config[config_cpu]) != None:
754             env['DIST_TARGET'] = 'i386'
755         elif re.search("powerpc", config[config_cpu]) != None:
756             env['DIST_TARGET'] = 'powerpc'
757         else:
758             env['DIST_TARGET'] = 'i686'
759     print "\n*******************************"
760     print "detected DIST_TARGET = " + env['DIST_TARGET']
761     print "*******************************\n"
762
763
764 if config[config_cpu] == 'powerpc' and env['DIST_TARGET'] != 'none':
765     #
766     # Apple/PowerPC optimization options
767     #
768     # -mcpu=7450 does not reliably work with gcc 3.*
769     #
770     if env['DIST_TARGET'] == 'panther' or env['DIST_TARGET'] == 'tiger':
771         if config[config_arch] == 'apple':
772             opt_flags.extend ([ "-mcpu=7450", "-faltivec"])
773         else:
774             opt_flags.extend ([ "-mcpu=7400", "-maltivec", "-mabi=altivec"])
775     else:
776         opt_flags.extend([ "-mcpu=750", "-mmultiple" ])
777     opt_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"])
778
779 elif ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None)) and env['DIST_TARGET'] != 'none':
780     
781     build_host_supports_sse = 0
782     
783     debug_flags.append ("-DARCH_X86")
784     opt_flags.append ("-DARCH_X86")
785     
786     if config[config_kernel] == 'linux' :
787         
788         if env['DIST_TARGET'] != 'i386':
789             
790             flag_line = os.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1]
791             x86_flags = flag_line.split (": ")[1:][0].split (' ')
792             
793             if "mmx" in x86_flags:
794                 opt_flags.append ("-mmmx")
795             if "sse" in x86_flags:
796                 build_host_supports_sse = 1
797             if "3dnow" in x86_flags:
798                 opt_flags.append ("-m3dnow")
799             
800             if config[config_cpu] == "i586":
801                 opt_flags.append ("-march=i586")
802             elif config[config_cpu] == "i686":
803                 opt_flags.append ("-march=i686")
804     
805     if ((env['DIST_TARGET'] == 'i686') or (env['DIST_TARGET'] == 'x86_64')) and build_host_supports_sse:
806         opt_flags.extend (["-msse", "-mfpmath=sse"])
807         debug_flags.extend (["-msse", "-mfpmath=sse"])
808 # end of processor-specific section
809
810 # optimization section
811 if env['FPU_OPTIMIZATION']:
812     if env['DIST_TARGET'] == 'tiger':
813         opt_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS")
814         debug_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS")
815         libraries['core'].Append(LINKFLAGS= '-framework Accelerate')
816     elif env['DIST_TARGET'] == 'i686' or env['DIST_TARGET'] == 'x86_64':
817         opt_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
818         debug_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
819         if env['DIST_TARGET'] == 'x86_64':
820             opt_flags.append ("-DUSE_X86_64_ASM")
821             debug_flags.append ("-DUSE_X86_64_ASM")
822         if build_host_supports_sse != 1:
823             print "\nWarning: you are building Ardour with SSE support even though your system does not support these instructions. (This may not be an error, especially if you are a package maintainer)"
824 # end optimization section
825
826 #
827 # save off guessed arch element in an env
828 #
829 env.Append(CONFIG_ARCH=config[config_arch])
830
831
832 #
833 # ARCH="..." overrides all
834 #
835
836 if env['ARCH'] != '':
837     opt_flags = env['ARCH'].split()
838
839 #
840 # prepend boiler plate optimization flags
841 #
842
843 opt_flags[:0] = [
844     "-O3",
845     "-fomit-frame-pointer",
846     "-ffast-math",
847     "-fstrength-reduce"
848     ]
849
850 if env['DEBUG'] == 1:
851     env.Append(CCFLAGS=" ".join (debug_flags))
852 else:
853     env.Append(CCFLAGS=" ".join (opt_flags))
854
855 #
856 # warnings flags
857 #
858
859 env.Append(CCFLAGS="-Wall")
860 env.Append(CXXFLAGS="-Woverloaded-virtual")
861
862 if env['EXTRA_WARN']:
863     env.Append(CCFLAGS="-Wextra -pedantic")
864     env.Append(CXXFLAGS="-ansi")
865
866 if env['LIBLO']:
867     env.Append(CCFLAGS="-DHAVE_LIBLO")
868
869 #
870 # everybody needs this
871 #
872
873 env.Merge ([ libraries['core'] ])
874
875 #
876 # fix scons nitpickiness on APPLE
877 #
878
879 if env['DIST_TARGET'] == 'panther' or env['DIST_TARGET'] == 'tiger':
880     env.Append(CCFLAGS="-I/opt/local/include", LINKFLAGS="-L/opt/local/lib")
881
882 #
883 # i18n support
884 #
885
886 conf = Configure (env)
887 if env['NLS']:
888     nls_error = 'This system is not configured for internationalized applications.  An english-only version will be built:'
889     print 'Checking for internationalization support ...'
890     have_gettext = conf.TryAction(Action('xgettext --version'))
891     if have_gettext[0] != 1:
892         nls_error += ' No xgettext command.'
893         env['NLS'] = 0
894     else:
895         print "Found xgettext"
896     
897     have_msgmerge = conf.TryAction(Action('msgmerge --version'))
898     if have_msgmerge[0] != 1:
899         nls_error += ' No msgmerge command.'
900         env['NLS'] = 0
901     else:
902         print "Found msgmerge"
903     
904     if not conf.CheckCHeader('libintl.h'):
905         nls_error += ' No libintl.h.'
906         env['NLS'] = 0
907         
908     if env['NLS'] == 0:
909         print nls_error
910     else:
911         print "International version will be built."
912 env = conf.Finish()
913
914 if env['NLS'] == 1:
915     env.Append(CCFLAGS="-DENABLE_NLS")
916
917 Export('env install_prefix final_prefix config_prefix final_config_prefix libraries i18n version subst_dict')
918
919 #
920 # the configuration file may be system dependent
921 #
922
923 conf = env.Configure ()
924
925 if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Versions/A/Headers/CoreAudio.h'):
926     subst_dict['%JACK_INPUT%'] = "coreaudio:Built-in Audio:in"
927     subst_dict['%JACK_OUTPUT%'] = "coreaudio:Built-in Audio:out"
928 else:
929     subst_dict['%JACK_INPUT%'] = "alsa_pcm:playback_"
930     subst_dict['%JACK_OUTPUT%'] = "alsa_pcm:capture_"
931
932 # posix_memalign available
933 if not conf.CheckFunc('posix_memalign'):
934     print 'Did not find posix_memalign(), using malloc'
935     env.Append(CCFLAGS='-DNO_POSIX_MEMALIGN')
936
937
938 env = conf.Finish()
939
940 rcbuild = env.SubstInFile ('ardour.rc','ardour.rc.in', SUBST_DICT = subst_dict)
941
942 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour_system.rc'))
943 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour.rc'))
944
945 Default (rcbuild)
946
947 # source tarball
948
949 Precious (env['DISTTREE'])
950
951 #
952 # note the special "cleanfirst" source name. this triggers removal
953 # of the existing disttree
954 #
955
956 env.Distribute (env['DISTTREE'],
957                 [ 'SConstruct',
958                   'COPYING', 'PACKAGER_README', 'README',
959                   'ardour.rc.in',
960                   'ardour_system.rc',
961                   'tools/config.guess'
962                   ] +
963                 glob.glob ('DOCUMENTATION/AUTHORS*') +
964                 glob.glob ('DOCUMENTATION/CONTRIBUTORS*') +
965                 glob.glob ('DOCUMENTATION/TRANSLATORS*') +
966                 glob.glob ('DOCUMENTATION/BUILD*') +
967                 glob.glob ('DOCUMENTATION/FAQ*') +
968                 glob.glob ('DOCUMENTATION/README*')
969                 )
970
971 srcdist = env.Tarball(env['TARBALL'], env['DISTTREE'])
972 env.Alias ('srctar', srcdist)
973 #
974 # don't leave the distree around
975 #
976 env.AddPreAction (env['DISTTREE'], Action ('rm -rf ' + str (File (env['DISTTREE']))))
977 env.AddPostAction (srcdist, Action ('rm -rf ' + str (File (env['DISTTREE']))))
978
979 #
980 # the subdirs
981 #
982
983 for subdir in coredirs:
984     SConscript (subdir + '/SConscript')
985
986 for sublistdir in [ subdirs, gtk_subdirs, surface_subdirs ]:
987     for subdir in sublistdir:
988         SConscript (subdir + '/SConscript')
989
990 # cleanup
991 env.Clean ('scrub', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log'])
992