-
Notifications
You must be signed in to change notification settings - Fork 3
/
sequencer
executable file
·304 lines (256 loc) · 11.2 KB
/
sequencer
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###############################################################################
# Copyright (C) Bull S.A.S (2010, 2011)
# Contributor: Pierre Vignéras <pierre.vigneras@bull.net>
#
# 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 3 of the License, 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; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
###############################################################################
"""
Command Line Interface (CLI) of the sequencer.
This command is the front end for the sequencer.
It basically provides the command line interface, parse options and
arguments and then call the sequencer API.
The sequencer is made of three stages:
1. Dependency Graph Maker (DGM): it creates the dependency graph according
to input.
2. Instructions Sequence Maker (ISM): it creates a sequence from the
previously created dependency graph
3. Instructions Sequence Executor (ISE): it executes the instructions
sequence specified.
Each stage as its own CLI that is wrapped by the sequencer command.
Finally, those three stages can be chained and executed directly.
"""
from __future__ import print_function
import ConfigParser
import logging
import optparse
import os, sys
import resource
import sequencer
from sequencer.tracer import init_trace
from sequencer.chain import cli as chain_cli
from sequencer.commons import get_package_name, get_version, get_basedir, get_lastcommit
from sequencer.dgm import cli as dgm_cli
from sequencer.dgm.db import SequencerFileDB
from sequencer.ise import cli as ise_cli
from sequencer.ism import cli as ism_cli
__author__ = "Pierre Vigneras"
__copyright__ = "Copyright (c) 2010 Bull S.A.S."
__credits__ = ["Pierre Vigneras"]
__version__ = get_version()
# Use the module name instead of a hardwired string in case the module
# name change
_logger = logging.getLogger(sequencer.__name__)
def tab(count=1):
"""
Define 'count' tabulations.
"""
return ' ' * (4 * count)
def add2path(path):
"""
Add the given path to this process PATH.
"""
if path is not None and len(path) > 0:
_logger.info("Including %s in current $PATH", path)
os.environ['PATH'] = os.environ['PATH'] + ":" + path
def _change_dir(option, opt_str, value, parser, *args):
"""
Callback handler used during options parsing when the dir
option has been specified.
This callback stores the directory specified and then call
_update_usage().
"""
assert len(args) == 1
usage_parms = args[0]
usage_parms['dir'] = value
_update_usage(parser, usage_parms)
def _change_base(option, opt_str, value, parser, *args):
"""
Callback handler used during options parsing when the base
option has been specified. This callback stores the directory
specified and then call _update_usage().
"""
assert len(args) == 1
usage_parms = args[0]
usage_parms['base'] = value
_update_usage(parser, usage_parms)
def _update_usage(parser, usage_parms):
"""
Update the usage with rulesets found in dir/base/*.rs
"""
basedir = os.path.join(usage_parms['dir'], usage_parms['base'])
db = SequencerFileDB(basedir)
usage_data_for = {}
shortcut_usage = []
rules_map = db.get_rules_map()
# Ruleset are shortcut for 'chain ruleset'
shortcuts = rules_map.keys()
for action in shortcuts:
usage_line = "\t%s" % action
shortcut_usage.append(usage_line)
usage_data_for[action] = {'doc': None, 'main':chain_cli.chain}
normal_usage = _get_normal_usage(usage_data_for)
cmd_name = os.path.basename(sys.argv[0])
usage = "%prog [global_options]" + \
" <action> [action_options] <action parameters>\n" + \
"\n" + tab() + "<normal actions>:\n" + \
"\n".join(sorted(normal_usage)) + \
"\n\n" + tab() + "<shortcut actions (equivalent to " + \
"'chain <shortcut>')>:\n" + \
"\n".join(sorted(shortcut_usage))
doc = "Use --help for the global help. Use <action> --help for" + \
" the specified action help."
parser.usage = usage
parser.description = doc
usage_parms['config'] = _get_config_from(basedir)
usage_parms['db'] = db
usage_parms['data'] = usage_data_for
usage_parms['shortcuts'] = shortcuts
def _get_normal_usage(usage_data_for):
"""
Returns the normal sequencer usage taken from each sequencer layer cli.
"""
normal_usage = []
for cli in [dgm_cli, ism_cli, ise_cli, chain_cli]:
usage_data = cli.get_usage_data()
for action_name in usage_data:
if action_name in usage_data_for:
raise ValueError("Ouch! Duplicate action name found: %s" %\
action_name)
usage_line = "\t%s: %s" %\
(action_name, usage_data[action_name]['doc'])
normal_usage.append(usage_line)
usage_data_for[action_name] = usage_data[action_name]
return normal_usage
def _get_config_from(basedir):
"""
Returns the ConfigParser instance from given 'basedir'
"""
config = ConfigParser.SafeConfigParser({'db_name':None,
'filter_path':None,
'depsfinder_path':None,
'action_path':None,
'guesser.module.name':'sequencer.guesser',
'guesser.factory.params': None,
'algo':'optimal',
'report':'none',
'fanout':'64',
'docache':'yes',
'doexec':'yes',
'dostats':'no',
'progress':'0.0',
})
config.add_section(dgm_cli.DEPMAKE_ACTION_NAME)
config.add_section(ism_cli.SEQMAKE_ACTION_NAME)
config.add_section(ise_cli.SEQEXEC_ACTION_NAME)
config_file = os.path.join(basedir, "config")
_logger.debug("Reading configuration file: %s", config_file)
config.read(config_file)
return config
def main():
"""
The main! ;-)
"""
parser = optparse.OptionParser()
parser.disable_interspersed_args()
usage_parms = dict()
usage_parms['base'] = ''
usage_parms['dir'] = get_basedir()
usage_parms['data'] = dict()
_update_usage(parser, usage_parms)
parser.add_option('-d', '--dir', dest="dir", type='string', nargs=1,
default=get_basedir(),
action="callback", callback=_change_dir,
callback_args = (usage_parms,),
help='Specify the configuration directory.' + \
' Default: %default.')
parser.add_option('-b', '--base', dest='base', type='string', nargs=1,
default='',
action="callback", callback=_change_base,
callback_args = (usage_parms,),
help = 'Specify a BASE directory relative to DIR' + \
' where rulesets should be found.')
parser.add_option("-v", "--verbose", dest="verbose",
action='store_true', default=False,
help="Display all level messages on output" + \
" except DEBUG level.")
parser.add_option("-q", "--quiet", dest="quiet",
action='store_true', default=False,
help="Display only WARNING, ERROR, CRITICAL" + \
" level messages on output (standard error).")
parser.add_option("-D", "--Debug", dest="debug",
action='store_true', default=False,
help="Display all level messages on output.")
parser.add_option("-V", "--Version", dest="version",
action="store_true", default=False,
help="Display name, version and copyright.")
parser.add_option("-l", "--log", dest="log", type='string', nargs=1,
metavar='FILE[:LEVEL]',
help="Log all messages above LEVEL to the given FILE." + \
" (LEVEL=DEBUG if not specified)")
(options, args) = parser.parse_args()
config = usage_parms['config']
db = usage_parms['db']
usage_data_for = usage_parms['data']
shortcuts = usage_parms['shortcuts']
init_trace(options)
if (options.version):
_logger.output(get_package_name() + " " + get_version() + \
"\nCopyright (C) 2009 Bull S. A. S.\n" + \
"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" + \
"This is free software: you are free to change and redistribute it.\n" + \
"There is NO WARRANTY, to the extent permitted by law.\n\n" + \
"Originally written by Pierre Vignéras")
_logger.info("Last commit: %s", get_lastcommit())
exit(0)
if len(args) < 1:
parser.error("main: wrong number of arguments.")
# Setting new limits on number of processes and number of open files
# to the maximum available (i.e. '-1').
nproc_hard_limit = resource.getrlimit(resource.RLIMIT_NPROC)[1]
nofile_hard_limit = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
_logger.info("Setting nproc and nofile to their hard limits: %d, %d",
nproc_hard_limit, nofile_hard_limit)
resource.setrlimit(resource.RLIMIT_NPROC, (nproc_hard_limit,
nproc_hard_limit))
resource.setrlimit(resource.RLIMIT_NOFILE, (nofile_hard_limit,
nofile_hard_limit))
filter_path = config.get(dgm_cli.DEPMAKE_ACTION_NAME, 'filter_path')
depsfinder_path = config.get(dgm_cli.DEPMAKE_ACTION_NAME, 'depsfinder_path')
action_path = config.get(ise_cli.SEQEXEC_ACTION_NAME, 'action_path')
# Currently, we just modify this process PATH with the path given
# by the configuration file (if provided). This will be forwarded
# to children.
# A better option would be to use the given path only for the given
# call (filter_path for filters, depsfinder_path for depsfinders).
add2path(filter_path)
add2path(depsfinder_path)
add2path(action_path)
action = args[0]
try:
data = usage_data_for[action]
# Remove the action name if it is not a shortcut
if action not in shortcuts:
params = args[1:]
else:
params = args
except KeyError:
parser.error("Unknown action: %s" % args[0])
main = data['main']
rc = main(db, config, params)
exit(rc)
if __name__ == "__main__":
main()