4 Experimental scons (www.scons.org) building:
6 scons TARGET builds from source directory ./TARGET (not recursive)
11 scons lily # build lily
13 LILYPONDPREFIX=out-scons/usr/share/lilypond lily/out-scons/lilypond-bin
14 scons doc # build web doc
16 ? scons fonts # build all font stuff (split this? )
18 scons config # reconfigure
20 scons / # builds all possible targets
26 scons build=DIR # scrdir build, write to new tree =build
27 scons out=DIR # write output to deeper dir DIR
29 Optionally, make a custom.py. I have
36 os.path.join (os.getcwd (), '=install')
37 prefix=os.path.join (os.environ['HOME'], 'usr', 'pkg', 'lilypond')
46 # * separate environments?
47 # - compile environment checks headers and libraries
48 # - doc environment checks doc stuff
50 # * commandline targets:
52 # * more fine-grained config.h -- move lilypondprefix to version.hh?
53 # - config.h: changes after system upgrades, affects all files
54 # - version.hh: prefix, version etc? affects few
63 # SConscripts are only needed in directories where something needs
64 # to be done, building or installing
65 # TODO: Documentation/*, input/*/*, vim, po
66 # rename Documentation/* to ./doc?
68 subdirs = ['flower', 'lily', 'mf', 'scm', 'ly', 'Documentation',
69 'Documentation/user', 'input', 'scripts', 'elisp',
70 'buildscripts', 'cygwin', 'debian']
73 scons [KEY=VALUE].. [TARGET]..
75 where TARGET is config|lily|all|fonts|doc|tar|dist|release
79 config_cache = 'config.cache'
92 # Put your favourite stuff in custom.py
93 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
94 opts.Add ('prefix', 'Install prefix', '/usr/')
95 opts.Add ('out', 'Output directory', 'out-scons')
96 opts.Add ('build', 'Build directory', '.')
98 BoolOption ('warnings', 'compile with -Wall and similiar',
100 BoolOption ('debugging', 'compile with debugging symbols',
102 BoolOption ('optimising', 'compile with optimising',
104 BoolOption ('shared', 'build shared libraries',
106 BoolOption ('static', 'build static libraries',
108 BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
110 BoolOption ('verbose', 'run commands with verbose flag',
114 srcdir = Dir ('.').srcnode ().abspath
117 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
119 package = packagepython.Package (srcdir)
122 prefix = '/usr/local'
124 version = packagepython.version_tuple_to_str (package.version)
125 bindir = os.path.join (prefix, 'bin')
126 sharedir = os.path.join (prefix, 'share')
127 libdir = os.path.join (prefix, 'lib')
128 localedir = os.path.join (sharedir, 'locale')
129 sharedir_package = os.path.join (sharedir, package.name)
130 sharedir_package_version = os.path.join (sharedir_package, version)
131 lilypondprefix = sharedir_package_version
133 ENV = { 'PATH' : os.environ['PATH'] }
134 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH']:
135 if os.environ.has_key(key):
136 ENV[key] = os.environ[key]
146 TOPLEVEL_VERSION = version,
147 lilypond_datadir = sharedir_package,
148 local_lilypond_datadir = sharedir_package_version,
149 lilypondprefix = lilypondprefix,
150 sharedir_package = sharedir_package,
151 sharedir_package_version = sharedir_package_version,
156 PYTHON = '/usr/bin/python',
157 MAKEINFO = 'LANG= makeinfo',
159 LILYPOND_BOOK = srcdir + '/scripts/lilypond-book.py',
160 LILYPOND_PY = srcdir + '/scripts/lilypond.py',
162 ABC2LY_PY = srcdir + '/scripts/abc2ly.py',
163 MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
164 LILYPOND_BOOK_FLAGS = '',
165 LILYPOND_BOOK_FORMAT = 'texi-html',
167 TEXINFO_PAPERSIZE_OPTION = '-t @afourpaper',
171 Help (usage + opts.GenerateHelpText (env))
173 map (lambda x: opts.AddOptions ((x,)), config_vars)
175 env.CacheDir (os.path.join (env['build'], '=build-cache'))
178 env.Append (CFLAGS = '-g')
179 env.Append (CXXFLAGS = '-g')
180 if env['optimising']:
181 env.Append (CFLAGS = '-O2')
182 env.Append (CXXFLAGS = '-O2')
183 env.Append (CXXFLAGS = '-DSTRING_UTILS_INLINED')
185 env.Append (CFLAGS = '-W ')
186 env.Append (CFLAGS = '-Wall')
187 # what about = ['-W', '-Wall', ...]?
188 env.Append (CXXFLAGS = '-W')
189 env.Append (CXXFLAGS = '-Wall')
190 env.Append (CXXFLAGS = '-Wconversion')
192 env['__verbose'] = '--verbose'
194 env.Append (PKG_CONFIG_PATH = [os.path.join (os.environ['HOME'],
195 'usr/pkg/gnome/lib'),
196 os.path.join (os.environ['HOME'],
197 'usr/pkg/pango/lib')])
199 env['srcdir'] = Dir ('.').srcnode ().abspath
201 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
202 config_h = os.path.join (outdir, 'config.h')
203 version_h = os.path.join (outdir, 'version.hh')
204 config_h = os.path.join (outdir, 'config.h')
205 env.Alias ('config', config_h)
208 ## Explicit dependencies
210 # Without target arguments, build lily only
211 if not COMMAND_LINE_TARGETS:
213 env.Alias ('all', '.')
216 'Documentation/user',
217 'Documentation/topdocs')
219 env.Depends ('doc', ['lily', 'mf'])
220 env.Depends ('input', ['lily', 'mf'])
221 env.Depends ('doc', ['lily', 'mf'])
229 def configure (target, source, env):
230 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
231 def get_version (program):
232 command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
233 pipe = os.popen (command)
234 output = pipe.read ()
237 v = re.sub (vre, '\\1', output)
238 return string.split (v, '.')
240 def test_program (lst, program, minimal, description, package):
241 sys.stdout.write ('Checking %s version... ' % program)
242 actual = get_version (program)
245 lst.append ((description, package, minimal, program,
248 sys.stdout.write (string.join (actual, '.'))
249 sys.stdout.write ('\n')
250 if actual < string.split (minimal, '.'):
251 lst.append ((description, package, minimal, program,
252 string.join (actual, '.')))
255 test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
256 test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
257 test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
258 test_program (required, 'guile-config', '1.6', 'GUILE development',
259 'libguile-dev or guile-devel')
260 # Do not use bison 1.50 and 1.75.
261 test_program (required, 'bison', '1.25', 'Bison -- parser generator',
263 test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
267 test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
268 test_program (optional, 'guile', '1.6', 'GUILE scheme',
269 'libguile-dev or guile-devel')
270 test_program (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
272 test_program (optional, 'perl', '4.0',
273 'Perl practical efficient readonly language', 'perl')
274 #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
276 def CheckYYCurrentBuffer (context):
277 context.Message ('Checking for yy_current_buffer... ')
278 ret = conf.TryCompile ("""using namespace std;
279 #include <FlexLexer.h>
280 class yy_flex_lexer: public yyFlexLexer
285 yy_current_buffer = 0;
290 conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
291 : CheckYYCurrentBuffer })
296 'TOPLEVEL_VERSION' : '"' + version + '"',
297 'PACKAGE': '"' + package.name + '"',
298 'DATADIR' : '"' + sharedir + '"',
299 'LILYPOND_DATADIR' : '"' + sharedir_package + '"',
300 'LOCAL_LILYPOND_DATADIR' : '"' + sharedir_package_version + '"',
301 'LOCALEDIR' : '"' + localedir + '"',
303 conf.env.Append (DEFINES = defines)
305 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
306 PYTHON_INCLUDE = os.popen (command).read ()
307 env.Append (CPPPATH = PYTHON_INCLUDE)
309 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
311 if conf.CheckCHeader (i):
312 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
313 conf.env['DEFINES'][key] = 1
315 ccheaders = ('sstream',)
317 if conf.CheckCXXHeader (i):
318 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
319 conf.env['DEFINES'][key] = 1
321 functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
323 if 0 or conf.CheckFunc (i):
324 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
325 conf.env['DEFINES'][key] = 1
327 if conf.CheckYYCurrentBuffer ():
328 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
330 if conf.CheckLib ('dl'):
333 if conf.CheckLib ('kpathsea'):
334 conf.env['DEFINES']['KPATHSEA'] = 1
337 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
338 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
339 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
340 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
342 #this could happen after flower...
343 env.ParseConfig ('guile-config compile')
345 #this could happen only for compiling pango-*
347 env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
348 env.ParseConfig ('pkg-config --cflags --libs pango')
349 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
350 conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
352 if conf.CheckLib ('pango-1.0',
353 'pango_fc_font_map_add_decoder_find_func'):
354 conf.env['DEFINES']['HAVE_PANGO_CVS'] = '1'
355 conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
359 print '********************************'
360 print 'Please install required packages'
362 print '%s: %s-%s or newer (found: %s %s)' % i
367 print '*************************************'
368 print 'Consider installing optional packages'
370 print '%s: %s-%s or newer (found: %s %s)' % i
372 return conf.Finish ()
374 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
375 os.unlink (config_cache)
377 # scons: *** Calling Configure from Builders is not supported.
378 # env.Command (config_cache, None, configure)
379 if not os.path.exists (config_cache) \
380 or (os.stat ('SConstruct')[stat.ST_MTIME]
381 > os.stat (config_cache)[stat.ST_MTIME]):
382 env = configure (None, None, env)
383 map (lambda x: opts.AddOptions ((x,)), config_vars)
384 opts.Save (config_cache, env)
386 def config_header (target, source, env):
387 config = open (str (target[0]), 'w')
388 for i in list_sort (env['DEFINES'].keys ()):
389 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
391 env.Command (config_h, config_cache, config_header)
393 env.Command (version_h, '#/VERSION',
394 '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
396 absbuild = Dir (env['build']).abspath
397 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
401 run_prefix = run_prefix,
402 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
404 LIBPATH = [os.path.join (absbuild, 'flower', env['out']),],
405 CPPPATH = [outdir, '#',],
406 LILYPOND_BIN = os.path.join (absbuild, 'lily', env['out'],
408 LILYPOND_BOOK_PATH = ['.', '#/input', '#/input/regression',
409 '#/input/test', '#/input/tutorial',
410 os.path.join (absbuild, 'mf', env['out']),
411 '#/Documentation/user',
412 os.path.join (absbuild, 'Documentation',
414 os.path.join (absbuild, 'Documentation/user',
417 MAKEINFO_PATH = ['.', '#/Documentation/user',
418 os.path.join (absbuild, 'Documentation/user',
423 SConscript ('buildscripts/builder.py')
426 def symlink_tree (target, source, env):
432 if not os.path.isdir (dir):
433 if os.path.exists (dir):
437 map (mkdir, string.split (dir, os.sep))
438 def symlink (src, dst):
440 dir = os.path.dirname (dst)
443 frm = os.path.join (srcdir, src[1:])
445 depth = len (string.split (dir, '/'))
446 frm = os.path.join ('../' * depth, src, env['out'])
447 os.symlink (frm, os.path.basename (dst))
448 prefix = os.path.join (env['out'], 'usr')
449 map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
450 (('python', 'lib/lilypond/python'),
451 # UGHR, lilypond.py uses lilypond-bin from PATH
453 ('#mf', 'share/lilypond/fonts/mf'),
454 ('mf', 'share/lilypond/fonts/afm'),
455 ('mf', 'share/lilypond/fonts/tfm'),
456 ('mf', 'share/lilypond/fonts/type1'),
457 ('#tex', 'share/lilypond/tex/source'),
458 ('mf', 'share/lilypond/tex/generate'),
459 ('#ly', 'share/lilypond/ly'),
460 ('#scm', 'share/lilypond/scm'),
461 ('#ps', 'share/lilypond/ps'),
462 ('elisp', 'share/lilypond/elisp')))
466 stamp = os.path.join (run_prefix, 'stamp')
467 env.Depends ('.', stamp)
468 env.Command (stamp, 'VERSION', [symlink_tree, 'touch $TARGET'])
474 def cvs_entry_is_dir (line):
475 return line[0] == 'D' and line[-2] == '/'
477 def cvs_entry_is_file (line):
478 return line[0] == '/' and line[-2] == '/'
481 ENTRIES = os.path.join (dir, 'CVS/Entries')
482 if not os.path.exists (ENTRIES):
484 entries = open (ENTRIES).readlines ()
485 dir_entries = filter (cvs_entry_is_dir, entries)
486 dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
488 return dirs + map (cvs_dirs, dirs)
491 ENTRIES = os.path.join (dir, 'CVS/Entries')
492 entries = open (ENTRIES).readlines ()
493 file_entries = filter (cvs_entry_is_file, entries)
494 files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
495 return map (lambda x: os.path.join (dir, x), files)
497 #subdirs = reduce (lambda x, y: x + y, cvs_dirs ('.'))
499 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
500 foo = map (lambda x: env.TXT (x + '.txt',
501 os.path.join ('Documentation/topdocs', x)),
503 txt_files = map (lambda x: x + '.txt', readme_files)
504 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
505 tar_base = package.name + '-' + version
506 tar_name = tar_base + '.tar.gz'
507 ball_prefix = os.path.join (outdir, tar_base)
508 tar_ball = os.path.join (outdir, tar_name)
510 dist_files = src_files + txt_files
511 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
512 map (lambda x: env.Depends (tar_ball, x), ball_files)
513 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
514 'ln $SOURCE $TARGET'), dist_files)
515 tar = env.Command (tar_ball, src_files,
516 'tar czf $TARGET -C $TARGET.dir %s' % tar_base)
517 env.Alias ('tar', tar)
519 dist_ball = os.path.join (package.release_dir, tar_name)
520 env.Command (dist_ball, tar_ball,
521 'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
522 + 'ln $SOURCE $TARGET')
523 env.Depends ('dist', dist_ball)
524 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
525 patch = env.PATCH (patch_name, tar_ball)
526 env.Depends (patch_name, dist_ball)
527 env.Alias ('release', patch)
531 if os.path.exists (os.path.join (d, 'SConscript')):
532 b = os.path.join (env['build'], d, env['out'])
533 # Support clean sourcetree build (--srcdir build)
535 if os.path.abspath (b) != os.path.abspath (d):
536 env.BuildDir (b, d, duplicate = 0)
537 SConscript (os.path.join (b, 'SConscript'))