blob: 235851c15535cd12656de954115140d66f46d35d [file] [log] [blame]
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -06001#!/usr/bin/env python3
2#
Jon Ashburn23d36b12016-02-02 17:47:28 -07003# Copyright (c) 2015-2016 The Khronos Group Inc.
4# Copyright (c) 2015-2016 Valve Corporation
5# Copyright (c) 2015-2016 LunarG, Inc.
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -06006#
Jon Ashburn23d36b12016-02-02 17:47:28 -07007# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and/or associated documentation files (the "Materials"), to
9# deal in the Materials without restriction, including without limitation the
10# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11# sell copies of the Materials, and to permit persons to whom the Materials are
12# furnished to do so, subject to the following conditions:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060013#
Jon Ashburn23d36b12016-02-02 17:47:28 -070014# The above copyright notice(s) and this permission notice shall be included in
15# all copies or substantial portions of the Materials.
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060016#
Jon Ashburn23d36b12016-02-02 17:47:28 -070017# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060018# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jon Ashburn23d36b12016-02-02 17:47:28 -070019# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20#
21# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
24# USE OR OTHER DEALINGS IN THE MATERIALS.Copyright (C) 2015 Valve Corporation
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060025#
Courtney Goeltzenleuchter05559522015-10-30 11:14:30 -060026# Author: Jon Ashburn <jon@lunarg.com>
27#
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060028
29import os, sys
30
31# add main repo directory so vulkan.py can be imported. This needs to be a complete path.
32ld_path = os.path.dirname(os.path.abspath(__file__))
33main_path = os.path.abspath(ld_path + "/../")
34sys.path.append(main_path)
35
36import vulkan
37
38def generate_get_proc_addr_check(name):
39 return " if (!%s || %s[0] != 'v' || %s[1] != 'k')\n" \
40 " return NULL;" % ((name,) * 3)
41
42class Subcommand(object):
43 def __init__(self, argv):
44 self.argv = argv
45 self.headers = vulkan.headers
46 self.protos = vulkan.protos
47
48 def run(self):
49 print(self.generate())
50
Jon Ashburn82974f42015-05-05 09:37:01 -060051 def _requires_special_trampoline_code(self, name):
52 # Dont be cute trying to use a general rule to programmatically populate this list
53 # it just obsfucates what is going on!
Ian Elliott7e40db92015-08-21 15:09:33 -060054 wsi_creates_dispatchable_object = ["CreateSwapchainKHR"]
Chia-I Wu3432a0c2015-10-27 18:04:07 +080055 creates_dispatchable_object = ["CreateDevice", "GetDeviceQueue", "AllocateCommandBuffers"] + wsi_creates_dispatchable_object
Jon Ashburn82974f42015-05-05 09:37:01 -060056 if name in creates_dispatchable_object:
57 return True
58 else:
59 return False
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060060
Jon Ashburn82974f42015-05-05 09:37:01 -060061 def _is_loader_non_trampoline_entrypoint(self, proto):
Jon Ashburn8d1b0b52015-05-18 13:20:15 -060062 if proto.name in ["GetDeviceProcAddr", "EnumeratePhysicalDevices", "EnumerateLayers", "DbgRegisterMsgCallback", "DbgUnregisterMsgCallback", "DbgSetGlobalOption", "DestroyInstance"]:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060063 return True
64 return not self.is_dispatchable_object_first_param(proto)
65
66
67 def is_dispatchable_object_first_param(self, proto):
68 in_objs = proto.object_in_params()
69 non_dispatch_objs = []
70 param0 = proto.params[0]
71 return (len(in_objs) > 0) and (in_objs[0].ty == param0.ty) and (param0.ty not in non_dispatch_objs)
72
73 def generate(self):
74 copyright = self.generate_copyright()
75 header = self.generate_header()
76 body = self.generate_body()
77 footer = self.generate_footer()
78
79 contents = []
80 if copyright:
81 contents.append(copyright)
82 if header:
83 contents.append(header)
84 if body:
85 contents.append(body)
86 if footer:
87 contents.append(footer)
88
89 return "\n\n".join(contents)
90
91 def generate_copyright(self):
92 return """/* THIS FILE IS GENERATED. DO NOT EDIT. */
93
94/*
Jon Ashburn23d36b12016-02-02 17:47:28 -070095 * Copyright (c) 2015-2016 The Khronos Group Inc.
96 * Copyright (c) 2015-2016 Valve Corporation
97 * Copyright (c) 2015-2016 LunarG, Inc.
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -060098 *
Jon Ashburn23d36b12016-02-02 17:47:28 -070099 * Permission is hereby granted, free of charge, to any person obtaining a copy
100 * of this software and/or associated documentation files (the "Materials"), to
101 * deal in the Materials without restriction, including without limitation the
102 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
103 * sell copies of the Materials, and to permit persons to whom the Materials are
104 * furnished to do so, subject to the following conditions:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600105 *
Jon Ashburn23d36b12016-02-02 17:47:28 -0700106 * The above copyright notice(s) and this permission notice shall be included in
107 * all copies or substantial portions of the Materials.
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600108 *
Jon Ashburn23d36b12016-02-02 17:47:28 -0700109 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600110 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jon Ashburn23d36b12016-02-02 17:47:28 -0700111 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
112 *
113 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
114 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
115 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
116 * USE OR OTHER DEALINGS IN THE MATERIALS.
Courtney Goeltzenleuchter05559522015-10-30 11:14:30 -0600117 *
118 * Author: Jon Ashburn <jon@lunarg.com>
Jon Ashburnfc1031e2015-11-17 15:31:02 -0700119 * Author: Chia-I Wu <olv@lunarg.com>
120 * Author: Courtney Goeltzenleuchter <courtney@lunarg.com>
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600121 */"""
122
123 def generate_header(self):
124 return "\n".join(["#include <" + h + ">" for h in self.headers])
125
126 def generate_body(self):
127 pass
128
129 def generate_footer(self):
130 pass
131
Jon Ashburnfc1031e2015-11-17 15:31:02 -0700132class DevExtTrampolineSubcommand(Subcommand):
133 def generate_header(self):
134 lines = []
135 lines.append("#include \"vk_loader_platform.h\"")
136 lines.append("#include \"loader.h\"")
137 lines.append("#if defined(__linux__)")
138 lines.append("#pragma GCC optimize(3) // force gcc to use tail-calls")
139 lines.append("#endif")
140 return "\n".join(lines)
141
142 def generate_body(self):
143 lines = []
144 for i in range(250):
145 lines.append('\nVKAPI_ATTR void VKAPI_CALL vkDevExt%s(VkDevice device)' % i)
146 lines.append('{')
147 lines.append(' const struct loader_dev_dispatch_table *disp;')
148 lines.append(' disp = loader_get_dev_dispatch(device);')
149 lines.append(' disp->ext_dispatch.DevExt[%s](device);' % i)
150 lines.append('}')
151 lines.append('')
152 lines.append('void *loader_get_dev_ext_trampoline(uint32_t index)')
153 lines.append('{')
154 lines.append(' switch (index) {')
155 for i in range(250):
156 lines.append(' case %s:' % i)
157 lines.append(' return vkDevExt%s;' % i)
158 lines.append(' }')
159 lines.append(' return NULL;')
160 lines.append('}')
161 return "\n".join(lines)
162
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600163class LoaderEntrypointsSubcommand(Subcommand):
164 def generate_header(self):
165 return "#include \"loader.h\""
166
167 def _generate_object_setup(self, proto):
168 method = "loader_init_dispatch"
169 cond = "res == VK_SUCCESS"
Jon Ashburn82974f42015-05-05 09:37:01 -0600170 setup = []
171
172 if not self._requires_special_trampoline_code(proto.name):
173 return setup
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600174
175 if "Get" in proto.name:
176 method = "loader_set_dispatch"
177
Ian Elliott7e40db92015-08-21 15:09:33 -0600178 if proto.name == "GetSwapchainInfoKHR":
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600179 ptype = proto.params[-3].name
180 psize = proto.params[-2].name
181 pdata = proto.params[-1].name
Ian Elliott7e40db92015-08-21 15:09:33 -0600182 cond = ("%s == VK_SWAP_CHAIN_INFO_TYPE_PERSISTENT_IMAGES_KHR && "
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600183 "%s && %s" % (ptype, pdata, cond))
Ian Elliott7e40db92015-08-21 15:09:33 -0600184 setup.append("VkSwapchainImageInfoKHR *info = %s;" % pdata)
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600185 setup.append("size_t count = *%s / sizeof(*info), i;" % psize)
186 setup.append("for (i = 0; i < count; i++) {")
187 setup.append(" %s(info[i].image, disp);" % method)
188 setup.append(" %s(info[i].memory, disp);" % method)
189 setup.append("}")
Jon Ashburn82974f42015-05-05 09:37:01 -0600190 else:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600191 obj_params = proto.object_out_params()
192 for param in obj_params:
193 setup.append("%s(*%s, disp);" % (method, param.name))
194
195 if setup:
196 joined = "\n ".join(setup)
197 setup = []
198 setup.append(" if (%s) {" % cond)
199 setup.append(" " + joined)
200 setup.append(" }")
201
202 return "\n".join(setup)
203
204 def _generate_loader_dispatch_entrypoints(self, qual=""):
205 if qual:
206 qual += " "
207
208 funcs = []
209 for proto in self.protos:
Jon Ashburn82974f42015-05-05 09:37:01 -0600210 if self._is_loader_non_trampoline_entrypoint(proto):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600211 continue
212 func = []
213
214 obj_setup = self._generate_object_setup(proto)
215
216 func.append(qual + proto.c_func(prefix="vk", attr="VKAPI"))
217 func.append("{")
218
219 # declare local variables
220 func.append(" const VkLayerDispatchTable *disp;")
221 if proto.ret != 'void' and obj_setup:
222 func.append(" VkResult res;")
223 func.append("")
224
225 # get dispatch table
226 func.append(" disp = loader_get_dispatch(%s);" %
227 proto.params[0].name)
228 func.append("")
229
230 # dispatch!
231 dispatch = "disp->%s;" % proto.c_call()
232 if proto.ret == 'void':
233 func.append(" " + dispatch)
234 elif not obj_setup:
235 func.append(" return " + dispatch)
236 else:
237 func.append(" res = " + dispatch)
238 func.append(obj_setup)
239 func.append("")
240 func.append(" return res;")
241
242 func.append("}")
243
244 funcs.append("\n".join(func))
245
246 return "\n\n".join(funcs)
247
248 def generate_body(self):
249 body = [self._generate_loader_dispatch_entrypoints("LOADER_EXPORT")]
250
251 return "\n\n".join(body)
252
253class DispatchTableOpsSubcommand(Subcommand):
254 def run(self):
255 if len(self.argv) != 1:
256 print("DispatchTableOpsSubcommand: <prefix> unspecified")
257 return
258
259 self.prefix = self.argv[0]
260 super().run()
261
262 def generate_header(self):
David Pinedo9316d3b2015-11-06 12:54:48 -0700263 return "\n".join(["#include <vulkan/vulkan.h>",
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600264 "#include <vkLayer.h>",
265 "#include <string.h>",
266 "#include \"loader_platform.h\""])
267
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600268 def _generate_init(self, type):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600269 stmts = []
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600270 func = []
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600271 if type == "device":
272 for proto in self.protos:
273 if self.is_dispatchable_object_first_param(proto) or proto.name == "CreateInstance":
274 stmts.append("table->%s = (PFN_vk%s) gpa(gpu, \"vk%s\");" %
275 (proto.name, proto.name, proto.name))
276 else:
277 stmts.append("table->%s = vk%s; /* non-dispatchable */" %
278 (proto.name, proto.name))
279 func.append("static inline void %s_init_device_dispatch_table(VkLayerDispatchTable *table,"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600280 % self.prefix)
Jon Ashburn8d1b0b52015-05-18 13:20:15 -0600281 func.append("%s PFN_vkGetDeviceProcAddr gpa,"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600282 % (" " * len(self.prefix)))
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600283 func.append("%s VkPhysicalDevice gpu)"
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600284 % (" " * len(self.prefix)))
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600285 else:
286 for proto in self.protos:
287 if proto.params[0].ty != "VkInstance" and proto.params[0].ty != "VkPhysicalDevice":
288 continue
289 stmts.append("table->%s = vk%s;" % (proto.name, proto.name))
290 func.append("static inline void %s_init_instance_dispatch_table(VkLayerInstanceDispatchTable *table)"
291 % self.prefix)
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600292 func.append("{")
293 func.append(" %s" % "\n ".join(stmts))
294 func.append("}")
295
296 return "\n".join(func)
297
298 def _generate_lookup(self):
299 lookups = []
300 for proto in self.protos:
301 if self.is_dispatchable_object_first_param(proto):
302 lookups.append("if (!strcmp(name, \"%s\"))" % (proto.name))
303 lookups.append(" return (void *) table->%s;"
304 % (proto.name))
305
306 func = []
307 func.append("static inline void *%s_lookup_dispatch_table(const VkLayerDispatchTable *table,"
308 % self.prefix)
309 func.append("%s const char *name)"
310 % (" " * len(self.prefix)))
311 func.append("{")
312 func.append(generate_get_proc_addr_check("name"))
313 func.append("")
314 func.append(" name += 2;")
315 func.append(" %s" % "\n ".join(lookups))
316 func.append("")
317 func.append(" return NULL;")
318 func.append("}")
319
320 return "\n".join(func)
321
322 def generate_body(self):
Jon Ashburnfbb4e252015-05-04 16:27:53 -0600323 body = [self._generate_init("device"),
324 self._generate_lookup(),
325 self._generate_init("instance")]
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600326
327 return "\n\n".join(body)
328
329class WinDefFileSubcommand(Subcommand):
330 def run(self):
331 library_exports = {
332 "all": [],
333 }
334
335 if len(self.argv) != 2 or self.argv[1] not in library_exports:
336 print("WinDefFileSubcommand: <library-name> {%s}" %
337 "|".join(library_exports.keys()))
338 return
339
340 self.library = self.argv[0]
341 self.exports = library_exports[self.argv[1]]
342
343 super().run()
344
345 def generate_copyright(self):
346 return """; THIS FILE IS GENERATED. DO NOT EDIT.
347
348;;;; Begin Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Jon Ashburn23d36b12016-02-02 17:47:28 -0700349; Copyright (c) 2015-2016 The Khronos Group Inc.
350; Copyright (c) 2015-2016 Valve Corporation
351; Copyright (c) 2015-2016 LunarG, Inc.
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600352;
Jon Ashburn23d36b12016-02-02 17:47:28 -0700353; Permission is hereby granted, free of charge, to any person obtaining a copy
354; of this software and/or associated documentation files (the "Materials"), to
355; deal in the Materials without restriction, including without limitation the
356; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
357; sell copies of the Materials, and to permit persons to whom the Materials are
358; furnished to do so, subject to the following conditions:
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600359;
Jon Ashburn23d36b12016-02-02 17:47:28 -0700360; The above copyright notice(s) and this permission notice shall be included in
361; all copies or substantial portions of the Materials.
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600362;
Jon Ashburn23d36b12016-02-02 17:47:28 -0700363; THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600364; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jon Ashburn23d36b12016-02-02 17:47:28 -0700365; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
366;
367; IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
368; DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
369; OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
370; USE OR OTHER DEALINGS IN THE MATERIALS.
371;
Courtney Goeltzenleuchterbdde0b22015-12-16 14:57:27 -0700372;
373; Author: Jon Ashburn <jon@lunarg.com>
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600374;;;; End Copyright Notice ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"""
375
376 def generate_header(self):
377 return "; The following is required on Windows, for exporting symbols from the DLL"
378
379 def generate_body(self):
380 body = []
381
382 body.append("LIBRARY " + self.library)
383 body.append("EXPORTS")
384
385 for proto in self.protos:
386 if self.exports and proto.name not in self.exports:
387 continue
Mark Lobodzinski3077be02015-11-24 14:03:18 -0700388# This was intended to reject WSI calls, but actually rejects ALL extensions
389# TODO: Make this WSI-extension specific
390# if proto.name.endswith("KHR"):
391# continue
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600392 body.append(" vk" + proto.name)
393
394 return "\n".join(body)
395
396class LoaderGetProcAddrSubcommand(Subcommand):
397 def run(self):
398 self.prefix = "vk"
399
400 # we could get the list from argv if wanted
401 self.intercepted = [proto.name for proto in self.protos]
402
403 for proto in self.protos:
Jon Ashburn8d1b0b52015-05-18 13:20:15 -0600404 if proto.name == "GetDeviceProcAddr":
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600405 self.gpa = proto
406
407 super().run()
408
409 def generate_header(self):
410 return "\n".join(["#include <string.h>"])
411
412 def generate_body(self):
413 lookups = []
414 for proto in self.protos:
415 if proto.name not in self.intercepted:
416 lookups.append("/* no %s%s */" % (self.prefix, proto.name))
417 continue
418
419 lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
420 lookups.append(" return (%s) %s%s;" %
421 (self.gpa.ret, self.prefix, proto.name))
422
423 special_lookups = []
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600424 for proto in self.protos:
Jon Ashburn82974f42015-05-05 09:37:01 -0600425 if self._is_loader_non_trampoline_entrypoint(proto) or self._requires_special_trampoline_code(proto.name):
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600426 special_lookups.append("if (!strcmp(name, \"%s\"))" % proto.name)
427 special_lookups.append(" return (%s) %s%s;" %
428 (self.gpa.ret, self.prefix, proto.name))
429 else:
430 continue
431 body = []
432 body.append("static inline %s globalGetProcAddr(const char *name)" %
433 self.gpa.ret)
434 body.append("{")
435 body.append(generate_get_proc_addr_check("name"))
436 body.append("")
437 body.append(" name += 2;")
438 body.append(" %s" % "\n ".join(lookups))
439 body.append("")
440 body.append(" return NULL;")
441 body.append("}")
442 body.append("")
443 body.append("static inline void *loader_non_passthrough_gpa(const char *name)")
444 body.append("{")
445 body.append(generate_get_proc_addr_check("name"))
446 body.append("")
447 body.append(" name += 2;")
448 body.append(" %s" % "\n ".join(special_lookups))
449 body.append("")
450 body.append(" return NULL;")
451 body.append("}")
452
453 return "\n".join(body)
454
455def main():
Mark Lobodzinski59816c72016-02-24 15:04:11 -0700456
457 wsi = {
458 "Win32",
459 "Android",
460 "Xcb",
461 "Xlib",
462 "Wayland",
463 "Mir"
464 }
465
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600466 subcommands = {
Jon Ashburnfc1031e2015-11-17 15:31:02 -0700467 "dev-ext-trampoline": DevExtTrampolineSubcommand,
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600468 "loader-entrypoints": LoaderEntrypointsSubcommand,
469 "dispatch-table-ops": DispatchTableOpsSubcommand,
470 "win-def-file": WinDefFileSubcommand,
471 "loader-get-proc-addr": LoaderGetProcAddrSubcommand,
472 }
473
Mark Lobodzinski59816c72016-02-24 15:04:11 -0700474 if len(sys.argv) < 3 or sys.argv[1] not in wsi or sys.argv[2] not in subcommands:
475 print("Usage: %s <wsi> <subcommand> [options]" % sys.argv[0])
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600476 print
Mark Lobodzinski59816c72016-02-24 15:04:11 -0700477 print("Available wsi (displayservers) are: %s" % " ".join(wsi))
478 print("Available subcommands are: %s" % " ".join(subcommands))
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600479 exit(1)
480
Mark Lobodzinski59816c72016-02-24 15:04:11 -0700481 subcmd = subcommands[sys.argv[2]](sys.argv[3:])
Jon Ashburn1dd0a5c2015-05-04 09:16:41 -0600482 subcmd.run()
483
484if __name__ == "__main__":
485 main()