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
62 # SConscripts are only needed in directories where something needs
63 # to be done, building or installing
64 # TODO: Documentation/*, input/*/*, vim, po
65 # rename Documentation/* to ./doc?
67 subdirs = ['flower', 'lily', 'mf', 'scm', 'ly', 'Documentation',
68 'Documentation/user', 'input', 'scripts', 'elisp',
69 'buildscripts', 'cygwin', 'debian']
72 scons [KEY=VALUE].. [TARGET]..
74 where TARGET is config|lily|all|fonts|doc|tar|dist|release
78 config_cache = 'config.cache'
91 # Put your favourite stuff in custom.py
92 opts = Options ([config_cache, 'custom.py'], ARGUMENTS)
93 opts.Add ('prefix', 'Install prefix', '/usr/')
94 opts.Add ('out', 'Output directory', 'out-scons')
95 opts.Add ('build', 'Build directory', '.')
97 BoolOption ('warnings', 'compile with -Wall and similiar',
99 BoolOption ('debugging', 'compile with debugging symbols',
101 BoolOption ('optimising', 'compile with optimising',
103 BoolOption ('shared', 'build shared libraries',
105 BoolOption ('static', 'build static libraries',
107 BoolOption ('gui', 'build with GNOME backend (EXPERIMENTAL)',
109 BoolOption ('verbose', 'run commands with verbose flag',
113 srcdir = Dir ('.').srcnode ().abspath
116 sys.path.append (os.path.join (srcdir, 'stepmake', 'bin'))
118 package = packagepython.Package (srcdir)
121 prefix = '/usr/local'
123 version = packagepython.version_tuple_to_str (package.version)
124 bindir = os.path.join (prefix, 'bin')
125 sharedir = os.path.join (prefix, 'share')
126 libdir = os.path.join (prefix, 'lib')
127 localedir = os.path.join (sharedir, 'locale')
128 sharedir_package = os.path.join (sharedir, package.name)
129 sharedir_package_version = os.path.join (sharedir_package, version)
130 lilypondprefix = sharedir_package_version
132 ENV = { 'PATH' : os.environ['PATH'] }
133 for key in ['LD_LIBRARY_PATH', 'GUILE_LOAD_PATH', 'PKG_CONFIG_PATH']:
134 if os.environ.has_key(key):
135 ENV[key] = os.environ[key]
144 TOPLEVEL_VERSION = version,
145 lilypond_datadir = sharedir_package,
146 local_lilypond_datadir = sharedir_package_version,
147 lilypondprefix = lilypondprefix,
148 sharedir_package = sharedir_package,
149 sharedir_package_version = sharedir_package_version,
154 PYTHON = '/usr/bin/python',
155 MAKEINFO = 'LANG= makeinfo',
157 LILYPOND_BOOK = srcdir + '/scripts/lilypond-book.py',
158 LILYPOND_PY = srcdir + '/scripts/lilypond.py',
160 ABC2LY_PY = srcdir + '/scripts/abc2ly.py',
161 MF_TO_TABLE_PY = srcdir + '/buildscripts/mf-to-table.py',
162 LILYPOND_BOOK_FLAGS = '',
163 LILYPOND_BOOK_FORMAT = 'texi-html',
165 TEXINFO_PAPERSIZE_OPTION = '-t @afourpaper',
170 Help (usage + opts.GenerateHelpText (env))
172 map (lambda x: opts.AddOptions ((x,)), config_vars)
174 env.CacheDir (os.path.join (env['build'], '=build-cache'))
177 env.Append (CFLAGS = '-g')
178 env.Append (CXXFLAGS = '-g')
179 if env['optimising']:
180 env.Append (CFLAGS = '-O2')
181 env.Append (CXXFLAGS = '-O2')
182 env.Append (CXXFLAGS = '-DSTRING_UTILS_INLINED')
184 env.Append (CFLAGS = '-W ')
185 env.Append (CFLAGS = '-Wall')
186 # what about = ['-W', '-Wall', ...]?
187 env.Append (CXXFLAGS = '-W')
188 env.Append (CXXFLAGS = '-Wall')
189 env.Append (CXXFLAGS = '-Wconversion')
191 env['__verbose'] = '--verbose'
193 env['srcdir'] = Dir ('.').srcnode ().abspath
195 outdir = os.path.join (Dir (env['build']).abspath, env['out'])
196 config_h = os.path.join (outdir, 'config.h')
197 version_h = os.path.join (outdir, 'version.hh')
198 config_h = os.path.join (outdir, 'config.h')
199 env.Alias ('config', config_h)
202 ## Explicit dependencies
204 # Without target arguments, build lily only
205 if not COMMAND_LINE_TARGETS:
207 env.Alias ('all', '.')
210 'Documentation/user',
211 'Documentation/topdocs')
213 env.Depends ('doc', ['lily', 'mf'])
214 env.Depends ('input', ['lily', 'mf'])
215 env.Depends ('doc', ['lily', 'mf'])
223 def configure (target, source, env):
224 vre = re.compile ('^.*[^-.0-9]([0-9][0-9]*\.[0-9][.0-9]*).*$', re.DOTALL)
225 def get_version (program):
226 command = '(%(program)s --version || %(program)s -V) 2>&1' % vars ()
227 pipe = os.popen (command)
228 output = pipe.read ()
231 v = re.sub (vre, '\\1', output)
232 return string.split (v, '.')
234 def test_program (lst, program, minimal, description, package):
235 sys.stdout.write ('Checking %s version... ' % program)
236 actual = get_version (program)
239 lst.append ((description, package, minimal, program,
242 sys.stdout.write (string.join (actual, '.'))
243 sys.stdout.write ('\n')
244 if actual < string.split (minimal, '.'):
245 lst.append ((description, package, minimal, program,
246 string.join (actual, '.')))
249 test_program (required, 'gcc', '2.8', 'GNU C compiler', 'gcc')
250 test_program (required, 'g++', '3.0.5', 'GNU C++ compiler', 'g++')
251 test_program (required, 'python', '2.1', 'Python (www.python.org)', 'python')
252 test_program (required, 'guile-config', '1.6', 'GUILE development',
253 'libguile-dev or guile-devel')
254 # Do not use bison 1.50 and 1.75.
255 test_program (required, 'bison', '1.25', 'Bison -- parser generator',
257 test_program (required, 'flex', '0.0', 'Flex -- lexer generator', 'flex')
261 test_program (optional, 'makeinfo', '4.7', 'Makeinfo tool', 'texinfo')
262 test_program (optional, 'guile', '1.6', 'GUILE scheme',
263 'libguile-dev or guile-devel')
264 test_program (optional, 'mftrace', '1.0.27', 'Metafont tracing Type1',
266 test_program (optional, 'perl', '4.0',
267 'Perl practical efficient readonly language', 'perl')
268 #test_program (optional, 'foo', '2.0', 'Foomatic tester', 'bar')
270 def CheckYYCurrentBuffer (context):
271 context.Message ('Checking for yy_current_buffer... ')
272 ret = conf.TryCompile ("""using namespace std;
273 #include <FlexLexer.h>
274 class yy_flex_lexer: public yyFlexLexer
279 yy_current_buffer = 0;
284 conf = Configure (env, custom_tests = { 'CheckYYCurrentBuffer'
285 : CheckYYCurrentBuffer })
290 'TOPLEVEL_VERSION' : '"' + version + '"',
291 'PACKAGE': '"' + package.name + '"',
292 'DATADIR' : '"' + sharedir + '"',
293 'LILYPOND_DATADIR' : '"' + sharedir_package + '"',
294 'LOCAL_LILYPOND_DATADIR' : '"' + sharedir_package_version + '"',
295 'LOCALEDIR' : '"' + localedir + '"',
297 conf.env.Append (DEFINES = defines)
299 command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % (sys.prefix, sys.version[:3]))'""" #"
300 PYTHON_INCLUDE = os.popen (command).read ()
301 env.Append (CPPPATH = PYTHON_INCLUDE)
303 headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h')
305 if conf.CheckCHeader (i):
306 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
307 conf.env['DEFINES'][key] = 1
309 ccheaders = ('sstream',)
311 if conf.CheckCXXHeader (i):
312 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
313 conf.env['DEFINES'][key] = 1
315 functions = ('gettext', 'isinf', 'memmem', 'snprintf', 'vsnprintf')
317 if 0 or conf.CheckFunc (i):
318 key = re.sub ('[./]', '_', 'HAVE_' + string.upper (i))
319 conf.env['DEFINES'][key] = 1
321 if conf.CheckYYCurrentBuffer ():
322 conf.env['DEFINES']['HAVE_FLEXLEXER_YY_CURRENT_BUFFER'] = 1
324 if conf.CheckLib ('dl'):
327 if conf.CheckLib ('kpathsea'):
328 conf.env['DEFINES']['KPATHSEA'] = 1
331 if conf.CheckLib ('kpathsea', 'kpse_find_file'):
332 conf.env['DEFINES']['HAVE_KPSE_FIND_FILE'] = '1'
333 if conf.CheckLib ('kpathsea', 'kpse_find_tfm'):
334 conf.env['DEFINES']['HAVE_KPSE_FIND_TFM'] = '1'
336 #this could happen after flower...
337 env.ParseConfig ('guile-config compile')
339 #this could happen only for compiling pango-*
341 env.ParseConfig ('pkg-config --cflags --libs gtk+-2.0')
342 env.ParseConfig ('pkg-config --cflags --libs pango')
343 if conf.CheckCHeader ('pango/pangofc-fontmap.h'):
344 conf.env['DEFINES']['HAVE_PANGO_PANGOFC_FONTMAP_H'] = '1'
346 if conf.CheckLib ('pango-1.0',
347 'pango_fc_font_map_add_decoder_find_func'):
348 conf.env['DEFINES']['HAVE_PANGO_CVS'] = '1'
349 conf.env['DEFINES']['HAVE_PANGO_FC_FONT_MAP_ADD_DECODER_FIND_FUNC'] = '1'
353 print '********************************'
354 print 'Please install required packages'
356 print '%s: %s-%s or newer (found: %s %s)' % i
361 print '*************************************'
362 print 'Consider installing optional packages'
364 print '%s: %s-%s or newer (found: %s %s)' % i
366 return conf.Finish ()
368 if os.path.exists (config_cache) and 'config' in COMMAND_LINE_TARGETS:
369 os.unlink (config_cache)
371 # scons: *** Calling Configure from Builders is not supported.
372 # env.Command (config_cache, None, configure)
373 if not os.path.exists (config_cache):
374 env = configure (None, None, env)
375 map (lambda x: opts.AddOptions ((x,)), config_vars)
376 opts.Save (config_cache, env)
378 def config_header (target, source, env):
379 config = open (str (target[0]), 'w')
380 for i in list_sort (env['DEFINES'].keys ()):
381 config.write ('#define %s %s\n' % (i, env['DEFINES'][i]))
383 env.Command (config_h, config_cache, config_header)
385 env.Command (version_h, '#/VERSION',
386 '$PYTHON ./stepmake/bin/make-version.py VERSION > $TARGET')
388 absbuild = Dir (env['build']).abspath
389 run_prefix = os.path.join (absbuild, os.path.join (env['out'], 'usr'))
393 run_prefix = run_prefix,
394 LILYPONDPREFIX = os.path.join (run_prefix, 'share/lilypond'),
396 LIBPATH = [os.path.join (absbuild, 'flower', env['out']),],
397 CPPPATH = [outdir, '#',],
398 LILYPOND_BIN = os.path.join (absbuild, 'lily', env['out'],
400 LILYPOND_BOOK_PATH = ['.', '#/input', '#/input/regression',
401 '#/input/test', '#/input/tutorial',
402 os.path.join (absbuild, 'mf', env['out']),
403 '#/Documentation/user',
404 os.path.join (absbuild, 'Documentation',
406 os.path.join (absbuild, 'Documentation/user',
409 MAKEINFO_PATH = ['.', '#/Documentation/user',
410 os.path.join (absbuild, 'Documentation/user',
415 SConscript ('buildscripts/builder.py')
418 def symlink_tree (target, source, env):
424 if not os.path.isdir (dir):
425 if os.path.exists (dir):
429 map (mkdir, string.split (dir, os.sep))
430 def symlink (src, dst):
432 dir = os.path.dirname (dst)
435 frm = os.path.join (srcdir, src[1:])
437 depth = len (string.split (dir, '/'))
438 frm = os.path.join ('../' * depth, src, out)
439 os.symlink (frm, os.path.basename (dst))
440 prefix = os.path.join (env['out'], 'usr')
441 map (lambda x: symlink (x[0], os.path.join (prefix, x[1])),
442 (('python', 'lib/lilypond/python'),
443 # UGHR, lilypond.py uses lilypond-bin from PATH
445 ('#mf', 'share/lilypond/fonts/mf'),
446 ('mf', 'share/lilypond/fonts/afm'),
447 ('mf', 'share/lilypond/fonts/tfm'),
448 ('mf', 'share/lilypond/fonts/type1'),
449 ('#tex', 'share/lilypond/tex/source'),
450 ('mf', 'share/lilypond/tex/generate'),
451 ('#ly', 'share/lilypond/ly'),
452 ('#scm', 'share/lilypond/scm'),
453 ('#ps', 'share/lilypond/ps'),
454 ('elisp', 'share/lilypond/elisp')))
458 print 'run_prefix:' + run_prefix
459 env.Command (os.path.join (run_prefix, 'stamp'), 'VERSION',
460 [symlink_tree, 'touch $TARGET'])
466 def cvs_entry_is_dir (line):
467 return line[0] == 'D' and line[-2] == '/'
469 def cvs_entry_is_file (line):
470 return line[0] == '/' and line[-2] == '/'
473 ENTRIES = os.path.join (dir, 'CVS/Entries')
474 if not os.path.exists (ENTRIES):
476 entries = open (ENTRIES).readlines ()
477 dir_entries = filter (cvs_entry_is_dir, entries)
478 dirs = map (lambda x: os.path.join (dir, x[2:x[2:].index ('/')+3]),
480 return dirs + map (cvs_dirs, dirs)
483 ENTRIES = os.path.join (dir, 'CVS/Entries')
484 entries = open (ENTRIES).readlines ()
485 file_entries = filter (cvs_entry_is_file, entries)
486 files = map (lambda x: x[1:x[1:].index ('/')+1], file_entries)
487 return map (lambda x: os.path.join (dir, x), files)
489 #subdirs = reduce (lambda x, y: x + y, cvs_dirs ('.'))
491 readme_files = ['AUTHORS', 'README', 'INSTALL', 'NEWS']
492 foo = map (lambda x: env.TXT (x + '.txt',
493 os.path.join ('Documentation/topdocs', x)),
495 txt_files = map (lambda x: x + '.txt', readme_files)
496 src_files = reduce (lambda x, y: x + y, map (cvs_files, subdirs))
497 tar_base = package.name + '-' + version
498 tar_name = tar_base + '.tar.gz'
499 ball_prefix = os.path.join (outdir, tar_base)
500 tar_ball = os.path.join (outdir, tar_name)
502 dist_files = src_files + txt_files
503 ball_files = map (lambda x: os.path.join (ball_prefix, x), dist_files)
504 map (lambda x: env.Depends (tar_ball, x), ball_files)
505 map (lambda x: env.Command (os.path.join (ball_prefix, x), x,
506 'ln $SOURCE $TARGET'), dist_files)
507 tar = env.Command (tar_ball, src_files,
508 'tar czf $TARGET -C $TARGET.dir %s' % tar_base)
509 env.Alias ('tar', tar)
511 dist_ball = os.path.join (package.release_dir, tar_name)
512 env.Command (dist_ball, tar_ball,
513 'if [ -e $SOURCE -a -e $TARGET ]; then rm $TARGET; fi;' \
514 + 'ln $SOURCE $TARGET')
515 env.Depends ('dist', dist_ball)
516 patch_name = os.path.join (outdir, tar_base + '.diff.gz')
517 patch = env.PATCH (patch_name, tar_ball)
518 env.Depends (patch_name, dist_ball)
519 env.Alias ('release', patch)
523 if os.path.exists (os.path.join (d, 'SConscript')):
524 b = os.path.join (env['build'], d, env['out'])
525 # Support clean sourcetree build (--srcdir build)
527 if os.path.abspath (b) != os.path.abspath (d):
528 env.BuildDir (b, d, duplicate = 0)
529 SConscript (os.path.join (b, 'SConscript'))