Merge pull request #382 from cousteaulecommandant/patch-3
[geany-mirror.git] / wscript
blob0973b2e8406f2da0d8afa057c0cf50fee1f6b348
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 top = '.'
62 out = '_build_'
65 mio_sources = set(['tagmanager/mio/mio.c'])
67 ctags_sources = set([
68     'tagmanager/ctags/abaqus.c',
69     'tagmanager/ctags/args.c',
70     'tagmanager/ctags/abc.c',
71     'tagmanager/ctags/actionscript.c',
72     'tagmanager/ctags/asciidoc.c',
73     'tagmanager/ctags/asm.c',
74     'tagmanager/ctags/basic.c',
75     'tagmanager/ctags/c.c',
76     'tagmanager/ctags/cobol.c',
77     'tagmanager/ctags/conf.c',
78     'tagmanager/ctags/css.c',
79     'tagmanager/ctags/ctags.c',
80     'tagmanager/ctags/diff.c',
81     'tagmanager/ctags/docbook.c',
82     'tagmanager/ctags/entry.c',
83     'tagmanager/ctags/fortran.c',
84     'tagmanager/ctags/get.c',
85     'tagmanager/ctags/haskell.c',
86     'tagmanager/ctags/haxe.c',
87     'tagmanager/ctags/html.c',
88     'tagmanager/ctags/js.c',
89     'tagmanager/ctags/keyword.c',
90     'tagmanager/ctags/latex.c',
91     'tagmanager/ctags/lregex.c',
92     'tagmanager/ctags/lua.c',
93     'tagmanager/ctags/make.c',
94     'tagmanager/ctags/markdown.c',
95     'tagmanager/ctags/matlab.c',
96     'tagmanager/ctags/nsis.c',
97     'tagmanager/ctags/nestlevel.c',
98     'tagmanager/ctags/objc.c',
99     'tagmanager/ctags/options.c',
100     'tagmanager/ctags/parse.c',
101     'tagmanager/ctags/pascal.c',
102     'tagmanager/ctags/r.c',
103     'tagmanager/ctags/perl.c',
104     'tagmanager/ctags/php.c',
105     'tagmanager/ctags/python.c',
106     'tagmanager/ctags/read.c',
107     'tagmanager/ctags/rest.c',
108     'tagmanager/ctags/ruby.c',
109     'tagmanager/ctags/rust.c',
110     'tagmanager/ctags/sh.c',
111     'tagmanager/ctags/sort.c',
112     'tagmanager/ctags/sql.c',
113     'tagmanager/ctags/strlist.c',
114     'tagmanager/ctags/txt2tags.c',
115     'tagmanager/ctags/tcl.c',
116     'tagmanager/ctags/vhdl.c',
117     'tagmanager/ctags/verilog.c',
118     'tagmanager/ctags/vstring.c'])
120 tagmanager_sources = set([
121     'tagmanager/src/tm_source_file.c',
122     'tagmanager/src/tm_tag.c',
123     'tagmanager/src/tm_workspace.c'])
125 scintilla_sources = set(['scintilla/gtk/scintilla-marshal.c'])
127 geany_sources = set([
128     'src/about.c', 'src/build.c', 'src/callbacks.c', 'src/dialogs.c', 'src/document.c',
129     'src/editor.c', 'src/encodings.c', 'src/filetypes.c', 'src/geanyentryaction.c',
130     'src/geanymenubuttonaction.c', 'src/geanyobject.c', 'src/geanywraplabel.c',
131     'src/highlighting.c', 'src/keybindings.c',
132     'src/keyfile.c', 'src/log.c', 'src/main.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c',
133     'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
134     'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/stash.c',
135     'src/symbols.c',
136     'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
137     'src/ui_utils.c', 'src/utils.c'])
139 geany_icons = {
140     'hicolor/16x16/apps':       ['16x16/classviewer-class.png',
141                                  '16x16/classviewer-macro.png',
142                                  '16x16/classviewer-member.png',
143                                  '16x16/classviewer-method.png',
144                                  '16x16/classviewer-namespace.png',
145                                  '16x16/classviewer-other.png',
146                                  '16x16/classviewer-struct.png',
147                                  '16x16/classviewer-var.png',
148                                  '16x16/geany.png'],
149     'hicolor/16x16/actions':    ['16x16/geany-build.png',
150                                  '16x16/geany-close-all.png',
151                                  '16x16/geany-save-all.png'],
152     'hicolor/24x24/actions':    ['24x24/geany-build.png',
153                                  '24x24/geany-close-all.png',
154                                  '24x24/geany-save-all.png'],
155     'hicolor/32x32/actions':    ['32x32/geany-build.png',
156                                  '32x32/geany-close-all.png',
157                                  '32x32/geany-save-all.png'],
158     'hicolor/48x48/actions':    ['48x48/geany-build.png',
159                                  '48x48/geany-close-all.png',
160                                  '48x48/geany-save-all.png'],
161     'hicolor/48x48/apps':       ['48x48/geany.png'],
162     'hicolor/scalable/apps':    ['scalable/geany.svg'],
163     'hicolor/scalable/actions': ['scalable/geany-build.svg',
164                                  'scalable/geany-close-all.svg',
165                                  'scalable/geany-save-all.svg'],
166     'Tango/16x16/actions':      ['tango/16x16/geany-save-all.png'],
167     'Tango/24x24/actions':      ['tango/24x24/geany-save-all.png'],
168     'Tango/32x32/actions':      ['tango/32x32/geany-save-all.png'],
169     'Tango/48x48/actions':      ['tango/48x48/geany-save-all.png'],
170     'Tango/scalable/actions':   ['tango/scalable/geany-save-all.svg']
172 geany_icons_indexes = {
173     'hicolor':  ['index.theme'],
174     'Tango':    ['tango/index.theme']
178 def configure(conf):
180     conf.check_waf_version(mini='1.6.1')
182     conf.load('compiler_c')
183     is_win32 = _target_is_win32(conf)
185     conf.check_cc(header_name='fcntl.h', mandatory=False)
186     conf.check_cc(header_name='fnmatch.h', mandatory=False)
187     conf.check_cc(header_name='glob.h', mandatory=False)
188     conf.check_cc(header_name='sys/time.h', mandatory=False)
189     conf.check_cc(header_name='sys/types.h', mandatory=False)
190     conf.check_cc(header_name='sys/stat.h', mandatory=False)
191     conf.define('HAVE_STDLIB_H', 1)  # are there systems without stdlib.h?
192     conf.define('STDC_HEADERS', 1)  # an optimistic guess ;-)
193     _add_to_env_and_define(conf, 'HAVE_REGCOMP', 1)  # needed for CTags
195     conf.check_cc(function_name='fgetpos', header_name='stdio.h', mandatory=False)
196     conf.check_cc(function_name='fnmatch', header_name='fnmatch.h', mandatory=False)
197     conf.check_cc(function_name='ftruncate', header_name='unistd.h', mandatory=False)
198     conf.check_cc(function_name='mkstemp', header_name='stdlib.h', mandatory=False)
199     conf.check_cc(function_name='strstr', header_name='string.h')
201     conf.check_cc(function_name='pow', header_name='math.h', lib='m', uselib_store='M')
203     # check sunOS socket support
204     if Options.platform == 'sunos':
205         conf.check_cc(function_name='socket', lib='socket',
206                       header_name='sys/socket.h', uselib_store='SUNOS_SOCKET', mandatory=True)
208     # check for cxx after the header and function checks have been done to ensure they are
209     # checked with cc not cxx
210     conf.load('compiler_cxx')
211     if is_win32:
212         conf.load('winres')
213     _load_intltool_if_available(conf)
215     # GTK / GIO version check
216     gtk_package_name = 'gtk+-3.0' if conf.options.use_gtk3 else 'gtk+-2.0'
217     minimum_gtk_version = MINIMUM_GTK3_VERSION if conf.options.use_gtk3 else MINIMUM_GTK_VERSION
218     conf.check_cfg(package=gtk_package_name, atleast_version=minimum_gtk_version, uselib_store='GTK',
219         mandatory=True, args='--cflags --libs')
220     conf.check_cfg(package='glib-2.0', atleast_version=MINIMUM_GLIB_VERSION, uselib_store='GLIB',
221         mandatory=True, args='--cflags --libs')
222     conf.check_cfg(package='gmodule-2.0', uselib_store='GMODULE',
223         mandatory=True, args='--cflags --libs')
224     conf.check_cfg(package='gio-2.0', uselib_store='GIO', args='--cflags --libs', mandatory=True)
225     gtk_version = conf.check_cfg(modversion=gtk_package_name, uselib_store='GTK') or 'Unknown'
226     conf.check_cfg(package='gthread-2.0', uselib_store='GTHREAD', args='--cflags --libs')
227     # remember GTK version for the build step
228     conf.env['gtk_package_name'] = gtk_package_name
229     conf.env['minimum_gtk_version'] = minimum_gtk_version
230     conf.env['use_gtk3'] = conf.options.use_gtk3
232     revision = _get_git_rev(conf)
234     # rst2html for the HTML manual
235     if not conf.options.no_html_doc and revision is not None:
236         try:
237             conf.env['RST2HTML'] = _find_rst2html(conf)
238         except WafError:
239             error_msg = '''Documentation enabled but rst2html not found.
240 You can explicitly disable building of the HTML manual with --disable-html-docs,
241 but you then may not have a local copy of the HTML manual.'''
242             raise WafError(error_msg)
244     # Windows specials
245     if is_win32:
246         if conf.env['PREFIX'].lower() == tempfile.gettempdir().lower():
247             # overwrite default prefix on Windows (tempfile.gettempdir() is the Waf default)
248             new_prefix = os.path.join(str(conf.root), '%s-%s' % (APPNAME, VERSION))
249             _add_to_env_and_define(conf, 'PREFIX', new_prefix, quote=True)
250             _add_to_env_and_define(conf, 'BINDIR', os.path.join(new_prefix, 'bin'), quote=True)
251         _add_to_env_and_define(conf, 'DOCDIR', os.path.join(conf.env['PREFIX'], 'doc'), quote=True)
252         _add_to_env_and_define(conf, 'LIBDIR', '%s/lib' % conf.env['PREFIX'], quote=True)
253         conf.define('LOCALEDIR', os.path.join('share' 'locale'), quote=True)
254         # overwrite LOCALEDIR to install message catalogues properly
255         conf.env['LOCALEDIR'] = os.path.join(conf.env['PREFIX'], 'share', 'locale')
256         # DATADIR is defined in objidl.h, so we remove it from config.h but keep it in env
257         conf.undefine('DATADIR')
258         conf.env['DATADIR'] = os.path.join(conf.env['PREFIX'], 'data')
259         conf.env.append_value('LINKFLAGS_cprogram', [
260             '-mwindows',
261             '-static-libgcc',
262             '-static-libstdc++'])
263         conf.env.append_value('LIB_WIN32', ['wsock32', 'uuid', 'ole32'])
264         # explicitly define Windows version for older Mingw environments
265         conf.define('WINVER', '0x0501', quote=False)  # for SHGetFolderPathAndSubDirW
266         conf.define('_WIN32_IE', '0x0500', quote=False)  # for SHGFP_TYPE
267     else:
268         conf.env['cshlib_PATTERN'] = '%s.so'
269         # DATADIR and LOCALEDIR are defined by the intltool tool
270         # but they are not added to the environment, so we need to
271         _add_define_to_env(conf, 'DATADIR')
272         _add_define_to_env(conf, 'LOCALEDIR')
273         docdir = os.path.join(conf.env['DATADIR'], 'doc', 'geany')
274         libdir = os.path.join(conf.env['PREFIX'], 'lib')
275         mandir = os.path.join(conf.env['DATADIR'], 'man')
276         _define_from_opt(conf, 'DOCDIR', conf.options.docdir, docdir)
277         _define_from_opt(conf, 'LIBDIR', conf.options.libdir, libdir)
278         _define_from_opt(conf, 'MANDIR', conf.options.mandir, mandir)
280     conf.define('ENABLE_NLS', 1)
281     conf.define('GEANY_LOCALEDIR', '' if is_win32 else conf.env['LOCALEDIR'], quote=True)
282     conf.define('GEANY_DATADIR', 'data' if is_win32 else conf.env['DATADIR'], quote=True)
283     conf.define('GEANY_DOCDIR', conf.env['DOCDIR'], quote=True)
284     conf.define('GEANY_LIBDIR', '' if is_win32 else conf.env['LIBDIR'], quote=True)
285     conf.define('GEANY_PREFIX', '' if is_win32 else conf.env['PREFIX'], quote=True)
286     conf.define('PACKAGE', APPNAME, quote=True)
287     conf.define('VERSION', VERSION, quote=True)
288     conf.define('REVISION', revision or '-1', quote=True)
290     conf.define('GETTEXT_PACKAGE', APPNAME, quote=True)
292     # no VTE on Windows
293     if is_win32:
294         conf.options.no_vte = True
296     _define_from_opt(conf, 'HAVE_PLUGINS', not conf.options.no_plugins, None)
297     _define_from_opt(conf, 'HAVE_SOCKET', not conf.options.no_socket, None)
298     _define_from_opt(conf, 'HAVE_VTE', not conf.options.no_vte, None)
300     conf.write_config_header('config.h', remove=False)
302     # some more compiler flags
303     conf.env.append_value('CFLAGS', ['-DHAVE_CONFIG_H'])
304     if conf.env['CC_NAME'] == 'gcc' and '-O' not in ''.join(conf.env['CFLAGS']):
305         conf.env.append_value('CFLAGS', ['-O2'])
306     if revision is not None:
307         conf.env.append_value('CFLAGS', ['-g', '-DGEANY_DEBUG'])
308     # Scintilla flags
309     conf.env.append_value('CFLAGS', ['-DGTK'])
310     conf.env.append_value('CXXFLAGS',
311         ['-DNDEBUG', '-DGTK', '-DSCI_LEXER', '-DG_THREADS_IMPL_NONE'])
313     # summary
314     Logs.pprint('BLUE', 'Summary:')
315     conf.msg('Install Geany ' + VERSION + ' in', conf.env['PREFIX'])
316     conf.msg('Using GTK version', gtk_version)
317     conf.msg('Build with plugin support', conf.options.no_plugins and 'no' or 'yes')
318     conf.msg('Use virtual terminal support', conf.options.no_vte and 'no' or 'yes')
319     if revision is not None:
320         conf.msg('Compiling Git revision', revision)
323 def options(opt):
324     # Disable MSVC detection on win32: building Geany with MSVC is currently not supported
325     # If anyone wants to add support for building with MSVC, this hack should be removed.
326     c_compiler['win32'] = ['gcc']
327     cxx_compiler['win32'] = ['g++']
329     opt.load('compiler_cc')
330     opt.load('compiler_cxx')
331     opt.load('intltool')
333     # Option
334     opt.add_option('--no-scm', action='store_true', default=False,
335         help='Disable SCM detection [default: No]', dest='no_scm')
336     # Features
337     opt.add_option('--disable-plugins', action='store_true', default=False,
338         help='compile without plugin support [default: No]', dest='no_plugins')
339     opt.add_option('--disable-socket', action='store_true', default=False,
340         help='compile without support to detect a running instance [[default: No]',
341         dest='no_socket')
342     opt.add_option('--disable-vte', action='store_true', default=False,
343         help='compile without support for an embedded virtual terminal [[default: No]',
344         dest='no_vte')
345     opt.add_option('--enable-gtk3', action='store_true', default=False,
346         help='compile with GTK3 support (experimental) [[default: No]',
347         dest='use_gtk3')
348     opt.add_option('--disable-html-docs', action='store_true', default=False,
349         help='do not generate HTML documentation using rst2html [[default: No]',
350         dest='no_html_doc')
351     # Paths
352     opt.add_option('--mandir', type='string', default='',
353         help='man documentation', dest='mandir')
354     opt.add_option('--docdir', type='string', default='',
355         help='documentation root', dest='docdir')
356     opt.add_option('--libdir', type='string', default='',
357         help='object code libraries', dest='libdir')
360 def build(bld):
361     is_win32 = _target_is_win32(bld)
363     if bld.cmd == 'clean':
364         _remove_linguas_file()
365     if bld.cmd in ('install', 'uninstall'):
366         bld.add_post_fun(_post_install)
368     def build_plugin(plugin_name, install=True, uselib_add=[]):
369         if install:
370             instpath = '${PREFIX}/lib' if is_win32 else '${LIBDIR}/geany'
371         else:
372             instpath = None
374         bld(
375             features                = ['c', 'cshlib'],
376             source                  = 'plugins/%s.c' % plugin_name,
377             includes                = ['.', 'src/', 'scintilla/include', 'tagmanager/src'],
378             defines                 = 'G_LOG_DOMAIN="%s"' % plugin_name,
379             target                  = plugin_name,
380             uselib                  = ['GTK', 'GLIB', 'GMODULE'] + uselib_add,
381             install_path            = instpath)
383     # CTags
384     bld(
385         features        = ['c', 'cstlib'],
386         source          = ctags_sources,
387         name            = 'ctags',
388         target          = 'ctags',
389         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
390         defines         = 'G_LOG_DOMAIN="CTags"',
391         uselib          = ['GLIB'],
392         install_path    = None)  # do not install this library
394     # Tagmanager
395     bld(
396         features        = ['c', 'cstlib'],
397         source          = tagmanager_sources,
398         name            = 'tagmanager',
399         target          = 'tagmanager',
400         includes        = ['.', 'tagmanager', 'tagmanager/ctags'],
401         defines         = ['GEANY_PRIVATE', 'G_LOG_DOMAIN="Tagmanager"'],
402         uselib          = ['GTK', 'GLIB'],
403         install_path    = None)  # do not install this library
405     # MIO
406     bld(
407         features        = ['c', 'cstlib'],
408         source          = mio_sources,
409         name            = 'mio',
410         target          = 'mio',
411         includes        = ['.', 'tagmanager/mio/'],
412         defines         = 'G_LOG_DOMAIN="MIO"',
413         uselib          = ['GTK', 'GLIB'],
414         install_path    = None)  # do not install this library
416     # Scintilla
417     files = bld.srcnode.ant_glob('scintilla/**/*.cxx', src=True, dir=False)
418     scintilla_sources.update(files)
419     bld(
420         features        = ['c', 'cxx', 'cxxstlib'],
421         name            = 'scintilla',
422         target          = 'scintilla',
423         source          = scintilla_sources,
424         includes        = ['.', 'scintilla/include', 'scintilla/src', 'scintilla/lexlib'],
425         uselib          = ['GTK', 'GLIB', 'GMODULE', 'M'],
426         install_path    = None)  # do not install this library
428     # Geany
429     if bld.env['HAVE_VTE'] == 1:
430         geany_sources.add('src/vte.c')
431     if is_win32:
432         geany_sources.add('src/win32.c')
433         geany_sources.add('geany_private.rc')
435     bld(
436         features        = ['c', 'cxx', 'cprogram'],
437         name            = 'geany',
438         target          = 'geany',
439         source          = geany_sources,
440         includes        = ['.', 'scintilla/include', 'tagmanager/src'],
441         defines         = ['G_LOG_DOMAIN="Geany"', 'GEANY_PRIVATE'],
442         uselib          = ['GTK', 'GLIB', 'GMODULE', 'GIO', 'GTHREAD', 'WIN32', 'SUNOS_SOCKET', 'M'],
443         use             = ['scintilla', 'ctags', 'tagmanager', 'mio'])
445     # geanyfunctions.h
446     bld(
447         source  = ['plugins/genapi.py', 'src/plugins.c'],
448         name    = 'geanyfunctions.h',
449         before  = ['c', 'cxx'],
450         cwd     = '%s/plugins' % bld.path.abspath(),
451         rule    = '%s genapi.py -q' % sys.executable)
453     # Plugins
454     if bld.env['HAVE_PLUGINS'] == 1:
455         build_plugin('classbuilder')
456         build_plugin('demoplugin', False)
457         build_plugin('export', uselib_add=['M'])
458         build_plugin('filebrowser')
459         build_plugin('htmlchars')
460         build_plugin('saveactions')
461         build_plugin('splitwindow')
463     # Translations
464     if bld.env['INTLTOOL']:
465         bld(
466             features        = ['linguas', 'intltool_po'],
467             podir           = 'po',
468             install_path    = '${LOCALEDIR}',
469             appname         = 'geany')
471     # HTML documentation (build if it is not part of the tree already, as it is required for install)
472     html_doc_filename = os.path.join(bld.out_dir, 'doc', 'geany.html')
473     if bld.env['RST2HTML']:
474         rst2html = bld.env['RST2HTML']
475         bld(
476             source  = ['doc/geany.txt'],
477             deps    = ['doc/geany.css'],
478             target  = 'doc/geany.html',
479             name    = 'geany.html',
480             cwd     = os.path.join(bld.path.abspath(), 'doc'),
481             rule    = '%s  -stg --stylesheet=geany.css geany.txt %s' % (rst2html, html_doc_filename))
483     # geany.pc
484     if is_win32:
485         # replace backward slashes by forward slashes as they could be interepreted as escape
486         # characters
487         geany_pc_prefix = bld.env['PREFIX'].replace('\\', '/')
488     else:
489         geany_pc_prefix = bld.env['PREFIX']
490     bld(
491         source          = 'geany.pc.in',
492         dct             = {'VERSION': VERSION,
493                            'DEPENDENCIES': '%s >= %s glib-2.0 >= %s' % \
494                                 (bld.env['gtk_package_name'],
495                                  bld.env['minimum_gtk_version'],
496                                  MINIMUM_GLIB_VERSION),
497                            'prefix': geany_pc_prefix,
498                            'exec_prefix': '${prefix}',
499                            'libdir': '${exec_prefix}/lib',
500                            'includedir': '${prefix}/include',
501                            'datarootdir': '${prefix}/share',
502                            'datadir': '${datarootdir}',
503                            'localedir': '${datarootdir}/locale'})
505     if not is_win32:
506         # geany.desktop
507         if bld.env['INTLTOOL']:
508             bld(
509                 features        = 'intltool_in',
510                 source          = 'geany.desktop.in',
511                 flags           = ['-d', '-q', '-u', '-c'],
512                 install_path    = '${DATADIR}/applications')
514         # geany.1
515         bld(
516             features        = 'subst',
517             source          = 'doc/geany.1.in',
518             target          = 'geany.1',
519             dct             = {'VERSION': VERSION,
520                                 'GEANY_DATA_DIR': bld.env['DATADIR'] + '/geany'},
521             install_path    = '${MANDIR}/man1')
523         # geany.spec
524         bld(
525             features        = 'subst',
526             source          = 'geany.spec.in',
527             target          = 'geany.spec',
528             install_path    = None,
529             dct             = {'VERSION': VERSION})
531         # Doxyfile
532         bld(
533             features        = 'subst',
534             source          = 'doc/Doxyfile.in',
535             target          = 'doc/Doxyfile',
536             install_path    = None,
537             dct             = {'VERSION': VERSION,
538                                'top_builddir': bld.out_dir,
539                                'top_srcdir': bld.top_dir,})
541     ###
542     # Install files
543     ###
544     # Headers
545     bld.install_files('${PREFIX}/include/geany', '''
546         src/app.h src/document.h src/editor.h src/encodings.h src/filetypes.h src/geany.h
547         src/highlighting.h src/keybindings.h src/msgwindow.h src/plugindata.h
548         src/prefs.h src/project.h src/search.h src/stash.h src/support.h
549         src/templates.h src/toolbar.h src/ui_utils.h src/utils.h src/build.h src/gtkcompat.h
550         plugins/geanyplugin.h plugins/geanyfunctions.h''')
551     bld.install_files('${PREFIX}/include/geany/scintilla', '''
552         scintilla/include/SciLexer.h scintilla/include/Scintilla.h
553         scintilla/include/Scintilla.iface scintilla/include/ScintillaWidget.h ''')
554     bld.install_files('${PREFIX}/include/geany/tagmanager', '''
555         tagmanager/src/tm_source_file.h
556         tagmanager/src/tm_tag.h
557         tagmanager/src/tm_tagmanager.h
558         tagmanager/src/tm_workspace.h ''')
559     # Docs
560     base_dir = '${PREFIX}' if is_win32 else '${DOCDIR}'
561     ext = '.txt' if is_win32 else ''
562     for filename in 'AUTHORS ChangeLog COPYING README NEWS THANKS TODO'.split():
563         basename = _uc_first(filename, bld)
564         destination_filename = '%s%s' % (basename, ext)
565         destination = os.path.join(base_dir, destination_filename)
566         bld.install_as(destination, filename)
568     # install HTML documentation only if it exists, i.e. it was built before
569     # local_html_doc_filename supports installing HTML doc from in-tree geany.html if it exists
570     local_html_doc_filename = os.path.join(bld.path.abspath(), 'doc', 'geany.html')
571     if os.path.exists(html_doc_filename) or os.path.exists(local_html_doc_filename):
572         html_dir = '' if is_win32 else 'html/'
573         html_name = 'Manual.html' if is_win32 else 'index.html'
574         start_dir = bld.path.find_dir('doc/images')
575         bld.install_files('${DOCDIR}/%simages' % html_dir, start_dir.ant_glob('*.png'), cwd=start_dir)
576         bld.install_as('${DOCDIR}/%s%s' % (html_dir, html_name), 'doc/geany.html')
578     bld.install_as('${DOCDIR}/%s' % _uc_first('manual.txt', bld), 'doc/geany.txt')
579     bld.install_as('${DOCDIR}/ScintillaLicense.txt', 'scintilla/License.txt')
580     if is_win32:
581         bld.install_as('${DOCDIR}/ReadMe.I18n.txt', 'README.I18N')
582         bld.install_as('${DOCDIR}/Hacking.txt', 'HACKING')
583     # Data
584     data_dir = '' if is_win32 else 'geany'
585     start_dir = bld.path.find_dir('data')
586     bld.install_as('${DATADIR}/%s/GPL-2' % data_dir, 'COPYING')
587     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('filetype*'), cwd=start_dir)
588     bld.install_files('${DATADIR}/%s' % data_dir, start_dir.ant_glob('*.tags'), cwd=start_dir)
589     bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.glade')
590     bld.install_files('${DATADIR}/%s' % data_dir, 'data/snippets.conf')
591     bld.install_files('${DATADIR}/%s' % data_dir, 'data/ui_toolbar.xml')
592     if bld.env['use_gtk3']:
593         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.css')
594     else:
595         bld.install_files('${DATADIR}/%s' % data_dir, 'data/geany.gtkrc')
597     start_dir = bld.path.find_dir('data/colorschemes')
598     template_dest = '${DATADIR}/%s/colorschemes' % data_dir
599     bld.install_files(template_dest, start_dir.ant_glob('*'), cwd=start_dir)
600     start_dir = bld.path.find_dir('data/templates')
601     template_dest = '${DATADIR}/%s/templates' % data_dir
602     bld.install_files(template_dest, start_dir.ant_glob('**/*'), cwd=start_dir, relative_trick=True)
603     # Icons
604     for dest, srcs in geany_icons.items():
605         dest_dir = os.path.join('${PREFIX}/share/icons' if is_win32 else '${DATADIR}/icons', dest)
606         bld.install_files(dest_dir, srcs, cwd=bld.path.find_dir('icons'))
607     # install theme indexes on Windows
608     if is_win32:
609         for dest, srcs in geany_icons_indexes.items():
610             bld.install_files(os.path.join('${PREFIX}/share/icons', dest), srcs, cwd=bld.path.find_dir('icons'))
613 def distclean(ctx):
614     Scripting.distclean(ctx)
615     _remove_linguas_file()
618 def _remove_linguas_file():
619     # remove LINGUAS file as well
620     try:
621         os.unlink(LINGUAS_FILE)
622     except OSError:
623         pass
626 @feature('linguas')
627 @before_method('apply_intltool_po')
628 def write_linguas_file(self):
629     if os.path.exists(LINGUAS_FILE):
630         return
631     linguas = ''
632     if 'LINGUAS' in self.env:
633         files = self.env['LINGUAS']
634         for po_filename in files.split(' '):
635             if os.path.exists('po/%s.po' % po_filename):
636                 linguas += '%s ' % po_filename
637     else:
638         files = os.listdir('%s/po' % self.path.abspath())
639         files.sort()
640         for filename in files:
641             if filename.endswith('.po'):
642                 linguas += '%s ' % filename[:-3]
643     file_h = open(LINGUAS_FILE, 'w')
644     file_h.write('# This file is autogenerated. Do not edit.\n%s\n' % linguas)
645     file_h.close()
648 def _post_install(ctx):
649     is_win32 = _target_is_win32(ctx)
650     if is_win32:
651         return
652     for d in 'hicolor', 'Tango':
653         theme_dir = Utils.subst_vars('${DATADIR}/icons/' + d, ctx.env)
654         icon_cache_updated = False
655         if not ctx.options.destdir:
656             ctx.exec_command('gtk-update-icon-cache -q -f -t %s' % theme_dir)
657             Logs.pprint('GREEN', 'GTK icon cache updated.')
658             icon_cache_updated = True
659         if not icon_cache_updated:
660             Logs.pprint('YELLOW', 'Icon cache not updated. After install, run this:')
661             Logs.pprint('YELLOW', 'gtk-update-icon-cache -q -f -t %s' % theme_dir)
664 def updatepo(ctx):
665     """update the message catalogs for internationalization"""
666     potfile = '%s.pot' % APPNAME
667     os.chdir('%s/po' % top)
668     try:
669         try:
670             old_size = os.stat(potfile).st_size
671         except OSError:
672             old_size = 0
673         ctx.exec_command('intltool-update --pot -g %s' % APPNAME)
674         size_new = os.stat(potfile).st_size
675         if size_new != old_size:
676             Logs.pprint('CYAN', 'Updated POT file.')
677             Logs.pprint('CYAN', 'Updating translations')
678             ret = ctx.exec_command('intltool-update -r -g %s' % APPNAME)
679             if ret != 0:
680                 Logs.pprint('RED', 'Updating translations failed')
681         else:
682             Logs.pprint('CYAN', 'POT file is up to date.')
683     except OSError:
684         Logs.pprint('RED', 'Failed to generate pot file.')
687 def apidoc(ctx):
688     """generate API reference documentation"""
689     ctx = BuildContext()  # create our own context to have ctx.top_dir
690     basedir = ctx.top_dir
691     doxygen = _find_program(ctx, 'doxygen')
692     doxyfile = '%s/doc/Doxyfile' % ctx.out_dir
693     Logs.pprint('CYAN', 'Generating API documentation')
694     ret = ctx.exec_command('%s %s' % (doxygen, doxyfile))
695     if ret != 0:
696         raise WafError('Generating API documentation failed')
699 def hackingdoc(ctx):
700     """generate HACKING documentation"""
701     ctx = BuildContext()  # create our own context to have ctx.top_dir
702     Logs.pprint('CYAN', 'Generating HACKING documentation')
703     cmd = _find_rst2html(ctx)
704     hacking_file = os.path.join(ctx.top_dir, 'HACKING')
705     hacking_html_file = os.path.join(ctx.top_dir, 'doc', 'hacking.html')
706     stylesheet = os.path.join(ctx.top_dir, 'doc', 'geany.css')
707     ret = ctx.exec_command('%s  -stg --stylesheet=%s %s %s' % (
708         cmd, stylesheet, hacking_file, hacking_html_file))
709     if ret != 0:
710         raise WafError('Generating HACKING documentation failed')
713 def _find_program(ctx, cmd, **kw):
714     def noop(*args):
715         pass
717     if ctx is None or not isinstance(ctx, ConfigurationContext):
718         ctx = ConfigurationContext()
719         ctx.to_log = noop
720         ctx.msg = noop
721     return ctx.find_program(cmd, **kw)
724 def _find_rst2html(ctx):
725     cmds = ['rst2html', 'rst2html2']
726     for command in cmds:
727         cmd = _find_program(ctx, command, mandatory=False, exts=',.py')
728         if cmd:
729             break
730     if not cmd:
731         raise WafError(
732             'rst2html.py could not be found. Please install the Python docutils package.')
733     return cmd
736 def _add_define_to_env(conf, key):
737     value = conf.get_define(key)
738     # strip quotes
739     value = value.replace('"', '')
740     conf.env[key] = value
743 def _add_to_env_and_define(conf, key, value, quote=False):
744     conf.define(key, value, quote)
745     conf.env[key] = value
748 def _define_from_opt(conf, define_name, opt_value, default_value, quote=1):
749     value = default_value
750     if opt_value:
751         if isinstance(opt_value, bool):
752             opt_value = 1
753         value = opt_value
755     if value is not None:
756         _add_to_env_and_define(conf, define_name, value, quote)
757     else:
758         conf.undefine(define_name)
761 def _get_git_rev(conf):
762     if conf.options.no_scm:
763         return
765     if not os.path.isdir('.git'):
766         return
768     try:
769         cmd = 'git rev-parse --short --revs-only HEAD'
770         revision = conf.cmd_and_log(cmd).strip()
771     except WafError:
772         return None
773     else:
774         return revision
777 def _load_intltool_if_available(conf):
778     try:
779         conf.load('intltool')
780         if 'LINGUAS' in os.environ:
781             conf.env['LINGUAS'] = os.environ['LINGUAS']
782     except WafError:
783         # on Windows, we don't hard depend on intltool, on all other platforms raise an error
784         if not _target_is_win32(conf):
785             raise
788 def _target_is_win32(ctx):
789     if 'is_win32' in ctx.env:
790         # cached
791         return ctx.env['is_win32']
792     is_win32 = None
793     if sys.platform == 'win32':
794         is_win32 = True
795     if is_win32 is None:
796         if ctx.env and 'CC' in ctx.env:
797             env_cc = ctx.env['CC']
798             if not isinstance(env_cc, str):
799                 env_cc = ''.join(env_cc)
800             is_win32 = (env_cc.find('mingw') != -1)
801     if is_win32 is None:
802         is_win32 = False
803     # cache for future checks
804     ctx.env['is_win32'] = is_win32
805     return is_win32
808 def _uc_first(string, ctx):
809     if _target_is_win32(ctx):
810         return string.title()
811     return string