forked from mysql/mysql-utilities
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.py
434 lines (365 loc) · 13.9 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
#!/usr/bin/env python
#
# Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
#
# 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; version 2 of the License.
#
# 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; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
"""Setup script for MySQL Utilities"""
from __future__ import absolute_import
import ConfigParser
import fnmatch
import os
import platform
import sys
import distutils.core
from distutils.core import setup
from distutils.command.build_scripts import build_scripts as _build_scripts
from distutils.command.install import install as _install
from distutils.command.install_data import install_data as _install_data
from distutils.util import change_root
from distutils.file_util import DistutilsFileError
from distutils import log, dir_util
from info import META_INFO, INSTALL
# Check required Python version
if sys.version_info[0:2] not in [(2, 6), (2, 7)]:
log.error("MySQL Utilities requires Python v2.6 or v2.7")
sys.exit(1)
COMMANDS = {
'cmdclass': {
},
}
# Custom DistUtils command
try:
from internal.packaging.commands import (dist_deb, dist_rpm, bdist,
build, sdist)
except ImportError:
pass # Use default when not available
else:
COMMANDS['cmdclass'].update({
'sdist': sdist.GenericSourceGPL,
'build': build.Build,
'sdist_com': sdist.SourceCommercial,
'bdist_com': bdist.BuiltCommercial,
'bdist_deb': dist_deb.BuildDistDebian,
'bdist_com_deb': dist_deb.BuildCommercialDistDebian,
'bdist_rpm': dist_rpm.BuiltDistRPM,
'sdist_rpm': dist_rpm.SourceRPM,
'bdist_com_rpm': dist_rpm.BuiltCommercialRPM,
})
if platform.uname()[0] == 'Darwin':
try:
from internal.packaging.commands.dist_osx import (
BuildDistOSX,
BuildDistOSXcom
)
except ImportError:
pass
else:
COMMANDS['cmdclass'].update({
'bdist_osx': BuildDistOSX,
'bdist_com_osx': BuildDistOSXcom
})
if platform.uname()[0] == 'SunOS':
try:
from internal.packaging.commands.dist_solaris import (
BuildDistSunOS,
BuildDistSunOScom
)
except ImportError:
pass
else:
COMMANDS['cmdclass'].update({
'bdist_sunos': BuildDistSunOS,
'bdist_com_sunos': BuildDistSunOScom
})
ARGS = {
}
PROFILE_SCRIPT = '''
prepend_path () (
IFS=':'
for D in $PATH; do
if test x$D != x$1; then
OUTPATH="${OUTPATH:+$OUTPATH:}$D"
fi
done
echo "$1:$OUTPATH"
)
PATH=`prepend_path %s`
'''
class install(_install):
"""Install MySQL Utilities"""
user_options = _install.user_options + [
("skip-profile", None, "Skip installing a profile script"),
]
skip_profile = None
boolean_options = _install.boolean_options + ['skip-profile']
def initialize_options(self):
"""Initialize options"""
_install.initialize_options(self)
self.skip_profile = False
def finalize_options(self):
"""Finalize options"""
_install.finalize_options(self)
def run(self):
_install.run(self)
class install_man(distutils.core.Command):
"""install_man"""
description = "Install Unix manual pages"
root = None
prefix = None
record = None
_outfiles = []
user_options = [
('prefix=', None, 'installation prefix (default /usr/share/man)'),
('root=', None,
"install everything relative to this alternate root directory"),
('record=', None,
"filename in which to record list of installed files"),
]
def initialize_options(self):
"""Initialize options"""
pass
def finalize_options(self):
"""Finalize options"""
self.set_undefined_options('install',
('root', 'root'),
('record', 'record'))
if not self.prefix:
self.prefix = '/usr/share/man'
if self.root:
self.prefix = change_root(self.root, self.prefix)
def run(self):
"""Run the command"""
srcdir = os.path.join('docs', 'man')
manpages = os.listdir(srcdir)
self._outfiles = []
for man in manpages:
src_man = os.path.join(srcdir, man)
section = os.path.splitext(man)[1][1:]
dest_dir = os.path.join(self.prefix, 'man' + section)
self.mkpath(dest_dir) # Could be different section
dest_man = os.path.join(dest_dir, man)
self.copy_file(src_man, dest_man)
self._outfiles.append(dest_man)
# Disabled, done in the RPM spec
# self._write_record()
def _write_record(self):
"""Write list of installed files"""
if self.record:
outputs = self.get_outputs()
if self.root: # strip any package prefix
root_len = len(self.root)
for counter in xrange(len(outputs)):
outputs[counter] = outputs[counter][root_len:]
log.info("writing list of installed files to '{0}'".format(
self.record))
f = open(self.record, "a")
for line in outputs:
f.write(line + "\n")
def get_outputs(self):
"""Returns the list of installed files"""
return self._outfiles
class install_scripts(_install):
"""Install MySQL Utilities scripts"""
description = "Install the Shell Profile (Linux/Unix)"
skip_profile = False
install_dir = None
user_options = _install.user_options + [
('root=', None,
"install everything relative to this alternate root directory"),
]
boolean_options = _install.boolean_options + ['skip-profile']
profile_filename = 'mysql-utilities.sh'
profile_d_dir = '/etc/profile.d/'
def initialize_options(self):
"""initialize options"""
_install.initialize_options(self)
self.skip_profile = False
self.root = None
self.install_dir = None
def finalize_options(self):
"""Finalize options"""
_install.finalize_options(self)
self.set_undefined_options('install',
('install_dir', 'install_dir'),
('root', 'root'))
def _create_shell_profile(self):
"""Creates and installes the shell profile
This method will create and try to install the shell
profile file under /etc/profile.d/. It will skip this
step when the --skip-profile install option has been
given, or when the user installing MySQL Utilities
has no permission.
"""
if self.skip_profile:
log.info("Not adding shell profile %s (skipped)" % (
os.path.join(self.profile_d_dir, self.profile_filename)))
return
if self.root:
profile_dir = change_root(self.root, self.profile_d_dir)
else:
profile_dir = self.profile_d_dir
try:
dir_util.mkpath(profile_dir)
except DistutilsFileError as err:
log.info("Not installing mysql-utilities.sh: {0}".format(err))
self.skip_profile = True
return
destfile = os.path.join(profile_dir, self.profile_filename)
if not os.access(os.path.dirname(destfile), os.X_OK | os.W_OK):
log.info("Not installing mysql-utilities.sh in "
"{folder} (no permission)".format(folder=destfile))
self.skip_profile = True
return
if os.path.exists(os.path.dirname(destfile)):
if os.path.isdir(destfile) and not os.path.islink(destfile):
dir_util.remove_tree(destfile)
elif os.path.exists(destfile):
log.info("Removing {filename}".format(filename=destfile))
os.unlink(destfile)
script = PROFILE_SCRIPT % (self.install_dir,)
log.info("Writing {filename}".format(filename=destfile))
open(destfile, "w+").write(script)
def run(self):
"""Run the command"""
self._create_shell_profile()
def get_outputs(self):
"""Get installed files"""
outputs = _install.get_outputs(self)
return outputs
class build_scripts(_build_scripts):
"""Class for providing a customized version of build_scripts.
When ``run`` is called, this command class will:
1. Create a copy of all ``.py`` files in the **scripts** option
that does not have the ``.py`` extension.
2. Replace the list in the **scripts** attribute with a list
consisting of the script files with the ``.py`` extension
removed.
3. Call run method in `distutils.command.build_scripts`.
4. Restore the scripts list to the old value, for other commands
to use."""
outfiles = []
scripts = []
def run(self):
if not self.scripts:
return
saved_scripts = self.scripts
self.scripts = []
for script in saved_scripts:
script = distutils.util.convert_path(script)
script_copy, script_ext = os.path.splitext(script)
if script_ext != '.py':
log.debug("Not removing extension from {script} "
"since it's not '.py'".format(script=script))
else:
log.debug("Copying {orig} -> {dest}".format(
orig=script, dest=script_copy))
self.copy_file(script, script_copy)
self.scripts.append(script_copy)
# distutils is compatible with 2.1 so we cannot use super() to
# call it.
_build_scripts.run(self)
self.outfiles = self.scripts
self.scripts = saved_scripts
def get_outputs(self):
"""Get installed files"""
return self.outfiles
COMMANDS['cmdclass'].update({
'install': install,
})
# We need to edit the configuration file before installing it
class install_data(_install_data):
"""Install data and edits the configuration file before installing it"""
user = None
home = None
data_files = None
def initialize_options(self):
_install_data.initialize_options(self)
def finalize_options(self):
self.set_undefined_options('install',
('user', 'user'),
('home', 'home'))
_install_data.finalize_options(self)
def run(self):
from itertools import groupby
if not self.data_files:
log.info("no data files to install")
return
# Set up paths to write to config file
install_dir = self.install_dir
install_logdir = '/var/log'
if self.user or self.home:
install_sysconfdir = os.path.join(install_dir, 'etc')
elif os.name == 'posix' and install_dir in ('/', '/usr'):
install_sysconfdir = '/etc'
elif os.name == 'nt':
install_sysconfdir = os.path.join(install_dir, 'etc')
else:
install_sysconfdir = '/etc'
# Go over all entries in data_files and process it if needed
new_data_files = []
for df in self.data_files:
# Figure out what the entry contain and collect a list of files.
if isinstance(df, str):
# This was just a file name, so it will be installed
# in the install_dir location. This is a copy of the
# behaviour inside distutils intall_data.
directory = install_dir
filenames = [df]
else:
directory = df[0]
filenames = df[1]
# Process all the files for the entry and build a list of
# tuples (directory, file)
data_files = []
for filename in filenames:
# It was a config file template, add install
# directories to the config file.
if fnmatch.fnmatch(filename, 'data/*.cfg.in'):
config = ConfigParser.RawConfigParser({
'prefix': '', # install_dir,
'logdir': install_logdir,
'sysconfdir': install_sysconfdir,
})
config.readfp(open(filename))
filename = os.path.splitext(filename)[0]
config.write(open(filename, "w"))
# change directory 'fabric' to mysql
directory = os.path.join(install_sysconfdir, 'mysql')
data_files.append((directory, filename))
new_data_files.extend(data_files)
# Re-construct the data_files entry from what was provided by
# merging all tuples with same directory and provide a list of
# files as second item, e.g.:
# [('foo', 1), ('bar', 2), ('foo', 3), ('foo', 4), ('bar', 5)]
# --> [('bar', [2, 5]), ('foo', [1, 3, 4])]
data_files.sort()
data_files = [
(d, [f[1] for f in fs]) for d, fs in groupby(new_data_files,
key=lambda x: x[0])
]
self.data_files = data_files
_install_data.run(self)
COMMANDS['cmdclass'].update({
'install_data': install_data,
})
if os.name != "nt":
COMMANDS['cmdclass'].update({
'build_scripts': build_scripts,
'install_man': install_man,
})
ARGS.update(META_INFO)
ARGS.update(INSTALL)
ARGS.update(COMMANDS)
setup(**ARGS)