The mkstemps special case for windows is not necessary
[geany-mirror.git] / wscript
blobdf165d153ad233b09dc7afcd328d8a9cb8d46aa8
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.16.0'
58 MINIMUM_GTK3_VERSION = '3.0.0'
59 MINIMUM_GLIB_VERSION = '2.20.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/entry.c',
84     'tagmanager/ctags/fortran.c',
85     'tagmanager/ctags/get.c',
86     'tagmanager/ctags/go.c',
87     'tagmanager/ctags/haskell.c',
88     'tagmanager/ctags/haxe.c',
89     'tagmanager/ctags/html.c',
90     'tagmanager/ctags/js.c',
91     'tagmanager/ctags/json.c',
92     'tagmanager/ctags/keyword.c',
93     'tagmanager/ctags/latex.c',
94     'tagmanager/ctags/lregex.c',
95     'tagmanager/ctags/lua.c',
96     'tagmanager/ctags/make.c',
97     'tagmanager/ctags/markdown.c',
98     'tagmanager/ctags/matlab.c',
99     'tagmanager/ctags/nsis.c',
100     'tagmanager/ctags/nestlevel.c',
101     'tagmanager/ctags/objc.c',
102     'tagmanager/ctags/options.c',
103     'tagmanager/ctags/parse.c',
104     'tagmanager/ctags/pascal.c',
105     'tagmanager/ctags/r.c',
106     'tagmanager/ctags/perl.c',
107     'tagmanager/ctags/php.c',
108     'tagmanager/ctags/python.c',
109     'tagmanager/ctags/read.c',
110     'tagmanager/ctags/rest.c',
111     'tagmanager/ctags/ruby.c',
112     'tagmanager/ctags/rust.c',
113     'tagmanager/ctags/sh.c',
114     'tagmanager/ctags/sort.c',
115     'tagmanager/ctags/sql.c',
116     'tagmanager/ctags/strlist.c',
117     'tagmanager/ctags/txt2tags.c',
118     'tagmanager/ctags/tcl.c',
119     'tagmanager/ctags/vhdl.c',
120     'tagmanager/ctags/verilog.c',
121     'tagmanager/ctags/vstring.c'])
123 tagmanager_sources = set([
124     'tagmanager/src/tm_source_file.c',
125     'tagmanager/src/tm_tag.c',
126     'tagmanager/src/tm_workspace.c'])
128 scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c'])
130 geany_sources = set([
131     'src/about.c', 'src/build.c', 'src/callbacks.c', 'src/dialogs.c', 'src/document.c',
132     'src/editor.c', 'src/encodings.c', 'src/filetypes.c', 'src/geanyentryaction.c',
133     'src/geanymenubuttonaction.c', 'src/geanyobject.c', 'src/geanywraplabel.c',
134     'src/highlighting.c', 'src/keybindings.c',
135     'src/keyfile.c', 'src/log.c', 'src/libmain.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c', 'src/osx.c',
136     'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
137     'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/stash.c',
138     'src/symbols.c',
139     'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
140     'src/ui_utils.c', 'src/utils.c'])
142 geany_icons = {
143     'hicolor/16x16/apps':       ['16x16/classviewer-class.png',
144                                  '16x16/classviewer-macro.png',
145                                  '16x16/classviewer-member.png',
146                                  '16x16/classviewer-method.png',
147                                  '16x16/classviewer-namespace.png',
148                                  '16x16/classviewer-other.png',
149                                  '16x16/classviewer-struct.png',
150                                  '16x16/classviewer-var.png',
151                                  '16x16/geany.png'],
152     'hicolor/16x16/actions':    ['16x16/geany-build.png',
153                                  '16x16/geany-close-all.png',
154                                  '16x16/geany-save-all.png'],
155     'hicolor/24x24/actions':    ['24x24/geany-build.png',
156                                  '24x24/geany-close-all.png',
157                                  '24x24/geany-save-all.png'],
158     'hicolor/32x32/actions':    ['32x32/geany-build.png',
159                                  '32x32/geany-close-all.png',
160                                  '32x32/geany-save-all.png'],
161     'hicolor/48x48/actions':    ['48x48/geany-build.png',
162                                  '48x48/geany-close-all.png',
163                                  '48x48/geany-save-all.png'],
164     'hicolor/48x48/apps':       ['48x48/geany.png'],
165     'hicolor/scalable/apps':    ['scalable/geany.svg'],
166     'hicolor/scalable/actions': ['scalable/geany-build.svg',
167                                  'scalable/geany-close-all.svg',
168                                  'scalable/geany-save-all.svg'],
169     'Tango/16x16/actions':      ['tango/16x16/geany-save-all.png'],
170     'Tango/24x24/actions':      ['tango/24x24/geany-save-all.png'],
171     'Tango/32x32/actions':      ['tango/32x32/geany-save-all.png'],
172     'Tango/48x48/actions':      ['tango/48x48/geany-save-all.png'],
173     'Tango/scalable/actions':   ['tango/scalable/geany-save-all.svg']
175 geany_icons_indexes = {
176     'hicolor':  ['index.theme'],
177     'Tango':    ['tango/index.theme']
181 def configure(conf):
183     conf.check_waf_version(mini='1.6.1')
185     conf.load('compiler_c')
186     is_win32 = _target_is_win32(conf)
188     visibility_hidden_supported = conf.check_cc(cflags=['-Werror', '-fvisibility=hidden'], mandatory=False)
189     conf.check_cc(header_name='fcntl.h', mandatory=False)
190     conf.check_cc(header_name='fnmatch.h', mandatory=False)
191     conf.check_cc(header_name='glob.h', mandatory=False)
192     conf.check_cc(header_name='sys/time.h', mandatory=False)
193     conf.check_cc(header_name='sys/types.h', mandatory=False)
194     conf.check_cc(header_name='sys/stat.h', mandatory=False)
195     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
196     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
197     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
199     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
200     conf.check_cc(function_name='fnmatch', header_name='fnmatch.h', mandatory=False)
201     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
202     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
203     conf.check_cc(function_name='strstr', header_name='string.h')
205     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
207     # check sunOS socket support
208     if Options.platform == 'sunos':
209         conf.check_cc(function_name='socket', lib='socket',
210                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
212     # check for cxx after the header and function checks have been done to ensure they are
213     # checked with cc not cxx
214     conf.load('compiler_cxx')
215     if is_win32:
216         conf.load('winres')
217     _load_intltool_if_available(conf)
219     # GTK / GIO version check
220     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
221     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
222     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
223         mandatory=True, args='--cflags --libs')
224     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
225         mandatory=True, args='--cflags --libs')
226     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
227         mandatory=True, args='--cflags --libs')
228     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
229     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
230     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
231     if conf.options.enable_mac_integration:
232         pkgname = 'gtk-mac-integration-gtk3' if conf.options.use_gtk3 else 'gtk-mac-integration-gtk2'
233         conf.check_cfg(package=pkgname, uselib_store='MAC_INTEGRATION', 
234             mandatory=True, args='--cflags --libs')
235     # remember GTK version for the build step
236     conf.env['gtk_package_name'] = gtk_package_name
237     conf.env['minimum_gtk_version'] = minimum_gtk_version
238     conf.env['use_gtk3'] = conf.options.use_gtk3
240     revision = _get_git_rev(conf)
242     # rst2html for the HTML manual
243     if not conf.options.no_html_doc and revision is not None:
244         try:
245             conf.env['RST2HTML'] = _find_rst2html(conf)
246         except WafError:
247             error_msg = '''Documentation enabled but rst2html not found.
248 You can explicitly disable building of the HTML manual with --disable-html-docs,
249 but you then may not have a local copy of the HTML manual.'''
250             raise WafError(error_msg)
252     # Windows specials
253     if is_win32:
254         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
255             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
256             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
257             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
258             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
259         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
260         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
261         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
262         # overwrite LOCALEDIR to install message catalogues properly
263         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
264         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
265         conf.undefine('DATADIR')
266         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
267         conf.env.append_value('LINKFLAGS_cprogram', [
268             '-mwindows',
269             '-static-libgcc',
270             '-static-libstdc++'])
271         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32', 'comdlg32'])
272         # explicitly define Windows version for older Mingw environments
273         conf.define('WINVER', '0x0501', quote=False)  # for SHGetFolderPathAndSubDirW
274         conf.define('_WIN32_IE', '0x0500', quote=False)  # for SHGFP_TYPE
275     else:
276         conf.env['cshlib_PATTERN'] = '%s.so'
277         # DATADIR and LOCALEDIR are defined by the intltool tool
278         # but they are not added to the environment, so we need to
279         _add_define_to_env(conf, 'DATADIR')
280         _add_define_to_env(conf, 'LOCALEDIR')
281         docdir = os.path.join(conf.env['DATADIR'], 'doc', 'geany')
282         libdir = os.path.join(conf.env['PREFIX'], 'lib')
283         mandir = os.path.join(conf.env['DATADIR'], 'man')
284         _define_from_opt(conf, 'DOCDIR', conf.options.docdir, docdir)
285         _define_from_opt(conf, 'LIBDIR', conf.options.libdir, libdir)
286         _define_from_opt(conf, 'MANDIR', conf.options.mandir, mandir)
287         # The check could be improved, -fPIC iso only really necessary on 64bit linux
288         # It is needed because waf doesn't realize the static files go into
289         # a shared library
290         conf.env.append_value('CFLAGS_cstlib', '-fPIC')
291         conf.env.append_value('CFLAGS_cxxstlib', '-fPIC')
292         conf.env.append_value('CXXFLAGS_cxxstlib', '-fPIC')
294     conf.define('ENABLE_NLS', 1)
295     conf.define('GEANY_LOCALEDIR', '' if is_win32 else conf.env['LOCALEDIR'], quote=True)
296     conf.define('GEANY_DATADIR', 'data' if is_win32 else conf.env['DATADIR'], quote=True)
297     conf.define('GEANY_DOCDIR', conf.env['DOCDIR'], quote=True)
298     conf.define('GEANY_LIBDIR', '' if is_win32 else conf.env['LIBDIR'], quote=True)
299     conf.define('GEANY_PREFIX', '' if is_win32 else conf.env['PREFIX'], quote=True)
300     conf.define('PACKAGE', APPNAME, quote=True)
301     conf.define('VERSION', VERSION, quote=True)
302     conf.define('REVISION', revision or '-1', quote=True)
304     conf.define('GETTEXT_PACKAGE', APPNAME, quote=True)
306     # no VTE on Windows
307     if is_win32:
308         conf.options.no_vte = True
310     _define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
311     _define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
312     _define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
314     conf.write_config_header('config.h', remove=False)
316     # GEANY_EXPORT_SYMBOL and GEANY_API_SYMBOL
317     if is_win32:
318         geany_symbol_flags = ['-DGEANY_EXPORT_SYMBOL=__declspec(dllexport)']
319     elif visibility_hidden_supported:
320         geany_symbol_flags = ['-fvisibility=hidden',
321                               '-DGEANY_EXPORT_SYMBOL=__attribute__((visibility("default")))']
322     else:  # unknown, define to nothing
323         geany_symbol_flags = ['-DGEANY_EXPORT_SYMBOL=']
324     geany_symbol_flags.append('-DGEANY_API_SYMBOL=GEANY_EXPORT_SYMBOL')
325     conf.env.append_value('CFLAGS', geany_symbol_flags)
326     conf.env.append_value('CXXFLAGS', geany_symbol_flags)
328     # some more compiler flags
329     conf.env.append_value('CFLAGS', ['-DHAVE_CONFIG_H'])
330     if conf.env['CC_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CFLAGS']):
331         conf.env.append_value('CFLAGS', ['-O2'])
332     if revision is not None:
333         conf.env.append_value('CFLAGS', ['-g', '-DGEANY_DEBUG'])
334     # Scintilla flags
335     conf.env.append_value('CFLAGS', ['-DGTK'])
336     conf.env.append_value('CXXFLAGS',
337         ['-DNDEBUG', '-DGTK', '-DSCI_LEXER', '-DG_THREADS_IMPL_NONE'])
339     # summary
340     Logs.pprint('BLUE', 'Summary:')
341     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
342     conf.msg('Using GTK version', gtk_version)
343     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
344     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
345     if revision is not None:
346         conf.msg('Compiling Git revision', revision)
349 def options(opt):
350     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
351     # If anyone wants to add support for building with MSVC, this hack should be removed.
352     c_compiler['win32'] = ['gcc']
353     cxx_compiler['win32'] = ['g++']
355     opt.load('compiler_cc')
356     opt.load('compiler_cxx')
357     opt.load('intltool')
359     # Option
360     opt.add_option('--no-scm', action='store_true', default=False,
361         help='Disable SCM detection [default: No]', dest='no_scm')
362     # Features
363     opt.add_option('--disable-plugins', action='store_true', default=False,
364         help='compile without plugin support [default: No]', dest='no_plugins')
365     opt.add_option('--disable-socket', action='store_true', default=False,
366         help='compile without support to detect a running instance [[default: No]',
367         dest='no_socket')
368     opt.add_option('--disable-vte', action='store_true', default=False,
369         help='compile without support for an embedded virtual terminal [[default: No]',
370         dest='no_vte')
371     opt.add_option('--enable-gtk3', action='store_true', default=False,
372         help='compile with GTK3 support (experimental) [[default: No]',
373         dest='use_gtk3')
374     opt.add_option('--enable-mac-integration', action='store_true', default=False,
375         help='use gtk-mac-integration to enable improved OS X integration [[default: No]',
376         dest='enable_mac_integration')
377     opt.add_option('--disable-html-docs', action='store_true', default=False,
378         help='do not generate HTML documentation using rst2html [[default: No]',
379         dest='no_html_doc')
380     # Paths
381     opt.add_option('--mandir', type='string', default='',
382         help='man documentation', dest='mandir')
383     opt.add_option('--docdir', type='string', default='',
384         help='documentation root', dest='docdir')
385     opt.add_option('--libdir', type='string', default='',
386         help='object code libraries', dest='libdir')
389 def build(bld):
390     is_win32 = _target_is_win32(bld)
392     if bld.cmd == 'clean':
393         _remove_linguas_file()
394     if bld.cmd in ('install', 'uninstall'):
395         bld.add_post_fun(_post_install)
397     def build_plugin(plugin_name, install=True, uselib_add=[]):
398         if install:
399             instpath = '${PREFIX}/lib' if is_win32 else '${LIBDIR}/geany'
400         else:
401             instpath = None
403         bld(
404             features                = ['c', 'cshlib'],
405             source                  = 'plugins/%s.c' % plugin_name,
406             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
407             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
408             target                  = plugin_name,
409             uselib                  = ['GTK', 'GLIB', 'GMODULE'] + uselib_add,
410             use                     = ['geany'],
411             install_path            = instpath)
413     # CTags
414     bld(
415         features        = ['c', 'cstlib'],
416         source          = ctags_sources,
417         name            = 'ctags',
418         target          = 'ctags',
419         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
420         defines         = 'G_LOG_DOMAIN="CTags"',
421         uselib          = ['GLIB'],
422         install_path    = None)  # do not install this library
424     # Tagmanager
425     bld(
426         features        = ['c', 'cstlib'],
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          = ['GTK', 'GLIB'],
433         install_path    = None)  # do not install this library
435     # MIO
436     bld(
437         features        = ['c', 'cstlib'],
438         source          = mio_sources,
439         name            = 'mio',
440         target          = 'mio',
441         includes        = ['.', 'tagmanager/mio/'],
442         defines         = 'G_LOG_DOMAIN="MIO"',
443         uselib          = ['GTK', 'GLIB'],
444         install_path    = None)  # do not install this library
446     # Scintilla
447     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
448     scintilla_sources.update([file.path_from(bld.srcnode) for file in files])
449     bld(
450         features        = ['c', 'cxx', 'cxxstlib'],
451         name            = 'scintilla',
452         target          = 'scintilla',
453         source          = scintilla_sources,
454         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
455         uselib          = ['GTK', 'GLIB', 'GMODULE', 'M'],
456         install_path    = None)  # do not install this library
458     # Geany
459     if bld.env['HAVE_VTE'] == 1:
460         geany_sources.add('src/vte.c')
461     if is_win32:
462         geany_sources.add('src/win32.c')
463         geany_sources.add('geany_private.rc')
465     def gen_signallist(task):
466         from xml.etree import ElementTree
468         def find_handlers(xml_filename):
469             tree = ElementTree.parse(xml_filename)
470             signals = tree.getroot().findall(".//signal")
471             return [sig.attrib["handler"] for sig in signals]
473         handlers = []
474         for node in task.inputs:
475             handlers += find_handlers(node.abspath())
476         handlers = sorted(set(handlers))
478         for node in task.outputs:
479             node.write("/* This file is auto-generated, do not edit. */\n" +
480                        ''.join(["ITEM(%s)\n" % h for h in handlers]))
482     # signallist.i
483     bld(
484         source  = 'data/geany.glade',
485         target  = 'src/signallist.i',
486         name    = 'signallist.i',
487         rule    = gen_signallist)
489     base_uselibs = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'MAC_INTEGRATION', 'SUNOS_SOCKET', 'M']
491     # libgeany
492     instpath = '${PREFIX}/bin' if is_win32 else '${LIBDIR}'
493     linkflags = bld.env['LINKFLAGS_cprogram']
495     bld(
496         features        = ['c', 'cxx', 'cshlib'],
497         name            = 'geany',
498         target          = 'geany',
499         source          = geany_sources,
500         includes        = ['.', 'scintilla/include', 'tagmanager/src', 'src'],
501         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
502         uselib          = base_uselibs,
503         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'],
504         linkflags       = linkflags,
505         vnum            = GEANY_LIB_VERSION,
506         install_path    = instpath)
508     # geany executable
509     bld(
510         features        = ['c', 'cxx', 'cprogram'],
511         name            = 'geany_bin',
512         target          = 'geany',
513         source          = ['src/main.c'],
514         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
515         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
516         uselib          = base_uselibs,
517         use             = ['geany'])
519     # Plugins
520     if bld.env['HAVE_PLUGINS'] == 1:
521         build_plugin('classbuilder')
522         build_plugin('demoplugin', False)
523         build_plugin('export', uselib_add=['M'])
524         build_plugin('filebrowser')
525         build_plugin('htmlchars')
526         build_plugin('saveactions')
527         build_plugin('splitwindow')
529     # Translations
530     if bld.env['INTLTOOL']:
531         bld(
532             features        = ['linguas', 'intltool_po'],
533             podir           = 'po',
534             install_path    = '${LOCALEDIR}',
535             appname         = 'geany')
537     # HTML documentation (build if it is not part of the tree already, as it is required for install)
538     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
539     if bld.env['RST2HTML']:
540         rst2html = bld.env['RST2HTML']
541         bld(
542             source  = ['doc/geany.txt'],
543             deps    = ['doc/geany.css'],
544             target  = 'doc/geany.html',
545             name    = 'geany.html',
546             cwd     = os.path.join(bld.path.abspath(), 'doc'),
547             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
549     # geany.pc
550     if is_win32:
551         # replace backward slashes by forward slashes as they could be interepreted as escape
552         # characters
553         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
554     else:
555         geany_pc_prefix = bld.env['PREFIX']
556     bld(
557         source          = 'geany.pc.in',
558         dct             = {'VERSION': VERSION,
559                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
560                                 (bld.env['gtk_package_name'],
561                                  bld.env['minimum_gtk_version'],
562                                  MINIMUM_GLIB_VERSION),
563                            'prefix': geany_pc_prefix,
564                            'exec_prefix': '${prefix}',
565                            'libdir': '${exec_prefix}/lib',
566                            'includedir': '${prefix}/include',
567                            'datarootdir': '${prefix}/share',
568                            'datadir': '${datarootdir}',
569                            'localedir': '${datarootdir}/locale'})
571     if not is_win32:
572         # geany.desktop
573         if bld.env['INTLTOOL']:
574             bld(
575                 features        = 'intltool_in',
576                 source          = 'geany.desktop.in',
577                 flags           = ['-d', '-q', '-u', '-c'],
578                 install_path    = '${DATADIR}/applications')
580         # geany.1
581         bld(
582             features        = 'subst',
583             source          = 'doc/geany.1.in',
584             target          = 'geany.1',
585             dct             = {'VERSION': VERSION,
586                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
587             install_path    = '${MANDIR}/man1')
589         # geany.spec
590         bld(
591             features        = 'subst',
592             source          = 'geany.spec.in',
593             target          = 'geany.spec',
594             install_path    = None,
595             dct             = {'VERSION': VERSION})
597         # Doxyfile
598         bld(
599             features        = 'subst',
600             source          = 'doc/Doxyfile.in',
601             target          = 'doc/Doxyfile',
602             install_path    = None,
603             dct             = {'VERSION': VERSION,
604                                'top_builddir': bld.out_dir,
605                                'top_srcdir': bld.top_dir,})
607     ###
608     # Install files
609     ###
610     # Headers
611     bld.install_files('${PREFIX}/include/geany', '''
612         src/app.h
613         src/build.h
614         src/dialogs.h
615         src/document.h
616         src/editor.h
617         src/encodings.h
618         src/filetypes.h
619         src/geany.h
620         src/highlighting.h
621         src/keybindings.h
622         src/main.h
623         src/msgwindow.h
624         src/navqueue.h
625         src/plugindata.h
626         src/pluginutils.h
627         src/prefs.h
628         src/project.h
629         src/sciwrappers.h
630         src/search.h
631         src/stash.h
632         src/support.h
633         src/symbols.h
634         src/templates.h
635         src/toolbar.h
636         src/ui_utils.h
637         src/utils.h
638         src/gtkcompat.h
639         plugins/geanyplugin.h
640         plugins/geanyfunctions.h
641         ''')
642     bld.install_files('${PREFIX}/include/geany/scintilla', '''
643         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
644         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h ''')
645     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
646         tagmanager/src/tm_source_file.h
647         tagmanager/src/tm_tag.h
648         tagmanager/src/tm_tagmanager.h
649         tagmanager/src/tm_workspace.h ''')
650     # Docs
651     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
652     ext = '.txt' if is_win32 else ''
653     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
654         basename = _uc_first(filename, bld)
655         destination_filename = '%s%s' % (basename, ext)
656         destination = os.path.join(base_dir, destination_filename)
657         bld.install_as(destination, filename)
659     # install HTML documentation only if it exists, i.e. it was built before
660     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
661     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
662     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
663         html_dir = '' if is_win32 else 'html/'
664         html_name = 'Manual.html' if is_win32 else 'index.html'
665         start_dir = bld.path.find_dir('doc/images')
666         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
667         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
669     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
670     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
671     if is_win32:
672         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
673         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
674     # Data
675     data_dir = '' if is_win32 else 'geany'
676     start_dir = bld.path.find_dir('data')
677     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
678     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
679     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
680     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
681     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
682     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
683     if bld.env['use_gtk3']:
684         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
685     else:
686         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
688     start_dir = bld.path.find_dir('data/colorschemes')
689     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
690     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
691     start_dir = bld.path.find_dir('data/templates')
692     template_dest = '${DATADIR}/%s/templates' % data_dir
693     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
694     # Icons
695     for dest, srcs in geany_icons.items():
696         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
697         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
698     # install theme indexes on Windows
699     if is_win32:
700         for dest, srcs in geany_icons_indexes.items():
701             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
704 def distclean(ctx):
705     Scripting.distclean(ctx)
706     _remove_linguas_file()
709 def _remove_linguas_file():
710     # remove LINGUAS file as well
711     try:
712         os.unlink(LINGUAS_FILE)
713     except OSError:
714         pass
717 @feature('linguas')
718 @before_method('apply_intltool_po')
719 def write_linguas_file(self):
720     if os.path.exists(LINGUAS_FILE):
721         return
722     linguas = ''
723     if 'LINGUAS' in self.env:
724         files = self.env['LINGUAS']
725         for po_filename in files.split(' '):
726             if os.path.exists('po/%s.po' % po_filename):
727                 linguas += '%s ' % po_filename
728     else:
729         files = os.listdir('%s/po' % self.path.abspath())
730         files.sort()
731         for filename in files:
732             if filename.endswith('.po'):
733                 linguas += '%s ' % filename[:-3]
734     file_h = open(LINGUAS_FILE, 'w')
735     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
736     file_h.close()
739 def _post_install(ctx):
740     is_win32 = _target_is_win32(ctx)
741     if is_win32:
742         return
743     for d in 'hicolor', 'Tango':
744         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
745         icon_cache_updated = False
746         if not ctx.options.destdir:
747             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
748             Logs.pprint('GREEN', 'GTK icon cache updated.')
749             icon_cache_updated = True
750         if not icon_cache_updated:
751             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
752             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
755 def updatepo(ctx):
756     """update the message catalogs for internationalization"""
757     potfile = '%s.pot' % APPNAME
758     os.chdir('%s/po' % top)
759     try:
760         try:
761             old_size = os.stat(potfile).st_size
762         except OSError:
763             old_size = 0
764         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
765         size_new = os.stat(potfile).st_size
766         if size_new != old_size:
767             Logs.pprint('CYAN', 'Updated POT file.')
768             Logs.pprint('CYAN', 'Updating translations')
769             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
770             if ret != 0:
771                 Logs.pprint('RED', 'Updating translations failed')
772         else:
773             Logs.pprint('CYAN', 'POT file is up to date.')
774     except OSError:
775         Logs.pprint('RED', 'Failed to generate pot file.')
778 def apidoc(ctx):
779     """generate API reference documentation"""
780     ctx = BuildContext()  # create our own context to have ctx.top_dir
781     basedir = ctx.top_dir
782     doxygen = _find_program(ctx, 'doxygen')
783     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
784     Logs.pprint('CYAN', 'Generating API documentation')
785     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
786     if ret != 0:
787         raise WafError('Generating API documentation failed')
790 def hackingdoc(ctx):
791     """generate HACKING documentation"""
792     ctx = BuildContext()  # create our own context to have ctx.top_dir
793     Logs.pprint('CYAN', 'Generating HACKING documentation')
794     cmd = _find_rst2html(ctx)
795     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
796     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
797     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
798     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
799         cmd, stylesheet, hacking_file, hacking_html_file))
800     if ret != 0:
801         raise WafError('Generating HACKING documentation failed')
804 def _find_program(ctx, cmd, **kw):
805     def noop(*args):
806         pass
808     if ctx is None or not isinstance(ctx, ConfigurationContext):
809         ctx = ConfigurationContext()
810         ctx.to_log = noop
811         ctx.msg = noop
812     return ctx.find_program(cmd, **kw)
815 def _find_rst2html(ctx):
816     cmds = ['rst2html', 'rst2html2']
817     for command in cmds:
818         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
819         if cmd:
820             break
821     if not cmd:
822         raise WafError(
823             'rst2html.py could not be found. Please install the Python docutils package.')
824     return cmd
827 def _add_define_to_env(conf, key):
828     value = conf.get_define(key)
829     # strip quotes
830     value = value.replace('"', '')
831     conf.env[key] = value
834 def _add_to_env_and_define(conf, key, value, quote=False):
835     conf.define(key, value, quote)
836     conf.env[key] = value
839 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
840     value = default_value
841     if opt_value:
842         if isinstance(opt_value, bool):
843             opt_value = 1
844         value = opt_value
846     if value is not None:
847         _add_to_env_and_define(conf, define_name, value, quote)
848     else:
849         conf.undefine(define_name)
852 def _get_git_rev(conf):
853     if conf.options.no_scm:
854         return
856     if not os.path.isdir('.git'):
857         return
859     try:
860         cmd = 'git rev-parse --short --revs-only HEAD'
861         revision = conf.cmd_and_log(cmd).strip()
862     except WafError:
863         return None
864     else:
865         return revision
868 def _load_intltool_if_available(conf):
869     try:
870         conf.load('intltool')
871         if 'LINGUAS' in os.environ:
872             conf.env['LINGUAS'] = os.environ['LINGUAS']
873     except WafError:
874         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
875         if not _target_is_win32(conf):
876             raise
879 def _target_is_win32(ctx):
880     if 'is_win32' in ctx.env:
881         # cached
882         return ctx.env['is_win32']
883     is_win32 = None
884     if sys.platform == 'win32':
885         is_win32 = True
886     if is_win32 is None:
887         if ctx.env and 'CC' in ctx.env:
888             env_cc = ctx.env['CC']
889             if not isinstance(env_cc, str):
890                 env_cc = ''.join(env_cc)
891             is_win32 = (env_cc.find('mingw') != -1)
892     if is_win32 is None:
893         is_win32 = False
894     # cache for future checks
895     ctx.env['is_win32'] = is_win32
896     return is_win32
899 def _uc_first(string, ctx):
900     if _target_is_win32(ctx):
901         return string.title()
902     return string