1
2
3 """Module containing the commandline interface for the Maya Depdendency Parser"""
4
5 __all__ = None
6
7 import mrv
8 from mdepparse import *
9
10 from networkx.readwrite import gpickle
11
12 from itertools import chain
13 import getopt
14 import sys
15
16
17
18 -def main( fileList, **kwargs ):
19 """Called if this module is called directly, creating a file containing
20 dependency information
21
22 :param kwargs: will be passed directly to `createFromFiles`"""
23 return MayaFileGraph.createFromFiles( fileList, **kwargs )
24
25
27 sys.stdout.write("""bpython mdepparse.py [-shortflags ] [--longflags] file_to_parse.ma [file_to_parse, ...]
28
29 OUTPUT
30 ------
31 All actual information goes to stdout, everything else to stderr
32
33 EDIT
34 -----
35 -t Target file used to store the parsed dependency information
36 If not given, the command will automatically be in query mode.
37 The file format is simply a pickle of the underlying Networkx graph
38
39 -s Source dependency file previously written with -t. If specified, this file
40 will be read to quickly be read for queries. If not given, the information
41 will be parsed first. Thus it is recommended to have a first run storing
42 the dependencies and do all queries just reading in the dependencies using
43 -s
44
45 -i if given, a list of input files will be read from stdin. The tool will start
46 parsing the files as the come through the pipe
47
48 -a if given, all paths will be parsed from the input files. This will take longer
49 than just parsing references as the whole file needs to be read
50 TODO: actual implementation
51
52 --to-fs-map tokenmap
53 map one part of the path to another in order to make it a valid path
54 in the filesystem, i.e:
55 --to-fs-map source=target[=...]
56 --to-fs-map c:\\=/mnt/data/
57 sort it with the longest remapping first to assure no accidential matches.
58 Should be used if environment variables are used which are not set in the system
59 or if there are other path inconsistencies
60
61 --to-db-map tokenmap
62 map one part of the fs path previously remapped by --to-fs-map to a
63 more general one suitable to be a key in the dependency database.
64 The format is equal to the one used in --to-fs-map
65
66 -o output the dependency database as dot file at the given path, so it can
67 be read by any dot reader and interpreted that way.
68 If input arguments are given, only the affected portions of the database
69 will be available in the dot file. Also, the depths of the dependency information
70 is lost, thus there are only direct connections, although it might in
71 fact be a sub-reference.
72
73 QUERY
74 -----
75 All values returned in query mode will be new-line separated file paths
76 --affects retrieve all files that are affected by the input files
77 --affected-by retrieve all files that are affect the input files
78
79 -l if set, only leaf paths, thus paths being at the end of the chain
80 will be returned.
81 If not given, all paths, i.e. all intermediate references, will
82 be returned as well
83
84 -d int if not set, all references and subreferences will be retrieved
85 if 1, only direct references will be returned
86 if > 1, also sub[sub...] references will returned
87
88 -b if set and no input arg exists, return all bad or invalid files stored in the database
89 if an input argument is given, it acts as a filter and only returns
90 filepaths that are marked invalid
91
92 -e return full edges instead of only the successors/predecessors.
93 This allows tools to parse the output and make more sense of it
94 Will be ignored in nice mode
95
96 -n nice output, designed to be human-readable
97
98 -v enable more verbose output
99
100 """)
101 if msg:
102 sys.stdout.write(msg+"\n")
103
104
105 sys.exit( 1 )
106
107
109 """Return a function applying remapping as defined by tokenstring
110
111 :note: it also applies a mapping from mb to ma, no matter what.
112 Thus we currently only store .ma files as keys even though it might be mb files"""
113 tokens = tokenstring.split( "=" )
114 if len( tokens ) % 2 != 0:
115 raise ValueError( "Invalid map format: %s" % tokenstring )
116
117 remap_tuples = zip( tokens[0::2], tokens[1::2] )
118
119 def path_replace( f ):
120 for source, dest in remap_tuples:
121 f = f.replace( source, dest )
122 return f
123
124 return path_replace
125
126
127
128
129
130 if __name__ == "__main__":
131
132 try:
133 opts, rest = getopt.getopt( sys.argv[1:], "iat:s:ld:benvo:", [ "affects", "affected-by",
134 "to-fs-map=","to-db-map=" ] )
135 except getopt.GetoptError,e:
136 _usageAndExit( str( e ) )
137
138
139 if not opts and not rest:
140 _usageAndExit()
141
142 opts = dict( opts )
143 fromstdin = "-i" in opts
144
145
146
147 allpaths = "-a" in opts
148 kwargs_creategraph = dict( ( ( "parse_all_paths", allpaths ), ) )
149 kwargs_query = dict()
150
151
152
153
154
155
156 for kw,flag in ( "to_os_path","--to-fs-map" ),( "os_path_to_db_key", "--to-db-map" ):
157 if flag not in opts:
158 continue
159
160 remap_func = tokensToRemapFunc( opts.get( flag ) )
161 kwargs_creategraph[ kw ] = remap_func
162 kwargs_query[ kw ] = remap_func
163
164
165
166
167
168 filelist = rest
169 if fromstdin:
170 filelist = chain( sys.stdin, rest )
171
172
173 targetFile = opts.get( "-t", None )
174 sourceFile = opts.get( "-s", None )
175
176
177
178
179 graph = None
180 verbose = "-v" in opts
181
182 if not sourceFile:
183 graph = main( filelist, **kwargs_creategraph )
184 else:
185 if verbose:
186 sys.stdout.write("Reading dependencies from: %s\n" % sourceFile)
187 graph = gpickle.read_gpickle( sourceFile )
188
189
190
191
192
193
194 if targetFile:
195 if verbose:
196 sys.stdout.write("Saving dependencies to %s\n" % targetFile)
197 gpickle.write_gpickle( graph, targetFile )
198
199
200
201
202 return_invalid = "-b" in opts
203 depth = int( opts.get( "-d", -1 ) )
204 as_edge = "-e" in opts
205 nice_mode = "-n" in opts
206 dotgraph = None
207 dotOutputFile = opts.get( "-o", None )
208 kwargs_query[ 'invalid_only' ] = return_invalid
209
210 if dotOutputFile:
211 dotgraph = MayaFileGraph()
212
213 queried_files = False
214 for flag, direction in ( ( "--affects", MayaFileGraph.kAffects ),
215 ("--affected-by",MayaFileGraph.kAffectedBy ) ):
216 if not flag in opts:
217 continue
218
219
220 prune = lambda i,g: False
221 if "-l" in opts:
222 degreefunc = ( ( direction == MayaFileGraph.kAffects ) and MayaFileGraph.out_degree ) or MayaFileGraph.in_degree
223 prune = lambda i,g: degreefunc( g, i ) != 0
224
225 listcopy = list()
226
227
228
229 for filepath in filelist:
230 listcopy.append( filepath )
231 queried_files = True
232 filepath = filepath.strip()
233 depends = graph.depends( filepath, direction = direction, prune = prune,
234 visit_once=1, branch_first=1, depth=depth,
235 return_unresolved=0, **kwargs_query )
236
237
238 if not depends:
239 continue
240
241
242
243 if dotgraph is not None:
244 for dep in depends:
245 dotgraph.add_edge( ( filepath, dep ) )
246
247
248 if nice_mode:
249 depthstr = "unlimited"
250 if depth != -1:
251 depthstr = str( depth )
252
253 affectsstr = "is affected by: "
254 if direction == MayaFileGraph.kAffects:
255 affectsstr = "affects: "
256
257 headline = "\n%s ( depth = %s, invalid only = %i )\n" % ( filepath, depthstr, return_invalid )
258 sys.stdout.write( headline )
259 sys.stdout.write( "-" * len( headline ) + "\n" )
260
261 sys.stdout.write( affectsstr + "\n" )
262 sys.stdout.writelines( "\t - " + dep + "\n" for dep in depends )
263 else:
264 prefix = ""
265 if as_edge:
266 prefix = "%s->" % filepath
267 sys.stdout.writelines( ( prefix + dep + "\n" for dep in depends ) )
268
269
270
271
272 filelist = listcopy
273
274
275
276
277
278 if not queried_files and return_invalid:
279 invalidFiles = graph.invalidFiles()
280 sys.stdout.writelines( ( iv + "\n" for iv in invalidFiles ) )
281
282
283
284
285 if dotOutputFile:
286 if verbose:
287 sys.stdout.write("Saving dot file to %s\n" % dotOutputFile)
288 try:
289 import networkx.drawing.nx_pydot as pydot
290 except ImportError:
291 sys.stderr.write( "Required pydot module not installed" )
292 else:
293 if queried_files and dotgraph is not None:
294 pydot.write_dot( dotgraph, dotOutputFile )
295 else:
296 pydot.write_dot( graph, dotOutputFile )
297
298