Merge pull request #61 from MightyCreak/add-meson

Replace install.py with Meson
This commit is contained in:
Creak 2020-08-13 15:42:04 -04:00 committed by GitHub
commit b358b2c5d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 100 additions and 303 deletions

1
.gitignore vendored
View File

@ -13,6 +13,7 @@ __pycache__/
# Distribution / packaging
.Python
build/
builddir/
develop-eggs/
dist/
downloads/

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
### Changed
- Replace old install.py with the more standard Meson
- Remove `u` string prefixes since Python 3 is in UTF-8 by default
- Replaced some interpolation operators (`%`) for the `f` string prefix
- Use the window scale factor for the icons generation

View File

@ -53,15 +53,20 @@ fixed by removing the offending lines (or the entire file) from
## Installing on POSIX systems
Run the `install.py` script to install Diffuse. The script accepts several
options for customising the installation behaviour. Use the `--help` option
to obtain a full listing.
Diffuse build system is meson.
Diffuse can be removed by running the `install.py` script with the `--remove`
option.
To install diffuse locally:
Specify the `--destdir=` and `--files-only` options for the `install.py`
script when using it to build a platform specific package of Diffuse.
meson builddir
meson install -C builddir
To uninstall diffuse afterwards:
sudo ninja uninstall -C builddir
sudo rm -v /usr/local/share/locale/*/LC_MESSAGES/diffuse.mo
Meson allows to change the default installation directories, see
[command-line documentation](https://mesonbuild.com/Commands.html#configure).
## Installing on Windows

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
import sysconfig
from compileall import compile_dir
from os import environ, path
from subprocess import call
prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local')
datadir = path.join(prefix, 'share')
destdir = environ.get('DESTDIR', '')
# Package managers set this so we don't need to run
if not destdir:
print('Updating icon cache...')
call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')])

View File

@ -1,294 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2009-2010 Derrick Moser <derrick_moser@yahoo.com>
# Copyright (C) 2015-2020 Romain "Creak" Failliot
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the licence, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. You may also obtain a copy of the GNU General Public License
# from the Free Software Foundation by visiting their web site
# (http://www.fsf.org/) or by writing to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import glob
import os
import stat
import subprocess
import sys
app_path = sys.argv[0]
# print a message to stderr
def logError(s):
sys.stderr.write(f'{app_path}: {s}\n')
# this install script should not be used on Windows
if os.name == 'nt':
logError('Wrong platform. Use scripts from the "windows-installer" directory instead.')
sys.exit(1)
# reset the umask so files we create will have the expected permissions
os.umask(stat.S_IWGRP | stat.S_IWOTH)
# option defaults
options = { 'destdir': '/',
'prefix': '/usr/local/',
'sysconfdir': '/etc/',
'examplesdir': '${sysconfdir}',
'mandir': '${prefix}/share/man/',
'pythonbin': '/usr/bin/env python' }
install = True
files_only = False
# process --help option
if len(sys.argv) == 2 and sys.argv[1] == '--help':
print(f"""Usage: {app_path} [OPTION...]
Install or remove Diffuse.
Options:
--help
print this help text and quit
--remove
remove the program
--destdir=PATH
path to the installation's root directory
default: {options['destdir']}
--prefix=PATH
common installation prefix for files
default: {options['prefix']}
--sysconfdir=PATH
directory for installing read-only single-machine data
default: {options['sysconfdir']}
--examplesdir=PATH
directory for example configuration files
default: {options['examplesdir']}
--mandir=PATH
directory for man pages
default: {options['mandir']}
--pythonbin=PATH
command for python interpreter
default: {options['pythonbin']}
--files-only
only install/remove files; skip the post install/removal tasks""")
sys.exit(0)
# returns the list of components used in a path
def components(s):
return [ p for p in s.split(os.sep) if p != '' ]
# returns a relative path from 'src' to 'dst'
def relpath(src, dst):
s1, s2, i = components(src), components(dst), 0
while i < len(s1) and i < len(s2) and s1[i] == s2[i]:
i += 1
s = [ os.pardir ] * (len(s1) - i)
s.extend(s2[i:])
return os.sep.join(s)
# apply a set of text substitution rules on a string
def replace(s, rules, i=0):
if i < len(rules):
k, v = rules[i]
a = s.split(k)
for j in range(len(a)):
a[j] = replace(a[j], rules, i + 1)
s = v.join(a)
return s
# create directories
def createDirs(d):
p = os.sep
for c in components(d):
p = os.path.join(p, c)
if not os.path.isdir(p):
os.mkdir(p)
# remove a file
def removeFile(f):
try:
os.unlink(f)
except OSError:
logError(f'Error removing "{f}".')
# install/remove sets of files
def processFiles(install, dst, src, template):
for k, v in template.items():
for s in glob.glob(os.path.join(src, k)):
d = s.replace(src, dst, 1)
if install:
createDirs(os.path.dirname(d))
# install file
f = open(s, 'rb')
c = f.read()
f.close()
if v is not None:
c = replace(c, v)
print(f'Installing {d}')
f = open(d, 'wb')
f.write(c)
f.close()
if k == 'bin/diffuse':
# turn on the execute bits
os.chmod(d, 0o755)
else:
# remove file
print(f'Removing {d}')
removeFile(d)
# compile .po files and install
def processTranslations(install, dst):
for s in glob.glob('translations/*.po'):
lang = s[13:-3]
d = os.path.join(dst, f'share/locale/{lang}/LC_MESSAGES/diffuse.mo')
if install:
# install file
try:
print(f'Installing {d}')
createDirs(os.path.dirname(d))
if subprocess.Popen(['msgfmt', '-o', d, s]).wait() != 0:
raise OSError()
except OSError:
logError(f'WARNING: Failed to compile "{lang}" localisation.')
else:
# remove file
removeFile(d)
# parse command line arguments
for arg in sys.argv[1:]:
if arg == '--remove':
install = False
elif arg == '--files-only':
files_only = True
else:
for opt in options.keys():
key = f'--{opt}='
if arg.startswith(key):
options[opt] = arg[len(key):]
break
else:
logError(f'Unknown option "{arg}".')
sys.exit(1)
# expand variables
for s in 'sysconfdir', 'examplesdir', 'mandir':
for k in 'prefix', 'sysconfdir':
if s != k:
options[s] = options[s].replace(f'${{{k}}}', options[k])
# validate inputs
if options['destdir'] == '':
options['destdir'] = '/'
for opt in 'prefix', 'sysconfdir', 'examplesdir', 'mandir':
p = options[opt]
c = components(p)
if os.pardir in c or os.curdir in c:
logError(f'Bad value for option "{opt}".')
sys.exit(1)
c.insert(0, '')
c.append('')
options[opt] = os.sep.join(c)
destdir = options['destdir']
prefix = options['prefix']
sysconfdir = options['sysconfdir']
examplesdir = options['examplesdir']
mandir = options['mandir']
pythonbin = options['pythonbin']
# tell the user what we are about to do
if install:
stage = 'install'
else:
stage = 'removal'
print(f'''Performing {stage} with:
destdir={destdir}
prefix={prefix}
sysconfdir={sysconfdir}
examplesdir={examplesdir}
mandir={mandir}
pythonbin={pythonbin}''')
# install files to prefix
processFiles(install, os.path.join(destdir, prefix[1:]), 'src/usr/', {
'bin/diffuse': [ (b"'../../etc/diffuserc'", repr(relpath(os.path.join(prefix, 'bin'), os.path.join(sysconfdir, 'diffuserc'))).encode()),
(b'/usr/bin/env python', pythonbin.encode()) ],
'share/applications/diffuse.desktop': None,
'share/diffuse/syntax/*.syntax': None,
'share/gnome/help/diffuse/*/diffuse.xml': [ (b'/usr/', prefix.encode()), (b'/etc/', sysconfdir.encode()) ],
'share/omf/diffuse/diffuse-*.omf': [ (b'/usr/', prefix.encode()) ],
'share/icons/hicolor/*/apps/diffuse.png': None
})
# install manual
processFiles(install, os.path.join(destdir, mandir[1:]), 'src/usr/share/man/', {
'man1/diffuse.1': [ (b'/usr/', prefix.encode()), (b'/etc/', sysconfdir.encode()) ],
'*/man1/diffuse.1': [ (b'/usr/', prefix.encode()), (b'/etc/', sysconfdir.encode()) ]
})
# install files to sysconfdir
processFiles(install, os.path.join(destdir, examplesdir[1:]), 'src/etc/', { 'diffuserc': [ (b'/etc/', sysconfdir.encode()),
(b'../usr', relpath(sysconfdir, prefix).encode()) ] })
# install translations
processTranslations(install, os.path.join(destdir, prefix[1:]))
if not install:
# remove directories we own
dirs_to_remove = [
'share/omf/diffuse',
'share/gnome/help/diffuse/C',
'share/gnome/help/diffuse/cs',
'share/gnome/help/diffuse/it',
'share/gnome/help/diffuse/ru',
'share/gnome/help/diffuse',
'share/diffuse/syntax',
'share/diffuse'
]
for s in dirs_to_remove:
d = os.path.join(destdir, os.path.join(prefix, s)[1:])
try:
os.rmdir(d)
except OSError:
logError(f'Error removing "{d}".')
# do post install/removal tasks
if not files_only:
print(f'Performing post {stage} tasks.')
cmds = [ [ 'update-desktop-database' ],
[ 'gtk-update-icon-cache', os.path.join(destdir, os.path.join(prefix, 'share/icons/hicolor')[1:]) ] ]
if install:
cmds.append([ 'scrollkeeper-update', '-q', '-o', os.path.join(destdir, os.path.join(prefix, 'share/omf/diffuse')[1:]) ])
else:
cmds.append([ 'scrollkeeper-update', '-q' ])
for c in cmds:
for p in os.environ['PATH'].split(os.pathsep):
if os.path.exists(os.path.join(p, c[0])):
print(' '.join(c))
try:
if subprocess.Popen(c).wait() != 0:
raise OSError()
except OSError:
logError(f'WARNING: Failed to update documentation database with {c[0]}.')
break
else:
print(f'WARNING: {c[0]} is not installed')

28
meson.build Normal file
View File

@ -0,0 +1,28 @@
project('diffuse',
version: '0.6.0',
meson_version: '>= 0.50',
license: 'GPL-2.0-or-later')
i18n = import('i18n')
python = import('python')
py_installation = python.find_installation('python3')
find_program('gtk-update-icon-cache', required: false)
prefix = get_option('prefix')
bindir = prefix / get_option('bindir')
datadir = prefix / get_option('datadir')
localedir = prefix / get_option('localedir')
libexecdir = prefix / get_option('libexecdir')
sysconfdir = prefix / get_option('sysconfdir')
pythondir = py_installation.get_path('purelib')
pkgdatadir = join_paths(datadir, meson.project_name())
podir = join_paths(meson.source_root(), 'translations')
subdir('translations')
subdir('src')
meson.add_install_script('build-scripts/meson-postinstall.py')

View File

@ -2,4 +2,4 @@
#
# Copyright (C) 2006-2009 Derrick Moser <derrick_moser@yahoo.com>
import ../usr/share/diffuse/syntax/*.syntax
import @PKGDATADIR@/syntax/*.syntax

26
src/meson.build Normal file
View File

@ -0,0 +1,26 @@
# Diffuse binary file
diffuse_conf = configuration_data()
diffuse_conf.set('SYSCONFIGDIR', sysconfdir)
configure_file(
input: 'usr/bin/diffuse.py.in',
output: 'diffuse',
configuration: diffuse_conf,
install: true,
install_dir: bindir
)
# Diffuse config file
diffuserc_conf = configuration_data()
diffuserc_conf.set('PKGDATADIR', pkgdatadir)
configure_file(
input: 'etc/diffuserc.py.in',
output: 'diffuserc',
configuration: diffuserc_conf,
install: true,
install_dir: sysconfdir
)
# Data files
install_subdir('usr/share', install_dir: datadir, strip_directory: true)

View File

@ -8345,7 +8345,7 @@ if __name__ == '__main__':
if isWindows():
rc_file = os.path.join(bin_dir, 'diffuserc')
else:
rc_file = os.path.join(bin_dir, '../../etc/diffuserc')
rc_file = os.path.join(bin_dir, '@SYSCONFIGDIR@/diffuserc')
for rc_file in rc_file, os.path.join(rc_dir, 'diffuserc'):
if os.path.isfile(rc_file):
rc_files.append(rc_file)

14
translations/LINGUAS Normal file
View File

@ -0,0 +1,14 @@
# Please keep this list sorted alphabetically
cs
de
es
it
ja
ko
pl
pt
ru
sv
th
zh_CN
zh_TW

1
translations/meson.build Normal file
View File

@ -0,0 +1 @@
i18n.gettext(meson.project_name(), preset: 'glib')