4 # Copyright (C) 2015-2018 Karl Linden <karl.j.linden@gmail.com>
5 # Copyleft (C) 2008-2022 Nedko Arnaudov
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 from __future__ import print_function
27 from waflib import Logs, Options, TaskGen
28 from waflib import Context
29 from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext
34 # these variables are mandatory ('/' are converted automatically)
38 def display_feature(conf, msg, build):
40 conf.msg(msg, 'yes', color='GREEN')
42 conf.msg(msg, 'no', color='YELLOW')
46 # options provided by the modules
47 opt.load('compiler_cxx')
48 opt.load('compiler_c')
49 opt.load('autooptions')
56 help='HTML documentation directory [Default: <prefix>/share/jack-audio-connection-kit/reference/html/',
58 opt.add_option('--libdir', type='string', help='Library directory [Default: <prefix>/lib]')
59 opt.add_option('--pkgconfigdir', type='string', help='pkg-config file directory [Default: <libdir>/pkgconfig]')
60 opt.add_option('--mandir', type='string', help='Manpage directory [Default: <prefix>/share/man/man1]')
62 # options affecting binaries
67 help='Target platform for cross-compiling, e.g. cygwin or win32',
69 opt.add_option('--debug', action='store_true', default=False, dest='debug', help='Build debuggable binaries')
71 #opt.set_auto_options_define('HAVE_%s')
72 #opt.set_auto_options_style('yesno_and_hack')
74 # options with third party dependencies
75 #doxygen = opt.add_auto_option(
77 # help='Build doxygen documentation',
78 # conf_dest='BUILD_DOXYGEN_DOCS',
80 #doxygen.find_program('doxygen')
85 # this must be called before the configure phase
86 #opt.apply_auto_options_hack()
89 def detect_platform(conf):
90 # GNU/kFreeBSD and GNU/Hurd are treated as Linux
92 # ('KEY, 'Human readable name', ['strings', 'to', 'check', 'for'])
93 ('IS_LINUX', 'Linux', ['gnu0', 'gnukfreebsd', 'linux', 'posix']),
94 ('IS_FREEBSD', 'FreeBSD', ['freebsd']),
95 ('IS_MACOSX', 'MacOS X', ['darwin']),
96 ('IS_SUN', 'SunOS', ['sunos']),
97 ('IS_WINDOWS', 'Windows', ['cygwin', 'msys', 'win32'])
100 for key, name, strings in platforms:
101 conf.env[key] = False
103 conf.start_msg('Checking platform')
104 platform = Options.options.platform
105 for key, name, strings in platforms:
107 if platform.startswith(s):
109 conf.end_msg(name, color='CYAN')
112 class WafToolchainFlags:
114 Waf helper class for handling set of CFLAGS
115 and related. The flush() method will
116 prepend so to allow supplied by (downstream/distro/builder) waf caller flags
117 to override the upstream flags in wscript.
118 TODO: upstream this or find alternative easy way of doing the same
120 def __init__(self, conf):
122 :param conf: Waf configuration object
126 for x in ('CPPFLAGS', 'CFLAGS', 'CXXFLAGS', 'LINKFLAGS'):
131 Flush flags to the configuration object
132 Prepend is used so to allow supplied by
133 (downstream/distro/builder) waf caller flags
134 to override the upstream flags in wscript.
136 for key, val in self.flags.items():
137 self.conf.env.prepend_value(key, val)
139 def add(self, key, val):
141 :param key: Set to add flags to. 'CPPFLAGS', 'CFLAGS', 'CXXFLAGS' or 'LINKFLAGS'
142 :param val: string or list of strings
144 flags = self.flags[key]
145 if isinstance(val, list):
148 if not isinstance(x, str):
149 raise Exception("value must be string or list of strings. ", type(x))
151 elif isinstance(val, str):
154 raise Exception("value must be string or list of strings")
156 def add_cpp(self, value):
158 Add flag or list of flags to CPPFLAGS
159 :param value: string or list of strings
161 self.add('CPPFLAGS', value)
163 def add_c(self, value):
165 Add flag or list of flags to CFLAGS
166 :param value: string or list of strings
168 self.add('CFLAGS', value)
170 def add_cxx(self, value):
172 Add flag or list of flags to CXXFLAGS
173 :param value: string or list of strings
175 self.add('CXXFLAGS', value)
177 def add_candcxx(self, value):
179 Add flag or list of flags to CFLAGS and CXXFLAGS
180 :param value: string or list of strings
185 def add_link(self, value):
187 Add flag or list of flags to LINKFLAGS
188 :param value: string or list of strings
190 self.add('LINKFLAGS', value)
193 conf.load('compiler_cxx')
194 conf.load('compiler_c')
196 detect_platform(conf)
197 flags = WafToolchainFlags(conf)
199 conf.check_cfg(package='jackserver', uselib_store='JACKSERVER', args=["--cflags", "--libs"])
201 conf.check_cfg(package='expat', args='--cflags --libs')
204 flags.add_cxx(['-Wall', '-Wno-invalid-offsetof'])
205 flags.add_cxx('-std=gnu++11')
207 if conf.env['IS_FREEBSD']:
208 conf.check(lib='execinfo', uselib='EXECINFO', define_name='EXECINFO')
209 conf.check_cfg(package='libsysinfo', args='--cflags --libs')
211 if not conf.env['IS_MACOSX']:
212 conf.env.append_unique('LDFLAGS', '-Wl,--no-undefined')
214 conf.check(lib='aften', uselib='AFTEN', define_name='AFTEN')
217 + '#include <aften/aften.h>\n'
221 + 'AftenContext fAftenContext;\n'
222 + 'aften_set_defaults(&fAftenContext);\n'
223 + 'unsigned char *fb;\n'
224 + 'float *buf=new float[10];\n'
225 + 'int res = aften_encode_frame(&fAftenContext, fb, buf, 1);\n'
228 msg='Checking for aften_encode_frame()',
229 define_name='HAVE_AFTEN_NEW_API',
233 flags.add_cxx('-Wno-deprecated-register')
235 conf.load('autooptions')
237 conf.env['LIB_PTHREAD'] = ['pthread']
238 conf.env['LIB_DL'] = ['dl']
239 conf.env['LIB_RT'] = ['rt']
240 conf.env['LIB_M'] = ['m']
241 conf.env['LIB_STDC++'] = ['stdc++']
242 conf.env['JACK_VERSION'] = VERSION
244 conf.env['BINDIR'] = conf.env['PREFIX'] + '/bin'
246 if Options.options.htmldir:
247 conf.env['HTMLDIR'] = Options.options.htmldir
249 # set to None here so that the doxygen code can find out the highest
250 # directory to remove upon install
251 conf.env['HTMLDIR'] = None
253 if Options.options.libdir:
254 conf.env['LIBDIR'] = Options.options.libdir
256 conf.env['LIBDIR'] = conf.env['PREFIX'] + '/lib'
258 if Options.options.pkgconfigdir:
259 conf.env['PKGCONFDIR'] = Options.options.pkgconfigdir
261 conf.env['PKGCONFDIR'] = conf.env['LIBDIR'] + '/pkgconfig'
263 if Options.options.mandir:
264 conf.env['MANDIR'] = Options.options.mandir
266 conf.env['MANDIR'] = conf.env['PREFIX'] + '/share/man/man1'
268 if conf.env['BUILD_DEBUG']:
269 flags.add_candcxx('-g')
272 conf.define('JACK_VERSION', conf.env['JACK_VERSION'])
273 conf.write_config_header('config.h', remove=False)
280 version_msg = APPNAME + "-" + VERSION
281 if os.access('version.h', os.R_OK):
282 data = open('version.h').read()
283 m = re.match(r'^#define GIT_VERSION "([^"]*)"$', data)
285 version_msg += " exported from " + m.group(1)
286 elif os.access('.git', os.R_OK):
287 version_msg += " git revision will be checked and eventually updated during build"
290 conf.msg('Install prefix', conf.env['PREFIX'], color='CYAN')
291 conf.msg('Library directory', conf.all_envs['']['LIBDIR'], color='CYAN')
292 display_feature(conf, 'Build debuggable binaries', conf.env['BUILD_DEBUG'])
295 ('C compiler flags', ['CFLAGS', 'CPPFLAGS']),
296 ('C++ compiler flags', ['CXXFLAGS', 'CPPFLAGS']),
297 ('Linker flags', ['LINKFLAGS', 'LDFLAGS'])
299 for name, vars in tool_flags:
302 flags += conf.all_envs[''][var]
303 conf.msg(name, repr(flags), color='NORMAL')
305 #conf.summarize_auto_options()
307 conf.msg('D-Bus service install directory', conf.env['DBUS_SERVICES_DIR'], color='CYAN')
309 if conf.env['DBUS_SERVICES_DIR'] != conf.env['DBUS_SERVICES_DIR_REAL']:
311 print(Logs.colors.RED + 'WARNING: D-Bus session services directory as reported by pkg-config is')
312 print(Logs.colors.RED + 'WARNING:', end=' ')
313 print(Logs.colors.CYAN + conf.env['DBUS_SERVICES_DIR_REAL'])
314 print(Logs.colors.RED + 'WARNING: but service file will be installed in')
315 print(Logs.colors.RED + 'WARNING:', end=' ')
316 print(Logs.colors.CYAN + conf.env['DBUS_SERVICES_DIR'])
318 Logs.colors.RED + 'WARNING: You may need to adjust your D-Bus configuration after installing jackdbus'
320 print('WARNING: You can override dbus service install directory')
321 print('WARNING: with --enable-pkg-config-dbus-service-dir option to this script')
322 print(Logs.colors.NORMAL, end=' ')
326 bld = self.generator.bld
327 header = self.outputs[0].abspath()
328 if os.access('./version.h', os.R_OK):
329 header = os.path.join(os.getcwd(), out, "version.h")
330 shutil.copy('./version.h', header)
331 data = open(header).read()
332 m = re.match(r'^#define GIT_VERSION "([^"]*)"$', data)
334 self.ver = m.group(1)
335 Logs.pprint('BLUE', "tarball from git revision " + self.ver)
340 if bld.srcnode.find_node('.git'):
341 self.ver = bld.cmd_and_log("LANG= git rev-parse HEAD", quiet=Context.BOTH).splitlines()[0]
342 if bld.cmd_and_log("LANG= git diff-index --name-only HEAD", quiet=Context.BOTH).splitlines():
345 Logs.pprint('BLUE', "git revision " + self.ver)
349 fi = open(header, 'w')
350 fi.write('#define GIT_VERSION "%s"\n' % self.ver)
354 bld(rule=git_ver, target='version.h', update_outputs=True, always=True, ext_out=['.h'])
356 # process subfolders from here
358 if bld.env['IS_LINUX'] or bld.env['IS_FREEBSD']:
362 if bld.env['BUILD_DOXYGEN_DOCS']:
363 html_build_dir = bld.path.find_or_declare('html').abspath()
367 source='doxyfile.in',
369 HTML_BUILD_DIR=html_build_dir,
370 SRCDIR=bld.srcnode.abspath(),
374 # There are two reasons for logging to doxygen.log and using it as
375 # target in the build rule (rather than html_build_dir):
376 # (1) reduce the noise when running the build
377 # (2) waf has a regular file to check for a timestamp. If the directory
378 # is used instead waf will rebuild the doxygen target (even upon
381 doxyfile = task.inputs[0].abspath()
382 logfile = task.outputs[0].abspath()
383 cmd = '%s %s &> %s' % (task.env['DOXYGEN'][0], doxyfile, logfile)
384 return task.exec_command(cmd)
392 # Determine where to install HTML documentation. Since share_dir is the
393 # highest directory the uninstall routine should remove, there is no
394 # better candidate for share_dir, but the requested HTML directory if
395 # --htmldir is given.
396 if bld.env['HTMLDIR']:
397 html_install_dir = bld.options.destdir + bld.env['HTMLDIR']
398 share_dir = html_install_dir
400 share_dir = bld.options.destdir + bld.env['PREFIX'] + '/share/jack-audio-connection-kit'
401 html_install_dir = share_dir + '/reference/html/'
403 if bld.cmd == 'install':
404 if os.path.isdir(html_install_dir):
405 Logs.pprint('CYAN', 'Removing old doxygen documentation installation...')
406 shutil.rmtree(html_install_dir)
407 Logs.pprint('CYAN', 'Removing old doxygen documentation installation done.')
408 Logs.pprint('CYAN', 'Installing doxygen documentation...')
409 shutil.copytree(html_build_dir, html_install_dir)
410 Logs.pprint('CYAN', 'Installing doxygen documentation done.')
411 elif bld.cmd == 'uninstall':
412 Logs.pprint('CYAN', 'Uninstalling doxygen documentation...')
413 if os.path.isdir(share_dir):
414 shutil.rmtree(share_dir)
415 Logs.pprint('CYAN', 'Uninstalling doxygen documentation done.')
416 elif bld.cmd == 'clean':
417 if os.access(html_build_dir, os.R_OK):
418 Logs.pprint('CYAN', 'Removing doxygen generated documentation...')
419 shutil.rmtree(html_build_dir)
420 Logs.pprint('CYAN', 'Removing doxygen generated documentation done.')
423 @TaskGen.extension('.mm')
424 def mm_hook(self, node):
425 """Alias .mm files to be compiled the same as .cpp files, gcc will do the right thing."""
426 return self.create_compiled_task('cxx', node)