Bump GEANY_API_VERSION for langType rename
[geany-mirror.git] / wscript
blobac6e49dd23566b990726389411a7a7316a5e6858
1 # -*- coding: utf-8 -*-
3 # WAF build script - this file is part of Geany, a fast and lightweight IDE
5 # Copyright 2008-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
6 # Copyright 2008-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """
23 This is a WAF build script (http://code.google.com/p/waf/).
24 It can be used as an alternative build system to autotools
25 for Geany. It does not (yet) cover all of the autotools tests and
26 configure options but all important things are working.
27 "make dist" should be done with autotools, most other targets and
28 functions should work better (regarding performance and flexibility)
29 or at least equally.
31 Missing features: --enable-binreloc, make targets: dist, pdf (in doc/)
32 Known issues: Dependency handling is buggy, e.g. if src/document.h is
33               changed, depending source files are not rebuilt (maybe Waf bug).
35 The code of this file itself loosely follows PEP 8 with some exceptions
36 (line width 100 characters and some other minor things).
38 Requires WAF 1.6.1 and Python 2.5 (or later).
39 """
42 import sys
43 import os
44 import tempfile
45 from waflib import Logs, Options, Scripting, Utils
46 from waflib.Build import BuildContext
47 from waflib.Configure import ConfigurationContext
48 from waflib.Errors import ConfigurationError, WafError
49 from waflib.TaskGen import feature, before_method
50 from waflib.Tools.compiler_c import c_compiler
51 from waflib.Tools.compiler_cxx import cxx_compiler
54 APPNAME = 'geany'
55 VERSION = '1.26'
56 LINGUAS_FILE = os.path.join('po', 'LINGUAS')
57 MINIMUM_GTK_VERSION = '2.24.0'
58 MINIMUM_GTK3_VERSION = '3.0.0'
59 MINIMUM_GLIB_VERSION = '2.28.0'
61 GEANY_LIB_VERSION = '0.0.0'
63 top = '.'
64 out = '_build_'
66 mio_sources = set(['tagmanager/mio/mio.c'])
68 ctags_sources = set([
69     'tagmanager/ctags/abaqus.c',
70     'tagmanager/ctags/args.c',
71     'tagmanager/ctags/abc.c',
72     'tagmanager/ctags/actionscript.c',
73     'tagmanager/ctags/asciidoc.c',
74     'tagmanager/ctags/asm.c',
75     'tagmanager/ctags/basic.c',
76     'tagmanager/ctags/c.c',
77     'tagmanager/ctags/cobol.c',
78     'tagmanager/ctags/conf.c',
79     'tagmanager/ctags/css.c',
80     'tagmanager/ctags/ctags.c',
81     'tagmanager/ctags/diff.c',
82     'tagmanager/ctags/docbook.c',
83     'tagmanager/ctags/erlang.c',
84     'tagmanager/ctags/entry.c',
85     'tagmanager/ctags/fortran.c',
86     'tagmanager/ctags/get.c',
87     'tagmanager/ctags/go.c',
88     'tagmanager/ctags/haskell.c',
89     'tagmanager/ctags/haxe.c',
90     'tagmanager/ctags/html.c',
91     'tagmanager/ctags/js.c',
92     'tagmanager/ctags/json.c',
93     'tagmanager/ctags/keyword.c',
94     'tagmanager/ctags/latex.c',
95     'tagmanager/ctags/lregex.c',
96     'tagmanager/ctags/lua.c',
97     'tagmanager/ctags/make.c',
98     'tagmanager/ctags/markdown.c',
99     'tagmanager/ctags/matlab.c',
100     'tagmanager/ctags/nsis.c',
101     'tagmanager/ctags/nestlevel.c',
102     'tagmanager/ctags/objc.c',
103     'tagmanager/ctags/options.c',
104     'tagmanager/ctags/parse.c',
105     'tagmanager/ctags/pascal.c',
106     'tagmanager/ctags/r.c',
107     'tagmanager/ctags/perl.c',
108     'tagmanager/ctags/php.c',
109     'tagmanager/ctags/powershell.c',
110     'tagmanager/ctags/python.c',
111     'tagmanager/ctags/read.c',
112     'tagmanager/ctags/rest.c',
113     'tagmanager/ctags/ruby.c',
114     'tagmanager/ctags/rust.c',
115     'tagmanager/ctags/sh.c',
116     'tagmanager/ctags/sort.c',
117     'tagmanager/ctags/sql.c',
118     'tagmanager/ctags/strlist.c',
119     'tagmanager/ctags/txt2tags.c',
120     'tagmanager/ctags/tcl.c',
121     'tagmanager/ctags/vhdl.c',
122     'tagmanager/ctags/verilog.c',
123     'tagmanager/ctags/vstring.c'])
125 tagmanager_sources = set([
126     'tagmanager/src/tm_source_file.c',
127     'tagmanager/src/tm_tag.c',
128     'tagmanager/src/tm_workspace.c'])
130 scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c'])
132 geany_sources = set([
133     'src/about.c', 'src/build.c', 'src/callbacks.c', 'src/dialogs.c', 'src/document.c',
134     'src/editor.c', 'src/encodings.c', 'src/filetypes.c', 'src/geanyentryaction.c',
135     'src/geanymenubuttonaction.c', 'src/geanyobject.c', 'src/geanywraplabel.c',
136     'src/highlighting.c', 'src/keybindings.c',
137     'src/keyfile.c', 'src/log.c', 'src/libmain.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c', 'src/osx.c',
138     'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
139     'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/spawn.c', 'src/stash.c',
140     'src/symbols.c',
141     'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
142     'src/ui_utils.c', 'src/utils.c'])
144 geany_bin_sources = set(['src/main.c'])
146 geany_icons = {
147     'hicolor/16x16/apps':       ['16x16/classviewer-class.png',
148                                  '16x16/classviewer-macro.png',
149                                  '16x16/classviewer-member.png',
150                                  '16x16/classviewer-method.png',
151                                  '16x16/classviewer-namespace.png',
152                                  '16x16/classviewer-other.png',
153                                  '16x16/classviewer-struct.png',
154                                  '16x16/classviewer-var.png',
155                                  '16x16/geany.png'],
156     'hicolor/16x16/actions':    ['16x16/geany-build.png',
157                                  '16x16/geany-close-all.png',
158                                  '16x16/geany-save-all.png'],
159     'hicolor/24x24/actions':    ['24x24/geany-build.png',
160                                  '24x24/geany-close-all.png',
161                                  '24x24/geany-save-all.png'],
162     'hicolor/32x32/actions':    ['32x32/geany-build.png',
163                                  '32x32/geany-close-all.png',
164                                  '32x32/geany-save-all.png'],
165     'hicolor/32x32/apps':       ['32x32/geany.png'],
166     'hicolor/48x48/actions':    ['48x48/geany-build.png',
167                                  '48x48/geany-close-all.png',
168                                  '48x48/geany-save-all.png'],
169     'hicolor/48x48/apps':       ['48x48/geany.png'],
170     'hicolor/scalable/apps':    ['scalable/geany.svg'],
171     'hicolor/scalable/actions': ['scalable/geany-build.svg',
172                                  'scalable/geany-close-all.svg',
173                                  'scalable/geany-save-all.svg'],
174     'Tango/16x16/actions':      ['tango/16x16/geany-save-all.png'],
175     'Tango/24x24/actions':      ['tango/24x24/geany-save-all.png'],
176     'Tango/32x32/actions':      ['tango/32x32/geany-save-all.png'],
177     'Tango/48x48/actions':      ['tango/48x48/geany-save-all.png'],
178     'Tango/scalable/actions':   ['tango/scalable/geany-save-all.svg']
180 geany_icons_indexes = {
181     'hicolor':  ['index.theme'],
182     'Tango':    ['tango/index.theme']
186 def configure(conf):
188     conf.check_waf_version(mini='1.6.1')
190     conf.load('compiler_c')
191     _check_c99(conf)
192     is_win32 = _target_is_win32(conf)
194     visibility_hidden_supported = conf.check_cc(cflags=['-Werror', '-fvisibility=hidden'], mandatory=False)
195     conf.check_cc(header_name='fcntl.h', mandatory=False)
196     conf.check_cc(header_name='fnmatch.h', mandatory=False)
197     conf.check_cc(header_name='glob.h', mandatory=False)
198     conf.check_cc(header_name='sys/time.h', mandatory=False)
199     conf.check_cc(header_name='sys/types.h', mandatory=False)
200     conf.check_cc(header_name='sys/stat.h', mandatory=False)
201     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
202     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
203     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
205     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
206     conf.check_cc(function_name='fnmatch', header_name='fnmatch.h', mandatory=False)
207     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
208     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
209     conf.check_cc(function_name='strstr', header_name='string.h')
211     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
213     # check sunOS socket support
214     if Options.platform == 'sunos':
215         conf.check_cc(function_name='socket', lib='socket',
216                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
218     # check for cxx after the header and function checks have been done to ensure they are
219     # checked with cc not cxx
220     conf.load('compiler_cxx')
221     if is_win32:
222         conf.load('winres')
223     _load_intltool_if_available(conf)
225     # GTK / GIO version check
226     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
227     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
228     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
229         mandatory=True, args='--cflags --libs')
230     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
231         mandatory=True, args='--cflags --libs')
232     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
233         mandatory=True, args='--cflags --libs')
234     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
235     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
236     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
237     if conf.options.enable_mac_integration:
238         pkgname = 'gtk-mac-integration-gtk3' if conf.options.use_gtk3 else 'gtk-mac-integration-gtk2'
239         conf.check_cfg(package=pkgname, uselib_store='MAC_INTEGRATION',
240             mandatory=True, args='--cflags --libs')
241     # remember GTK version for the build step
242     conf.env['gtk_package_name'] = gtk_package_name
243     conf.env['gtk_version'] = gtk_version
244     conf.env['minimum_gtk_version'] = minimum_gtk_version
245     conf.env['use_gtk3'] = conf.options.use_gtk3
247     revision = _get_git_rev(conf)
249     # rst2html for the HTML manual
250     if not conf.options.no_html_doc and revision is not None:
251         try:
252             conf.env['RST2HTML'] = _find_rst2html(conf)
253         except WafError:
254             error_msg = '''Documentation enabled but rst2html not found.
255 You can explicitly disable building of the HTML manual with --disable-html-docs,
256 but you then may not have a local copy of the HTML manual.'''
257             raise WafError(error_msg)
259     # Windows specials
260     if is_win32:
261         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
262             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
263             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
264             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
265             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
266         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
267         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
268         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
269         # overwrite LOCALEDIR to install message catalogues properly
270         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
271         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
272         conf.undefine('DATADIR')
273         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
274         conf.env.append_value('LINKFLAGS_cprogram', [
275             '-mwindows',
276             '-static-libgcc',
277             '-static-libstdc++'])
278         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32', 'comdlg32'])
279     else:
280         conf.env['cshlib_PATTERN'] = '%s.so'
281         # DATADIR and LOCALEDIR are defined by the intltool tool
282         # but they are not added to the environment, so we need to
283         _add_define_to_env(conf, 'DATADIR')
284         _add_define_to_env(conf, 'LOCALEDIR')
285         docdir = os.path.join(conf.env['DATADIR'], 'doc', 'geany')
286         libdir = os.path.join(conf.env['PREFIX'], 'lib')
287         mandir = os.path.join(conf.env['DATADIR'], 'man')
288         _define_from_opt(conf, 'DOCDIR', conf.options.docdir, docdir)
289         _define_from_opt(conf, 'LIBDIR', conf.options.libdir, libdir)
290         _define_from_opt(conf, 'MANDIR', conf.options.mandir, mandir)
292     conf.define('ENABLE_NLS', 1)
293     conf.define('GEANY_LOCALEDIR', '' if is_win32 else conf.env['LOCALEDIR'], quote=True)
294     conf.define('GEANY_DATADIR', 'data' if is_win32 else conf.env['DATADIR'], quote=True)
295     conf.define('GEANY_DOCDIR', conf.env['DOCDIR'], quote=True)
296     conf.define('GEANY_LIBDIR', '' if is_win32 else conf.env['LIBDIR'], quote=True)
297     conf.define('GEANY_PREFIX', '' if is_win32 else conf.env['PREFIX'], quote=True)
298     conf.define('PACKAGE', APPNAME, quote=True)
299     conf.define('VERSION', VERSION, quote=True)
300     conf.define('REVISION', revision or '-1', quote=True)
302     conf.define('GETTEXT_PACKAGE', APPNAME, quote=True)
304     # no VTE on Windows
305     if is_win32:
306         conf.options.no_vte = True
308     _define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
309     _define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
310     _define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
312     conf.write_config_header('config.h', remove=False)
314     # GEANY_EXPORT_SYMBOL and GEANY_API_SYMBOL
315     if is_win32:
316         geanyexport_cflags = []
317         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=__declspec(dllexport)']
318     elif visibility_hidden_supported:
319         geanyexport_cflags = ['-fvisibility=hidden']
320         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=__attribute__((visibility("default")))']
321     else:  # unknown, define to nothing
322         geanyexport_cflags = []
323         geanyexport_defines = ['GEANY_EXPORT_SYMBOL=']
324     geanyexport_defines.append('GEANY_API_SYMBOL=GEANY_EXPORT_SYMBOL')
325     conf.env['DEFINES_geanyexport'] = geanyexport_defines
326     conf.env['CFLAGS_geanyexport'] = geanyexport_cflags
327     conf.env['CXXFLAGS_geanyexport'] = geanyexport_cflags
329     # some more compiler flags
330     conf.env.append_value('CFLAGS', ['-DHAVE_CONFIG_H'])
331     if conf.env['CC_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CFLAGS']):
332         conf.env.append_value('CFLAGS', ['-O2'])
333     if revision is not None:
334         conf.env.append_value('CFLAGS', ['-g', '-DGEANY_DEBUG'])
335     # Scintilla flags
336     conf.env.append_value('CFLAGS', ['-DGTK'])
337     conf.env.append_value('CXXFLAGS',
338         ['-DNDEBUG', '-DGTK', '-DSCI_LEXER', '-DG_THREADS_IMPL_NONE'])
339     if conf.env['CXX_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CXXFLAGS']):
340         conf.env.append_value('CXXFLAGS', ['-O2'])
341     if revision is not None:
342         conf.env.append_value('CXXFLAGS', ['-g'])
344     # summary
345     Logs.pprint('BLUE', 'Summary:')
346     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
347     conf.msg('Using GTK version', gtk_version)
348     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
349     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
350     if revision is not None:
351         conf.msg('Compiling Git revision', revision)
352     # deprecation warning
353     _show_deprecation_warning(conf)
356 def _show_deprecation_warning(ctx):
357     Logs.pprint(
358         'RED',
359         'The Waf build system is deprecated and will be removed in the removed in Geany 1.27. '
360         'Please use the Autotools build system.')
363 def options(opt):
364     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
365     # If anyone wants to add support for building with MSVC, this hack should be removed.
366     c_compiler['win32'] = ['gcc']
367     cxx_compiler['win32'] = ['g++']
369     opt.load('compiler_cc')
370     opt.load('compiler_cxx')
371     opt.load('intltool')
373     # Option
374     opt.add_option('--no-scm', action='store_true', default=False,
375         help='Disable SCM detection [default: No]', dest='no_scm')
376     # Features
377     opt.add_option('--disable-plugins', action='store_true', default=False,
378         help='compile without plugin support [default: No]', dest='no_plugins')
379     opt.add_option('--disable-socket', action='store_true', default=False,
380         help='compile without support to detect a running instance [[default: No]',
381         dest='no_socket')
382     opt.add_option('--disable-vte', action='store_true', default=False,
383         help='compile without support for an embedded virtual terminal [[default: No]',
384         dest='no_vte')
385     opt.add_option('--enable-gtk3', action='store_true', default=False,
386         help='compile with GTK3 support (experimental) [[default: No]',
387         dest='use_gtk3')
388     opt.add_option('--enable-mac-integration', action='store_true', default=False,
389         help='use gtk-mac-integration to enable improved OS X integration [[default: No]',
390         dest='enable_mac_integration')
391     opt.add_option('--disable-html-docs', action='store_true', default=False,
392         help='do not generate HTML documentation using rst2html [[default: No]',
393         dest='no_html_doc')
394     # Paths
395     opt.add_option('--mandir', type='string', default='',
396         help='man documentation', dest='mandir')
397     opt.add_option('--docdir', type='string', default='',
398         help='documentation root', dest='docdir')
399     opt.add_option('--libdir', type='string', default='',
400         help='object code libraries', dest='libdir')
403 def build(bld):
404     is_win32 = _target_is_win32(bld)
405     bld.add_post_fun(_show_deprecation_warning)
407     if bld.cmd == 'clean':
408         _remove_linguas_file()
409     if bld.cmd in ('install', 'uninstall'):
410         bld.add_post_fun(_post_install)
412     def build_plugin(plugin_name, install=True, uselib_add=[]):
413         if install:
414             instpath = '${LIBDIR}/geany'
415         else:
416             instpath = None
418         bld(
419             features                = ['c', 'cshlib'],
420             source                  = 'plugins/%s.c' % plugin_name,
421             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
422             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
423             target                  = plugin_name,
424             uselib                  = ['GTK', 'GLIB', 'GMODULE', 'C99'] + uselib_add,
425             use                     = ['geany'],
426             install_path            = instpath)
428     # CTags
429     bld.objects(
430         features        = ['c'],
431         source          = ctags_sources,
432         name            = 'ctags',
433         target          = 'ctags',
434         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
435         defines         = 'G_LOG_DOMAIN="CTags"',
436         uselib          = ['cshlib', 'GLIB', 'geanyexport', 'C99'])
438     # Tagmanager
439     bld.objects(
440         features        = ['c'],
441         source          = tagmanager_sources,
442         name            = 'tagmanager',
443         target          = 'tagmanager',
444         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
445         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
446         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport', 'C99'])
448     # MIO
449     bld.objects(
450         features        = ['c'],
451         source          = mio_sources,
452         name            = 'mio',
453         target          = 'mio',
454         includes        = ['.', 'tagmanager/mio/'],
455         defines         = 'G_LOG_DOMAIN="MIO"',
456         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
458     # Scintilla
459     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
460     scintilla_sources.update([file.path_from(bld.srcnode) for file in files])
461     bld.objects(
462         features        = ['c', 'cxx'],
463         name            = 'scintilla',
464         target          = 'scintilla',
465         source          = scintilla_sources,
466         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
467         uselib          = ['cshlib', 'cxxshlib', 'GTK', 'GLIB', 'GMODULE', 'M', 'geanyexport'])
469     # Geany
470     if bld.env['HAVE_VTE'] == 1:
471         geany_sources.add('src/vte.c')
472     if is_win32:
473         geany_sources.add('src/win32.c')
474         geany_bin_sources.add('geany_private.rc')
476     def gen_signallist(task):
477         from xml.etree import ElementTree
479         def find_handlers(xml_filename):
480             tree = ElementTree.parse(xml_filename)
481             signals = tree.getroot().findall(".//signal")
482             return [sig.attrib["handler"] for sig in signals]
484         handlers = []
485         for node in task.inputs:
486             handlers += find_handlers(node.abspath())
487         handlers = sorted(set(handlers))
489         for node in task.outputs:
490             node.write("/* This file is auto-generated, do not edit. */\n" +
491                        ''.join(["ITEM(%s)\n" % h for h in handlers]))
493     # signallist.i
494     bld(
495         source  = 'data/geany.glade',
496         target  = bld.path.get_bld().make_node('src/signallist.i'),
497         name    = 'signallist.i',
498         rule    = gen_signallist)
500     base_uselibs = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'MAC_INTEGRATION', 'SUNOS_SOCKET', 'M', 'C99']
502     # libgeany
503     bld.shlib(
504         features        = ['c', 'cxx'],
505         name            = 'geany',
506         target          = 'geany',
507         source          = geany_sources,
508         includes        = ['.', 'scintilla/include', 'tagmanager/src', 'src'],
509         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
510         uselib          = base_uselibs + ['geanyexport'],
511         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'],
512         linkflags       = bld.env['LINKFLAGS_cprogram'],
513         vnum            = GEANY_LIB_VERSION,
514         install_path    = '${PREFIX}/bin' if is_win32 else '${LIBDIR}')
516     # geany executable
517     t = bld.program(
518         features        = ['c', 'cxx'],
519         name            = 'geany_bin',
520         target          = 'geany',
521         source          = geany_bin_sources,
522         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
523         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
524         uselib          = base_uselibs + ['geanyexport'],
525         use             = ['geany'])
526     if not is_win32:
527         # http://www.freehackers.org/~tnagy/testdoc/single.html#common_c
528         t.rpath = bld.env['LIBDIR']
530     # Plugins
531     if bld.env['HAVE_PLUGINS'] == 1:
532         build_plugin('classbuilder')
533         build_plugin('demoplugin', False)
534         build_plugin('export', uselib_add=['M'])
535         build_plugin('filebrowser')
536         build_plugin('htmlchars')
537         build_plugin('saveactions')
538         build_plugin('splitwindow')
540     # Translations
541     if bld.env['INTLTOOL']:
542         bld(
543             features        = ['linguas', 'intltool_po'],
544             podir           = 'po',
545             install_path    = '${LOCALEDIR}',
546             appname         = 'geany')
548     # HTML documentation (build if it is not part of the tree already, as it is required for install)
549     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
550     if bld.env['RST2HTML']:
551         rst2html = bld.env['RST2HTML']
552         bld(
553             source  = ['doc/geany.txt'],
554             deps    = ['doc/geany.css'],
555             target  = bld.path.get_bld().make_node('doc/geany.html'),
556             name    = 'geany.html',
557             cwd     = os.path.join(bld.path.abspath(), 'doc'),
558             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
560     # geany.pc
561     if is_win32:
562         # replace backward slashes by forward slashes as they could be interepreted as escape
563         # characters
564         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
565     else:
566         geany_pc_prefix = bld.env['PREFIX']
567     bld(
568         source          = 'geany.pc.in',
569         dct             = {'VERSION': VERSION,
570                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
571                                 (bld.env['gtk_package_name'],
572                                  bld.env['minimum_gtk_version'],
573                                  MINIMUM_GLIB_VERSION),
574                            'prefix': geany_pc_prefix,
575                            'exec_prefix': '${prefix}',
576                            'libdir': '${exec_prefix}/lib',
577                            'includedir': '${prefix}/include',
578                            'datarootdir': '${prefix}/share',
579                            'datadir': '${datarootdir}',
580                            'localedir': '${datarootdir}/locale'})
582     # geany.nsi
583     bld(
584         features        = 'subst',
585         source          = 'geany.nsi.in',
586         target          = 'geany.nsi',
587         dct             = {'VERSION': VERSION,
588                            'GTK_VERSION': bld.env['gtk_version']},
589         install_path    = None)
591     if not is_win32:
592         # geany.desktop
593         if bld.env['INTLTOOL']:
594             bld(
595                 features        = 'intltool_in',
596                 source          = 'geany.desktop.in',
597                 flags           = ['-d', '-q', '-u', '-c'],
598                 install_path    = '${DATADIR}/applications')
600         # geany.1
601         bld(
602             features        = 'subst',
603             source          = 'doc/geany.1.in',
604             target          = 'geany.1',
605             dct             = {'VERSION': VERSION,
606                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
607             install_path    = '${MANDIR}/man1')
609         # geany.spec
610         bld(
611             features        = 'subst',
612             source          = 'geany.spec.in',
613             target          = 'geany.spec',
614             install_path    = None,
615             dct             = {'VERSION': VERSION})
617         # Doxyfile
618         bld(
619             features        = 'subst',
620             source          = 'doc/Doxyfile.in',
621             target          = 'doc/Doxyfile',
622             install_path    = None,
623             dct             = {'VERSION': VERSION,
624                                'top_builddir': bld.out_dir,
625                                'top_srcdir': bld.top_dir,})
627     # disable build/install phase interleaving
628     bld.add_group()
630     ###
631     # Install files
632     ###
633     # Headers
634     bld.install_files('${PREFIX}/include/geany', '''
635         src/app.h
636         src/build.h
637         src/dialogs.h
638         src/document.h
639         src/editor.h
640         src/encodings.h
641         src/filetypes.h
642         src/geany.h
643         src/highlighting.h
644         src/keybindings.h
645         src/main.h
646         src/msgwindow.h
647         src/navqueue.h
648         src/plugindata.h
649         src/pluginutils.h
650         src/prefs.h
651         src/project.h
652         src/sciwrappers.h
653         src/search.h
654         src/spawn.h
655         src/stash.h
656         src/support.h
657         src/symbols.h
658         src/templates.h
659         src/toolbar.h
660         src/ui_utils.h
661         src/utils.h
662         src/gtkcompat.h
663         plugins/geanyplugin.h
664         plugins/geanyfunctions.h
665         ''')
666     bld.install_files('${PREFIX}/include/geany/scintilla', '''
667         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
668         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h
669         scintilla/include/Sci_Position.h ''')
670     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
671         tagmanager/src/tm_source_file.h
672         tagmanager/src/tm_tag.h
673         tagmanager/src/tm_tagmanager.h
674         tagmanager/src/tm_workspace.h
675         tagmanager/src/tm_parser.h ''')
676     # Docs
677     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
678     ext = '.txt' if is_win32 else ''
679     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
680         basename = _uc_first(filename, bld)
681         destination_filename = '%s%s' % (basename, ext)
682         destination = os.path.join(base_dir, destination_filename)
683         bld.install_as(destination, filename)
685     # install HTML documentation only if it exists, i.e. it was built before
686     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
687     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
688     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
689         html_dir = '' if is_win32 else 'html/'
690         html_name = 'Manual.html' if is_win32 else 'index.html'
691         start_dir = bld.path.find_dir('doc/images')
692         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
693         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
695     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
696     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
697     if is_win32:
698         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
699         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
700     # Data
701     data_dir = '' if is_win32 else 'geany'
702     start_dir = bld.path.find_dir('data')
703     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
704     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
705     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
706     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
707     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
708     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
709     if bld.env['use_gtk3']:
710         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
711     else:
712         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
714     start_dir = bld.path.find_dir('data/colorschemes')
715     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
716     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
717     start_dir = bld.path.find_dir('data/templates')
718     template_dest = '${DATADIR}/%s/templates' % data_dir
719     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
720     # Icons
721     for dest, srcs in geany_icons.items():
722         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
723         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
724     # install theme indexes on Windows
725     if is_win32:
726         for dest, srcs in geany_icons_indexes.items():
727             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
730 def distclean(ctx):
731     Scripting.distclean(ctx)
732     _remove_linguas_file()
735 def _remove_linguas_file():
736     # remove LINGUAS file as well
737     try:
738         os.unlink(LINGUAS_FILE)
739     except OSError:
740         pass
743 @feature('linguas')
744 @before_method('apply_intltool_po')
745 def write_linguas_file(self):
746     if os.path.exists(LINGUAS_FILE):
747         return
748     linguas = ''
749     if 'LINGUAS' in self.env:
750         files = self.env['LINGUAS']
751         for po_filename in files.split(' '):
752             if os.path.exists('po/%s.po' % po_filename):
753                 linguas += '%s ' % po_filename
754     else:
755         files = os.listdir('%s/po' % self.path.abspath())
756         files.sort()
757         for filename in files:
758             if filename.endswith('.po'):
759                 linguas += '%s ' % filename[:-3]
760     file_h = open(LINGUAS_FILE, 'w')
761     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
762     file_h.close()
765 def _post_install(ctx):
766     is_win32 = _target_is_win32(ctx)
767     if is_win32:
768         return
769     for d in 'hicolor', 'Tango':
770         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
771         icon_cache_updated = False
772         if not ctx.options.destdir:
773             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
774             Logs.pprint('GREEN', 'GTK icon cache updated.')
775             icon_cache_updated = True
776         if not icon_cache_updated:
777             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
778             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
781 def updatepo(ctx):
782     """update the message catalogs for internationalization"""
783     potfile = '%s.pot' % APPNAME
784     os.chdir('%s/po' % top)
785     try:
786         try:
787             old_size = os.stat(potfile).st_size
788         except OSError:
789             old_size = 0
790         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
791         size_new = os.stat(potfile).st_size
792         if size_new != old_size:
793             Logs.pprint('CYAN', 'Updated POT file.')
794             Logs.pprint('CYAN', 'Updating translations')
795             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
796             if ret != 0:
797                 Logs.pprint('RED', 'Updating translations failed')
798         else:
799             Logs.pprint('CYAN', 'POT file is up to date.')
800     except OSError:
801         Logs.pprint('RED', 'Failed to generate pot file.')
804 def apidoc(ctx):
805     """generate API reference documentation"""
806     ctx = BuildContext()  # create our own context to have ctx.top_dir
807     basedir = ctx.top_dir
808     doxygen = _find_program(ctx, 'doxygen')
809     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
810     Logs.pprint('CYAN', 'Generating API documentation')
811     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
812     if ret != 0:
813         raise WafError('Generating API documentation failed')
816 def hackingdoc(ctx):
817     """generate HACKING documentation"""
818     ctx = BuildContext()  # create our own context to have ctx.top_dir
819     Logs.pprint('CYAN', 'Generating HACKING documentation')
820     cmd = _find_rst2html(ctx)
821     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
822     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
823     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
824     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
825         cmd, stylesheet, hacking_file, hacking_html_file))
826     if ret != 0:
827         raise WafError('Generating HACKING documentation failed')
830 def _find_program(ctx, cmd, **kw):
831     def noop(*args):
832         pass
834     if ctx is None or not isinstance(ctx, ConfigurationContext):
835         ctx = ConfigurationContext()
836         ctx.to_log = noop
837         ctx.msg = noop
838     return ctx.find_program(cmd, **kw)
841 def _find_rst2html(ctx):
842     cmds = ['rst2html', 'rst2html2']
843     for command in cmds:
844         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
845         if cmd:
846             break
847     if not cmd:
848         raise WafError(
849             'rst2html.py could not be found. Please install the Python docutils package.')
850     return cmd
853 def _add_define_to_env(conf, key):
854     value = conf.get_define(key)
855     # strip quotes
856     value = value.replace('"', '')
857     conf.env[key] = value
860 def _add_to_env_and_define(conf, key, value, quote=False):
861     conf.define(key, value, quote)
862     conf.env[key] = value
865 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
866     value = default_value
867     if opt_value:
868         if isinstance(opt_value, bool):
869             opt_value = 1
870         value = opt_value
872     if value is not None:
873         _add_to_env_and_define(conf, define_name, value, quote)
874     else:
875         conf.undefine(define_name)
878 def _get_git_rev(conf):
879     if conf.options.no_scm:
880         return
882     if not os.path.isdir('.git'):
883         return
885     try:
886         cmd = 'git rev-parse --short --revs-only HEAD'
887         revision = conf.cmd_and_log(cmd).strip()
888     except WafError:
889         return None
890     else:
891         return revision
894 def _load_intltool_if_available(conf):
895     try:
896         conf.load('intltool')
897         if 'LINGUAS' in os.environ:
898             conf.env['LINGUAS'] = os.environ['LINGUAS']
899     except WafError:
900         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
901         if not _target_is_win32(conf):
902             raise
905 def _target_is_win32(ctx):
906     if 'is_win32' in ctx.env:
907         # cached
908         return ctx.env['is_win32']
909     is_win32 = None
910     if sys.platform == 'win32':
911         is_win32 = True
912     if is_win32 is None:
913         if ctx.env and 'CC' in ctx.env:
914             env_cc = ctx.env['CC']
915             if not isinstance(env_cc, str):
916                 env_cc = ''.join(env_cc)
917             is_win32 = (env_cc.find('mingw') != -1)
918     if is_win32 is None:
919         is_win32 = False
920     # cache for future checks
921     ctx.env['is_win32'] = is_win32
922     return is_win32
925 def _uc_first(string, ctx):
926     if _target_is_win32(ctx):
927         return string.title()
928     return string
931 # Copied from Geany-Plugins
932 def _check_c99(conf):
933     # FIXME: improve some checks?
934     # TODO: look at Autoconf's C99 checks?
935     fragment = '''
936     // single-line comments
938     #include <stdbool.h>
940     struct s { int a, b; };
942     // inlines
943     static inline void fun_inline(struct s param) {}
945     int main(void) {
946         _Bool b = false;
948         // variable declaration in for body
949         for (int i = 0; i < 2; i++);
951         // compound literals
952         fun_inline((struct s) { 1, 2 });
954         // mixed declarations and code
955         int mixed = 0;
957         // named initializers
958         struct s name_inited = {
959             .a = 42,
960             .b = 64
961         };
963         return (b || mixed || ! name_inited.a);
964     }
965     '''
967     exc = None
968     # list of flags is stolen from Autoconf 2.69
969     flags = ['', '-std=gnu99', '-std=c99', '-c99', '-AC99',
970              '-D_STDC_C99=', '-qlanglvl=extc99']
971     for flag in flags:
972         try:
973             desc = ['with flag %s' % flag, 'with no flags'][not flag]
974             conf.check_cc(fragment=fragment, uselib_store='C99', cflags=flag,
975                           msg="Checking for C99 support (%s)" % desc)
976             exc = None
977             break
978         except ConfigurationError as e:
979             exc = e
980     if exc:
981         raise exc
982     return True