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('NLS', 'Set to turn on i18n support', 1),
33 BoolOption('NOARCH', 'Do not use architecture-specific compilation flags', 0),
34 PathOption('PREFIX', 'Set the install "prefix"', '/usr/local'),
35 BoolOption('VST', 'Compile with support for VST', 0),
36 BoolOption('VERSIONED', 'Add version information to ardour/gtk executable name inside the build directory', 0),
37 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),
38 BoolOption('BUILD_SSE_OPTIMIZATIONS', 'Use our hand-written x86/SSE optimizations when possible (off by default)', 0)
42 #----------------------------------------------------------------------
43 # a handy helper that provides a way to merge compile/link information
44 # from multiple different "environments"
45 #----------------------------------------------------------------------
47 class LibraryInfo(Environment):
48 def __init__(self,*args,**kw):
49 Environment.__init__ (self,*args,**kw)
51 def Merge (self,others):
53 self.Append (LIBS = other.get ('LIBS',[]))
54 self.Append (LIBPATH = other.get ('LIBPATH', []))
55 self.Append (CPPPATH = other.get('CPPPATH', []))
56 self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
59 env = LibraryInfo (options = opts,
62 TARBALL='ardour-' + version + '.tar.bz2',
64 DISTTREE = '#ardour-' + version,
65 DISTCHECKDIR = '#ardour-' + version + '/check'
69 #----------------------------------------------------------------------
71 #----------------------------------------------------------------------
73 # Handy subst-in-file builder
76 def do_subst_in_file(targetfile, sourcefile, dict):
77 """Replace all instances of the keys of dict with their values.
78 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
79 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
82 f = open(sourcefile, 'rb')
86 raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
87 for (k,v) in dict.items():
88 contents = re.sub(k, v, contents)
90 f = open(targetfile, 'wb')
94 raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
97 def subst_in_file(target, source, env):
98 if not env.has_key('SUBST_DICT'):
99 raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
100 d = dict(env['SUBST_DICT']) # copy it
101 for (k,v) in d.items():
103 d[k] = env.subst(v())
104 elif SCons.Util.is_String(v):
107 raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
108 for (t,s) in zip(target, source):
109 return do_subst_in_file(str(t), str(s), d)
111 def subst_in_file_string(target, source, env):
112 """This is what gets printed on the console."""
113 return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
114 for (t,s) in zip(target, source)])
116 def subst_emitter(target, source, env):
117 """Add dependency from substituted SUBST_DICT to target.
118 Returns original target, source tuple unchanged.
120 d = env['SUBST_DICT'].copy() # copy it
121 for (k,v) in d.items():
123 d[k] = env.subst(v())
124 elif SCons.Util.is_String(v):
126 Depends(target, SCons.Node.Python.Value(d))
127 # Depends(target, source) # this doesn't help the install-sapphire-linux.sh problem
128 return target, source
130 subst_action = Action (subst_in_file, subst_in_file_string)
131 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
134 # internationalization
139 # this is not a builder. we can't list the .po files as a target,
140 # because then scons -c will remove them (even Precious doesn't alter
141 # this). this function is called whenever a .mo file is being
142 # built, and will conditionally update the .po file if necessary.
145 def po_helper(po,pot):
151 print 'Updating ' + po
152 return os.spawnvp (os.P_WAIT, 'msgmerge', args)
154 # mo_builder: builder function for (binary) message catalogs (.mo)
156 # first source: .po file
157 # second source: .pot file
160 def mo_builder(target,source,env):
161 po_helper (source[0].get_path(), source[1].get_path())
165 target[0].get_path(),
168 return os.spawnvp (os.P_WAIT, 'msgfmt', args)
170 mo_bld = Builder (action = mo_builder)
171 env.Append(BUILDERS = {'MoBuild' : mo_bld})
173 # pot_builder: builder function for message templates (.pot)
175 # source: list of C/C++ etc. files to extract messages from
178 def pot_builder(target,source,env):
183 '-o', target[0].get_path(),
184 "--default-domain=" + env['PACKAGE'],
185 '--copyright-holder="Paul Davis"' ]
186 args += [ src.get_path() for src in source ]
188 return os.spawnvp (os.P_WAIT, 'xgettext', args)
190 pot_bld = Builder (action = pot_builder)
191 env.Append(BUILDERS = {'PotBuild' : pot_bld})
194 # utility function, not a builder
197 def i18n (buildenv, sources, installenv):
198 domain = buildenv['PACKAGE']
199 potfile = buildenv['POTFILE']
201 installenv.Alias ('potupdate', buildenv.PotBuild (potfile, sources))
203 p_oze = [ os.path.basename (po) for po in glob.glob ('po/*.po') ]
204 languages = [ po.replace ('.po', '') for po in p_oze ]
205 m_oze = [ po.replace (".po", ".mo") for po in p_oze ]
208 po = 'po/' + mo.replace (".mo", ".po")
209 installenv.Alias ('install', buildenv.MoBuild (mo, [ po, potfile ]))
211 for lang in languages[:]:
212 modir = (os.path.join (install_prefix, 'share/locale/' + lang + '/LC_MESSAGES/'))
213 moname = domain + '.mo'
214 installenv.Alias('install', installenv.InstallAs (os.path.join (modir, moname), lang + '.mo'))
217 # A generic builder for version.cc files
219 # note: requires that DOMAIN, MAJOR, MINOR, MICRO are set in the construction environment
220 # note: assumes one source files, the header that declares the version variables
222 def version_builder (target, source, env):
223 text = "int " + env['DOMAIN'] + "_major_version = " + str (env['MAJOR']) + ";\n"
224 text += "int " + env['DOMAIN'] + "_minor_version = " + str (env['MINOR']) + ";\n"
225 text += "int " + env['DOMAIN'] + "_micro_version = " + str (env['MICRO']) + ";\n"
228 o = file (target[0].get_path(), 'w')
232 print "Could not open", target[0].get_path(), " for writing\n"
235 text = "#ifndef __" + env['DOMAIN'] + "_version_h__\n";
236 text += "#define __" + env['DOMAIN'] + "_version_h__\n";
237 text += "extern int " + env['DOMAIN'] + "_major_version;\n"
238 text += "extern int " + env['DOMAIN'] + "_minor_version;\n"
239 text += "extern int " + env['DOMAIN'] + "_micro_version;\n"
240 text += "#endif /* __" + env['DOMAIN'] + "_version_h__ */\n";
243 o = file (target[1].get_path(), 'w')
247 print "Could not open", target[1].get_path(), " for writing\n"
252 version_bld = Builder (action = version_builder)
253 env.Append (BUILDERS = {'VersionBuild' : version_bld})
256 # a builder that makes a hard link from the 'source' executable to a name with
257 # a "build ID" based on the most recent CVS activity that might be reasonably
258 # related to version activity. this relies on the idea that the SConscript
259 # file that builds the executable is updated with new version info and committed
260 # to the source code repository whenever things change.
263 def versioned_builder(target,source,env):
264 # build ID is composed of a representation of the date of the last CVS transaction
265 # for this (SConscript) file
268 o = file (source[0].get_dir().get_path() + '/CVS/Entries', "r")
270 print "Could not CVS/Entries for reading"
274 lines = o.readlines()
276 if line[0:12] == '/SConscript/':
277 parts = line.split ("/")
283 print "No SConscript CVS update info found - versioned executable cannot be built"
286 tag = time.strftime ('%Y%M%d%H%m', time.strptime (last_date));
287 print "The current build ID is " + tag
289 tagged_executable = source[0].get_path() + '-' + tag
291 if os.path.exists (tagged_executable):
292 print "Replacing existing executable with the same build tag."
293 os.unlink (tagged_executable)
295 return os.link (source[0].get_path(), tagged_executable)
297 verbuild = Builder (action = versioned_builder)
298 env.Append (BUILDERS = {'VersionedExecutable' : verbuild})
301 # source tar file builder
304 def distcopy (target, source, env):
305 treedir = str (target[0])
309 except OSError, (errnum, strerror):
310 if errnum != errno.EEXIST:
311 print 'mkdir ', treedir, ':', strerror
315 # we don't know what characters might be in the file names
316 # so quote them all before passing them to the shell
318 all_files = ([ str(s) for s in source ])
319 cmd += " ".join ([ "'%s'" % quoted for quoted in all_files])
320 cmd += ' | (cd ' + treedir + ' && tar xf -)'
324 def tarballer (target, source, env):
325 cmd = 'tar -jcf ' + str (target[0]) + ' ' + str(source[0]) + " --exclude '*~'"
326 print 'running ', cmd, ' ... '
330 dist_bld = Builder (action = distcopy,
331 target_factory = SCons.Node.FS.default_fs.Entry,
332 source_factory = SCons.Node.FS.default_fs.Entry,
335 tarball_bld = Builder (action = tarballer,
336 target_factory = SCons.Node.FS.default_fs.Entry,
337 source_factory = SCons.Node.FS.default_fs.Entry)
339 env.Append (BUILDERS = {'Distribute' : dist_bld})
340 env.Append (BUILDERS = {'Tarball' : tarball_bld})
342 # ----------------------------------------------------------------------
343 # Construction environment setup
344 # ----------------------------------------------------------------------
348 libraries['core'] = LibraryInfo (CPPPATH = [ '#libs'])
350 libraries['sndfile'] = LibraryInfo()
351 libraries['sndfile'].ParseConfig('pkg-config --cflags --libs sndfile')
353 libraries['lrdf'] = LibraryInfo()
354 libraries['lrdf'].ParseConfig('pkg-config --cflags --libs lrdf')
356 libraries['raptor'] = LibraryInfo()
357 libraries['raptor'].ParseConfig('pkg-config --cflags --libs raptor')
359 libraries['samplerate'] = LibraryInfo()
360 libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate')
362 libraries['jack'] = LibraryInfo()
363 libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
365 libraries['xml'] = LibraryInfo()
366 libraries['xml'].ParseConfig('pkg-config --cflags --libs libxml-2.0')
368 libraries['glib2'] = LibraryInfo()
369 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs glib-2.0')
370 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gobject-2.0')
371 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gmodule-2.0')
373 libraries['gtk2'] = LibraryInfo()
374 libraries['gtk2'].ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
376 libraries['pango'] = LibraryInfo()
377 libraries['pango'].ParseConfig ('pkg-config --cflags --libs pango')
379 libraries['libgnomecanvas2'] = LibraryInfo()
380 libraries['libgnomecanvas2'].ParseConfig ('pkg-config --cflags --libs libgnomecanvas-2.0')
382 libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
383 libraries['midi++2'] = LibraryInfo (LIBS='midi++', LIBPATH='#libs/midi++2', CPPPATH='#libs/midi++2')
384 libraries['pbd3'] = LibraryInfo (LIBS='pbd', LIBPATH='#libs/pbd3', CPPPATH='#libs/pbd3')
385 libraries['gtkmm2ext'] = LibraryInfo (LIBS='gtkmm2ext', LIBPATH='#libs/gtkmm2ext', CPPPATH='#libs/gtkmm2ext')
386 #libraries['cassowary'] = LibraryInfo(LIBS='cassowary', LIBPATH='#libs/cassowary', CPPPATH='#libs/cassowary')
388 libraries['fst'] = LibraryInfo()
390 libraries['fst'].ParseConfig('pkg-config --cflags --libs libfst')
393 # Audio/MIDI library (needed for MIDI, since audio is all handled via JACK)
396 conf = Configure(env)
398 if conf.CheckCHeader('alsa/asoundlib.h'):
399 libraries['sysmidi'] = LibraryInfo (LIBS='asound')
400 env['SYSMIDI'] = 'ALSA Sequencer'
401 subst_dict['%MIDITAG%'] = "seq"
402 subst_dict['%MIDITYPE%'] = "alsa/sequencer"
403 elif conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'):
404 # this line is needed because scons can't handle -framework in ParseConfig() yet.
405 libraries['sysmidi'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -bind_at_load')
406 env['SYSMIDI'] = 'CoreMIDI'
407 subst_dict['%MIDITAG%'] = "ardour"
408 subst_dict['%MIDITYPE%'] = "coremidi"
414 libraries['sigc2'] = LibraryInfo()
415 libraries['sigc2'].ParseConfig('pkg-config --cflags --libs sigc++-2.0')
417 libraries['gtkmm2'] = LibraryInfo()
418 libraries['gtkmm2'].ParseConfig ('pkg-config --cflags --libs gtkmm-2.0')
420 libraries['soundtouch'] = LibraryInfo(LIBS='SoundTouch')
440 libraries['sigc2'] = LibraryInfo(LIBS='sigc++2',
441 LIBPATH='#libs/sigc++2',
442 CPPPATH='#libs/sigc++2')
443 libraries['glibmm2'] = LibraryInfo(LIBS='glibmm2',
444 LIBPATH='#libs/glibmm2',
445 CPPPATH='#libs/glibmm2')
446 libraries['pangomm'] = LibraryInfo(LIBS='pangomm',
447 LIBPATH='#libs/gtkmm2/pango',
448 CPPPATH='#libs/gtkmm2/pango')
449 libraries['atkmm'] = LibraryInfo(LIBS='atkmm',
450 LIBPATH='#libs/gtkmm2/atk',
451 CPPPATH='#libs/gtkmm2/atk')
452 libraries['gdkmm2'] = LibraryInfo(LIBS='gdkmm2',
453 LIBPATH='#libs/gtkmm2/gdk',
454 CPPPATH='#libs/gtkmm2/gdk')
455 libraries['gtkmm2'] = LibraryInfo(LIBS='gtkmm2',
456 LIBPATH="#libs/gtkmm2/gtk",
457 CPPPATH='#libs/gtkmm2/gtk/')
458 libraries['libgnomecanvasmm'] = LibraryInfo(LIBS='libgnomecanvasmm',
459 LIBPATH='#libs/libgnomecanvasmm',
460 CPPPATH='#libs/libgnomecanvasmm')
462 libraries['soundtouch'] = LibraryInfo(LIBS='soundtouch',
463 LIBPATH='#libs/soundtouch',
464 CPPPATH=['#libs', '#libs/soundtouch'])
485 'libs/libgnomecanvasmm',
490 opts.Save('scache.conf', env)
491 Help(opts.GenerateHelpText(env))
493 if os.environ.has_key('PATH'):
494 env.Append(PATH = os.environ['PATH'])
496 if os.environ.has_key('PKG_CONFIG_PATH'):
497 env.Append(PKG_CONFIG_PATH = os.environ['PKG_CONFIG_PATH'])
499 if os.environ.has_key('CC'):
500 env['CC'] = os.environ['CC']
502 if os.environ.has_key('CXX'):
503 env['CXX'] = os.environ['CXX']
505 if os.environ.has_key('DISTCC_HOSTS'):
506 env['ENV']['DISTCC_HOSTS'] = os.environ['DISTCC_HOSTS']
507 env['ENV']['HOME'] = os.environ['HOME']
509 final_prefix = '$PREFIX'
510 install_prefix = '$DESTDIR/$PREFIX'
512 if env['PREFIX'] == '/usr':
513 final_config_prefix = '/etc'
515 final_config_prefix = env['PREFIX'] + '/etc'
517 config_prefix = '$DESTDIR' + final_config_prefix
520 # Compiler flags and other system-dependent stuff
524 debug_flags = [ '-g' ]
526 # guess at the platform, used to define compiler flags
528 config_guess = os.popen("tools/config.guess").read()[:-1]
534 config = config_guess.split ("-")
537 # on OS X darwinports puts things in /opt/local by default
539 if config[config_arch] == 'apple':
540 if os.path.isdir('/opt/local/lib'):
541 libraries['core'].Append (LIBPATH = [ '/opt/local/lib' ])
542 if os.path.isdir('/opt/local/include'):
543 libraries['core'].Append (CPPPATH = [ '/opt/local/include' ])
544 if config[config_cpu] == 'powerpc':
546 # Apple/PowerPC optimization options
548 # -mcpu=7450 does not reliably work with gcc 3.*
550 if env['NOARCH'] == 0:
551 if env['ALTIVEC'] == 1:
552 if config[config_arch] == 'apple':
553 opt_flags.extend ([ "-mcpu=7450", "-faltivec"])
555 opt_flags.extend ([ "-mcpu=7400", "-maltivec", "-mabi=altivec"])
557 opt_flags.extend([ "-mcpu=750", "-mmultiple" ])
558 opt_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"])
560 elif ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None)):
562 build_host_supports_sse = 0
564 if env['NOARCH'] == 0:
566 debug_flags.append ("-DARCH_X86")
567 opt_flags.append ("-DARCH_X86")
569 if config[config_kernel] == 'linux' :
571 flag_line = os.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1]
572 x86_flags = flag_line.split (": ")[1:][0].split (' ')
574 if "mmx" in x86_flags:
575 opt_flags.append ("-mmmx")
576 if "sse" in x86_flags:
577 build_host_supports_sse = 1
578 if "3dnow" in x86_flags:
579 opt_flags.append ("-m3dnow")
581 if config[config_cpu] == "i586":
582 opt_flags.append ("-march=i586")
583 elif config[config_cpu] == "i686":
584 opt_flags.append ("-march=i686")
586 if env['USE_SSE_EVERYWHERE'] == 1:
587 opt_flags.extend (["-msse", "-mfpmath=sse"])
588 debug_flags.extend (["-msse", "-mfpmath=sse"])
589 if build_host_supports_sse != 1:
590 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)"
592 if env['BUILD_SSE_OPTIMIZATIONS'] == 1:
593 opt_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
594 debug_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
595 if build_host_supports_sse != 1:
596 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)"
598 # end of processor-specific section
601 # ARCH="..." overrides all
604 if env['ARCH'] != '':
605 opt_flags = env['ARCH'].split()
608 # prepend boiler plate optimization flags
613 "-fomit-frame-pointer",
618 if env['DEBUG'] == 1:
619 env.Append(CCFLAGS=" ".join (debug_flags))
621 env.Append(CCFLAGS=" ".join (opt_flags))
623 env.Append(CCFLAGS="-Wall")
626 env.Append(CCFLAGS="-DVST_SUPPORT")
630 # everybody needs this
633 env.Merge ([ libraries['core'] ])
639 conf = Configure (env)
642 print 'Checking for internationalization support ...'
643 have_gettext = conf.TryAction(Action('xgettext --version'))
644 if have_gettext[0] != 1:
645 print 'This system is not configured for internationalized applications (no xgettext command). An english-only version will be built\n'
648 if conf.CheckCHeader('libintl.h') == None:
649 print 'This system is not configured for internationalized applications (no libintl.h). An english-only version will be built\n'
656 env.Append(CCFLAGS="-DENABLE_NLS")
659 Export('env install_prefix final_prefix config_prefix final_config_prefix libraries i18n version subst_dict')
662 # the configuration file may be system dependent
665 conf = env.Configure ()
667 if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Versions/A/Headers/CoreAudio.h'):
668 subst_dict['%JACK_BACKEND%'] = "coreaudio:Built-in Audio:in"
670 subst_dict['%JACK_BACKEND%'] = "alsa_pcm:playback_"
672 # posix_memalign available
673 if not conf.CheckFunc('posix_memalign'):
674 print 'Did not find posix_memalign(), using malloc'
675 env.Append(CCFLAGS='-DNO_POSIX_MEMALIGN')
680 rcbuild = env.SubstInFile ('ardour.rc','ardour.rc.in', SUBST_DICT = subst_dict)
682 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour'), 'ardour_system.rc'))
683 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour'), 'ardour.rc'))
689 Precious (env['DISTTREE'])
692 # note the special "cleanfirst" source name. this triggers removal
693 # of the existing disttree
696 env.Distribute (env['DISTTREE'],
698 'COPYING', 'PACKAGER_README', 'README',
703 glob.glob ('DOCUMENTATION/AUTHORS*') +
704 glob.glob ('DOCUMENTATION/CONTRIBUTORS*') +
705 glob.glob ('DOCUMENTATION/TRANSLATORS*') +
706 glob.glob ('DOCUMENTATION/BUILD*') +
707 glob.glob ('DOCUMENTATION/FAQ*') +
708 glob.glob ('DOCUMENTATION/README*')
711 srcdist = env.Tarball(env['TARBALL'], env['DISTTREE'])
712 env.Alias ('srctar', srcdist)
714 # don't leave the distree around
716 env.AddPreAction (env['DISTTREE'], Action ('rm -rf ' + str (File (env['DISTTREE']))))
717 env.AddPostAction (srcdist, Action ('rm -rf ' + str (File (env['DISTTREE']))))
723 for subdir in coredirs:
724 SConscript (subdir + '/SConscript')
726 for sublistdir in [subdirs, gtk_subdirs]:
727 for subdir in sublistdir:
728 SConscript (subdir + '/SConscript')
731 env.Clean ('scrub', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log'])