4 # Contents: Build script for SuperCollider plugins
5 # Authors: Stefan Kersten <sk AT k-hornz DOT de>
6 # nescivi AT gmail DOT com
8 # This is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2, or (at your option) any later version.
12 # This software is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 # You should have received a copy of the GNU General Public License along
18 # with this program; if not, write to the Free Software Foundation, Inc., 675
19 # Mass Ave, Cambridge, MA 02139, USA.
21 # ======================================================================
24 # This script compiles `simple' SuperCollider plugins, i.e. plugins that
25 # consist of only one source file. Source files may be C++ implementations or
26 # a Faust[1] DSP specification.
28 # Source file paths are passed either in a file (specified with the
29 # PLUGIN_SOURCES option) or via standard input.
31 # E.g., when source file paths are in a file called sources.txt:
33 # $ sc-build-plugins PLUGIN_SOURCES=sources.txt
35 # Alternatively, source file paths can be passed via standard input:
37 # $ my-command | sc-build-plugins
39 # ======================================================================
41 # ======================================================================
42 # Bootstrap (ruby): Execute scons with content after __END__
46 Tempfile
.open("r") { |f
|
49 system("scons", "-f", f
.path
, *ARGV)
54 # ======================================================================
55 # NOTE: Please use an indentation level of 4 spaces, i.e. no mixing of
57 # ======================================================================
59 # ======================================================================
61 # ======================================================================
63 EnsureSConsVersion(0,96)
64 EnsurePythonVersion(2,3)
67 # ======================================================================
69 # ======================================================================
79 # ======================================================================
81 # ======================================================================
83 PACKAGE
= 'SuperCollider'
85 def short_cpu_name(cpu
):
86 if cpu
== 'Power Macintosh':
90 PLATFORM
= os
.uname()[0].lower()
91 CPU
= short_cpu_name(os
.uname()[4])
93 if PLATFORM
== 'darwin':
94 PLATFORM_SYMBOL
= 'SC_DARWIN'
97 elif PLATFORM
== 'freebsd':
98 PLATFORM_SYMBOL
= 'SC_FREEBSD'
100 DEFAULT_PREFIX
= '/usr/local'
101 elif PLATFORM
== 'linux':
102 PLATFORM_SYMBOL
= 'SC_LINUX'
104 DEFAULT_PREFIX
= '/usr/local'
105 elif PLATFORM
== 'windows':
106 PLATFORM_SYMBOL
= 'SC_WIN32'
110 print
'Unknown platform: %s' % PLATFORM
114 DEFAULT_OPT_ARCH
= '7450'
115 elif CPU
in [ 'i586', 'i686' ]:
116 # FIXME: better detection
117 DEFAULT_OPT_ARCH
= CPU
119 DEFAULT_OPT_ARCH
= None
121 # ======================================================================
123 # ======================================================================
125 def make_os_env(*keys
):
133 def CheckPKGConfig(context
, version):
134 context
.Message( 'Checking for pkg-config... ' )
135 ret
= context
.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
136 context
.Result( ret
)
139 def CheckPKG(context
, name
):
140 context
.Message('Checking for %s... ' % name
)
141 ret
= context
.TryAction('pkg-config --exists \'%s\'' % name
)[0]
144 res
= Environment(ENV = make_os_env('PATH', 'PKG_CONFIG_PATH'))
145 res
.ParseConfig('pkg-config --cflags --libs \'%s\'' % name
)
146 res
['PKGCONFIG'] = name
150 def get_new_pkg_env():
151 return Environment(ENV = make_os_env('PATH', 'PKG_CONFIG_PATH'))
153 def merge_lib_info(env, *others
):
155 env.AppendUnique(CCFLAGS
= other
.get('CCFLAGS', []))
156 env.AppendUnique(CPPDEFINES
= other
.get('CPPDEFINES', []))
157 env.AppendUnique(CPPPATH
= other
.get('CPPPATH', []))
158 env.AppendUnique(CXXFLAGS
= other
.get('CXXFLAGS', []))
159 env.AppendUnique(LIBS
= other
.get('LIBS', []))
160 env.AppendUnique(LIBPATH
= other
.get('LIBPATH', []))
161 env['LINKFLAGS'] = env['LINKFLAGS'] + other
.get('LINKFLAGS', "")
163 def flatten_dir(dir
):
165 for root
, dirs
, files
in os
.walk(dir
):
166 if 'CVS' in dirs
: dirs
.remove('CVS')
167 if '.svn' in dirs
: dirs
.remove('.svn')
169 res
.append(os
.path
.join(root
, f
))
172 def install_dir(env, src_dir
, dst_dir
, filter_re
, strip_levels
=0):
174 for root
, dirs
, files
in os
.walk(src_dir
):
177 if 'CVS' in dirs
: dirs
.remove('CVS')
178 if '.svn' in dirs
: dirs
.remove('.svn')
180 if filter_re
.match(d
):
181 src_paths
+= flatten_dir(os
.path
.join(root
, d
))
184 if filter_re
.match(f
):
185 src_paths
.append(os
.path
.join(root
, f
))
190 *f
.split(os
.path
.sep
)[strip_levels
:]),
192 nodes
+= env.InstallAs(dst_paths
, src_paths
)
196 pat
= re
.compile('^install.*$')
197 for x
in COMMAND_LINE_TARGETS
:
198 if pat
.match(x
): return True
202 return os
.path
.join(prefix
, 'bin')
204 return os
.path
.join(prefix
, 'lib')
206 def pkg_data_dir(prefix
, *args
):
207 if PLATFORM
== 'darwin':
208 base
= os
.path
.join(prefix
, 'Library/Application Support')
210 base
= os
.path
.join(prefix
, 'share')
211 return os
.path
.join(base
, PACKAGE
, *args
)
212 def pkg_doc_dir(prefix
, *args
):
213 if PLATFORM
== 'darwin':
214 base
= os
.path
.join(prefix
, 'Library/Documentation')
216 base
= os
.path
.join(prefix
, 'share', 'doc')
217 return os
.path
.join(base
, PACKAGE
, *args
)
218 def pkg_include_dir(prefix
, *args
):
219 return os
.path
.join(prefix
, 'include', PACKAGE
, *args
)
220 def pkg_lib_dir(prefix
, *args
):
221 return os
.path
.join(lib_dir(prefix
), PACKAGE
, *args
)
223 def pkg_classlib_dir(prefix
, *args
):
224 return pkg_data_dir(prefix
, 'SCClassLibrary', *args
)
225 def pkg_extension_dir(prefix
, *args
):
226 return pkg_data_dir(prefix
, 'Extensions', *args
)
228 def make_opt_flags(env):
231 ## "-fomit-frame-pointer", # can behave strangely for sclang
235 arch
= env.get('OPT_ARCH')
238 flags
.extend([ "-mcpu=%s" % (arch
,) ])
240 flags
.extend([ "-march=%s" % (arch
,) ])
242 flags
.extend([ "-fsigned-char", "-mhard-float",
243 ## "-mpowerpc-gpopt", # crashes sqrt
248 # ======================================================================
250 # ======================================================================
252 def faustInitEnvironment(env):
254 action
= 'faust -a supercollider.cpp -o $TARGET $SOURCE',
258 action
= ['faust -o /dev/null -xml $SOURCE', Move('$TARGET', '${SOURCE}.xml')],
262 action
= '$FAUST2SC --prefix="$FAUST2SC_PREFIX" -o $TARGET $SOURCE',
264 src_suffix
= '.dsp.xml')
265 env.Append(BUILDERS
= { 'Faust' : dsp
,
269 # ======================================================================
270 # Command line options
271 # ======================================================================
273 opts
= Options('scache.conf', ARGUMENTS
)
275 BoolOption('ALTIVEC',
276 'Build with Altivec support', 1),
277 PathOption('BUILD_DIR',
278 'Directory to build temporary files in', '.',
279 PathOption
.PathIsDirCreate
),
280 BoolOption('BUILD_SC', 'Build SuperCollider class files', 1),
281 BoolOption('BUILD_XML', 'Build Faust XML files', 1),
282 ('CC', 'C compiler executable'),
283 ('CCFLAGS', 'C compiler flags'),
284 ('CXX', 'C++ compiler executable'),
285 ('CXXFLAGS', 'C++ compiler flags'),
286 BoolOption('CROSSCOMPILE',
287 'Crosscompile for another platform (does not do SSE support check)', 0),
289 'Build with debugging information', 0),
290 PathOption('DESTDIR',
291 'Intermediate installation prefix for packaging', '/'),
292 PathOption('FAUST2SC',
293 'Path to faust2sc script', 'faust2sc',
294 PathOption
.PathAccept
),
296 'Prefix for SC classes generated from Faust definitions', ''),
298 'Installation prefix', DEFAULT_PREFIX
),
299 PathOption('INSTALLDIR',
300 'Installation directory', '',
301 PathOption
.PathAccept
),
303 'Build with SSE support', 1),
305 'Architecture to optimize for', DEFAULT_OPT_ARCH
),
306 PathOption('PLUGIN_SOURCES',
307 'File containing plugin sources (C++ or Faust)', None
,
308 PathOption
.PathAccept
),
309 PathOption('SC_SOURCE_DIR',
310 'SuperCollider source directory', '../supercollider',
311 PathOption
.PathAccept
),
314 if PLATFORM
== 'darwin':
316 BoolOption('UNIVERSAL',
317 'Build universal binaries (see UNIVERSAL_ARCHS)', 1),
318 ListOption('UNIVERSAL_ARCHS',
319 'Architectures to build for',
320 'all', ['ppc', 'i386'])
323 # ======================================================================
325 # ======================================================================
327 env = Environment(options
= opts
,
328 ENV = make_os_env('PATH', 'PKG_CONFIG_PATH'),
330 URL
= 'http://supercollider.sourceforge.net')
331 env.Append(PATH
= ['/usr/local/bin', '/usr/bin', '/bin'])
332 faustInitEnvironment(env)
334 # checks for DISTCC and CCACHE as used in modern linux-distros:
336 if os
.path
.exists('/usr/lib/distcc/bin'):
337 os
.environ
['PATH'] = '/usr/lib/distcc/bin:' + os
.environ
['PATH']
338 env['ENV']['DISTCC_HOSTS'] = os
.environ
['DISTCC_HOSTS']
340 if os
.path
.exists('/usr/lib/ccache/bin'):
341 os
.environ
['PATH'] = '/usr/lib/ccache/bin:' + os
.environ
['PATH']
342 env['ENV']['CCACHE_DIR'] = os
.environ
['CCACHE_DIR']
344 env['ENV']['PATH'] = os
.environ
['PATH']
345 env['ENV']['HOME'] = os
.environ
['HOME']
347 # ======================================================================
348 # installation directories
349 # ======================================================================
351 FINAL_PREFIX
= '$PREFIX'
352 INSTALL_PREFIX
= os
.path
.join('$DESTDIR', '$PREFIX')
354 # ======================================================================
356 # ======================================================================
359 return Configure(env, custom_tests
= { 'CheckPKGConfig' : CheckPKGConfig
,
360 'CheckPKG' : CheckPKG
})
362 def isDefaultBuild():
363 return not env.GetOption('clean') and not 'debian' in COMMAND_LINE_TARGETS
365 conf
= make_conf(env)
372 if not conf
.CheckPKGConfig('0'):
373 print
'pkg-config not found.'
376 # SC includes and flags
377 success
, libraries
['scplugin'] = conf
.CheckPKG('libscplugin')
379 success
, libraries
['scplugin'] = conf
.CheckPKG('libscsynth')
381 libraries
['scplugin']['LIBS'] = []
383 src_dir
= env['SC_SOURCE_DIR']
384 if os
.path
.isdir(src_dir
):
385 libraries
['scplugin'] = Environment(
386 CPPDEFINES
= [PLATFORM_SYMBOL
],
387 CPPPATH
= [os
.path
.join(src_dir
, 'Headers/common'),
388 os
.path
.join(src_dir
, 'Headers/plugin_interface'),
389 os
.path
.join(src_dir
, 'Headers/server')])
391 print
"Please specify the SC_SOURCE_DIR option."
394 libraries
['scplugin'] = Environment()
396 # only _one_ Configure context can be alive at a time
401 if PLATFORM
== 'darwin':
402 altivec_flags
= [ '-faltivec' ]
404 altivec_flags
= [ '-maltivec', '-mabi=altivec' ]
405 libraries
['altivec'] = env.Copy()
406 libraries
['altivec'].Append(
407 CCFLAGS
= altivec_flags
,
408 CPPDEFINES
= [('SC_MEMORY_ALIGNMENT', 16)])
409 altiConf
= Configure(libraries
['altivec'])
410 features
['altivec'] = altiConf
.CheckCHeader('altivec.h')
413 features
['altivec'] = False
417 libraries
['sse'] = env.Copy()
418 libraries
['sse'].Append(
419 CCFLAGS
= ['-msse', '-mfpmath=sse'],
420 CPPDEFINES
= [('SC_MEMORY_ALIGNMENT', 16)])
421 sseConf
= Configure(libraries
['sse'])
422 hasSSEHeader
= sseConf
.CheckCHeader('xmmintrin.h')
423 if env['CROSSCOMPILE']:
424 build_host_supports_sse
= True
425 print
'NOTICE: cross compiling for another platform: assuming SSE support'
427 build_host_supports_sse
= False
429 if PLATFORM
== 'freebsd':
430 machine_info
= os
.popen ("sysctl -a hw.instruction_sse").read()[:-1]
432 if "1" in [x
for x
in machine_info
]:
433 build_host_supports_sse
= True
435 elif PLATFORM
!= 'darwin':
436 flag_line
= os
.popen ("cat /proc/cpuinfo | grep '^flags'").read()[:-1]
437 x86_flags
= flag_line
.split (": ")[1:][0].split ()
439 machine_info
= os
.popen ("sysctl -a machdep.cpu").read()[:-1]
440 x86_flags
= machine_info
.split()
441 if "sse" in [x
.lower() for x
in x86_flags
]:
442 build_host_supports_sse
= True
443 print
'NOTICE: CPU has SSE support'
445 print
'NOTICE: CPU does not have SSE support'
446 features
['sse'] = hasSSEHeader
and build_host_supports_sse
449 features
['sse'] = False
451 opts
.Save('scache.conf', env)
452 Help(opts
.GenerateHelpText(env))
454 # defines and compiler flags
456 CPPDEFINES
= [ '_REENTRANT' ],
457 CCFLAGS
= [ '-Wno-unknown-pragmas' ],
458 CXXFLAGS
= [ '-Wno-deprecated' ]
463 env.Append(CCFLAGS
= '-g')
466 CCFLAGS
= make_opt_flags(env),
467 CPPDEFINES
= ['NDEBUG'])
470 if PLATFORM
== 'darwin':
472 CCFLAGS
= '-fvisibility=hidden'
476 if features
['altivec']:
477 merge_lib_info(env, libraries
['altivec'])
478 elif features
['sse']:
479 merge_lib_info(env, libraries
['sse'])
481 env.AppendUnique(CPPDEFINES
= [('SC_MEMORY_ALIGNMENT', 1)])
483 # ======================================================================
485 # ======================================================================
487 pluginEnv
= env.Copy(
489 SHLIBSUFFIX
= PLUGIN_EXT
)
490 if PLATFORM
== 'darwin':
491 pluginEnv
['SHLINKFLAGS'] = '$LINKFLAGS -bundle'
492 merge_lib_info(pluginEnv
, libraries
['scplugin'])
494 def get_sources(env):
495 if env.has_key('PLUGIN_SOURCES') and env['PLUGIN_SOURCES']:
496 fd
= open(env['PLUGIN_SOURCES'], "r")
497 sources
= fd
.readlines()
500 print
"Reading PLUGIN_SOURCES from <stdin>"
501 sources
= sys
.stdin.readlines()
502 return map(lambda x
: x
.strip(), sources
)
504 def make_build_file(env, path
, ext
=""):
505 newpath
= os
.path
.splitext(os
.path
.basename(path
))[0] + ext
506 return os
.path
.join(env['BUILD_DIR'], newpath
)
508 def make_plugin_target(env, path
):
509 name
, ext
= os
.path
.splitext(path
)
511 src
= env.Faust(make_build_file(env, path
), path
)
514 return env.SharedLibrary(make_build_file(env, path
), src
)
516 def make_xml_target(env, path
):
517 name
, ext
= os
.path
.splitext(path
)
520 xml
= env.FaustXML(make_build_file(env, path
), path
)
521 sc
= env.FaustSC(make_build_file(env, path
), xml
)
526 elif
env['BUILD_XML']:
527 return env.FaustXML(make_build_file(env, path
), path
)
530 sources
= get_sources(env)
531 plugins
= map(lambda x
: make_plugin_target(pluginEnv
, x
), sources
)
532 xml
= map(lambda x
: make_xml_target(pluginEnv
, x
), sources
)
534 # ======================================================================
536 # ======================================================================
538 env.Alias('install', env.Install(
539 os
.path
.join(pkg_extension_dir(INSTALL_PREFIX
), env['INSTALLDIR']),
542 # ======================================================================
544 # ======================================================================
547 Split('config.log scache.conf .sconf_temp .sconsign.dblite'))
549 # ======================================================================
550 # configuration summary
551 # ======================================================================
557 print
'------------------------------------------------------------------------'
558 print
' ALTIVEC: %s' % yesorno(features
['altivec'])
559 print
' BUILD_SC: %s' % yesorno(env['BUILD_SC'])
560 print
' BUILD_XML: %s' % yesorno(env['BUILD_XML'])
561 print
' FAUST2SC: %s' % env['FAUST2SC']
562 if env['INSTALLDIR']:
563 print
' INSTALLDIR: %s' % env['INSTALLDIR']
564 print
' DEBUG: %s' % yesorno(env['DEBUG'])
565 if env.has_key('PLUGIN_SOURCES'):
566 print
' PLUGIN_SOURCES: %s' % env['PLUGIN_SOURCES']
568 print
' PLUGIN_SOURCES: %s' % 'stdin'
569 print
' PREFIX: %s' % env['PREFIX']
570 print
' SC_SOURCE_DIR %s' % env['SC_SOURCE_DIR']
571 print
' SSE: %s' % yesorno(features
['sse'])
572 print
' CROSSCOMPILE: %s' % yesorno(env['CROSSCOMPILE'])
573 print
'------------------------------------------------------------------------'
575 # ======================================================================