2 # -*- coding: utf-8 -*-
4 # Autowaf, useful waf utilities with support for recursive projects
5 # Copyright 2008-2011 David Robillard
7 # Licensed under the GNU GPL v2 or later, see COPYING file for details.
14 from waflib import Configure, Context, Logs, Node, Options, Task, Utils
15 from waflib.TaskGen import feature, before, after
20 # Only run autowaf hooks once (even if sub projects call several times)
24 # Compute dependencies globally
26 #preproc.go_absolute = True
29 @after('apply_incpaths')
30 def include_config_h(self):
31 self.env.append_value('INCPATHS', self.bld.bldnode.abspath())
33 def set_options(opt, debug_by_default=False):
34 "Add standard autowaf options if they havn't been added yet"
39 # Install directory options
40 dirs_options = opt.add_option_group('Installation directories', '')
42 # Move --prefix and --destdir to directory options group
43 for k in ('--prefix', '--destdir'):
44 option = opt.parser.get_option(k)
46 opt.parser.remove_option(k)
47 dirs_options.add_option(option)
49 # Standard directory options
50 dirs_options.add_option('--bindir', type='string',
51 help="Executable programs [Default: PREFIX/bin]")
52 dirs_options.add_option('--configdir', type='string',
53 help="Configuration data [Default: PREFIX/etc]")
54 dirs_options.add_option('--datadir', type='string',
55 help="Shared data [Default: PREFIX/share]")
56 dirs_options.add_option('--includedir', type='string',
57 help="Header files [Default: PREFIX/include]")
58 dirs_options.add_option('--libdir', type='string',
59 help="Libraries [Default: PREFIX/lib]")
60 dirs_options.add_option('--mandir', type='string',
61 help="Manual pages [Default: DATADIR/man]")
62 dirs_options.add_option('--docdir', type='string',
63 help="HTML documentation [Default: DATADIR/doc]")
67 opt.add_option('--optimize', action='store_false', default=True, dest='debug',
68 help="Build optimized binaries")
70 opt.add_option('--debug', action='store_true', default=False, dest='debug',
71 help="Build debuggable binaries")
73 opt.add_option('--pardebug', action='store_true', default=False, dest='pardebug',
74 help="Build parallel-installable debuggable libraries with D suffix")
76 opt.add_option('--grind', action='store_true', default=False, dest='grind',
77 help="Run tests in valgrind")
78 opt.add_option('--strict', action='store_true', default=False, dest='strict',
79 help="Use strict compiler flags and show all warnings")
80 opt.add_option('--ultra-strict', action='store_true', default=False, dest='ultra_strict',
81 help="Use even stricter compiler flags (likely to trigger many warnings in library headers)")
82 opt.add_option('--docs', action='store_true', default=False, dest='docs',
83 help="Build documentation - requires doxygen")
86 opt.add_option('--lv2-user', action='store_true', default=False, dest='lv2_user',
87 help="Install LV2 bundles to user location")
88 opt.add_option('--lv2-system', action='store_true', default=False, dest='lv2_system',
89 help="Install LV2 bundles to system location")
90 dirs_options.add_option('--lv2dir', type='string',
91 help="LV2 bundles [Default: LIBDIR/lv2]")
94 def check_header(conf, lang, name, define='', mandatory=True):
96 includes = '' # search default system include paths
97 if sys.platform == "darwin":
98 includes = '/opt/local/include'
101 check_func = conf.check_cc
103 check_func = conf.check_cxx
105 Logs.error("Unknown header language `%s'" % lang)
109 check_func(header_name=name, includes=includes,
110 define_name=define, mandatory=mandatory)
112 check_func(header_name=name, includes=includes, mandatory=mandatory)
115 return name.replace('/', '_').replace('++', 'PP').replace('-', '_').replace('.', '_')
117 def define(conf, var_name, value):
118 conf.define(var_name, value)
119 conf.env[var_name] = value
121 def check_pkg(conf, name, **args):
122 "Check for a package iff it hasn't been checked for yet"
123 if args['uselib_store'].lower() in conf.env['AUTOWAF_LOCAL_LIBS']:
128 var_name = 'CHECKED_' + nameify(args['uselib_store'])
129 check = not var_name in conf.env
130 mandatory = not 'mandatory' in args or args['mandatory']
131 if not check and 'atleast_version' in args:
132 # Re-check if version is newer than previous check
133 checked_version = conf.env['VERSION_' + name]
134 if checked_version and checked_version < args['atleast_version']:
136 if not check and mandatory and conf.env[var_name] == CheckType.OPTIONAL:
137 # Re-check if previous check was optional but this one is mandatory
141 pkg_var_name = 'PKG_' + name.replace('-', '_')
143 if conf.env.PARDEBUG:
144 args['mandatory'] = False # Smash mandatory arg
145 found = conf.check_cfg(package=pkg_name + 'D', args="--cflags --libs", **args)
149 args['mandatory'] = True # Unsmash mandatory arg
151 found = conf.check_cfg(package=pkg_name, args="--cflags --libs", **args)
153 conf.env[pkg_var_name] = pkg_name
154 if 'atleast_version' in args:
155 conf.env['VERSION_' + name] = args['atleast_version']
157 conf.env[var_name] = CheckType.MANDATORY
159 conf.env[var_name] = CheckType.OPTIONAL
163 if sys.platform == 'win32':
164 return os.path.normpath(path).replace('\\', '/')
166 return os.path.normpath(path)
172 def append_cxx_flags(flags):
173 conf.env.append_value('CFLAGS', flags)
174 conf.env.append_value('CXXFLAGS', flags)
176 display_header('Global Configuration')
178 if Options.options.docs:
181 conf.env['DOCS'] = Options.options.docs
182 conf.env['DEBUG'] = Options.options.debug or Options.options.pardebug
183 conf.env['PARDEBUG'] = Options.options.pardebug
184 conf.env['PREFIX'] = normpath(os.path.abspath(os.path.expanduser(conf.env['PREFIX'])))
186 def config_dir(var, opt, default):
188 conf.env[var] = normpath(opt)
190 conf.env[var] = normpath(default)
192 opts = Options.options
193 prefix = conf.env['PREFIX']
195 config_dir('BINDIR', opts.bindir, os.path.join(prefix, 'bin'))
196 config_dir('SYSCONFDIR', opts.configdir, os.path.join(prefix, 'etc'))
197 config_dir('DATADIR', opts.datadir, os.path.join(prefix, 'share'))
198 config_dir('INCLUDEDIR', opts.includedir, os.path.join(prefix, 'include'))
199 config_dir('LIBDIR', opts.libdir, os.path.join(prefix, 'lib'))
200 config_dir('MANDIR', opts.mandir, os.path.join(conf.env['DATADIR'], 'man'))
201 config_dir('DOCDIR', opts.docdir, os.path.join(conf.env['DATADIR'], 'doc'))
203 if Options.options.lv2dir:
204 conf.env['LV2DIR'] = Options.options.lv2dir
205 elif Options.options.lv2_user:
206 if sys.platform == "darwin":
207 conf.env['LV2DIR'] = os.path.join(os.getenv('HOME'), 'Library/Audio/Plug-Ins/LV2')
208 elif sys.platform == "win32":
209 conf.env['LV2DIR'] = os.path.join(os.getenv('APPDATA'), 'LV2')
211 conf.env['LV2DIR'] = os.path.join(os.getenv('HOME'), '.lv2')
212 elif Options.options.lv2_system:
213 if sys.platform == "darwin":
214 conf.env['LV2DIR'] = '/Library/Audio/Plug-Ins/LV2'
215 elif sys.platform == "win32":
216 conf.env['LV2DIR'] = os.path.join(os.getenv('COMMONPROGRAMFILES'), 'LV2')
218 conf.env['LV2DIR'] = os.path.join(conf.env['LIBDIR'], 'lv2')
220 conf.env['LV2DIR'] = os.path.join(conf.env['LIBDIR'], 'lv2')
222 conf.env['LV2DIR'] = normpath(conf.env['LV2DIR'])
224 if Options.options.docs:
225 doxygen = conf.find_program('doxygen')
227 conf.fatal("Doxygen is required to build with --docs")
229 dot = conf.find_program('dot')
231 conf.fatal("Graphviz (dot) is required to build with --docs")
233 if Options.options.debug:
234 if conf.env['MSVC_COMPILER']:
235 conf.env['CFLAGS'] = ['/Od', '/Zi', '/MTd']
236 conf.env['CXXFLAGS'] = ['/Od', '/Zi', '/MTd']
237 conf.env['LINKFLAGS'] = ['/DEBUG']
239 conf.env['CFLAGS'] = ['-O0', '-g']
240 conf.env['CXXFLAGS'] = ['-O0', '-g']
242 if conf.env['MSVC_COMPILER']:
243 conf.env['CFLAGS'] = ['/MD']
244 conf.env['CXXFLAGS'] = ['/MD']
245 append_cxx_flags(['-DNDEBUG'])
247 if Options.options.ultra_strict:
248 Options.options.strict = True
249 conf.env.append_value('CFLAGS', ['-Wredundant-decls',
250 '-Wstrict-prototypes',
251 '-Wmissing-prototypes'])
253 if Options.options.strict:
254 conf.env.append_value('CFLAGS', ['-std=c99', '-pedantic', '-Wshadow'])
255 conf.env.append_value('CXXFLAGS', ['-ansi',
256 '-Wnon-virtual-dtor',
257 '-Woverloaded-virtual'])
258 append_cxx_flags(['-Wall',
262 if sys.platform != "darwin":
263 # this is really only to be avoid on OLD apple gcc, but not sure how to version check
264 append_cxx_flags(['-fstrict-overflow'])
266 if not conf.check_cc(fragment = '''
270 int main() { return 0; }''',
274 msg = 'Checking for clang'):
275 if sys.platform != "darwin":
276 # this is really only to be avoid on OLD apple gcc, but not sure how to version check
277 append_cxx_flags(['-Wunsafe-loop-optimizations'])
278 # this is invalid (still) on Lion apple gcc
279 append_cxx_flags(['-Wlogical-op'])
282 if not conf.env['MSVC_COMPILER']:
283 append_cxx_flags(['-fshow-column'])
285 conf.env.prepend_value('CFLAGS', '-I' + os.path.abspath('.'))
286 conf.env.prepend_value('CXXFLAGS', '-I' + os.path.abspath('.'))
288 display_msg(conf, "Install prefix", conf.env['PREFIX'])
289 display_msg(conf, "Debuggable build", str(conf.env['DEBUG']))
290 display_msg(conf, "Build documentation", str(conf.env['DOCS']))
295 def set_c99_mode(conf):
296 if conf.env.MSVC_COMPILER:
297 # MSVC has no hope or desire to compile C99, just compile as C++
298 conf.env.append_unique('CFLAGS', ['-TP'])
300 conf.env.append_unique('CFLAGS', ['-std=c99'])
302 def set_local_lib(conf, name, has_objects):
303 var_name = 'HAVE_' + nameify(name.upper())
304 define(conf, var_name, 1)
306 if type(conf.env['AUTOWAF_LOCAL_LIBS']) != dict:
307 conf.env['AUTOWAF_LOCAL_LIBS'] = {}
308 conf.env['AUTOWAF_LOCAL_LIBS'][name.lower()] = True
310 if type(conf.env['AUTOWAF_LOCAL_HEADERS']) != dict:
311 conf.env['AUTOWAF_LOCAL_HEADERS'] = {}
312 conf.env['AUTOWAF_LOCAL_HEADERS'][name.lower()] = True
314 def append_property(obj, key, val):
315 if hasattr(obj, key):
316 setattr(obj, key, getattr(obj, key) + val)
318 setattr(obj, key, val)
320 def use_lib(bld, obj, libs):
321 abssrcdir = os.path.abspath('.')
322 libs_list = libs.split()
324 in_headers = l.lower() in bld.env['AUTOWAF_LOCAL_HEADERS']
325 in_libs = l.lower() in bld.env['AUTOWAF_LOCAL_LIBS']
327 append_property(obj, 'use', ' lib%s ' % l.lower())
328 append_property(obj, 'framework', bld.env['FRAMEWORK_' + l])
329 if in_headers or in_libs:
330 inc_flag = '-iquote ' + os.path.join(abssrcdir, l.lower())
331 for f in ['CFLAGS', 'CXXFLAGS']:
332 if not inc_flag in bld.env[f]:
333 bld.env.prepend_value(f, inc_flag)
335 append_property(obj, 'uselib', ' ' + l)
338 @before('apply_link')
339 def version_lib(self):
340 if sys.platform == 'win32':
341 self.vnum = None # Prevent waf from automatically appending -0
342 if self.env['PARDEBUG']:
343 applicable = ['cshlib', 'cxxshlib', 'cstlib', 'cxxstlib']
344 if [x for x in applicable if x in self.features]:
345 self.target = self.target + 'D'
347 def set_lib_env(conf, name, version):
348 'Set up environment for local library as if found via pkg-config.'
350 major_ver = version.split('.')[0]
351 pkg_var_name = 'PKG_' + name.replace('-', '_')
352 lib_name = '%s-%s' % (name, major_ver)
353 if conf.env.PARDEBUG:
355 conf.env[pkg_var_name] = lib_name
356 conf.env['INCLUDES_' + NAME] = ['${INCLUDEDIR}/%s-%s' % (name, major_ver)]
357 conf.env['LIBPATH_' + NAME] = [conf.env.LIBDIR]
358 conf.env['LIB_' + NAME] = [lib_name]
360 def display_header(title):
361 Logs.pprint('BOLD', title)
363 def display_msg(conf, msg, status = None, color = None):
365 if type(status) == bool and status or status == "True":
367 elif type(status) == bool and not status or status == "False":
369 Logs.pprint('BOLD', " *", sep='')
370 Logs.pprint('NORMAL', "%s" % msg.ljust(conf.line_just - 3), sep='')
371 Logs.pprint('BOLD', ":", sep='')
372 Logs.pprint(color, status)
374 def link_flags(env, lib):
375 return ' '.join(map(lambda x: env['LIB_ST'] % x, env['LIB_' + lib]))
377 def compile_flags(env, lib):
378 return ' '.join(map(lambda x: env['CPPPATH_ST'] % x, env['INCLUDES_' + lib]))
389 def build_pc(bld, name, version, version_suffix, libs, subst_dict={}):
390 '''Build a pkg-config file for a library.
391 name -- uppercase variable name (e.g. 'SOMENAME')
392 version -- version string (e.g. '1.2.3')
393 version_suffix -- name version suffix (e.g. '2')
394 libs -- string/list of dependencies (e.g. 'LIBFOO GLIB')
396 pkg_prefix = bld.env['PREFIX']
397 if pkg_prefix[-1] == '/':
398 pkg_prefix = pkg_prefix[:-1]
400 target = name.lower()
401 if version_suffix != '':
402 target += '-' + version_suffix
404 if bld.env['PARDEBUG']:
409 libdir = bld.env['LIBDIR']
410 if libdir.startswith(pkg_prefix):
411 libdir = libdir.replace(pkg_prefix, '${exec_prefix}')
413 includedir = bld.env['INCLUDEDIR']
414 if includedir.startswith(pkg_prefix):
415 includedir = includedir.replace(pkg_prefix, '${prefix}')
417 obj = bld(features = 'subst',
418 source = '%s.pc.in' % name.lower(),
420 install_path = os.path.join(bld.env['LIBDIR'], 'pkgconfig'),
421 exec_prefix = '${prefix}',
423 EXEC_PREFIX = '${prefix}',
425 INCLUDEDIR = includedir)
427 if type(libs) != list:
430 subst_dict[name + '_VERSION'] = version
431 subst_dict[name + '_MAJOR_VERSION'] = version[0:version.find('.')]
433 subst_dict[i + '_LIBS'] = link_flags(bld.env, i)
434 lib_cflags = compile_flags(bld.env, i)
437 subst_dict[i + '_CFLAGS'] = lib_cflags
439 obj.__dict__.update(subst_dict)
441 def build_dir(name, subdir):
443 return os.path.join('build', name, subdir)
445 return os.path.join('build', subdir)
447 # Clean up messy Doxygen documentation after it is built
448 def make_simple_dox(name):
453 os.chdir(build_dir(name, 'doc/html'))
454 page = 'group__%s.html' % name
455 if not os.path.exists(page):
458 ['%s_API ' % NAME, ''],
459 ['%s_DEPRECATED ' % NAME, ''],
460 ['group__%s.html' % name, ''],
462 ['<script.*><\/script>', ''],
463 ['<hr\/><a name="details" id="details"><\/a><h2>.*<\/h2>', ''],
464 ['<link href=\"tabs.css\" rel=\"stylesheet\" type=\"text\/css\"\/>',
466 ['<img class=\"footer\" src=\"doxygen.png\" alt=\"doxygen\"\/>',
468 os.system("sed -i 's/%s/%s/g' %s" % (i[0], i[1], page))
469 os.rename('group__%s.html' % name, 'index.html')
470 for i in (glob.glob('*.png') +
471 glob.glob('*.html') +
474 if i != 'index.html' and i != 'style.css':
477 os.chdir(build_dir(name, 'doc/man/man3'))
478 for i in glob.glob('*.3'):
479 os.system("sed -i 's/%s_API //' %s" % (NAME, i))
480 for i in glob.glob('_*'):
483 except Exception as e:
484 Logs.error("Failed to fix up %s documentation: %s" % (name, e))
486 # Doxygen API documentation
487 def build_dox(bld, name, version, srcdir, blddir, outdir=''):
488 if not bld.env['DOCS']:
492 src_dir = os.path.join(srcdir, name.lower())
493 doc_dir = os.path.join(blddir, name.lower(), 'doc')
496 doc_dir = os.path.join(blddir, 'doc')
498 subst_tg = bld(features = 'subst',
499 source = 'doc/reference.doxygen.in',
500 target = 'doc/reference.doxygen',
505 name + '_VERSION' : version,
506 name + '_SRCDIR' : os.path.abspath(src_dir),
507 name + '_DOC_DIR' : os.path.abspath(doc_dir)
510 subst_tg.__dict__.update(subst_dict)
514 docs = bld(features = 'doxygen',
515 doxyfile = 'doc/reference.doxygen')
519 major = int(version[0:version.find('.')])
521 os.path.join('${DOCDIR}', '%s-%d' % (name.lower(), major), outdir, 'html'),
522 bld.path.get_bld().ant_glob('doc/html/*'))
523 for i in range(1, 8):
524 bld.install_files('${MANDIR}/man%d' % i,
525 bld.path.get_bld().ant_glob('doc/man/man%d/*' % i,
528 # Version code file generation
529 def build_version_files(header_path, source_path, domain, major, minor, micro):
530 header_path = os.path.abspath(header_path)
531 source_path = os.path.abspath(source_path)
532 text = "int " + domain + "_major_version = " + str(major) + ";\n"
533 text += "int " + domain + "_minor_version = " + str(minor) + ";\n"
534 text += "int " + domain + "_micro_version = " + str(micro) + ";\n"
536 o = open(source_path, 'w')
540 Logs.error('Failed to open %s for writing\n' % source_path)
543 text = "#ifndef __" + domain + "_version_h__\n"
544 text += "#define __" + domain + "_version_h__\n"
545 text += " extern const char* " + domain + "_revision;\n"
546 text += " extern int " + domain + "_major_version;\n"
547 text += " extern int " + domain + "_minor_version;\n"
548 text += " extern int " + domain + "_micro_version;\n"
549 text += "#endif /* __" + domain + "_version_h__ */\n"
551 o = open(header_path, 'w')
555 Logs.warn('Failed to open %s for writing\n' % header_path)
560 def build_i18n_pot(bld, srcdir, dir, name, sources, copyright_holder=None):
561 Logs.info('Generating pot file from %s' % name)
562 pot_file = '%s.pot' % name
573 cmd += ['--copyright-holder="%s"' % copyright_holder]
576 Logs.info('Updating ' + pot_file)
577 subprocess.call(cmd, cwd=os.path.join(srcdir, dir))
579 def build_i18n_po(bld, srcdir, dir, name, sources, copyright_holder=None):
581 os.chdir(os.path.join(srcdir, dir))
582 pot_file = '%s.pot' % name
583 po_files = glob.glob('po/*.po')
584 for po_file in po_files:
587 '--no-fuzzy-matching',
590 Logs.info('Updating ' + po_file)
594 def build_i18n_mo(bld, srcdir, dir, name, sources, copyright_holder=None):
596 os.chdir(os.path.join(srcdir, dir))
597 pot_file = '%s.pot' % name
598 po_files = glob.glob('po/*.po')
599 for po_file in po_files:
600 mo_file = po_file.replace('.po', '.mo')
607 Logs.info('Generating ' + po_file)
611 def build_i18n(bld, srcdir, dir, name, sources, copyright_holder=None):
612 build_i18n_pot(bld, srcdir, dir, name, sources, copyright_holder)
613 build_i18n_po(bld, srcdir, dir, name, sources, copyright_holder)
614 build_i18n_mo(bld, srcdir, dir, name, sources, copyright_holder)
616 def cd_to_build_dir(ctx, appname):
617 orig_dir = os.path.abspath(os.curdir)
618 top_level = (len(ctx.stack_path) > 1)
620 os.chdir(os.path.join('build', appname))
623 Logs.pprint('GREEN', "Waf: Entering directory `%s'" % os.path.abspath(os.getcwd()))
625 def cd_to_orig_dir(ctx, child):
627 os.chdir(os.path.join('..', '..'))
631 def pre_test(ctx, appname, dirs=['src']):
634 diropts += ' -d ' + i
635 cd_to_build_dir(ctx, appname)
636 clear_log = open('lcov-clear.log', 'w')
639 # Clear coverage data
640 subprocess.call(('lcov %s -z' % diropts).split(),
641 stdout=clear_log, stderr=clear_log)
643 Logs.warn('Failed to run lcov, no coverage report will be generated')
647 def post_test(ctx, appname, dirs=['src'], remove=['*boost*', 'c++*']):
650 diropts += ' -d ' + i
651 coverage_log = open('lcov-coverage.log', 'w')
652 coverage_lcov = open('coverage.lcov', 'w')
653 coverage_stripped_lcov = open('coverage-stripped.lcov', 'w')
660 # Generate coverage data
661 subprocess.call(('lcov -c %s -b %s' % (diropts, base)).split(),
662 stdout=coverage_lcov, stderr=coverage_log)
664 # Strip unwanted stuff
666 ['lcov', '--remove', 'coverage.lcov'] + remove,
667 stdout=coverage_stripped_lcov, stderr=coverage_log)
669 # Generate HTML coverage output
670 if not os.path.isdir('coverage'):
671 os.makedirs('coverage')
672 subprocess.call('genhtml -o coverage coverage-stripped.lcov'.split(),
673 stdout=coverage_log, stderr=coverage_log)
676 Logs.warn('Failed to run lcov, no coverage report will be generated')
678 coverage_stripped_lcov.close()
679 coverage_lcov.close()
683 Logs.pprint('GREEN', "Waf: Leaving directory `%s'" % os.path.abspath(os.getcwd()))
684 top_level = (len(ctx.stack_path) > 1)
686 cd_to_orig_dir(ctx, top_level)
689 Logs.pprint('BOLD', 'Coverage:', sep='')
690 print('<file://%s>\n\n' % os.path.abspath('coverage/index.html'))
692 def run_tests(ctx, appname, tests, desired_status=0, dirs=['src'], name='*'):
696 diropts += ' -d ' + i
701 if type(i) == type([]):
704 Logs.pprint('BOLD', '** Test', sep='')
705 Logs.pprint('NORMAL', '%s' % s)
707 if Options.options.grind:
708 cmd = 'valgrind ' + i
709 if subprocess.call(cmd, shell=True) == desired_status:
710 Logs.pprint('GREEN', '** Pass')
713 Logs.pprint('RED', '** FAIL')
717 Logs.pprint('GREEN', '** Pass: All %s.%s tests passed' % (appname, name))
719 Logs.pprint('RED', '** FAIL: %d %s.%s tests failed' % (failures, appname, name))
721 def run_ldconfig(ctx):
722 if (ctx.cmd == 'install'
723 and not ctx.env['RAN_LDCONFIG']
724 and ctx.env['LIBDIR']
725 and not 'DESTDIR' in os.environ
726 and not Options.options.destdir):
728 Logs.info("Waf: Running `/sbin/ldconfig %s'" % ctx.env['LIBDIR'])
729 subprocess.call(['/sbin/ldconfig', ctx.env['LIBDIR']])
730 ctx.env['RAN_LDCONFIG'] = True
734 def write_news(name, in_files, out_file, top_entries=None, extra_entries=None):
737 from time import strftime, strptime
739 doap = rdflib.Namespace('http://usefulinc.com/ns/doap#')
740 dcs = rdflib.Namespace('http://ontologi.es/doap-changeset#')
741 rdfs = rdflib.Namespace('http://www.w3.org/2000/01/rdf-schema#')
742 foaf = rdflib.Namespace('http://xmlns.com/foaf/0.1/')
743 rdf = rdflib.Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
744 m = rdflib.ConjunctiveGraph()
748 m.parse(i, format='n3')
750 Logs.warn('Error parsing data, unable to generate NEWS')
753 proj = m.value(None, rdf.type, doap.Project)
754 for f in m.triples([proj, rdfs.seeAlso, None]):
755 if f[2].endswith('.ttl'):
756 m.parse(f[2], format='n3')
759 for r in m.triples([proj, doap.release, None]):
761 revision = m.value(release, doap.revision, None)
762 date = m.value(release, doap.created, None)
763 blamee = m.value(release, dcs.blame, None)
764 changeset = m.value(release, dcs.changeset, None)
765 dist = m.value(release, doap['file-release'], None)
767 if revision and date and blamee and changeset:
768 entry = '%s (%s) stable;\n' % (name, revision)
770 for i in m.triples([changeset, dcs.item, None]):
771 item = textwrap.wrap(m.value(i[2], rdfs.label, None), width=79)
772 entry += '\n * ' + '\n '.join(item)
773 if dist and top_entries is not None:
774 if not str(dist) in top_entries:
775 top_entries[str(dist)] = []
776 top_entries[str(dist)] += [
777 '%s: %s' % (name, '\n '.join(item))]
780 for i in extra_entries[str(dist)]:
785 blamee_name = m.value(blamee, foaf.name, None)
786 blamee_mbox = m.value(blamee, foaf.mbox, None)
787 if blamee_name and blamee_mbox:
788 entry += ' %s <%s>' % (blamee_name,
789 blamee_mbox.replace('mailto:', ''))
791 entry += ' %s\n\n' % (
792 strftime('%a, %d %b %Y %H:%M:%S +0000', strptime(date, '%Y-%m-%d')))
794 entries[revision] = entry
796 Logs.warn('Ignored incomplete %s release description' % name)
799 news = open(out_file, 'w')
800 for e in sorted(entries.keys(), reverse=True):
801 news.write(entries[e])