13 EnsureSConsVersion(0, 96)
20 # Command-line options
23 opts = Options('scache.conf')
25 BoolOption('ALTIVEC', 'Compile using Altivec instructions', 0),
26 ('ARCH', 'Set architecture-specific compilation flags by hand (all flags as 1 argument)',''),
27 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),
28 BoolOption('DEBUG', 'Set to build with debugging information and no optimizations', 0),
29 PathOption('DESTDIR', 'Set the intermediate install "prefix"', '/'),
30 BoolOption('DEVBUILD', 'Use shared libardour (developers only)', 0),
31 BoolOption('SIGCCVSBUILD', 'Use if building sigc++ with a new configure.ac (developers only)', 0),
32 BoolOption('GTK', 'Build the GTK (graphical) version of Ardour', 1),
33 BoolOption('KSI', 'Build the KSI (text) version of Ardour', 0),
34 BoolOption('NLS', 'Set to turn on i18n support', 1),
35 BoolOption('NOARCH', 'Do not use architecture-specific compilation flags', 0),
36 PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'),
37 BoolOption('VST', 'Compile with support for VST', 0),
38 BoolOption('VERSIONED', 'Add version information to ardour/gtk executable name inside the build directory', 0),
39 BoolOption('USE_SSE_EVERYWHERE', 'Ask the compiler to use x86/SSE instructions and also our hand-written x86/SSE optimizations when possible (off by default)', 0),
40 BoolOption('BUILD_SSE_OPTIMIZATIONS', 'Use our hand-written x86/SSE optimizations when possible (off by default)', 0),
41 BoolOption('BUILD_VECLIB_OPTIMIZATIONS', 'Build with Apple Accelerate/vecLib optimizations when possible (off by default)', 0)
45 #----------------------------------------------------------------------
46 # a handy helper that provides a way to merge compile/link information
47 # from multiple different "environments"
48 #----------------------------------------------------------------------
50 class LibraryInfo(Environment):
51 def __init__(self,*args,**kw):
52 Environment.__init__ (self,*args,**kw)
54 def Merge (self,others):
56 self.Append (LIBS = other.get ('LIBS',[]))
57 self.Append (LIBPATH = other.get ('LIBPATH', []))
58 self.Append (CPPPATH = other.get('CPPPATH', []))
59 self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
62 env = LibraryInfo (options = opts,
65 TARBALL='ardour-' + version + '.tar.bz2',
67 DISTTREE = '#ardour-' + version,
68 DISTCHECKDIR = '#ardour-' + version + '/check'
72 #----------------------------------------------------------------------
74 #----------------------------------------------------------------------
76 # Handy subst-in-file builder
79 def do_subst_in_file(targetfile, sourcefile, dict):
80 """Replace all instances of the keys of dict with their values.
81 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
82 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
85 f = open(sourcefile, 'rb')
89 raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
90 for (k,v) in dict.items():
91 contents = re.sub(k, v, contents)
93 f = open(targetfile, 'wb')
97 raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
100 def subst_in_file(target, source, env):
101 if not env.has_key('SUBST_DICT'):
102 raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
103 d = dict(env['SUBST_DICT']) # copy it
104 for (k,v) in d.items():
106 d[k] = env.subst(v())
107 elif SCons.Util.is_String(v):
110 raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
111 for (t,s) in zip(target, source):
112 return do_subst_in_file(str(t), str(s), d)
114 def subst_in_file_string(target, source, env):
115 """This is what gets printed on the console."""
116 return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
117 for (t,s) in zip(target, source)])
119 def subst_emitter(target, source, env):
120 """Add dependency from substituted SUBST_DICT to target.
121 Returns original target, source tuple unchanged.
123 d = env['SUBST_DICT'].copy() # copy it
124 for (k,v) in d.items():
126 d[k] = env.subst(v())
127 elif SCons.Util.is_String(v):
129 Depends(target, SCons.Node.Python.Value(d))
130 # Depends(target, source) # this doesn't help the install-sapphire-linux.sh problem
131 return target, source
133 subst_action = Action (subst_in_file, subst_in_file_string)
134 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
137 # internationalization
142 # this is not a builder. we can't list the .po files as a target,
143 # because then scons -c will remove them (even Precious doesn't alter
144 # this). this function is called whenever a .mo file is being
145 # built, and will conditionally update the .po file if necessary.
148 def po_helper(po,pot):
154 print 'Updating ' + po
155 return os.spawnvp (os.P_WAIT, 'msgmerge', args)
157 # mo_builder: builder function for (binary) message catalogs (.mo)
159 # first source: .po file
160 # second source: .pot file
163 def mo_builder(target,source,env):
164 po_helper (source[0].get_path(), source[1].get_path())
168 target[0].get_path(),
171 return os.spawnvp (os.P_WAIT, 'msgfmt', args)
173 mo_bld = Builder (action = mo_builder)
174 env.Append(BUILDERS = {'MoBuild' : mo_bld})
176 # pot_builder: builder function for message templates (.pot)
178 # source: list of C/C++ etc. files to extract messages from
181 def pot_builder(target,source,env):
186 '-o', target[0].get_path(),
187 "--default-domain=" + env['PACKAGE'],
188 '--copyright-holder="Paul Davis"' ]
189 args += [ src.get_path() for src in source ]
191 return os.spawnvp (os.P_WAIT, 'xgettext', args)
193 pot_bld = Builder (action = pot_builder)
194 env.Append(BUILDERS = {'PotBuild' : pot_bld})
197 # utility function, not a builder
200 def i18n (buildenv, sources, installenv):
201 domain = buildenv['PACKAGE']
202 potfile = buildenv['POTFILE']
204 installenv.Alias ('potupdate', buildenv.PotBuild (potfile, sources))
206 p_oze = [ os.path.basename (po) for po in glob.glob ('po/*.po') ]
207 languages = [ po.replace ('.po', '') for po in p_oze ]
208 m_oze = [ po.replace (".po", ".mo") for po in p_oze ]
211 po = 'po/' + mo.replace (".mo", ".po")
212 installenv.Alias ('install', buildenv.MoBuild (mo, [ po, potfile ]))
214 for lang in languages[:]:
215 modir = (os.path.join (install_prefix, 'share/locale/' + lang + '/LC_MESSAGES/'))
216 moname = domain + '.mo'
217 installenv.Alias('install', installenv.InstallAs (os.path.join (modir, moname), lang + '.mo'))
220 # A generic builder for version.cc files
222 # note: requires that DOMAIN, MAJOR, MINOR, MICRO are set in the construction environment
223 # note: assumes one source files, the header that declares the version variables
225 def version_builder (target, source, env):
226 text = "int " + env['DOMAIN'] + "_major_version = " + str (env['MAJOR']) + ";\n"
227 text += "int " + env['DOMAIN'] + "_minor_version = " + str (env['MINOR']) + ";\n"
228 text += "int " + env['DOMAIN'] + "_micro_version = " + str (env['MICRO']) + ";\n"
231 o = file (target[0].get_path(), 'w')
235 print "Could not open", target[0].get_path(), " for writing\n"
238 text = "#ifndef __" + env['DOMAIN'] + "_version_h__\n";
239 text += "#define __" + env['DOMAIN'] + "_version_h__\n";
240 text += "extern int " + env['DOMAIN'] + "_major_version;\n"
241 text += "extern int " + env['DOMAIN'] + "_minor_version;\n"
242 text += "extern int " + env['DOMAIN'] + "_micro_version;\n"
243 text += "#endif /* __" + env['DOMAIN'] + "_version_h__ */\n";
246 o = file (target[1].get_path(), 'w')
250 print "Could not open", target[1].get_path(), " for writing\n"
255 version_bld = Builder (action = version_builder)
256 env.Append (BUILDERS = {'VersionBuild' : version_bld})
259 # a builder that makes a hard link from the 'source' executable to a name with
260 # a "build ID" based on the most recent CVS activity that might be reasonably
261 # related to version activity. this relies on the idea that the SConscript
262 # file that builds the executable is updated with new version info and committed
263 # to the source code repository whenever things change.
266 def versioned_builder(target,source,env):
267 # build ID is composed of a representation of the date of the last CVS transaction
268 # for this (SConscript) file
271 o = file (source[0].get_dir().get_path() + '/CVS/Entries', "r")
273 print "Could not CVS/Entries for reading"
277 lines = o.readlines()
279 if line[0:12] == '/SConscript/':
280 parts = line.split ("/")
286 print "No SConscript CVS update info found - versioned executable cannot be built"
289 tag = time.strftime ('%Y%M%d%H%m', time.strptime (last_date));
290 print "The current build ID is " + tag
292 tagged_executable = source[0].get_path() + '-' + tag
294 if os.path.exists (tagged_executable):
295 print "Replacing existing executable with the same build tag."
296 os.unlink (tagged_executable)
298 return os.link (source[0].get_path(), tagged_executable)
300 verbuild = Builder (action = versioned_builder)
301 env.Append (BUILDERS = {'VersionedExecutable' : verbuild})
304 # source tar file builder
307 def distcopy (target, source, env):
308 treedir = str (target[0])
312 except OSError, (errnum, strerror):
313 if errnum != errno.EEXIST:
314 print 'mkdir ', treedir, ':', strerror
318 # we don't know what characters might be in the file names
319 # so quote them all before passing them to the shell
321 all_files = ([ str(s) for s in source ])
322 cmd += " ".join ([ "'%s'" % quoted for quoted in all_files])
323 cmd += ' | (cd ' + treedir + ' && tar xf -)'
327 def tarballer (target, source, env):
328 cmd = 'tar -jcf ' + str (target[0]) + ' ' + str(source[0]) + " --exclude '*~'"
329 print 'running ', cmd, ' ... '
333 dist_bld = Builder (action = distcopy,
334 target_factory = SCons.Node.FS.default_fs.Entry,
335 source_factory = SCons.Node.FS.default_fs.Entry,
338 tarball_bld = Builder (action = tarballer,
339 target_factory = SCons.Node.FS.default_fs.Entry,
340 source_factory = SCons.Node.FS.default_fs.Entry)
342 env.Append (BUILDERS = {'Distribute' : dist_bld})
343 env.Append (BUILDERS = {'Tarball' : tarball_bld})
345 # ----------------------------------------------------------------------
346 # Construction environment setup
347 # ----------------------------------------------------------------------
351 libraries['core'] = LibraryInfo (CPPPATH = [ '#libs'])
353 libraries['sndfile'] = LibraryInfo()
354 libraries['sndfile'].ParseConfig('pkg-config --cflags --libs sndfile')
356 libraries['lrdf'] = LibraryInfo()
357 libraries['lrdf'].ParseConfig('pkg-config --cflags --libs lrdf')
359 libraries['raptor'] = LibraryInfo()
360 libraries['raptor'].ParseConfig('pkg-config --cflags --libs raptor')
362 libraries['samplerate'] = LibraryInfo()
363 libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate')
365 libraries['jack'] = LibraryInfo()
366 libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
368 libraries['xml'] = LibraryInfo()
369 libraries['xml'].ParseConfig('pkg-config --cflags --libs libxml-2.0')
371 libraries['libart'] = LibraryInfo()
372 libraries['libart'].ParseConfig('pkg-config --cflags --libs libart-2.0')
374 libraries['gtk'] = LibraryInfo()
375 libraries['gtk'].ParseConfig ('gtk-config --cflags --libs')
377 libraries['glib'] = LibraryInfo()
378 libraries['glib'].ParseConfig ('glib-config --cflags --libs')
380 libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
381 libraries['midi++'] = LibraryInfo (LIBS='midi++', LIBPATH='#libs/midi++', CPPPATH='#libs/midi++')
382 libraries['pbd'] = LibraryInfo (LIBS='pbd', LIBPATH='#libs/pbd', CPPPATH='#libs/pbd')
383 libraries['gtkmmext'] = LibraryInfo (LIBS='gtkmmext', LIBPATH='#libs/gtkmmext', CPPPATH='#libs/gtkmmext')
385 libraries['fst'] = LibraryInfo()
387 libraries['fst'].ParseConfig('pkg-config --cflags --libs libfst')
390 # Audio/MIDI library (needed for MIDI, since audio is all handled via JACK)
393 conf = Configure(env)
395 if conf.CheckCHeader('alsa/asoundlib.h'):
396 libraries['sysmidi'] = LibraryInfo (LIBS='asound')
397 env['SYSMIDI'] = 'ALSA Sequencer'
398 subst_dict['%MIDITAG%'] = "seq"
399 subst_dict['%MIDITYPE%'] = "alsa/sequencer"
400 elif conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'):
401 # this line is needed because scons can't handle -framework in ParseConfig() yet.
402 libraries['sysmidi'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -bind_at_load')
403 env['SYSMIDI'] = 'CoreMIDI'
404 subst_dict['%MIDITAG%'] = "ardour"
405 subst_dict['%MIDITYPE%'] = "coremidi"
411 libraries['sigc'] = LibraryInfo()
412 libraries['sigc'].ParseConfig('sigc-config --cflags --libs')
414 libraries['gtkmm'] = LibraryInfo()
415 libraries['gtkmm'].ParseConfig ('gtkmm-config --cflags --libs')
417 libraries['soundtouch'] = LibraryInfo(LIBS='SoundTouch')
418 libraries['gtk-canvas'] = LibraryInfo(LIBS='gtk-canvas')
437 libraries['sigc'] = LibraryInfo(LIBS='sigc++',
438 LIBPATH='#libs/sigc++',
439 CPPPATH=['#libs/sigc++', '#libs/sigc++/sigc++/config'])
440 libraries['gtkmm'] = LibraryInfo(LIBS='gtkmm',
441 LIBPATH="#libs/gtkmm",
442 CPPPATH=[ '#libs/gtkmm', '#libs/gtkmm/gdk--', '#libs/gtkmm/src'])
443 libraries['soundtouch'] = LibraryInfo(LIBS='soundtouch',
444 LIBPATH='#libs/soundtouch',
445 CPPPATH=['#libs', '#libs/soundtouch'])
446 libraries['gtk-canvas'] = LibraryInfo(LIBS='gtk-canvas',
447 LIBPATH='#libs/gtk-canvas',
448 CPPPATH='#libs/gtk-canvas')
469 opts.Save('scache.conf', env)
470 Help(opts.GenerateHelpText(env))
472 if os.environ.has_key('PATH'):
473 env.Append(PATH = os.environ['PATH'])
475 if os.environ.has_key('PKG_CONFIG_PATH'):
476 env.Append(PKG_CONFIG_PATH = os.environ['PKG_CONFIG_PATH'])
478 if os.environ.has_key('CC'):
479 env['CC'] = os.environ['CC']
481 if os.environ.has_key('CXX'):
482 env['CXX'] = os.environ['CXX']
484 if os.environ.has_key('DISTCC_HOSTS'):
485 env['ENV']['DISTCC_HOSTS'] = os.environ['DISTCC_HOSTS']
486 env['ENV']['HOME'] = os.environ['HOME']
488 final_prefix = '$PREFIX'
489 install_prefix = '$DESTDIR/$PREFIX'
491 if env['PREFIX'] == '/usr':
492 final_config_prefix = '/etc'
494 final_config_prefix = env['PREFIX'] + '/etc'
496 config_prefix = '$DESTDIR' + final_config_prefix
499 # Compiler flags and other system-dependent stuff
503 debug_flags = [ '-g' ]
505 # guess at the platform, used to define compiler flags
507 config_guess = os.popen("tools/config.guess").read()[:-1]
513 config = config_guess.split ("-")
516 # on OS X darwinports puts things in /opt/local by default
518 if config[config_arch] == 'apple':
519 if os.path.isdir('/opt/local/lib'):
520 libraries['core'].Append (LIBPATH = [ '/opt/local/lib' ])
521 if os.path.isdir('/opt/local/include'):
522 libraries['core'].Append (CPPPATH = [ '/opt/local/include' ])
524 if env['BUILD_VECLIB_OPTIMIZATIONS'] == 1:
525 opt_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS")
526 debug_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS")
527 libraries['core'].Append(LINKFLAGS= '-framework Accelerate')
530 if config[config_cpu] == 'powerpc':
532 # Apple/PowerPC optimization options
534 # -mcpu=7450 does not reliably work with gcc 3.*
536 if env['NOARCH'] == 0:
537 if env['ALTIVEC'] == 1:
538 if config[config_arch] == 'apple':
539 opt_flags.extend ([ "-mcpu=7450", "-faltivec"])
541 opt_flags.extend ([ "-mcpu=7400", "-maltivec", "-mabi=altivec"])
543 opt_flags.extend([ "-mcpu=750", "-mmultiple" ])
544 opt_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"])
546 elif ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None)):
548 build_host_supports_sse = 0
550 if env['NOARCH'] == 0:
552 debug_flags.append ("-DARCH_X86")
553 opt_flags.append ("-DARCH_X86")
555 if config[config_kernel] == 'linux' :
557 flag_line = os.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1]
558 x86_flags = flag_line.split (": ")[1:][0].split (' ')
560 if "mmx" in x86_flags:
561 opt_flags.append ("-mmmx")
562 if "sse" in x86_flags:
563 build_host_supports_sse = 1
564 if "3dnow" in x86_flags:
565 opt_flags.append ("-m3dnow")
567 if config[config_cpu] == "i586":
568 opt_flags.append ("-march=i586")
569 elif config[config_cpu] == "i686":
570 opt_flags.append ("-march=i686")
572 if env['USE_SSE_EVERYWHERE'] == 1:
573 opt_flags.extend (["-msse", "-mfpmath=sse"])
574 debug_flags.extend (["-msse", "-mfpmath=sse"])
575 if build_host_supports_sse != 1:
576 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)"
578 if env['BUILD_SSE_OPTIMIZATIONS'] == 1:
579 opt_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
580 debug_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
581 if build_host_supports_sse != 1:
582 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)"
584 # end of processor-specific section
587 # ARCH="..." overrides all
590 if env['ARCH'] != '':
591 opt_flags = env['ARCH'].split()
594 # prepend boiler plate optimization flags
599 "-fomit-frame-pointer",
604 if env['DEBUG'] == 1:
605 env.Append(CCFLAGS=" ".join (debug_flags))
607 env.Append(CCFLAGS=" ".join (opt_flags))
609 env.Append(CCFLAGS="-Wall")
612 env.Append(CCFLAGS="-DVST_SUPPORT")
616 # everybody needs this
619 env.Merge ([ libraries['core'] ])
625 conf = Configure (env)
628 print 'Checking for internationalization support ...'
629 have_gettext = conf.TryAction(Action('xgettext --version'))
630 if have_gettext[0] != 1:
631 print 'This system is not configured for internationalized applications (no xgettext command). An english-only version will be built\n'
634 if conf.CheckCHeader('libintl.h') == None:
635 print 'This system is not configured for internationalized applications (no libintl.h). An english-only version will be built\n'
642 env.Append(CCFLAGS="-DENABLE_NLS")
645 Export('env install_prefix final_prefix config_prefix final_config_prefix libraries i18n version subst_dict')
648 # the configuration file may be system dependent
651 conf = env.Configure ()
653 if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Versions/A/Headers/CoreAudio.h'):
654 subst_dict['%JACK_INPUT%'] = "coreaudio:Built-in Audio:in"
655 subst_dict['%JACK_OUTPUT%'] = "coreaudio:Built-in Audio:out"
657 subst_dict['%JACK_INPUT%'] = "alsa_pcm:playback_"
658 subst_dict['%JACK_OUTPUT%'] = "alsa_pcm:capture_"
660 # posix_memalign available
661 if not conf.CheckFunc('posix_memalign'):
662 print 'Did not find posix_memalign(), using malloc'
663 env.Append(CCFLAGS='-DNO_POSIX_MEMALIGN')
668 rcbuild = env.SubstInFile ('ardour.rc','ardour.rc.in', SUBST_DICT = subst_dict)
670 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour'), 'ardour_system.rc'))
671 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour'), 'ardour.rc'))
677 Precious (env['DISTTREE'])
680 # note the special "cleanfirst" source name. this triggers removal
681 # of the existing disttree
684 env.Distribute (env['DISTTREE'],
686 'COPYING', 'PACKAGER_README', 'README',
691 glob.glob ('DOCUMENTATION/AUTHORS*') +
692 glob.glob ('DOCUMENTATION/CONTRIBUTORS*') +
693 glob.glob ('DOCUMENTATION/TRANSLATORS*') +
694 glob.glob ('DOCUMENTATION/BUILD*') +
695 glob.glob ('DOCUMENTATION/FAQ*') +
696 glob.glob ('DOCUMENTATION/README*')
699 srcdist = env.Tarball(env['TARBALL'], env['DISTTREE'])
700 env.Alias ('srctar', srcdist)
702 # don't leave the distree around
704 env.AddPreAction (env['DISTTREE'], Action ('rm -rf ' + str (File (env['DISTTREE']))))
705 env.AddPostAction (srcdist, Action ('rm -rf ' + str (File (env['DISTTREE']))))
711 for subdir in coredirs:
712 SConscript (subdir + '/SConscript')
714 if env['GTK'] or env['KSI']:
715 for subdir in subdirs:
716 SConscript (subdir + '/SConscript')
718 if env['GTK'] or 'tarball' in COMMAND_LINE_TARGETS:
719 for subdir in gtk_subdirs:
720 SConscript (subdir + '/SConscript')
722 if env['KSI'] or 'tarball' in COMMAND_LINE_TARGETS:
723 SConscript ('ksi_ardour/SConscript')
726 env.Clean ('scrub', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log'])