Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

536 lines
19KB

  1. #!/usr/bin/env python
  2. import glob
  3. import os
  4. import shutil
  5. import subprocess
  6. import waflib.Logs as Logs
  7. import waflib.Options as Options
  8. import waflib.extras.autowaf as autowaf
  9. # Library and package version (UNIX style major, minor, micro)
  10. # major increment <=> incompatible changes
  11. # minor increment <=> compatible changes (additions)
  12. # micro increment <=> no interface changes
  13. SERD_VERSION = '0.24.0'
  14. SERD_MAJOR_VERSION = '0'
  15. # Mandatory waf variables
  16. APPNAME = 'serd' # Package name for waf dist
  17. VERSION = SERD_VERSION # Package version for waf dist
  18. top = '.' # Source directory
  19. out = 'build' # Build directory
  20. def options(opt):
  21. opt.load('compiler_c')
  22. autowaf.set_options(opt, test=True)
  23. opt.add_option('--no-utils', action='store_true', dest='no_utils',
  24. help='Do not build command line utilities')
  25. opt.add_option('--stack-check', action='store_true', dest='stack_check',
  26. help='Include runtime stack sanity checks')
  27. opt.add_option('--static', action='store_true', dest='static',
  28. help='Build static library')
  29. opt.add_option('--no-shared', action='store_true', dest='no_shared',
  30. help='Do not build shared library')
  31. opt.add_option('--static-progs', action='store_true', dest='static_progs',
  32. help='Build programs as static binaries')
  33. opt.add_option('--largefile', action='store_true', dest='largefile',
  34. help='Build with large file support on 32-bit systems')
  35. opt.add_option('--no-posix', action='store_true', dest='no_posix',
  36. help='Do not use posix_memalign, posix_fadvise, and fileno, even if present')
  37. def configure(conf):
  38. conf.load('compiler_c')
  39. autowaf.configure(conf)
  40. autowaf.display_header('Serd Configuration')
  41. autowaf.set_c99_mode(conf)
  42. conf.env.BUILD_UTILS = not Options.options.no_utils
  43. conf.env.BUILD_SHARED = not Options.options.no_shared
  44. conf.env.STATIC_PROGS = Options.options.static_progs
  45. conf.env.BUILD_STATIC = (Options.options.static or
  46. Options.options.static_progs)
  47. if not conf.env.BUILD_SHARED and not conf.env.BUILD_STATIC:
  48. conf.fatal('Neither a shared nor a static build requested')
  49. if Options.options.stack_check:
  50. autowaf.define(conf, 'SERD_STACK_CHECK', SERD_VERSION)
  51. if Options.options.largefile:
  52. conf.env.append_unique('DEFINES', ['_FILE_OFFSET_BITS=64'])
  53. if not Options.options.no_posix:
  54. conf.check(function_name = 'posix_memalign',
  55. header_name = 'stdlib.h',
  56. define_name = 'HAVE_POSIX_MEMALIGN',
  57. defines = ['_POSIX_C_SOURCE=200809L'],
  58. mandatory = False)
  59. conf.check(function_name = 'posix_fadvise',
  60. header_name = 'fcntl.h',
  61. define_name = 'HAVE_POSIX_FADVISE',
  62. defines = ['_POSIX_C_SOURCE=200809L'],
  63. mandatory = False)
  64. conf.check(function_name = 'fileno',
  65. header_name = 'stdio.h',
  66. define_name = 'HAVE_FILENO',
  67. defines = ['_POSIX_C_SOURCE=200809L'],
  68. mandatory = False)
  69. autowaf.define(conf, 'SERD_VERSION', SERD_VERSION)
  70. autowaf.set_lib_env(conf, 'serd', SERD_VERSION)
  71. conf.write_config_header('serd_config.h', remove=False)
  72. autowaf.display_msg(conf, 'Utilities', bool(conf.env.BUILD_UTILS))
  73. autowaf.display_msg(conf, 'Unit tests', bool(conf.env.BUILD_TESTS))
  74. print('')
  75. lib_source = [
  76. 'src/env.c',
  77. 'src/node.c',
  78. 'src/reader.c',
  79. 'src/string.c',
  80. 'src/uri.c',
  81. 'src/writer.c',
  82. ]
  83. def build(bld):
  84. # C Headers
  85. includedir = '${INCLUDEDIR}/serd-%s/serd' % SERD_MAJOR_VERSION
  86. bld.install_files(includedir, bld.path.ant_glob('serd/*.h'))
  87. # Pkgconfig file
  88. autowaf.build_pc(bld, 'SERD', SERD_VERSION, SERD_MAJOR_VERSION, [],
  89. {'SERD_MAJOR_VERSION' : SERD_MAJOR_VERSION})
  90. libflags = ['-fvisibility=hidden']
  91. libs = ['m']
  92. defines = []
  93. if bld.env.MSVC_COMPILER:
  94. libflags = []
  95. libs = []
  96. defines = ['snprintf=_snprintf']
  97. # Shared Library
  98. if bld.env.BUILD_SHARED:
  99. bld(features = 'c cshlib',
  100. export_includes = ['.'],
  101. source = lib_source,
  102. includes = ['.', './src'],
  103. lib = libs,
  104. name = 'libserd',
  105. target = 'serd-%s' % SERD_MAJOR_VERSION,
  106. vnum = SERD_VERSION,
  107. install_path = '${LIBDIR}',
  108. defines = defines + ['SERD_SHARED', 'SERD_INTERNAL'],
  109. cflags = libflags)
  110. # Static library
  111. if bld.env.BUILD_STATIC:
  112. bld(features = 'c cstlib',
  113. export_includes = ['.'],
  114. source = lib_source,
  115. includes = ['.', './src'],
  116. lib = libs,
  117. name = 'libserd_static',
  118. target = 'serd-%s' % SERD_MAJOR_VERSION,
  119. vnum = SERD_VERSION,
  120. install_path = '${LIBDIR}',
  121. defines = defines + ['SERD_INTERNAL'])
  122. if bld.env.BUILD_TESTS:
  123. test_libs = libs
  124. test_cflags = ['']
  125. test_linkflags = ['']
  126. if not bld.env.NO_COVERAGE:
  127. test_cflags += ['--coverage']
  128. test_linkflags += ['--coverage']
  129. # Profiled static library for test coverage
  130. bld(features = 'c cstlib',
  131. source = lib_source,
  132. includes = ['.', './src'],
  133. lib = test_libs,
  134. name = 'libserd_profiled',
  135. target = 'serd_profiled',
  136. install_path = '',
  137. defines = defines + ['SERD_INTERNAL'],
  138. cflags = test_cflags,
  139. linkflags = test_linkflags)
  140. # Static profiled serdi for tests
  141. bld(features = 'c cprogram',
  142. source = 'src/serdi.c',
  143. includes = ['.', './src'],
  144. use = 'libserd_profiled',
  145. lib = test_libs,
  146. target = 'serdi_static',
  147. install_path = '',
  148. defines = defines,
  149. cflags = test_cflags,
  150. linkflags = test_linkflags)
  151. # Unit test program
  152. bld(features = 'c cprogram',
  153. source = 'tests/serd_test.c',
  154. includes = ['.', './src'],
  155. use = 'libserd_profiled',
  156. lib = test_libs,
  157. target = 'serd_test',
  158. install_path = '',
  159. defines = defines,
  160. cflags = test_cflags,
  161. linkflags = test_linkflags)
  162. # Utilities
  163. if bld.env.BUILD_UTILS:
  164. obj = bld(features = 'c cprogram',
  165. source = 'src/serdi.c',
  166. target = 'serdi',
  167. includes = ['.', './src'],
  168. use = 'libserd',
  169. lib = libs,
  170. install_path = '${BINDIR}')
  171. if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS:
  172. obj.use = 'libserd_static'
  173. if bld.env.STATIC_PROGS:
  174. obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER
  175. obj.linkflags = ['-static']
  176. # Documentation
  177. autowaf.build_dox(bld, 'SERD', SERD_VERSION, top, out)
  178. # Man page
  179. bld.install_files('${MANDIR}/man1', 'doc/serdi.1')
  180. bld.add_post_fun(autowaf.run_ldconfig)
  181. if bld.env.DOCS:
  182. bld.add_post_fun(fix_docs)
  183. def lint(ctx):
  184. subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include src/* serd/*', shell=True)
  185. def amalgamate(ctx):
  186. shutil.copy('serd/serd.h', 'build/serd.h')
  187. amalgamation = open('build/serd.c', 'w')
  188. serd_internal_h = open('src/serd_internal.h')
  189. for l in serd_internal_h:
  190. if l == '#include "serd/serd.h"\n':
  191. amalgamation.write('#include "serd.h"\n')
  192. else:
  193. amalgamation.write(l)
  194. serd_internal_h.close()
  195. for f in lib_source:
  196. fd = open(f)
  197. amalgamation.write('\n/**\n @file %s\n*/' % f)
  198. header = True
  199. for l in fd:
  200. if header:
  201. if l == '*/\n':
  202. header = False
  203. else:
  204. if l != '#include "serd_internal.h"\n':
  205. amalgamation.write(l)
  206. fd.close()
  207. amalgamation.close()
  208. for i in ['c', 'h']:
  209. Logs.info('Wrote build/serd.%s' % i)
  210. def fix_docs(ctx):
  211. if ctx.cmd == 'build':
  212. autowaf.make_simple_dox(APPNAME)
  213. def upload_docs(ctx):
  214. os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/serd/')
  215. for page in glob.glob('doc/*.[1-8]'):
  216. os.system('soelim %s | pre-grohtml troff -man -wall -Thtml | post-grohtml > build/%s.html' % (page, page))
  217. os.system('rsync -avz --delete -e ssh build/%s.html drobilla@drobilla.net:~/drobilla.net/man/' % page)
  218. def file_equals(patha, pathb, subst_from='', subst_to=''):
  219. fa = open(patha, 'rU')
  220. fb = open(pathb, 'rU')
  221. for line in fa:
  222. if line.replace(subst_from, subst_to) != fb.readline().replace(subst_from, subst_to):
  223. return False
  224. fa.close()
  225. fb.close()
  226. return True
  227. def earl_assertion(test, passed, asserter):
  228. import datetime
  229. asserter_str = ''
  230. if asserter is not None:
  231. asserter_str = '\n\tearl:assertedBy <%s> ;' % asserter
  232. passed_str = 'earl:failed'
  233. if passed:
  234. passed_str = 'earl:passed'
  235. return '''
  236. []
  237. a earl:Assertion ;%s
  238. earl:subject <http://drobilla.net/sw/serd> ;
  239. earl:test <%s> ;
  240. earl:result [
  241. a earl:TestResult ;
  242. earl:outcome %s ;
  243. dc:date "%s"^^xsd:dateTime
  244. ] .
  245. ''' % (asserter_str,
  246. test,
  247. passed_str,
  248. datetime.datetime.now().replace(microsecond=0).isoformat())
  249. def check_output(out_filename, check_filename, subst_from='', subst_to=''):
  250. if not os.access(out_filename, os.F_OK):
  251. Logs.pprint('RED', 'FAIL: output %s is missing' % out_filename)
  252. elif not file_equals(check_filename, out_filename, subst_from, subst_to):
  253. Logs.pprint('RED', 'FAIL: %s != %s' % (os.path.abspath(out_filename),
  254. check_filename))
  255. else:
  256. return True
  257. return False
  258. def test_thru(ctx, base, path, check_filename, flags):
  259. in_filename = os.path.join(ctx.path.abspath(), path);
  260. out_filename = path + '.thru'
  261. command = ('%s %s -i turtle -o turtle -p foo "%s" "%s" | '
  262. '%s -i turtle -o ntriples -c foo - "%s" > %s') % (
  263. 'serdi_static', flags.ljust(5),
  264. in_filename, base,
  265. 'serdi_static', base, out_filename)
  266. if autowaf.run_test(ctx, APPNAME, command, 0, name=out_filename):
  267. autowaf.run_test(ctx, APPNAME,
  268. [out_filename + '.check', check_output(out_filename, check_filename, '_:docid', '_:genid')],
  269. 1)
  270. else:
  271. Logs.pprint('RED', 'FAIL: error running %s' % command)
  272. def test_manifest(ctx, srcdir, testdir, report, base_uri):
  273. import rdflib
  274. import urlparse
  275. rdf = rdflib.Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
  276. rdfs = rdflib.Namespace('http://www.w3.org/2000/01/rdf-schema#')
  277. mf = rdflib.Namespace('http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#')
  278. rdft = rdflib.Namespace('http://www.w3.org/ns/rdftest#')
  279. earl = rdflib.Namespace('http://www.w3.org/ns/earl#')
  280. model = rdflib.ConjunctiveGraph()
  281. model.parse(os.path.join(srcdir, 'tests', testdir, 'manifest.ttl'),
  282. rdflib.URIRef(base_uri + 'manifest.ttl'),
  283. format='n3')
  284. asserter = ''
  285. if os.getenv('USER') == 'drobilla':
  286. asserter = 'http://drobilla.net/drobilla#me'
  287. def run_test(action_node, expected_return):
  288. output = os.path.join('tests', testdir, action_node + '.out')
  289. action = os.path.join(srcdir, 'tests', testdir, action_node)
  290. rel = os.path.relpath(action, os.path.join(srcdir, 'tests', testdir))
  291. command = 'serdi_static -f "%s" "%s" > %s' % (action, base_uri + rel, output)
  292. return autowaf.run_test(ctx, APPNAME, command, expected_return, name=str(action))
  293. def run_tests(test_class, expected_return, check_result=False):
  294. autowaf.begin_tests(ctx, APPNAME, str(test_class))
  295. for i in sorted(model.triples([None, rdf.type, test_class])):
  296. test = i[0]
  297. name = model.value(test, mf.name, None)
  298. action_node = model.value(test, mf.action, None)[len(base_uri):]
  299. passed = run_test(action_node, expected_return)
  300. if passed and check_result:
  301. result_node = model.value(test, mf.result, None)[len(base_uri):]
  302. action = os.path.join('tests', testdir, action_node)
  303. output = action + '.out'
  304. result = os.path.join(srcdir, 'tests', testdir, result_node)
  305. passed = check_output(output, result)
  306. test_thru(ctx, base_uri + action_node, action, result, "")
  307. report.write(earl_assertion(test, passed, asserter))
  308. autowaf.end_tests(ctx, APPNAME, str(test_class))
  309. run_tests(rdft.TestTurtlePositiveSyntax, 0)
  310. run_tests(rdft.TestTurtleNegativeSyntax, 1)
  311. run_tests(rdft.TestTurtleNegativeEval, 1)
  312. run_tests(rdft.TestTurtleEval, 0, True)
  313. def test(ctx):
  314. blddir = autowaf.build_dir(APPNAME, 'tests')
  315. for i in ['', 'bad', 'good', 'new', 'TurtleTests', 'extra']:
  316. try:
  317. os.makedirs(os.path.join(blddir, i))
  318. except:
  319. pass
  320. for i in glob.glob(blddir + '/*.*'):
  321. os.remove(i)
  322. srcdir = ctx.path.abspath()
  323. orig_dir = os.path.abspath(os.curdir)
  324. os.chdir(os.path.join(srcdir, 'tests', 'good'))
  325. old_good_tests = glob.glob('*.ttl')
  326. old_good_tests.sort()
  327. old_good_tests.remove('manifest.ttl')
  328. good_tests = { 'good': old_good_tests }
  329. os.chdir(orig_dir)
  330. os.chdir(srcdir)
  331. bad_tests = glob.glob('tests/bad/*.ttl')
  332. bad_tests.sort()
  333. os.chdir(orig_dir)
  334. autowaf.pre_test(ctx, APPNAME)
  335. os.environ['PATH'] = '.' + os.pathsep + os.getenv('PATH')
  336. autowaf.run_test(ctx, APPNAME, 'serd_test', dirs=['.'])
  337. autowaf.run_tests(ctx, APPNAME, [
  338. 'serdi_static -q -o turtle "%s/tests/good/base.ttl" "base.ttl" > tests/good/base.ttl.out' % srcdir
  339. ], 0, name='base')
  340. if not file_equals('%s/tests/good/base.ttl' % srcdir, 'tests/good/base.ttl.out'):
  341. Logs.pprint('RED', 'FAIL: build/tests/base.ttl.out is incorrect')
  342. nul = os.devnull
  343. autowaf.run_tests(ctx, APPNAME, [
  344. 'serdi_static "file://%s/tests/good/manifest.ttl" > %s' % (srcdir, nul),
  345. # 'serdi_static %s/tests/good/UTF-8.ttl > %s' % (srcdir, nul),
  346. 'serdi_static -v > %s' % nul,
  347. 'serdi_static -h > %s' % nul,
  348. 'serdi_static -s "<foo> a <#Thingie> ." > %s' % nul,
  349. 'serdi_static %s > %s' % (nul, nul)
  350. ], 0, name='serdi-cmd-good')
  351. # Test read error by reading a directory
  352. autowaf.run_test(ctx, APPNAME, 'serdi_static "file://%s/"' % srcdir,
  353. 1, name='read_error')
  354. # Test write error by writing to /dev/full
  355. if os.path.exists('/dev/full'):
  356. autowaf.run_test(ctx, APPNAME,
  357. 'serdi_static "file://%s/tests/good/manifest.ttl" > /dev/full' % srcdir,
  358. 1, name='write_error')
  359. autowaf.run_tests(ctx, APPNAME, [
  360. 'serdi_static -q "file://%s/tests/bad-id-clash.ttl" > %s' % (srcdir, nul),
  361. 'serdi_static > %s' % nul,
  362. 'serdi_static ftp://example.org/unsupported.ttl > %s' % nul,
  363. 'serdi_static -i > %s' % nul,
  364. 'serdi_static -o > %s' % nul,
  365. 'serdi_static -z > %s' % nul,
  366. 'serdi_static -p > %s' % nul,
  367. 'serdi_static -c > %s' % nul,
  368. 'serdi_static -r > %s' % nul,
  369. 'serdi_static -i illegal > %s' % nul,
  370. 'serdi_static -o illegal > %s' % nul,
  371. 'serdi_static -i turtle > %s' % nul,
  372. 'serdi_static /no/such/file > %s' % nul],
  373. 1, name='serdi-cmd-bad')
  374. def test_base(test):
  375. return ('http://www.w3.org/2001/sw/DataAccess/df1/tests/'
  376. + test.replace('\\', '/'))
  377. # Good tests
  378. for tdir, tests in good_tests.items():
  379. commands = []
  380. for test in tests:
  381. for lax in ['', '-l']:
  382. path = os.path.join('tests', tdir, test)
  383. commands += [ 'serdi_static %s -f "%s" "%s" > %s.out' % (
  384. lax, os.path.join(srcdir, path), test_base(test), path) ]
  385. autowaf.run_tests(ctx, APPNAME, commands, 0, name=tdir)
  386. verify_tests = []
  387. for test in tests:
  388. check_filename = os.path.join(
  389. srcdir, 'tests', tdir, test.replace('.ttl', '.nt'))
  390. out_filename = os.path.join('tests', tdir, test + '.out')
  391. if check_output(out_filename, check_filename):
  392. verify_tests += [[out_filename, 0]]
  393. else:
  394. verify_tests += [[out_filename, 1]]
  395. autowaf.run_tests(ctx, APPNAME, verify_tests, name='verify_turtle_to_ntriples')
  396. # Bad tests
  397. commands = []
  398. for test in bad_tests:
  399. for lax in ['', '-l']:
  400. commands += [ 'serdi_static %s -q "%s" "%s" > %s.out' % (
  401. lax, os.path.join(srcdir, test), test_base(test), test) ]
  402. autowaf.run_tests(ctx, APPNAME, commands, 1, name='bad')
  403. # Don't do a round-trip test for test-id.ttl, IDs have changed
  404. good_tests['good'].remove('test-id.ttl')
  405. # Round-trip good tests
  406. autowaf.begin_tests(ctx, APPNAME, 'round_trip_good')
  407. for tdir, tests in good_tests.items():
  408. thru_tests = tests;
  409. commands = []
  410. num = 0
  411. for test in thru_tests:
  412. num += 1
  413. flags = ''
  414. if (num % 2 == 0):
  415. flags += '-b'
  416. if (num % 5 == 0):
  417. flags += ' -f'
  418. if (num % 3 == 0):
  419. flags += ' -r http://www.w3.org/'
  420. if (num % 7 == 0):
  421. flags += ' -e'
  422. path = os.path.join('tests', tdir, test)
  423. check = os.path.join(srcdir, path.replace('.ttl', '.nt'))
  424. test_thru(ctx, test_base(test), path, check, flags)
  425. autowaf.end_tests(ctx, APPNAME, 'round_trip_good')
  426. # New manifest-driven tests
  427. try:
  428. report = open('earl.ttl', 'w')
  429. report.write('''@prefix earl: <http://www.w3.org/ns/earl#> .
  430. @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n''')
  431. serd_ttl = open(os.path.join(srcdir, 'serd.ttl'))
  432. for line in serd_ttl:
  433. report.write(line)
  434. serd_ttl.close()
  435. turtle_tests = 'http://www.w3.org/2013/TurtleTests/'
  436. test_manifest(ctx, srcdir, 'TurtleTests', report, turtle_tests)
  437. report.close()
  438. except:
  439. pass
  440. autowaf.post_test(ctx, APPNAME)
  441. def posts(ctx):
  442. path = str(ctx.path.abspath())
  443. autowaf.news_to_posts(
  444. os.path.join(path, 'NEWS'),
  445. {'title' : 'Serd',
  446. 'description' : autowaf.get_blurb(os.path.join(path, 'README')),
  447. 'dist_pattern' : 'http://download.drobilla.net/serd-%s.tar.bz2'},
  448. { 'Author' : 'drobilla',
  449. 'Tags' : 'Hacking, RDF, Serd' },
  450. os.path.join(out, 'posts'))