1 import glob, os, subprocess, sys
2 from waflib import Build, Task, TaskGen, Utils
4 APPNAME = 'VapourSynth'
10 class preproc(Task.Task):
11 "Preprocess Cython source files"
18 if self.env.CXX_NAME == 'gcc':
19 params = ['-E', '-x', 'c']
20 elif self.env.CXX_NAME == 'msvc':
21 params = ['/nologo', '/E']
23 args = [Utils.subst_vars('${CC}', self.env)] + params + [self.inputs[0].abspath()]
25 with open(self.outputs[0].abspath(), 'w') as f:
26 subprocess.Popen(args, stdout = f).wait()
28 @TaskGen.extension('.pyx')
29 def add_pyx_file(self, node):
30 self.create_task('preproc', node, node.get_bld().change_ext('.pyx'))
32 class docs(Task.Task):
33 "Build Sphinx documentation"
40 subprocess.Popen('make html BUILDDIR={0}'.format(os.path.join(os.pardir, OUT)),
43 stdout = subprocess.PIPE).wait()
45 @TaskGen.feature('docs')
46 @TaskGen.before_method('process_source')
51 for x in self.to_nodes(self.source):
52 if x.name.endswith('.rst'):
57 self.source = no_nodes
59 inst = getattr(self, 'install_path', '${DOCDIR}')
60 mod = getattr(self, 'chmod', Utils.O644)
65 for node in rst_nodes:
66 n = self.path.find_node(OUT).make_node('html')
71 while not cur is self.path.find_node('doc'):
75 for dir in reversed(dirs):
76 n = n.make_node(dir.name)
78 n = n.make_node(node.name).change_ext('.html')
85 for dir in reversed(dirs):
86 path = os.path.join(path, dir.name)
88 setattr(self, 'install_task_{0}'.format(i), self.bld.install_files(path, n, env = self.env, chmod = mod))
92 self.rst_task = self.create_task('docs', rst_nodes, bld_nodes)
95 opt.load('compiler_c')
96 opt.load('compiler_cxx')
99 opt.add_option('--libdir', action = 'store', default = '${PREFIX}/lib', help = 'library installation directory')
100 opt.add_option('--plugindir', action = 'store', default = '${LIBDIR}/vapoursynth', help = 'plugin installation directory')
101 opt.add_option('--docdir', action = 'store', default = '${PREFIX}/share/doc/vapoursynth', help = 'documentation installation directory')
102 opt.add_option('--includedir', action = 'store', default = '${PREFIX}/include/vapoursynth', help = 'header installation directory')
103 opt.add_option('--mode', action = 'store', default = 'release', help = 'the mode to compile in (debug/release)')
104 opt.add_option('--shared', action = 'store', default = 'true', help = 'build a shared library (true/false)')
105 opt.add_option('--static', action = 'store', default = 'false', help = 'build a static library (true/false)')
106 opt.add_option('--filters', action = 'store', default = 'true', help = 'build included filters (true/false)')
107 opt.add_option('--cython', action = 'store', default = 'true', help = 'build Cython wrapper (true/false)')
108 opt.add_option('--avisynth', action = 'store', default = 'true', help = 'build Avisynth compatibility layer (true/false)')
109 opt.add_option('--docs', action = 'store', default = 'false', help = 'build the documentation (true/false)')
110 opt.add_option('--examples', action = 'store', default = 'false', help = 'install SDK examples (true/false)')
113 def add_options(flags, options):
115 conf.env.append_unique(flag, options)
117 conf.load('compiler_c')
118 conf.load('compiler_cxx')
121 if conf.env.DEST_CPU in ['x86', 'x86_64', 'x64', 'amd64', 'x86_amd64']:
122 # Load Yasm explicitly, then the Nasm module which
123 # supports both Nasm and Yasm.
124 conf.find_program('yasm', var = 'AS', mandatory = True)
127 conf.find_program(['python3', 'python'], var = 'PYTHON', mandatory = True)
129 if conf.env.DEST_OS == 'darwin':
130 if conf.env.CXX_NAME == 'gcc':
131 add_options(['ASFLAGS'],
134 if conf.env.CXX_NAME == 'gcc':
135 add_options(['CFLAGS', 'CXXFLAGS'],
138 elif conf.env.CXX_NAME == 'msvc':
139 add_options(['CFLAGS', 'CXXFLAGS'],
144 add_options(['ASFLAGS'],
147 '-Wunrecognized-char',
148 '-Dprogram_name=vs'])
150 if conf.env.DEST_CPU in ['x86_64', 'x64', 'amd64', 'x86_amd64']:
151 add_options(['ASFLAGS'],
155 if conf.env.DEST_OS == 'darwin':
157 elif conf.env.DEST_OS in ['win32', 'cygwin', 'msys', 'uwin']:
161 elif conf.env.DEST_CPU == 'x86':
162 add_options(['ASFLAGS'],
165 if conf.env.DEST_OS == 'darwin':
167 elif conf.env.DEST_OS in ['win32', 'cygwin', 'msys', 'uwin']:
172 if conf.env.DEST_CPU in ['x86', 'x86_64', 'x64']:
173 add_options(['ASFLAGS'],
174 ['-f{0}'.format(fmt)])
176 if conf.options.mode == 'debug':
177 if conf.env.CXX_NAME == 'gcc':
178 add_options(['CFLAGS', 'CXXFLAGS'],
183 elif conf.env.CXX_NAME == 'msvc':
184 add_options(['CFLAGS', 'CXXFLAGS'],
188 add_options(['ASFLAGS'],
191 if conf.env.DEST_OS in ['win32', 'cygwin', 'msys', 'uwin']:
196 add_options(['ASFLAGS'],
197 ['-g{0}'.format(dbgfmt)])
198 elif conf.options.mode == 'release':
199 if conf.env.CXX_NAME == 'gcc':
200 add_options(['CFLAGS', 'CXXFLAGS'],
202 elif conf.env.CXX_NAME == 'msvc':
203 add_options(['CFLAGS', 'CXXFLAGS'],
206 conf.fatal('--mode must be either debug or release.')
208 # Waf always uses gcc/g++ for linking when using a GCC
209 # compatible C/C++ compiler.
210 if conf.env.CXX_NAME == 'gcc':
211 if not conf.env.DEST_OS in ['darwin', 'win32', 'cygwin', 'msys', 'uwin']:
212 add_options(['LINKFLAGS_cshlib',
213 'LINKFLAGS_cprogram',
214 'LINKFLAGS_cxxshlib',
215 'LINKFLAGS_cxxprogram'],
217 '-Wl,-z,noexecstack'])
219 conf.msg("Setting DEST_OS to", conf.env.DEST_OS)
220 conf.msg("Setting DEST_CPU to", conf.env.DEST_CPU)
221 conf.msg("Setting DEST_BINFMT to", conf.env.DEST_BINFMT)
223 def check_feature(name, desc):
224 val = conf.options.__dict__[name]
226 if not val in ['true', 'false']:
227 conf.fatal('--{0} must be either true or false.'.format(name))
232 conf.define('FEATURE_' + u, 1 if val == 'true' else 0)
233 conf.msg("Enabling {0}?".format(desc), 'yes' if conf.env[u] == 'true' else 'no')
235 check_feature('shared', 'shared library')
236 check_feature('static', 'static library')
237 check_feature('filters', 'included filters')
238 check_feature('cython', 'Cython wrapper')
239 check_feature('avisynth', 'Avisynth compatibility')
240 check_feature('docs', 'documentation')
241 check_feature('examples', 'SDK examples')
243 conf.define('PATH_PREFIX', conf.env.PREFIX)
244 conf.msg("Setting PREFIX to", conf.env.PREFIX)
246 for dir in ['libdir', 'plugindir', 'docdir', 'includedir']:
249 conf.env[u] = Utils.subst_vars(conf.options.__dict__[dir], conf.env)
250 conf.define('PATH_' + u, conf.env[u])
251 conf.msg("Setting {0} to".format(u), conf.env[u])
253 conf.check_cxx(use = ['QTCORE'], header_name = 'QtCore/QtCore')
254 conf.check_cxx(use = ['QTCORE'], header_name = 'QtCore/QtCore', type_name = 'QAtomicInt')
256 conf.check_cc(lib = 'swscale')
257 conf.check_cc(use = ['SWSCALE'], header_name = 'libswscale/swscale.h')
258 conf.check_cc(use = ['SWSCALE'], header_name = 'libswscale/swscale.h', function_name = 'swscale_license')
260 conf.check_cc(lib = 'avutil')
261 conf.check_cc(use = ['AVUTIL'], header_name = 'libavutil/avutil.h')
262 conf.check_cc(use = ['AVUTIL'], header_name = 'libavutil/avutil.h', function_name = 'avutil_license')
264 conf.check_cc(lib = 'avcodec')
265 conf.check_cc(use = ['AVCODEC'], header_name = 'libavcodec/avcodec.h')
266 conf.check_cc(use = ['AVCODEC'], header_name = 'libavcodec/avcodec.h', function_name = 'avcodec_license')
268 conf.check_cc(lib = 'ass', mandatory = False)
269 conf.check_cc(use = ['ASS'], header_name = 'ass/ass.h', mandatory = False)
270 conf.check_cc(use = ['ASS'], header_name = 'ass/ass.h', function_name = 'ass_library_init', mandatory = False)
274 if not conf.env.DEST_OS in ['darwin', 'freebsd', 'netbsd', 'openbsd']:
277 conf.env.LIBS = libs.strip()
280 def search_paths(paths):
284 srcpaths += [os.path.join(path, '*.c'),
285 os.path.join(path, '*.cpp'),
286 os.path.join(path, '*.asm')]
290 sources = search_paths([os.path.join('src', 'core'),
291 os.path.join('src', 'core', 'asm')])
293 if bld.env.DEST_OS in ['win32', 'cygwin', 'msys', 'uwin'] and bld.env.AVISYNTH == 'true':
294 sources += search_paths([os.path.join('src', 'avisynth')])
296 bld(features = 'c qxx asm',
297 includes = 'include',
298 use = ['QTCORE', 'SWSCALE', 'AVUTIL', 'AVCODEC'],
299 source = bld.path.ant_glob(sources),
302 if bld.env.SHARED == 'true':
303 bld(features = 'c qxx asm cxxshlib',
305 target = 'vapoursynth',
306 install_path = '${LIBDIR}')
308 if bld.env.STATIC == 'true':
309 bld(features = 'c qxx asm cxxstlib',
310 use = ['objs', 'QTCORE', 'SWSCALE', 'AVUTIL'],
311 target = 'vapoursynth',
312 install_path = '${LIBDIR}')
314 if bld.env.FILTERS == 'true':
315 bld(features = 'c qxx asm cxxshlib',
316 includes = 'include',
317 source = bld.path.ant_glob(search_paths([os.path.join('src', 'filters', 'eedi3')])),
319 install_path = '${PLUGINDIR}')
321 bld(features = 'c qxx asm cxxshlib',
322 includes = 'include',
323 source = bld.path.ant_glob(search_paths([os.path.join('src', 'filters', 'vivtc')])),
325 install_path = '${PLUGINDIR}')
328 bld(features = 'c cxxshlib',
329 includes = 'include',
331 source = bld.path.ant_glob(search_paths([os.path.join('src', 'filters', 'assvapour')])),
332 target = 'assvapour',
333 install_path = '${PLUGINDIR}')
335 if bld.env.CYTHON == 'true':
336 bld(features = 'preproc',
337 source = bld.path.ant_glob([os.path.join('src', 'cython', '*.pyx')]))
339 if bld.env.DOCS == 'true':
340 bld(features = 'docs',
341 source = bld.path.ant_glob([os.path.join('doc', '*.rst'),
342 os.path.join('doc', '**', '*.rst')]),
343 install_path = '${DOCDIR}')
345 if bld.env.EXAMPLES == 'true':
346 bld(features = 'c cxxshlib',
347 includes = 'include',
348 source = os.path.join('sdk', 'filter_skeleton.c'),
349 target = 'example_skeleton',
352 bld(features = 'c cxxshlib',
353 includes = 'include',
354 source = os.path.join('sdk', 'invert_example.c'),
355 target = 'example_invert',
358 bld.install_files('${DOCDIR}/examples',
359 bld.path.ant_glob([os.path.join('sdk', '*')]))
361 bld.install_files('${INCLUDEDIR}', [os.path.join('include', 'VapourSynth.h'),
362 os.path.join('include', 'VSHelper.h')])
364 bld(source = 'vapoursynth.pc.in',
365 install_path = '${LIBDIR}/pkgconfig',
366 PREFIX = bld.env.PREFIX,
367 LIBDIR = bld.env.LIBDIR,
368 INCLUDEDIR = bld.env.INCLUDEDIR,
373 '''runs the Cython tests'''
375 for name in glob.glob(os.path.join('test', '*.py')):
376 if subprocess.Popen([ctx.env.PYTHON, name]).wait() != 0:
377 ctx.fatal('Test {0} failed'.format(name))
379 class TestContext(Build.BuildContext):