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)
41 #----------------------------------------------------------------------
42 # a handy helper that provides a way to merge compile/link information
43 # from multiple different "environments"
44 #----------------------------------------------------------------------
46 class LibraryInfo(Environment):
47 def __init__(self,*args,**kw):
48 Environment.__init__ (self,*args,**kw)
50 def Merge (self,others):
52 self.Append (LIBS = other.get ('LIBS',[]))
53 self.Append (LIBPATH = other.get ('LIBPATH', []))
54 self.Append (CPPPATH = other.get('CPPPATH', []))
55 self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
58 env = LibraryInfo (options = opts,
61 TARBALL='ardour-' + version + '.tar.bz2',
63 DISTTREE = '#ardour-' + version,
64 DISTCHECKDIR = '#ardour-' + version + '/check'
68 #----------------------------------------------------------------------
70 #----------------------------------------------------------------------
72 # Handy subst-in-file builder
75 def do_subst_in_file(targetfile, sourcefile, dict):
76 """Replace all instances of the keys of dict with their values.
77 For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
78 then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
81 f = open(sourcefile, 'rb')
85 raise SCons.Errors.UserError, "Can't read source file %s"%sourcefile
86 for (k,v) in dict.items():
87 contents = re.sub(k, v, contents)
89 f = open(targetfile, 'wb')
93 raise SCons.Errors.UserError, "Can't write target file %s"%targetfile
96 def subst_in_file(target, source, env):
97 if not env.has_key('SUBST_DICT'):
98 raise SCons.Errors.UserError, "SubstInFile requires SUBST_DICT to be set."
99 d = dict(env['SUBST_DICT']) # copy it
100 for (k,v) in d.items():
102 d[k] = env.subst(v())
103 elif SCons.Util.is_String(v):
106 raise SCons.Errors.UserError, "SubstInFile: key %s: %s must be a string or callable"%(k, repr(v))
107 for (t,s) in zip(target, source):
108 return do_subst_in_file(str(t), str(s), d)
110 def subst_in_file_string(target, source, env):
111 """This is what gets printed on the console."""
112 return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
113 for (t,s) in zip(target, source)])
115 def subst_emitter(target, source, env):
116 """Add dependency from substituted SUBST_DICT to target.
117 Returns original target, source tuple unchanged.
119 d = env['SUBST_DICT'].copy() # copy it
120 for (k,v) in d.items():
122 d[k] = env.subst(v())
123 elif SCons.Util.is_String(v):
125 Depends(target, SCons.Node.Python.Value(d))
126 # Depends(target, source) # this doesn't help the install-sapphire-linux.sh problem
127 return target, source
129 subst_action = Action (subst_in_file, subst_in_file_string)
130 env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
133 # internationalization
138 # this is not a builder. we can't list the .po files as a target,
139 # because then scons -c will remove them (even Precious doesn't alter
140 # this). this function is called whenever a .mo file is being
141 # built, and will conditionally update the .po file if necessary.
144 def po_helper(po,pot):
150 print 'Updating ' + po
151 return os.spawnvp (os.P_WAIT, 'msgmerge', args)
153 # mo_builder: builder function for (binary) message catalogs (.mo)
155 # first source: .po file
156 # second source: .pot file
159 def mo_builder(target,source,env):
160 po_helper (source[0].get_path(), source[1].get_path())
164 target[0].get_path(),
167 return os.spawnvp (os.P_WAIT, 'msgfmt', args)
169 mo_bld = Builder (action = mo_builder)
170 env.Append(BUILDERS = {'MoBuild' : mo_bld})
172 # pot_builder: builder function for message templates (.pot)
174 # source: list of C/C++ etc. files to extract messages from
177 def pot_builder(target,source,env):
182 '-o', target[0].get_path(),
183 "--default-domain=" + env['PACKAGE'],
184 '--copyright-holder="Paul Davis"' ]
185 args += [ src.get_path() for src in source ]
187 return os.spawnvp (os.P_WAIT, 'xgettext', args)
189 pot_bld = Builder (action = pot_builder)
190 env.Append(BUILDERS = {'PotBuild' : pot_bld})
193 # utility function, not a builder
196 def i18n (buildenv, sources, installenv):
197 domain = buildenv['PACKAGE']
198 potfile = buildenv['POTFILE']
200 installenv.Alias ('potupdate', buildenv.PotBuild (potfile, sources))
202 p_oze = [ os.path.basename (po) for po in glob.glob ('po/*.po') ]
203 languages = [ po.replace ('.po', '') for po in p_oze ]
204 m_oze = [ po.replace (".po", ".mo") for po in p_oze ]
207 po = 'po/' + mo.replace (".mo", ".po")
208 installenv.Alias ('install', buildenv.MoBuild (mo, [ po, potfile ]))
210 for lang in languages[:]:
211 modir = (os.path.join (install_prefix, 'share/locale/' + lang + '/LC_MESSAGES/'))
212 moname = domain + '.mo'
213 installenv.Alias('install', installenv.InstallAs (os.path.join (modir, moname), lang + '.mo'))
216 # A generic builder for version.cc files
218 # note: requires that DOMAIN, MAJOR, MINOR, MICRO are set in the construction environment
219 # note: assumes one source files, the header that declares the version variables
221 def version_builder (target, source, env):
222 text = "int " + env['DOMAIN'] + "_major_version = " + str (env['MAJOR']) + ";\n"
223 text += "int " + env['DOMAIN'] + "_minor_version = " + str (env['MINOR']) + ";\n"
224 text += "int " + env['DOMAIN'] + "_micro_version = " + str (env['MICRO']) + ";\n"
227 o = file (target[0].get_path(), 'w')
231 print "Could not open", target[0].get_path(), " for writing\n"
234 text = "#ifndef __" + env['DOMAIN'] + "_version_h__\n";
235 text += "#define __" + env['DOMAIN'] + "_version_h__\n";
236 text += "extern int " + env['DOMAIN'] + "_major_version;\n"
237 text += "extern int " + env['DOMAIN'] + "_minor_version;\n"
238 text += "extern int " + env['DOMAIN'] + "_micro_version;\n"
239 text += "#endif /* __" + env['DOMAIN'] + "_version_h__ */\n";
242 o = file (target[1].get_path(), 'w')
246 print "Could not open", target[1].get_path(), " for writing\n"
251 version_bld = Builder (action = version_builder)
252 env.Append (BUILDERS = {'VersionBuild' : version_bld})
255 # a builder that makes a hard link from the 'source' executable to a name with
256 # a "build ID" based on the most recent CVS activity that might be reasonably
257 # related to version activity. this relies on the idea that the SConscript
258 # file that builds the executable is updated with new version info and committed
259 # to the source code repository whenever things change.
262 def versioned_builder(target,source,env):
263 # build ID is composed of a representation of the date of the last CVS transaction
264 # for this (SConscript) file
267 o = file (source[0].get_dir().get_path() + '/CVS/Entries', "r")
269 print "Could not CVS/Entries for reading"
273 lines = o.readlines()
275 if line[0:12] == '/SConscript/':
276 parts = line.split ("/")
282 print "No SConscript CVS update info found - versioned executable cannot be built"
285 tag = time.strftime ('%Y%M%d%H%m', time.strptime (last_date));
286 print "The current build ID is " + tag
288 tagged_executable = source[0].get_path() + '-' + tag
290 if os.path.exists (tagged_executable):
291 print "Replacing existing executable with the same build tag."
292 os.unlink (tagged_executable)
294 return os.link (source[0].get_path(), tagged_executable)
296 verbuild = Builder (action = versioned_builder)
297 env.Append (BUILDERS = {'VersionedExecutable' : verbuild})
300 # source tar file builder
303 def distcopy (target, source, env):
304 treedir = str (target[0])
308 except OSError, (errnum, strerror):
309 if errnum != errno.EEXIST:
310 print 'mkdir ', treedir, ':', strerror
314 # we don't know what characters might be in the file names
315 # so quote them all before passing them to the shell
317 all_files = ([ str(s) for s in source ])
318 cmd += " ".join ([ "'%s'" % quoted for quoted in all_files])
319 cmd += ' | (cd ' + treedir + ' && tar xf -)'
323 def tarballer (target, source, env):
324 cmd = 'tar -jcf ' + str (target[0]) + ' ' + str(source[0]) + " --exclude '*~'"
325 print 'running ', cmd, ' ... '
329 dist_bld = Builder (action = distcopy,
330 target_factory = SCons.Node.FS.default_fs.Entry,
331 source_factory = SCons.Node.FS.default_fs.Entry,
334 tarball_bld = Builder (action = tarballer,
335 target_factory = SCons.Node.FS.default_fs.Entry,
336 source_factory = SCons.Node.FS.default_fs.Entry)
338 env.Append (BUILDERS = {'Distribute' : dist_bld})
339 env.Append (BUILDERS = {'Tarball' : tarball_bld})
341 # ----------------------------------------------------------------------
342 # Construction environment setup
343 # ----------------------------------------------------------------------
347 libraries['core'] = LibraryInfo (CPPPATH = [ '#libs'])
349 libraries['sndfile'] = LibraryInfo()
350 libraries['sndfile'].ParseConfig('pkg-config --cflags --libs sndfile')
352 libraries['lrdf'] = LibraryInfo()
353 libraries['lrdf'].ParseConfig('pkg-config --cflags --libs lrdf')
355 libraries['raptor'] = LibraryInfo()
356 libraries['raptor'].ParseConfig('pkg-config --cflags --libs raptor')
358 libraries['samplerate'] = LibraryInfo()
359 libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate')
361 libraries['jack'] = LibraryInfo()
362 libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
364 libraries['xml'] = LibraryInfo()
365 libraries['xml'].ParseConfig('pkg-config --cflags --libs libxml-2.0')
367 libraries['glib2'] = LibraryInfo()
368 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs glib-2.0')
369 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gobject-2.0')
370 libraries['glib2'].ParseConfig ('pkg-config --cflags --libs gmodule-2.0')
372 libraries['gtk2'] = LibraryInfo()
373 libraries['gtk2'].ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
375 libraries['pango'] = LibraryInfo()
376 libraries['pango'].ParseConfig ('pkg-config --cflags --libs pango')
378 libraries['libgnomecanvas2'] = LibraryInfo()
379 libraries['libgnomecanvas2'].ParseConfig ('pkg-config --cflags --libs libgnomecanvas-2.0')
381 libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
383 libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
384 libraries['midi++2'] = LibraryInfo (LIBS='midi++', LIBPATH='#libs/midi++2', CPPPATH='#libs/midi++2')
385 libraries['pbd3'] = LibraryInfo (LIBS='pbd', LIBPATH='#libs/pbd3', CPPPATH='#libs/pbd3')
386 libraries['gtkmm2ext'] = LibraryInfo (LIBS='gtkmm2ext', LIBPATH='#libs/gtkmm2ext', CPPPATH='#libs/gtkmm2ext')
387 #libraries['cassowary'] = LibraryInfo(LIBS='cassowary', LIBPATH='#libs/cassowary', CPPPATH='#libs/cassowary')
389 libraries['fst'] = LibraryInfo()
391 libraries['fst'].ParseConfig('pkg-config --cflags --libs libfst')
394 # Audio/MIDI library (needed for MIDI, since audio is all handled via JACK)
397 conf = Configure(env)
399 if conf.CheckCHeader('alsa/asoundlib.h'):
400 libraries['sysmidi'] = LibraryInfo (LIBS='asound')
401 env['SYSMIDI'] = 'ALSA Sequencer'
402 subst_dict['%MIDITAG%'] = "seq"
403 subst_dict['%MIDITYPE%'] = "alsa/sequencer"
404 elif conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'):
405 # this line is needed because scons can't handle -framework in ParseConfig() yet.
406 libraries['sysmidi'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -bind_at_load')
407 env['SYSMIDI'] = 'CoreMIDI'
408 subst_dict['%MIDITAG%'] = "ardour"
409 subst_dict['%MIDITYPE%'] = "coremidi"
415 libraries['sigc2'] = LibraryInfo()
416 libraries['sigc2'].ParseConfig('pkg-config --cflags --libs sigc++-2.0')
418 libraries['gtkmm2'] = LibraryInfo()
419 libraries['gtkmm2'].ParseConfig ('pkg-config --cflags --libs gtkmm-2.0')
421 libraries['soundtouch'] = LibraryInfo(LIBS='SoundTouch')
441 libraries['sigc2'] = LibraryInfo(LIBS='sigc++2',
442 LIBPATH='#libs/sigc++2',
443 CPPPATH='#libs/sigc++2')
444 libraries['glibmm2'] = LibraryInfo(LIBS='glibmm2',
445 LIBPATH='#libs/glibmm2',
446 CPPPATH='#libs/glibmm2')
447 libraries['pangomm'] = LibraryInfo(LIBS='pangomm',
448 LIBPATH='#libs/gtkmm2/pango',
449 CPPPATH='#libs/gtkmm2/pango')
450 libraries['atkmm'] = LibraryInfo(LIBS='atkmm',
451 LIBPATH='#libs/gtkmm2/atk',
452 CPPPATH='#libs/gtkmm2/atk')
453 libraries['gdkmm2'] = LibraryInfo(LIBS='gdkmm2',
454 LIBPATH='#libs/gtkmm2/gdk',
455 CPPPATH='#libs/gtkmm2/gdk')
456 libraries['gtkmm2'] = LibraryInfo(LIBS='gtkmm2',
457 LIBPATH="#libs/gtkmm2/gtk",
458 CPPPATH='#libs/gtkmm2/gtk/')
459 libraries['libgnomecanvasmm'] = LibraryInfo(LIBS='libgnomecanvasmm',
460 LIBPATH='#libs/libgnomecanvasmm',
461 CPPPATH='#libs/libgnomecanvasmm')
463 libraries['soundtouch'] = LibraryInfo(LIBS='soundtouch',
464 LIBPATH='#libs/soundtouch',
465 CPPPATH=['#libs', '#libs/soundtouch'])
486 'libs/libgnomecanvasmm',
492 opts.Save('scache.conf', env)
493 Help(opts.GenerateHelpText(env))
495 if os.environ.has_key('PATH'):
496 env.Append(PATH = os.environ['PATH'])
498 if os.environ.has_key('PKG_CONFIG_PATH'):
499 env.Append(PKG_CONFIG_PATH = os.environ['PKG_CONFIG_PATH'])
501 if os.environ.has_key('CC'):
502 env['CC'] = os.environ['CC']
504 if os.environ.has_key('CXX'):
505 env['CXX'] = os.environ['CXX']
507 if os.environ.has_key('DISTCC_HOSTS'):
508 env['ENV']['DISTCC_HOSTS'] = os.environ['DISTCC_HOSTS']
509 env['ENV']['HOME'] = os.environ['HOME']
511 final_prefix = '$PREFIX'
512 install_prefix = '$DESTDIR/$PREFIX'
514 if env['PREFIX'] == '/usr':
515 final_config_prefix = '/etc'
517 final_config_prefix = env['PREFIX'] + '/etc'
519 config_prefix = '$DESTDIR' + final_config_prefix
522 # SCons should really do this for us
524 conf = Configure (env)
526 have_cxx = conf.TryAction (Action (env['CXX'] + ' --version'));
528 print "This system has no functional C++ compiler. You cannot build Ardour from source without one."
531 print "Congratulations, you have a functioning C++ compiler."
536 # Compiler flags and other system-dependent stuff
540 debug_flags = [ '-g' ]
542 # guess at the platform, used to define compiler flags
544 config_guess = os.popen("tools/config.guess").read()[:-1]
550 config = config_guess.split ("-")
553 # on OS X darwinports puts things in /opt/local by default
555 if config[config_arch] == 'apple':
556 if os.path.isdir('/opt/local/lib'):
557 libraries['core'].Append (LIBPATH = [ '/opt/local/lib' ])
558 if os.path.isdir('/opt/local/include'):
559 libraries['core'].Append (CPPPATH = [ '/opt/local/include' ])
560 if config[config_cpu] == 'powerpc':
562 # Apple/PowerPC optimization options
564 # -mcpu=7450 does not reliably work with gcc 3.*
566 if env['NOARCH'] == 0:
567 if env['ALTIVEC'] == 1:
568 if config[config_arch] == 'apple':
569 opt_flags.extend ([ "-mcpu=7450", "-faltivec"])
571 opt_flags.extend ([ "-mcpu=7400", "-maltivec", "-mabi=altivec"])
573 opt_flags.extend([ "-mcpu=750", "-mmultiple" ])
574 opt_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"])
576 elif ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_64", config[config_cpu]) != None)):
578 build_host_supports_sse = 0
580 if env['NOARCH'] == 0:
582 debug_flags.append ("-DARCH_X86")
583 opt_flags.append ("-DARCH_X86")
585 if config[config_kernel] == 'linux' :
587 flag_line = os.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1]
588 x86_flags = flag_line.split (": ")[1:][0].split (' ')
590 if "mmx" in x86_flags:
591 opt_flags.append ("-mmmx")
592 if "sse" in x86_flags:
593 build_host_supports_sse = 1
594 if "3dnow" in x86_flags:
595 opt_flags.append ("-m3dnow")
597 if config[config_cpu] == "i586":
598 opt_flags.append ("-march=i586")
599 elif config[config_cpu] == "i686":
600 opt_flags.append ("-march=i686")
602 if env['USE_SSE_EVERYWHERE'] == 1:
603 opt_flags.extend (["-msse", "-mfpmath=sse"])
604 debug_flags.extend (["-msse", "-mfpmath=sse"])
605 if build_host_supports_sse != 1:
606 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)"
608 if env['BUILD_SSE_OPTIMIZATIONS'] == 1:
609 opt_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
610 debug_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
611 if build_host_supports_sse != 1:
612 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)"
614 # end of processor-specific section
617 # ARCH="..." overrides all
620 if env['ARCH'] != '':
621 opt_flags = env['ARCH'].split()
624 # prepend boiler plate optimization flags
629 "-fomit-frame-pointer",
634 if env['DEBUG'] == 1:
635 env.Append(CCFLAGS=" ".join (debug_flags))
637 env.Append(CCFLAGS=" ".join (opt_flags))
639 env.Append(CCFLAGS="-Wall")
642 env.Append(CCFLAGS="-DVST_SUPPORT")
646 # everybody needs this
649 env.Merge ([ libraries['core'] ])
655 conf = Configure (env)
658 print 'Checking for internationalization support ...'
659 have_gettext = conf.TryAction(Action('xgettext --version'))
660 if have_gettext[0] != 1:
661 print 'This system is not configured for internationalized applications (no xgettext command). An english-only version will be built\n'
664 if conf.CheckCHeader('libintl.h') == None:
665 print 'This system is not configured for internationalized applications (no libintl.h). An english-only version will be built\n'
672 env.Append(CCFLAGS="-DENABLE_NLS")
675 Export('env install_prefix final_prefix config_prefix final_config_prefix libraries i18n version subst_dict')
678 # the configuration file may be system dependent
681 conf = env.Configure ()
683 if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Versions/A/Headers/CoreAudio.h'):
684 subst_dict['%JACK_BACKEND%'] = "coreaudio:Built-in Audio:in"
686 subst_dict['%JACK_BACKEND%'] = "alsa_pcm:playback_"
688 # posix_memalign available
689 if not conf.CheckFunc('posix_memalign'):
690 print 'Did not find posix_memalign(), using malloc'
691 env.Append(CCFLAGS='-DNO_POSIX_MEMALIGN')
696 rcbuild = env.SubstInFile ('ardour.rc','ardour.rc.in', SUBST_DICT = subst_dict)
698 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour'), 'ardour_system.rc'))
699 env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour'), 'ardour.rc'))
705 Precious (env['DISTTREE'])
708 # note the special "cleanfirst" source name. this triggers removal
709 # of the existing disttree
712 env.Distribute (env['DISTTREE'],
714 'COPYING', 'PACKAGER_README', 'README',
719 glob.glob ('DOCUMENTATION/AUTHORS*') +
720 glob.glob ('DOCUMENTATION/CONTRIBUTORS*') +
721 glob.glob ('DOCUMENTATION/TRANSLATORS*') +
722 glob.glob ('DOCUMENTATION/BUILD*') +
723 glob.glob ('DOCUMENTATION/FAQ*') +
724 glob.glob ('DOCUMENTATION/README*')
727 srcdist = env.Tarball(env['TARBALL'], env['DISTTREE'])
728 env.Alias ('srctar', srcdist)
730 # don't leave the distree around
732 env.AddPreAction (env['DISTTREE'], Action ('rm -rf ' + str (File (env['DISTTREE']))))
733 env.AddPostAction (srcdist, Action ('rm -rf ' + str (File (env['DISTTREE']))))
739 for subdir in coredirs:
740 SConscript (subdir + '/SConscript')
742 for sublistdir in [subdirs, gtk_subdirs]:
743 for subdir in sublistdir:
744 SConscript (subdir + '/SConscript')
747 env.Clean ('scrub', [ 'scache.conf', '.sconf_temp', '.sconsign.dblite', 'config.log'])