Remove pre-GTK+ 2.24 preprocessor checks and related code
[geany-mirror.git] / wscript
blob1786c2d1abe3facb5ddad1b41d735e40b42f75e7
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 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.25'
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/python.c',
110     'tagmanager/ctags/read.c',
111     'tagmanager/ctags/rest.c',
112     'tagmanager/ctags/ruby.c',
113     'tagmanager/ctags/rust.c',
114     'tagmanager/ctags/sh.c',
115     'tagmanager/ctags/sort.c',
116     'tagmanager/ctags/sql.c',
117     'tagmanager/ctags/strlist.c',
118     'tagmanager/ctags/txt2tags.c',
119     'tagmanager/ctags/tcl.c',
120     'tagmanager/ctags/vhdl.c',
121     'tagmanager/ctags/verilog.c',
122     'tagmanager/ctags/vstring.c'])
124 tagmanager_sources = set([
125     'tagmanager/src/tm_source_file.c',
126     'tagmanager/src/tm_tag.c',
127     'tagmanager/src/tm_workspace.c'])
129 scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c'])
131 geany_sources = set([
132     'src/about.c', 'src/build.c', 'src/callbacks.c', 'src/dialogs.c', 'src/document.c',
133     'src/editor.c', 'src/encodings.c', 'src/filetypes.c', 'src/geanyentryaction.c',
134     'src/geanymenubuttonaction.c', 'src/geanyobject.c', 'src/geanywraplabel.c',
135     'src/highlighting.c', 'src/keybindings.c',
136     'src/keyfile.c', 'src/log.c', 'src/libmain.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c', 'src/osx.c',
137     'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
138     'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/stash.c',
139     'src/symbols.c',
140     'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
141     'src/ui_utils.c', 'src/utils.c'])
143 geany_bin_sources = set(['src/main.c'])
145 geany_icons = {
146     'hicolor/16x16/apps':       ['16x16/classviewer-class.png',
147                                  '16x16/classviewer-macro.png',
148                                  '16x16/classviewer-member.png',
149                                  '16x16/classviewer-method.png',
150                                  '16x16/classviewer-namespace.png',
151                                  '16x16/classviewer-other.png',
152                                  '16x16/classviewer-struct.png',
153                                  '16x16/classviewer-var.png',
154                                  '16x16/geany.png'],
155     'hicolor/16x16/actions':    ['16x16/geany-build.png',
156                                  '16x16/geany-close-all.png',
157                                  '16x16/geany-save-all.png'],
158     'hicolor/24x24/actions':    ['24x24/geany-build.png',
159                                  '24x24/geany-close-all.png',
160                                  '24x24/geany-save-all.png'],
161     'hicolor/32x32/actions':    ['32x32/geany-build.png',
162                                  '32x32/geany-close-all.png',
163                                  '32x32/geany-save-all.png'],
164     'hicolor/32x32/apps':       ['32x32/geany.png'],
165     'hicolor/48x48/actions':    ['48x48/geany-build.png',
166                                  '48x48/geany-close-all.png',
167                                  '48x48/geany-save-all.png'],
168     'hicolor/48x48/apps':       ['48x48/geany.png'],
169     'hicolor/scalable/apps':    ['scalable/geany.svg'],
170     'hicolor/scalable/actions': ['scalable/geany-build.svg',
171                                  'scalable/geany-close-all.svg',
172                                  'scalable/geany-save-all.svg'],
173     'Tango/16x16/actions':      ['tango/16x16/geany-save-all.png'],
174     'Tango/24x24/actions':      ['tango/24x24/geany-save-all.png'],
175     'Tango/32x32/actions':      ['tango/32x32/geany-save-all.png'],
176     'Tango/48x48/actions':      ['tango/48x48/geany-save-all.png'],
177     'Tango/scalable/actions':   ['tango/scalable/geany-save-all.svg']
179 geany_icons_indexes = {
180     'hicolor':  ['index.theme'],
181     'Tango':    ['tango/index.theme']
185 def configure(conf):
187     conf.check_waf_version(mini='1.6.1')
189     conf.load('compiler_c')
190     is_win32 = _target_is_win32(conf)
192     visibility_hidden_supported = conf.check_cc(cflags=['-Werror', '-fvisibility=hidden'], mandatory=False)
193     conf.check_cc(header_name='fcntl.h', mandatory=False)
194     conf.check_cc(header_name='fnmatch.h', mandatory=False)
195     conf.check_cc(header_name='glob.h', mandatory=False)
196     conf.check_cc(header_name='sys/time.h', mandatory=False)
197     conf.check_cc(header_name='sys/types.h', mandatory=False)
198     conf.check_cc(header_name='sys/stat.h', mandatory=False)
199     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
200     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
201     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
203     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
204     conf.check_cc(function_name='fnmatch', header_name='fnmatch.h', mandatory=False)
205     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
206     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
207     conf.check_cc(function_name='strstr', header_name='string.h')
209     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
211     # check sunOS socket support
212     if Options.platform == 'sunos':
213         conf.check_cc(function_name='socket', lib='socket',
214                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
216     # check for cxx after the header and function checks have been done to ensure they are
217     # checked with cc not cxx
218     conf.load('compiler_cxx')
219     if is_win32:
220         conf.load('winres')
221     _load_intltool_if_available(conf)
223     # GTK / GIO version check
224     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
225     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
226     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
227         mandatory=True, args='--cflags --libs')
228     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
229         mandatory=True, args='--cflags --libs')
230     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
231         mandatory=True, args='--cflags --libs')
232     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
233     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
234     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
235     if conf.options.enable_mac_integration:
236         pkgname = 'gtk-mac-integration-gtk3' if conf.options.use_gtk3 else 'gtk-mac-integration-gtk2'
237         conf.check_cfg(package=pkgname, uselib_store='MAC_INTEGRATION',
238             mandatory=True, args='--cflags --libs')
239     # remember GTK version for the build step
240     conf.env['gtk_package_name'] = gtk_package_name
241     conf.env['minimum_gtk_version'] = minimum_gtk_version
242     conf.env['use_gtk3'] = conf.options.use_gtk3
244     revision = _get_git_rev(conf)
246     # rst2html for the HTML manual
247     if not conf.options.no_html_doc and revision is not None:
248         try:
249             conf.env['RST2HTML'] = _find_rst2html(conf)
250         except WafError:
251             error_msg = '''Documentation enabled but rst2html not found.
252 You can explicitly disable building of the HTML manual with --disable-html-docs,
253 but you then may not have a local copy of the HTML manual.'''
254             raise WafError(error_msg)
256     # Windows specials
257     if is_win32:
258         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
259             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
260             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
261             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
262             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
263         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
264         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
265         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
266         # overwrite LOCALEDIR to install message catalogues properly
267         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
268         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
269         conf.undefine('DATADIR')
270         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
271         conf.env.append_value('LINKFLAGS_cprogram', [
272             '-mwindows',
273             '-static-libgcc',
274             '-static-libstdc++'])
275         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32', 'comdlg32'])
276         # explicitly define Windows version for older Mingw environments
277         conf.define('WINVER', '0x0501', quote=False)  # for SHGetFolderPathAndSubDirW
278         conf.define('_WIN32_IE', '0x0500', quote=False)  # for SHGFP_TYPE
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'])
340     # summary
341     Logs.pprint('BLUE', 'Summary:')
342     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
343     conf.msg('Using GTK version', gtk_version)
344     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
345     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
346     if revision is not None:
347         conf.msg('Compiling Git revision', revision)
350 def options(opt):
351     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
352     # If anyone wants to add support for building with MSVC, this hack should be removed.
353     c_compiler['win32'] = ['gcc']
354     cxx_compiler['win32'] = ['g++']
356     opt.load('compiler_cc')
357     opt.load('compiler_cxx')
358     opt.load('intltool')
360     # Option
361     opt.add_option('--no-scm', action='store_true', default=False,
362         help='Disable SCM detection [default: No]', dest='no_scm')
363     # Features
364     opt.add_option('--disable-plugins', action='store_true', default=False,
365         help='compile without plugin support [default: No]', dest='no_plugins')
366     opt.add_option('--disable-socket', action='store_true', default=False,
367         help='compile without support to detect a running instance [[default: No]',
368         dest='no_socket')
369     opt.add_option('--disable-vte', action='store_true', default=False,
370         help='compile without support for an embedded virtual terminal [[default: No]',
371         dest='no_vte')
372     opt.add_option('--enable-gtk3', action='store_true', default=False,
373         help='compile with GTK3 support (experimental) [[default: No]',
374         dest='use_gtk3')
375     opt.add_option('--enable-mac-integration', action='store_true', default=False,
376         help='use gtk-mac-integration to enable improved OS X integration [[default: No]',
377         dest='enable_mac_integration')
378     opt.add_option('--disable-html-docs', action='store_true', default=False,
379         help='do not generate HTML documentation using rst2html [[default: No]',
380         dest='no_html_doc')
381     # Paths
382     opt.add_option('--mandir', type='string', default='',
383         help='man documentation', dest='mandir')
384     opt.add_option('--docdir', type='string', default='',
385         help='documentation root', dest='docdir')
386     opt.add_option('--libdir', type='string', default='',
387         help='object code libraries', dest='libdir')
390 def build(bld):
391     is_win32 = _target_is_win32(bld)
393     if bld.cmd == 'clean':
394         _remove_linguas_file()
395     if bld.cmd in ('install', 'uninstall'):
396         bld.add_post_fun(_post_install)
398     def build_plugin(plugin_name, install=True, uselib_add=[]):
399         if install:
400             instpath = '${PREFIX}/lib' if is_win32 else '${LIBDIR}/geany'
401         else:
402             instpath = None
404         bld(
405             features                = ['c', 'cshlib'],
406             source                  = 'plugins/%s.c' % plugin_name,
407             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
408             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
409             target                  = plugin_name,
410             uselib                  = ['GTK', 'GLIB', 'GMODULE'] + uselib_add,
411             use                     = ['geany'],
412             install_path            = instpath)
414     # CTags
415     bld.objects(
416         features        = ['c'],
417         source          = ctags_sources,
418         name            = 'ctags',
419         target          = 'ctags',
420         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
421         defines         = 'G_LOG_DOMAIN="CTags"',
422         uselib          = ['cshlib', 'GLIB', 'geanyexport'])
424     # Tagmanager
425     bld.objects(
426         features        = ['c'],
427         source          = tagmanager_sources,
428         name            = 'tagmanager',
429         target          = 'tagmanager',
430         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
431         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
432         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
434     # MIO
435     bld.objects(
436         features        = ['c'],
437         source          = mio_sources,
438         name            = 'mio',
439         target          = 'mio',
440         includes        = ['.', 'tagmanager/mio/'],
441         defines         = 'G_LOG_DOMAIN="MIO"',
442         uselib          = ['cshlib', 'GTK', 'GLIB', 'geanyexport'])
444     # Scintilla
445     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
446     scintilla_sources.update([file.path_from(bld.srcnode) for file in files])
447     bld.objects(
448         features        = ['c', 'cxx'],
449         name            = 'scintilla',
450         target          = 'scintilla',
451         source          = scintilla_sources,
452         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
453         uselib          = ['cshlib', 'cxxshlib', 'GTK', 'GLIB', 'GMODULE', 'M', 'geanyexport'])
455     # Geany
456     if bld.env['HAVE_VTE'] == 1:
457         geany_sources.add('src/vte.c')
458     if is_win32:
459         geany_sources.add('src/win32.c')
460         geany_sources.add('geany_private.rc')
461         geany_bin_sources.add('geany_private.rc')
463     def gen_signallist(task):
464         from xml.etree import ElementTree
466         def find_handlers(xml_filename):
467             tree = ElementTree.parse(xml_filename)
468             signals = tree.getroot().findall(".//signal")
469             return [sig.attrib["handler"] for sig in signals]
471         handlers = []
472         for node in task.inputs:
473             handlers += find_handlers(node.abspath())
474         handlers = sorted(set(handlers))
476         for node in task.outputs:
477             node.write("/* This file is auto-generated, do not edit. */\n" +
478                        ''.join(["ITEM(%s)\n" % h for h in handlers]))
480     # signallist.i
481     bld(
482         source  = 'data/geany.glade',
483         target  = 'src/signallist.i',
484         name    = 'signallist.i',
485         rule    = gen_signallist)
487     base_uselibs = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'MAC_INTEGRATION', 'SUNOS_SOCKET', 'M']
489     # libgeany
490     bld.shlib(
491         features        = ['c', 'cxx'],
492         name            = 'geany',
493         target          = 'geany',
494         source          = geany_sources,
495         includes        = ['.', 'scintilla/include', 'tagmanager/src', 'src'],
496         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
497         uselib          = base_uselibs + ['geanyexport'],
498         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'],
499         linkflags       = bld.env['LINKFLAGS_cprogram'],
500         vnum            = GEANY_LIB_VERSION,
501         install_path    = '${PREFIX}/bin' if is_win32 else '${LIBDIR}')
503     # geany executable
504     t = bld.program(
505         features        = ['c', 'cxx'],
506         name            = 'geany_bin',
507         target          = 'geany',
508         source          = geany_bin_sources,
509         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
510         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
511         uselib          = base_uselibs + ['geanyexport'],
512         use             = ['geany'])
513     if not is_win32:
514         # http://www.freehackers.org/~tnagy/testdoc/single.html#common_c
515         t.rpath = bld.env['LIBDIR']
517     # Plugins
518     if bld.env['HAVE_PLUGINS'] == 1:
519         build_plugin('classbuilder')
520         build_plugin('demoplugin', False)
521         build_plugin('export', uselib_add=['M'])
522         build_plugin('filebrowser')
523         build_plugin('htmlchars')
524         build_plugin('saveactions')
525         build_plugin('splitwindow')
527     # Translations
528     if bld.env['INTLTOOL']:
529         bld(
530             features        = ['linguas', 'intltool_po'],
531             podir           = 'po',
532             install_path    = '${LOCALEDIR}',
533             appname         = 'geany')
535     # HTML documentation (build if it is not part of the tree already, as it is required for install)
536     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
537     if bld.env['RST2HTML']:
538         rst2html = bld.env['RST2HTML']
539         bld(
540             source  = ['doc/geany.txt'],
541             deps    = ['doc/geany.css'],
542             target  = 'doc/geany.html',
543             name    = 'geany.html',
544             cwd     = os.path.join(bld.path.abspath(), 'doc'),
545             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
547     # geany.pc
548     if is_win32:
549         # replace backward slashes by forward slashes as they could be interepreted as escape
550         # characters
551         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
552     else:
553         geany_pc_prefix = bld.env['PREFIX']
554     bld(
555         source          = 'geany.pc.in',
556         dct             = {'VERSION': VERSION,
557                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
558                                 (bld.env['gtk_package_name'],
559                                  bld.env['minimum_gtk_version'],
560                                  MINIMUM_GLIB_VERSION),
561                            'prefix': geany_pc_prefix,
562                            'exec_prefix': '${prefix}',
563                            'libdir': '${exec_prefix}/lib',
564                            'includedir': '${prefix}/include',
565                            'datarootdir': '${prefix}/share',
566                            'datadir': '${datarootdir}',
567                            'localedir': '${datarootdir}/locale'})
569     if not is_win32:
570         # geany.desktop
571         if bld.env['INTLTOOL']:
572             bld(
573                 features        = 'intltool_in',
574                 source          = 'geany.desktop.in',
575                 flags           = ['-d', '-q', '-u', '-c'],
576                 install_path    = '${DATADIR}/applications')
578         # geany.1
579         bld(
580             features        = 'subst',
581             source          = 'doc/geany.1.in',
582             target          = 'geany.1',
583             dct             = {'VERSION': VERSION,
584                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
585             install_path    = '${MANDIR}/man1')
587         # geany.spec
588         bld(
589             features        = 'subst',
590             source          = 'geany.spec.in',
591             target          = 'geany.spec',
592             install_path    = None,
593             dct             = {'VERSION': VERSION})
595         # Doxyfile
596         bld(
597             features        = 'subst',
598             source          = 'doc/Doxyfile.in',
599             target          = 'doc/Doxyfile',
600             install_path    = None,
601             dct             = {'VERSION': VERSION,
602                                'top_builddir': bld.out_dir,
603                                'top_srcdir': bld.top_dir,})
605     ###
606     # Install files
607     ###
608     # Headers
609     bld.install_files('${PREFIX}/include/geany', '''
610         src/app.h
611         src/build.h
612         src/dialogs.h
613         src/document.h
614         src/editor.h
615         src/encodings.h
616         src/filetypes.h
617         src/geany.h
618         src/highlighting.h
619         src/keybindings.h
620         src/main.h
621         src/msgwindow.h
622         src/navqueue.h
623         src/plugindata.h
624         src/pluginutils.h
625         src/prefs.h
626         src/project.h
627         src/sciwrappers.h
628         src/search.h
629         src/stash.h
630         src/support.h
631         src/symbols.h
632         src/templates.h
633         src/toolbar.h
634         src/ui_utils.h
635         src/utils.h
636         src/gtkcompat.h
637         plugins/geanyplugin.h
638         plugins/geanyfunctions.h
639         ''')
640     bld.install_files('${PREFIX}/include/geany/scintilla', '''
641         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
642         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h ''')
643     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
644         tagmanager/src/tm_source_file.h
645         tagmanager/src/tm_tag.h
646         tagmanager/src/tm_tagmanager.h
647         tagmanager/src/tm_workspace.h ''')
648     # Docs
649     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
650     ext = '.txt' if is_win32 else ''
651     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
652         basename = _uc_first(filename, bld)
653         destination_filename = '%s%s' % (basename, ext)
654         destination = os.path.join(base_dir, destination_filename)
655         bld.install_as(destination, filename)
657     # install HTML documentation only if it exists, i.e. it was built before
658     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
659     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
660     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
661         html_dir = '' if is_win32 else 'html/'
662         html_name = 'Manual.html' if is_win32 else 'index.html'
663         start_dir = bld.path.find_dir('doc/images')
664         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
665         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
667     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
668     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
669     if is_win32:
670         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
671         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
672     # Data
673     data_dir = '' if is_win32 else 'geany'
674     start_dir = bld.path.find_dir('data')
675     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
676     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
677     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
678     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
679     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
680     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
681     if bld.env['use_gtk3']:
682         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
683     else:
684         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
686     start_dir = bld.path.find_dir('data/colorschemes')
687     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
688     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
689     start_dir = bld.path.find_dir('data/templates')
690     template_dest = '${DATADIR}/%s/templates' % data_dir
691     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
692     # Icons
693     for dest, srcs in geany_icons.items():
694         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
695         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
696     # install theme indexes on Windows
697     if is_win32:
698         for dest, srcs in geany_icons_indexes.items():
699             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
702 def distclean(ctx):
703     Scripting.distclean(ctx)
704     _remove_linguas_file()
707 def _remove_linguas_file():
708     # remove LINGUAS file as well
709     try:
710         os.unlink(LINGUAS_FILE)
711     except OSError:
712         pass
715 @feature('linguas')
716 @before_method('apply_intltool_po')
717 def write_linguas_file(self):
718     if os.path.exists(LINGUAS_FILE):
719         return
720     linguas = ''
721     if 'LINGUAS' in self.env:
722         files = self.env['LINGUAS']
723         for po_filename in files.split(' '):
724             if os.path.exists('po/%s.po' % po_filename):
725                 linguas += '%s ' % po_filename
726     else:
727         files = os.listdir('%s/po' % self.path.abspath())
728         files.sort()
729         for filename in files:
730             if filename.endswith('.po'):
731                 linguas += '%s ' % filename[:-3]
732     file_h = open(LINGUAS_FILE, 'w')
733     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
734     file_h.close()
737 def _post_install(ctx):
738     is_win32 = _target_is_win32(ctx)
739     if is_win32:
740         return
741     for d in 'hicolor', 'Tango':
742         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
743         icon_cache_updated = False
744         if not ctx.options.destdir:
745             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
746             Logs.pprint('GREEN', 'GTK icon cache updated.')
747             icon_cache_updated = True
748         if not icon_cache_updated:
749             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
750             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
753 def updatepo(ctx):
754     """update the message catalogs for internationalization"""
755     potfile = '%s.pot' % APPNAME
756     os.chdir('%s/po' % top)
757     try:
758         try:
759             old_size = os.stat(potfile).st_size
760         except OSError:
761             old_size = 0
762         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
763         size_new = os.stat(potfile).st_size
764         if size_new != old_size:
765             Logs.pprint('CYAN', 'Updated POT file.')
766             Logs.pprint('CYAN', 'Updating translations')
767             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
768             if ret != 0:
769                 Logs.pprint('RED', 'Updating translations failed')
770         else:
771             Logs.pprint('CYAN', 'POT file is up to date.')
772     except OSError:
773         Logs.pprint('RED', 'Failed to generate pot file.')
776 def apidoc(ctx):
777     """generate API reference documentation"""
778     ctx = BuildContext()  # create our own context to have ctx.top_dir
779     basedir = ctx.top_dir
780     doxygen = _find_program(ctx, 'doxygen')
781     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
782     Logs.pprint('CYAN', 'Generating API documentation')
783     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
784     if ret != 0:
785         raise WafError('Generating API documentation failed')
788 def hackingdoc(ctx):
789     """generate HACKING documentation"""
790     ctx = BuildContext()  # create our own context to have ctx.top_dir
791     Logs.pprint('CYAN', 'Generating HACKING documentation')
792     cmd = _find_rst2html(ctx)
793     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
794     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
795     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
796     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
797         cmd, stylesheet, hacking_file, hacking_html_file))
798     if ret != 0:
799         raise WafError('Generating HACKING documentation failed')
802 def _find_program(ctx, cmd, **kw):
803     def noop(*args):
804         pass
806     if ctx is None or not isinstance(ctx, ConfigurationContext):
807         ctx = ConfigurationContext()
808         ctx.to_log = noop
809         ctx.msg = noop
810     return ctx.find_program(cmd, **kw)
813 def _find_rst2html(ctx):
814     cmds = ['rst2html', 'rst2html2']
815     for command in cmds:
816         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
817         if cmd:
818             break
819     if not cmd:
820         raise WafError(
821             'rst2html.py could not be found. Please install the Python docutils package.')
822     return cmd
825 def _add_define_to_env(conf, key):
826     value = conf.get_define(key)
827     # strip quotes
828     value = value.replace('"', '')
829     conf.env[key] = value
832 def _add_to_env_and_define(conf, key, value, quote=False):
833     conf.define(key, value, quote)
834     conf.env[key] = value
837 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
838     value = default_value
839     if opt_value:
840         if isinstance(opt_value, bool):
841             opt_value = 1
842         value = opt_value
844     if value is not None:
845         _add_to_env_and_define(conf, define_name, value, quote)
846     else:
847         conf.undefine(define_name)
850 def _get_git_rev(conf):
851     if conf.options.no_scm:
852         return
854     if not os.path.isdir('.git'):
855         return
857     try:
858         cmd = 'git rev-parse --short --revs-only HEAD'
859         revision = conf.cmd_and_log(cmd).strip()
860     except WafError:
861         return None
862     else:
863         return revision
866 def _load_intltool_if_available(conf):
867     try:
868         conf.load('intltool')
869         if 'LINGUAS' in os.environ:
870             conf.env['LINGUAS'] = os.environ['LINGUAS']
871     except WafError:
872         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
873         if not _target_is_win32(conf):
874             raise
877 def _target_is_win32(ctx):
878     if 'is_win32' in ctx.env:
879         # cached
880         return ctx.env['is_win32']
881     is_win32 = None
882     if sys.platform == 'win32':
883         is_win32 = True
884     if is_win32 is None:
885         if ctx.env and 'CC' in ctx.env:
886             env_cc = ctx.env['CC']
887             if not isinstance(env_cc, str):
888                 env_cc = ''.join(env_cc)
889             is_win32 = (env_cc.find('mingw') != -1)
890     if is_win32 is None:
891         is_win32 = False
892     # cache for future checks
893     ctx.env['is_win32'] = is_win32
894     return is_win32
897 def _uc_first(string, ctx):
898     if _target_is_win32(ctx):
899         return string.title()
900     return string