[MD settings] moving attached() code
[chromium-blink-merge.git] / tools / generate_stubs / generate_stubs.py
blob7d2f91b8b67879dcd542a0e7d94e71db6e3f19d2
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Creates windows and posix stub files for a given set of signatures.
8 For libraries that need to be loaded outside of the standard executable startup
9 path mechanism, stub files need to be generated for the wanted functions. In
10 windows, this is done via "def" files and the delay load mechanism. On a posix
11 system, a set of stub functions need to be generated that dispatch to functions
12 found via dlsym.
14 This script takes a set of files, where each file is a list of C-style
15 signatures (one signature per line). The output is either a windows def file,
16 or a header + implementation file of stubs suitable for use in a posix system.
18 This script also handles varidiac functions, e.g.
19 void printf(const char* s, ...);
21 TODO(hclam): Fix the situation for varidiac functions.
22 Stub for the above function will be generated and inside the stub function it
23 is translated to:
24 void printf(const char* s, ...) {
25 printf_ptr(s, (void*)arg1);
28 Only one argument from the varidiac arguments is used and it will be used as
29 type void*.
30 """
32 __author__ = 'ajwong@chromium.org (Albert J. Wong)'
34 import optparse
35 import os
36 import re
37 import string
38 import subprocess
39 import sys
42 class Error(Exception):
43 pass
46 class BadSignatureError(Error):
47 pass
50 class SubprocessError(Error):
51 def __init__(self, message, error_code):
52 Error.__init__(self)
53 self.message = message
54 self.error_code = error_code
56 def __str__(self):
57 return 'Failed with code %s: %s' % (self.message, repr(self.error_code))
60 # Regular expression used to parse function signatures in the input files.
61 # The regex is built around identifying the "identifier" for the function name.
62 # We consider the identifier to be the string that follows these constraints:
64 # 1) Starts with [_a-ZA-Z] (C++ spec 2.10).
65 # 2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10).
66 # 3) Preceeds an opening parenthesis by 0 or more whitespace chars.
68 # From that, all preceeding characters are considered the return value.
69 # Trailing characters should have a substring matching the form (.*). That
70 # is considered the arguments.
71 SIGNATURE_REGEX = re.compile('(?P<return_type>.+?)'
72 '(?P<name>[_a-zA-Z][_a-zA-Z0-9]+)\s*'
73 '\((?P<params>.*?)\)')
75 # Used for generating C++ identifiers.
76 INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]')
78 # Constants defning the supported file types options.
79 FILE_TYPE_WIN_X86 = 'windows_lib'
80 FILE_TYPE_WIN_X64 = 'windows_lib_x64'
81 FILE_TYPE_POSIX_STUB = 'posix_stubs'
82 FILE_TYPE_WIN_DEF = 'windows_def'
84 # Template for generating a stub function definition. Includes a forward
85 # declaration marking the symbol as weak. This template takes the following
86 # named parameters.
87 # return_type: The return type.
88 # export: The macro used to alter the stub's visibility.
89 # name: The name of the function.
90 # params: The parameters to the function.
91 # return_prefix: 'return ' if this function is not void. '' otherwise.
92 # arg_list: The arguments used to call the stub function.
93 STUB_FUNCTION_DEFINITION = (
94 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
95 %(return_type)s %(export)s %(name)s(%(params)s) {
96 %(return_prefix)s%(name)s_ptr(%(arg_list)s);
97 }""")
99 # Template for generating a variadic stub function definition with return
100 # value.
101 # Includes a forward declaration marking the symbol as weak.
102 # This template takes the following named parameters.
103 # return_type: The return type.
104 # export: The macro used to alter the stub's visibility.
105 # name: The name of the function.
106 # params: The parameters to the function.
107 # arg_list: The arguments used to call the stub function without the
108 # variadic argument.
109 # last_named_arg: Name of the last named argument before the variadic
110 # argument.
111 VARIADIC_STUB_FUNCTION_DEFINITION = (
112 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
113 %(return_type)s %(export)s %(name)s(%(params)s) {
114 va_list args___;
115 va_start(args___, %(last_named_arg)s);
116 %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
117 va_end(args___);
118 return ret___;
119 }""")
121 # Template for generating a variadic stub function definition without
122 # return value.
123 # Includes a forward declaration marking the symbol as weak.
124 # This template takes the following named parameters.
125 # name: The name of the function.
126 # params: The parameters to the function.
127 # export: The macro used to alter the stub's visibility.
128 # arg_list: The arguments used to call the stub function without the
129 # variadic argument.
130 # last_named_arg: Name of the last named argument before the variadic
131 # argument.
132 VOID_VARIADIC_STUB_FUNCTION_DEFINITION = (
133 """extern void %(name)s(%(params)s) __attribute__((weak));
134 void %(export)s %(name)s(%(params)s) {
135 va_list args___;
136 va_start(args___, %(last_named_arg)s);
137 %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
138 va_end(args___);
139 }""")
141 # Template for the preamble for the stub header file with the header guards,
142 # standard set of includes, and namespace opener. This template takes the
143 # following named parameters:
144 # guard_name: The macro to use as the header guard.
145 # namespace: The namespace for the stub functions.
146 STUB_HEADER_PREAMBLE = """// This is generated file. Do not modify directly.
148 #ifndef %(guard_name)s
149 #define %(guard_name)s
151 #include <stdarg.h>
152 #include <map>
153 #include <string>
154 #include <vector>
156 #include "base/logging.h"
158 namespace %(namespace)s {
161 # Template for the end of the stub header. This closes the namespace and the
162 # header guards. This template takes the following named parameters:
163 # guard_name: The macro to use as the header guard.
164 # namespace: The namespace for the stub functions.
165 STUB_HEADER_CLOSER = """} // namespace %(namespace)s
167 #endif // %(guard_name)s
170 # The standard includes needed for the stub implementation file. Takes one
171 # string substition with the path to the associated stub header file.
172 IMPLEMENTATION_PREAMBLE = """// This is generated file. Do not modify directly.
174 #include "%s"
176 #include <stdlib.h> // For NULL.
177 #include <dlfcn.h> // For dysym, dlopen.
179 #include <map>
180 #include <vector>
183 # The start and end templates for the enum definitions used by the Umbrella
184 # initializer.
185 UMBRELLA_ENUM_START = """// Enum and typedef for umbrella initializer.
186 enum StubModules {
188 UMBRELLA_ENUM_END = """ kNumStubModules
193 # Start and end of the extern "C" section for the implementation contents.
194 IMPLEMENTATION_CONTENTS_C_START = """extern "C" {
197 IMPLEMENTATION_CONTENTS_C_END = """
198 } // extern "C"
203 # Templates for the start and end of a namespace. Takes one parameter, the
204 # namespace name.
205 NAMESPACE_START = """namespace %s {
208 NAMESPACE_END = """} // namespace %s
212 # Comment to include before the section declaring all the function pointers
213 # used by the stub functions.
214 FUNCTION_POINTER_SECTION_COMMENT = (
215 """// Static pointers that will hold the location of the real function
216 // implementations after the module has been loaded.
217 """)
219 # Template for the module initialization check function. This template
220 # takes two parameteres: the function name, and the conditional used to
221 # verify the module's initialization.
222 MODULE_INITIALIZATION_CHECK_FUNCTION = (
223 """// Returns true if all stubs have been properly initialized.
224 bool %s() {
225 if (%s) {
226 return true;
227 } else {
228 return false;
232 """)
234 # Template for the line that initialize the stub pointer. This template takes
235 # the following named parameters:
236 # name: The name of the function.
237 # return_type: The return type.
238 # params: The parameters to the function.
239 STUB_POINTER_INITIALIZER = """ %(name)s_ptr =
240 reinterpret_cast<%(return_type)s (*)(%(parameters)s)>(
241 dlsym(module, "%(name)s"));
242 VLOG_IF(1, !%(name)s_ptr) << "Couldn't load %(name)s, dlerror() says:\\n"
243 << dlerror();
246 # Template for module initializer function start and end. This template takes
247 # one parameter which is the initializer function name.
248 MODULE_INITIALIZE_START = """// Initializes the module stubs.
249 void %s(void* module) {
251 MODULE_INITIALIZE_END = """}
255 # Template for module uninitializer function start and end. This template
256 # takes one parameter which is the initializer function name.
257 MODULE_UNINITIALIZE_START = (
258 """// Uninitialize the module stubs. Reset pointers to NULL.
259 void %s() {
260 """)
261 MODULE_UNINITIALIZE_END = """}
266 # Open namespace and add typedef for internal data structures used by the
267 # umbrella initializer.
268 UMBRELLA_INITIALIZER_START = """namespace %s {
269 typedef std::map<StubModules, void*> StubHandleMap;
272 # Function close DSOs on error and clean up dangling references.
273 UMBRELLA_INITIALIZER_CLEANUP_FUNCTION = (
274 """static void CloseLibraries(StubHandleMap* stub_handles) {
275 for (StubHandleMap::const_iterator it = stub_handles->begin();
276 it != stub_handles->end();
277 ++it) {
278 dlclose(it->second);
281 stub_handles->clear();
283 """)
285 # Function to initialize each DSO for the given paths.
286 UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START = (
287 """bool InitializeStubs(const StubPathMap& path_map) {
288 StubHandleMap opened_libraries;
289 for (int i = 0; i < kNumStubModules; ++i) {
290 StubModules cur_module = static_cast<StubModules>(i);
291 // If a module is missing, we fail.
292 StubPathMap::const_iterator it = path_map.find(cur_module);
293 if (it == path_map.end()) {
294 CloseLibraries(&opened_libraries);
295 return false;
298 // Otherwise, attempt to dlopen the library.
299 const std::vector<std::string>& paths = it->second;
300 bool module_opened = false;
301 for (std::vector<std::string>::const_iterator dso_path = paths.begin();
302 !module_opened && dso_path != paths.end();
303 ++dso_path) {
304 void* handle = dlopen(dso_path->c_str(), RTLD_LAZY);
305 if (handle != NULL) {
306 module_opened = true;
307 opened_libraries[cur_module] = handle;
308 } else {
309 VLOG(1) << "dlopen(" << dso_path->c_str() << ") failed, "
310 << "dlerror() says:\\n" << dlerror();
314 if (!module_opened) {
315 CloseLibraries(&opened_libraries);
316 return false;
319 """)
321 # Template to generate code to check if each module initializer correctly
322 # completed, and cleanup on failures. This template takes the following
323 # named parameters.
324 # conditional: The conditional expression for successful initialization.
325 # uninitializers: The statements needed to uninitialize the modules.
326 UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP = (
327 """ // Check that each module is initialized correctly.
328 // Close all previously opened libraries on failure.
329 if (%(conditional)s) {
330 %(uninitializers)s;
331 CloseLibraries(&opened_libraries);
332 return false;
335 return true;
337 """)
339 # Template for Initialize, Unininitialize, and IsInitialized functions for each
340 # module. This template takes the following named parameters:
341 # initialize: Name of the Initialize function.
342 # uninitialize: Name of the Uninitialize function.
343 # is_initialized: Name of the IsInitialized function.
344 MODULE_FUNCTION_PROTOTYPES = """bool %(is_initialized)s();
345 void %(initialize)s(void* module);
346 void %(uninitialize)s();
350 # Template for umbrella initializer declaration and associated datatypes.
351 UMBRELLA_INITIALIZER_PROTOTYPE = (
352 """typedef std::map<StubModules, std::vector<std::string> > StubPathMap;
354 // Umbrella initializer for all the modules in this stub file.
355 bool InitializeStubs(const StubPathMap& path_map);
356 """)
359 def ExtractModuleName(infile_path):
360 """Infers the module name from the input file path.
362 The input filename is supposed to be in the form "ModuleName.sigs".
363 This function splits the filename from the extention on that basename of
364 the path and returns that as the module name.
366 Args:
367 infile_path: String holding the path to the input file.
369 Returns:
370 The module name as a string.
372 basename = os.path.basename(infile_path)
374 # This loop continously removes suffixes of the filename separated by a "."
375 # character.
376 while 1:
377 new_basename = os.path.splitext(basename)[0]
378 if basename == new_basename:
379 break
380 else:
381 basename = new_basename
382 return basename
385 def ParseSignatures(infile):
386 """Parses function signatures in the input file.
388 This function parses a file of signatures into a list of dictionaries that
389 represent the function signatures in the input file. Each dictionary has
390 the following keys:
391 return_type: A string with the return type.
392 name: A string with the name of the function.
393 params: A list of each function parameter declaration (type + name)
395 The format of the input file is one C-style function signature per line, no
396 trailing semicolon. Empty lines are allowed. An empty line is a line that
397 consists purely of whitespace. Lines that begin with a # or // are considered
398 comment lines and are ignored.
400 We assume that "int foo(void)" is the same as "int foo()", which is not
401 true in C where "int foo()" is equivalent to "int foo(...)". Our generated
402 code is C++, and we do not handle varargs, so this is a case that can be
403 ignored for now.
405 Args:
406 infile: File object holding a text file of function signatures.
408 Returns:
409 A list of dictionaries, where each dictionary represents one function
410 signature.
412 Raises:
413 BadSignatureError: A line could not be parsed as a signature.
415 signatures = []
416 for line in infile:
417 line = line.strip()
418 if line and line[0] != '#' and line[0:2] != '//':
419 m = SIGNATURE_REGEX.match(line)
420 if m is None:
421 raise BadSignatureError('Unparsable line: %s' % line)
422 signatures.append(
423 {'return_type': m.group('return_type').strip(),
424 'name': m.group('name').strip(),
425 'params': [arg.strip() for arg in m.group('params').split(',')]})
426 return signatures
429 def WriteWindowsDefFile(module_name, signatures, outfile):
430 """Writes a windows def file to the given output file object.
432 The def file format is basically a list of function names. Generation is
433 simple. After outputting the LIBRARY and EXPORTS lines, print out each
434 function name, one to a line, preceeded by 2 spaces.
436 Args:
437 module_name: The name of the module we are writing a stub for.
438 signatures: The list of signature hashes, as produced by ParseSignatures,
439 to create stubs for.
440 outfile: File handle to populate with definitions.
442 outfile.write('LIBRARY %s\n' % module_name)
443 outfile.write('EXPORTS\n')
445 for sig in signatures:
446 outfile.write(' %s\n' % sig['name'])
449 def QuietRun(args, filter=None, write_to=sys.stdout):
450 """Invoke |args| as command via subprocess.Popen, filtering lines starting
451 with |filter|."""
452 popen = subprocess.Popen(args, stdout=subprocess.PIPE)
453 out, _ = popen.communicate()
454 for line in out.splitlines():
455 if not filter or not line.startswith(filter):
456 write_to.write(line + '\n')
457 return popen.returncode
460 def CreateWindowsLib(module_name, signatures, intermediate_dir, outdir_path,
461 machine):
462 """Creates a windows library file.
464 Calling this function will create a lib file in the outdir_path that exports
465 the signatures passed into the object. A temporary def file will be created
466 in the intermediate_dir.
468 Args:
469 module_name: The name of the module we are writing a stub for.
470 signatures: The list of signature hashes, as produced by ParseSignatures,
471 to create stubs for.
472 intermediate_dir: The directory where the generated .def files should go.
473 outdir_path: The directory where generated .lib files should go.
474 machine: String holding the machine type, 'X86' or 'X64'.
476 Raises:
477 SubprocessError: If invoking the windows "lib" tool fails, this is raised
478 with the error code.
480 def_file_path = os.path.join(intermediate_dir,
481 module_name + '.def')
482 lib_file_path = os.path.join(outdir_path,
483 module_name + '.lib')
484 outfile = open(def_file_path, 'w')
485 try:
486 WriteWindowsDefFile(module_name, signatures, outfile)
487 finally:
488 outfile.close()
490 # Invoke the "lib" program on Windows to create stub .lib files for the
491 # generated definitions. These .lib files can then be used during
492 # delayloading of the dynamic libraries.
493 ret = QuietRun(['lib', '/nologo',
494 '/machine:' + machine,
495 '/def:' + def_file_path,
496 '/out:' + lib_file_path],
497 filter=' Creating library')
498 if ret != 0:
499 raise SubprocessError(
500 'Failed creating %s for %s' % (lib_file_path, def_file_path),
501 ret)
504 class PosixStubWriter(object):
505 """Creates a file of stub functions for a library that is opened via dlopen.
507 Windows provides a function in their compiler known as delay loading, which
508 effectively generates a set of stub functions for a dynamic library that
509 delays loading of the dynamic library/resolution of the symbols until one of
510 the needed functions are accessed.
512 In posix, RTLD_LAZY does something similar with DSOs. This is the default
513 link mode for DSOs. However, even though the symbol is not resolved until
514 first usage, the DSO must be present at load time of the main binary.
516 To simulate the windows delay load procedure, we need to create a set of
517 stub functions that allow for correct linkage of the main binary, but
518 dispatch to the dynamically resolved symbol when the module is initialized.
520 This class takes a list of function signatures, and generates a set of stub
521 functions plus initialization code for them.
524 def __init__(self, module_name, export_macro, signatures):
525 """Initializes PosixStubWriter for this set of signatures and module_name.
527 Args:
528 module_name: The name of the module we are writing a stub for.
529 export_macro: A preprocessor macro used to annotate stub symbols with
530 an EXPORT marking, to control visibility.
531 signatures: The list of signature hashes, as produced by ParseSignatures,
532 to create stubs for.
534 self.signatures = signatures
535 self.module_name = module_name
536 self.export_macro = export_macro
538 @classmethod
539 def CStyleIdentifier(cls, identifier):
540 """Generates a C style identifier.
542 The module_name has all invalid identifier characters removed (anything
543 that's not [_a-zA-Z0-9]) and is run through string.capwords to try
544 and approximate camel case.
546 Args:
547 identifier: The string with the module name to turn to C-style.
549 Returns:
550 A string that can be used as part of a C identifier.
552 return string.capwords(re.sub(INVALID_C_IDENT_CHARS, '', identifier))
554 @classmethod
555 def EnumName(cls, module_name):
556 """Gets the enum name for the module.
558 Takes the module name and creates a suitable enum name. The module_name
559 is munged to be a valid C identifier then prefixed with the string
560 "kModule" to generate a Google style enum name.
562 Args:
563 module_name: The name of the module to generate an enum name for.
565 Returns:
566 A string with the name of the enum value representing this module.
568 return 'kModule%s' % PosixStubWriter.CStyleIdentifier(module_name)
570 @classmethod
571 def IsInitializedName(cls, module_name):
572 """Gets the name of function that checks initialization of this module.
574 The name is in the format IsModuleInitialized. Where "Module" is replaced
575 with the module name, munged to be a valid C identifier.
577 Args:
578 module_name: The name of the module to generate the function name for.
580 Returns:
581 A string with the name of the initialization check function.
583 return 'Is%sInitialized' % PosixStubWriter.CStyleIdentifier(module_name)
585 @classmethod
586 def InitializeModuleName(cls, module_name):
587 """Gets the name of the function that initializes this module.
589 The name is in the format InitializeModule. Where "Module" is replaced
590 with the module name, munged to be a valid C identifier.
592 Args:
593 module_name: The name of the module to generate the function name for.
595 Returns:
596 A string with the name of the initialization function.
598 return 'Initialize%s' % PosixStubWriter.CStyleIdentifier(module_name)
600 @classmethod
601 def UninitializeModuleName(cls, module_name):
602 """Gets the name of the function that uninitializes this module.
604 The name is in the format UninitializeModule. Where "Module" is replaced
605 with the module name, munged to be a valid C identifier.
607 Args:
608 module_name: The name of the module to generate the function name for.
610 Returns:
611 A string with the name of the uninitialization function.
613 return 'Uninitialize%s' % PosixStubWriter.CStyleIdentifier(module_name)
615 @classmethod
616 def StubFunctionPointer(cls, signature):
617 """Generates a function pointer declaration for the given signature.
619 Args:
620 signature: A signature hash, as produced by ParseSignatures,
621 representating the function signature.
623 Returns:
624 A string with the declaration of the function pointer for the signature.
626 return 'static %s (*%s_ptr)(%s) = NULL;' % (signature['return_type'],
627 signature['name'],
628 ', '.join(signature['params']))
630 @classmethod
631 def StubFunction(cls, signature):
632 """Generates a stub function definition for the given signature.
634 The function definitions are created with __attribute__((weak)) so that
635 they may be overridden by a real static link or mock versions to be used
636 when testing.
638 Args:
639 signature: A signature hash, as produced by ParseSignatures,
640 representating the function signature.
642 Returns:
643 A string with the stub function definition.
645 return_prefix = ''
646 if signature['return_type'] != 'void':
647 return_prefix = 'return '
649 # Generate the argument list.
650 arguments = [re.split('[\*& ]', arg)[-1].strip() for arg in
651 signature['params']]
652 arg_list = ', '.join(arguments)
653 if arg_list == 'void':
654 arg_list = ''
656 if arg_list != '' and len(arguments) > 1 and arguments[-1] == '...':
657 # If the last argment is ... then this is a variadic function.
658 if return_prefix != '':
659 return VARIADIC_STUB_FUNCTION_DEFINITION % {
660 'return_type': signature['return_type'],
661 'name': signature['name'],
662 'params': ', '.join(signature['params']),
663 'arg_list': ', '.join(arguments[0:-1]),
664 'last_named_arg': arguments[-2],
665 'export': signature.get('export', '')}
666 else:
667 return VOID_VARIADIC_STUB_FUNCTION_DEFINITION % {
668 'name': signature['name'],
669 'params': ', '.join(signature['params']),
670 'arg_list': ', '.join(arguments[0:-1]),
671 'last_named_arg': arguments[-2],
672 'export': signature.get('export', '')}
673 else:
674 # This is a regular function.
675 return STUB_FUNCTION_DEFINITION % {
676 'return_type': signature['return_type'],
677 'name': signature['name'],
678 'params': ', '.join(signature['params']),
679 'return_prefix': return_prefix,
680 'arg_list': arg_list,
681 'export': signature.get('export', '')}
683 @classmethod
684 def WriteImplementationPreamble(cls, header_path, outfile):
685 """Write the necessary includes for the implementation file.
687 Args:
688 header_path: The path to the header file.
689 outfile: The file handle to populate.
691 outfile.write(IMPLEMENTATION_PREAMBLE % header_path)
693 @classmethod
694 def WriteUmbrellaInitializer(cls, module_names, namespace, outfile):
695 """Writes a single function that will open + initialize each module.
697 This intializer will take in an stl map of that lists the correct
698 dlopen target for each module. The map type is
699 std::map<enum StubModules, vector<std::string>> which matches one module
700 to a list of paths to try in dlopen.
702 This function is an all-or-nothing function. If any module fails to load,
703 all other modules are dlclosed, and the function returns. Though it is
704 not enforced, this function should only be called once.
706 Args:
707 module_names: A list with the names of the modules in this stub file.
708 namespace: The namespace these functions should be in.
709 outfile: The file handle to populate with pointer definitions.
711 outfile.write(UMBRELLA_INITIALIZER_START % namespace)
712 outfile.write(UMBRELLA_INITIALIZER_CLEANUP_FUNCTION)
714 # Create the initializaiton function that calls all module initializers,
715 # checks if they succeeded, and backs out module loads on an error.
716 outfile.write(UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START)
717 outfile.write(
718 '\n // Initialize each module if we have not already failed.\n')
719 for module in module_names:
720 outfile.write(' %s(opened_libraries[%s]);\n' %
721 (PosixStubWriter.InitializeModuleName(module),
722 PosixStubWriter.EnumName(module)))
723 outfile.write('\n')
725 # Output code to check the initialization status, clean up on error.
726 initializer_checks = ['!%s()' % PosixStubWriter.IsInitializedName(name)
727 for name in module_names]
728 uninitializers = ['%s()' % PosixStubWriter.UninitializeModuleName(name)
729 for name in module_names]
730 outfile.write(UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP % {
731 'conditional': ' ||\n '.join(initializer_checks),
732 'uninitializers': ';\n '.join(uninitializers)})
733 outfile.write('\n} // namespace %s\n' % namespace)
735 @classmethod
736 def WriteHeaderContents(cls, module_names, namespace, header_guard, outfile):
737 """Writes a header file for the stub file generated for module_names.
739 The header file exposes the following:
740 1) An enum, StubModules, listing with an entry for each enum.
741 2) A typedef for a StubPathMap allowing for specification of paths to
742 search for each module.
743 3) The IsInitialized/Initialize/Uninitialize functions for each module.
744 4) An umbrella initialize function for all modules.
746 Args:
747 module_names: A list with the names of each module in this stub file.
748 namespace: The namespace these functions should be in.
749 header_guard: The macro to use as our header guard.
750 outfile: The output handle to populate.
752 outfile.write(STUB_HEADER_PREAMBLE %
753 {'guard_name': header_guard, 'namespace': namespace})
755 # Generate the Initializer protoypes for each module.
756 outfile.write('// Individual module initializer functions.\n')
757 for name in module_names:
758 outfile.write(MODULE_FUNCTION_PROTOTYPES % {
759 'is_initialized': PosixStubWriter.IsInitializedName(name),
760 'initialize': PosixStubWriter.InitializeModuleName(name),
761 'uninitialize': PosixStubWriter.UninitializeModuleName(name)})
763 # Generate the enum for umbrella initializer.
764 outfile.write(UMBRELLA_ENUM_START)
765 outfile.write(' %s = 0,\n' % PosixStubWriter.EnumName(module_names[0]))
766 for name in module_names[1:]:
767 outfile.write(' %s,\n' % PosixStubWriter.EnumName(name))
768 outfile.write(UMBRELLA_ENUM_END)
770 outfile.write(UMBRELLA_INITIALIZER_PROTOTYPE)
771 outfile.write(STUB_HEADER_CLOSER % {
772 'namespace': namespace, 'guard_name':
773 header_guard})
775 def WriteImplementationContents(self, namespace, outfile):
776 """Given a file handle, write out the stub definitions for this module.
778 Args:
779 namespace: The namespace these functions should be in.
780 outfile: The file handle to populate.
782 outfile.write(IMPLEMENTATION_CONTENTS_C_START)
783 self.WriteFunctionPointers(outfile)
784 self.WriteStubFunctions(outfile)
785 outfile.write(IMPLEMENTATION_CONTENTS_C_END)
787 outfile.write(NAMESPACE_START % namespace)
788 self.WriteModuleInitializeFunctions(outfile)
789 outfile.write(NAMESPACE_END % namespace)
791 def WriteFunctionPointers(self, outfile):
792 """Write the function pointer declarations needed by the stubs.
794 We need function pointers to hold the actual location of the function
795 implementation returned by dlsym. This function outputs a pointer
796 definition for each signature in the module.
798 Pointers will be named with the following pattern "FuntionName_ptr".
800 Args:
801 outfile: The file handle to populate with pointer definitions.
803 outfile.write(FUNCTION_POINTER_SECTION_COMMENT)
805 for sig in self.signatures:
806 outfile.write('%s\n' % PosixStubWriter.StubFunctionPointer(sig))
807 outfile.write('\n')
809 def WriteStubFunctions(self, outfile):
810 """Write the function stubs to handle dispatching to real implementations.
812 Functions that have a return type other than void will look as follows:
814 ReturnType FunctionName(A a) {
815 return FunctionName_ptr(a);
818 Functions with a return type of void will look as follows:
820 void FunctionName(A a) {
821 FunctionName_ptr(a);
824 Args:
825 outfile: The file handle to populate.
827 outfile.write('// Stubs that dispatch to the real implementations.\n')
828 for sig in self.signatures:
829 sig['export'] = self.export_macro
830 outfile.write('%s\n' % PosixStubWriter.StubFunction(sig))
832 def WriteModuleInitializeFunctions(self, outfile):
833 """Write functions to initialize/query initlialization of the module.
835 This creates 2 functions IsModuleInitialized and InitializeModule where
836 "Module" is replaced with the module name, first letter capitalized.
838 The InitializeModule function takes a handle that is retrieved from dlopen
839 and attempts to assign each function pointer above via dlsym.
841 The IsModuleInitialized returns true if none of the required functions
842 pointers are NULL.
844 Args:
845 outfile: The file handle to populate.
847 ptr_names = ['%s_ptr' % sig['name'] for sig in self.signatures]
849 # Construct the conditional expression to check the initialization of
850 # all the function pointers above. It should generate a conjuntion
851 # with each pointer on its own line, indented by six spaces to match
852 # the indentation level of MODULE_INITIALIZATION_CHECK_FUNCTION.
853 initialization_conditional = ' &&\n '.join(ptr_names)
855 outfile.write(MODULE_INITIALIZATION_CHECK_FUNCTION % (
856 PosixStubWriter.IsInitializedName(self.module_name),
857 initialization_conditional))
859 # Create function that initializes the module.
860 outfile.write(MODULE_INITIALIZE_START %
861 PosixStubWriter.InitializeModuleName(self.module_name))
862 for sig in self.signatures:
863 outfile.write(STUB_POINTER_INITIALIZER % {
864 'name': sig['name'],
865 'return_type': sig['return_type'],
866 'parameters': ', '.join(sig['params'])})
867 outfile.write(MODULE_INITIALIZE_END)
869 # Create function that uninitializes the module (sets all pointers to
870 # NULL).
871 outfile.write(MODULE_UNINITIALIZE_START %
872 PosixStubWriter.UninitializeModuleName(self.module_name))
873 for sig in self.signatures:
874 outfile.write(' %s_ptr = NULL;\n' % sig['name'])
875 outfile.write(MODULE_UNINITIALIZE_END)
878 def CreateOptionParser():
879 """Creates an OptionParser for the configuration options of script.
881 Returns:
882 A OptionParser object.
884 parser = optparse.OptionParser(usage='usage: %prog [options] input')
885 parser.add_option('-o',
886 '--output',
887 dest='out_dir',
888 default=None,
889 help='Output location.')
890 parser.add_option('-i',
891 '--intermediate_dir',
892 dest='intermediate_dir',
893 default=None,
894 help=('Location of intermediate files. Ignored for %s type'
895 % FILE_TYPE_WIN_DEF))
896 parser.add_option('-t',
897 '--type',
898 dest='type',
899 default=None,
900 help=('Type of file. Valid types are "%s" or "%s" or "%s" '
901 'or "%s"' %
902 (FILE_TYPE_POSIX_STUB, FILE_TYPE_WIN_X86,
903 FILE_TYPE_WIN_X64, FILE_TYPE_WIN_DEF)))
904 parser.add_option('-s',
905 '--stubfile_name',
906 dest='stubfile_name',
907 default=None,
908 help=('Name of posix_stubs output file. Only valid with '
909 '%s type.' % FILE_TYPE_POSIX_STUB))
910 parser.add_option('-p',
911 '--path_from_source',
912 dest='path_from_source',
913 default=None,
914 help=('The relative path from the project root that the '
915 'generated file should consider itself part of (eg. '
916 'third_party/ffmpeg). This is used to generate the '
917 'header guard and namespace for our initializer '
918 'functions and does NOT affect the physical output '
919 'location of the file like -o does. Ignored for '
920 '%s and %s types.' %
921 (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64)))
922 parser.add_option('-e',
923 '--extra_stub_header',
924 dest='extra_stub_header',
925 default=None,
926 help=('File to insert after the system includes in the '
927 'generated stub implemenation file. Ignored for '
928 '%s and %s types.' %
929 (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64)))
930 parser.add_option('-m',
931 '--module_name',
932 dest='module_name',
933 default=None,
934 help=('Name of output DLL or LIB for DEF creation using '
935 '%s type.' % FILE_TYPE_WIN_DEF))
936 parser.add_option('-x',
937 '--export_macro',
938 dest='export_macro',
939 default='',
940 help=('A macro to place between the return type and '
941 'function name, e.g. MODULE_EXPORT, to control the '
942 'visbility of the stub functions.'))
944 return parser
947 def ParseOptions():
948 """Parses the options and terminates program if they are not sane.
950 Returns:
951 The pair (optparse.OptionValues, [string]), that is the output of
952 a successful call to parser.parse_args().
954 parser = CreateOptionParser()
955 options, args = parser.parse_args()
957 if not args:
958 parser.error('No inputs specified')
960 if options.out_dir is None:
961 parser.error('Output location not specified')
963 if (options.type not in
964 [FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64, FILE_TYPE_POSIX_STUB,
965 FILE_TYPE_WIN_DEF]):
966 parser.error('Invalid output file type: %s' % options.type)
968 if options.type == FILE_TYPE_POSIX_STUB:
969 if options.stubfile_name is None:
970 parser.error('Output file name needed for %s' % FILE_TYPE_POSIX_STUB)
971 if options.path_from_source is None:
972 parser.error('Path from source needed for %s' % FILE_TYPE_POSIX_STUB)
974 if options.type == FILE_TYPE_WIN_DEF:
975 if options.module_name is None:
976 parser.error('Module name needed for %s' % FILE_TYPE_WIN_DEF)
978 return options, args
981 def EnsureDirExists(dir):
982 """Creates a directory. Does not use the more obvious 'if not exists: create'
983 to avoid race with other invocations of the same code, which will error out
984 on makedirs if another invocation has succeeded in creating the directory
985 since the existence check."""
986 try:
987 os.makedirs(dir)
988 except:
989 if not os.path.isdir(dir):
990 raise
993 def CreateOutputDirectories(options):
994 """Creates the intermediate and final output directories.
996 Given the parsed options, create the intermediate and final output
997 directories if they do not exist. Returns the paths to both directories
998 as a pair.
1000 Args:
1001 options: An OptionParser.OptionValues object with the parsed options.
1003 Returns:
1004 The pair (out_dir, intermediate_dir), both of which are strings.
1006 out_dir = os.path.normpath(options.out_dir)
1007 intermediate_dir = os.path.normpath(options.intermediate_dir)
1008 if intermediate_dir is None:
1009 intermediate_dir = out_dir
1011 EnsureDirExists(out_dir)
1012 EnsureDirExists(intermediate_dir)
1014 return out_dir, intermediate_dir
1017 def CreateWindowsLibForSigFiles(sig_files, out_dir, intermediate_dir, machine,
1018 export_macro):
1019 """For each signature file, create a windows lib.
1021 Args:
1022 sig_files: Array of strings with the paths to each signature file.
1023 out_dir: String holding path to directory where the generated libs go.
1024 intermediate_dir: String holding path to directory generated intermdiate
1025 artifacts.
1026 machine: String holding the machine type, 'X86' or 'X64'.
1027 export_macro: A preprocessor macro used to annotate stub symbols with
1028 an EXPORT marking, to control visibility.
1030 for input_path in sig_files:
1031 infile = open(input_path, 'r')
1032 try:
1033 signatures = ParseSignatures(infile)
1034 module_name = ExtractModuleName(os.path.basename(input_path))
1035 for sig in signatures:
1036 sig['export'] = export_macro
1037 CreateWindowsLib(module_name, signatures, intermediate_dir, out_dir,
1038 machine)
1039 finally:
1040 infile.close()
1043 def CreateWindowsDefForSigFiles(sig_files, out_dir, module_name):
1044 """For all signature files, create a single windows def file.
1046 Args:
1047 sig_files: Array of strings with the paths to each signature file.
1048 out_dir: String holding path to directory where the generated def goes.
1049 module_name: Name of the output DLL or LIB which will link in the def file.
1051 signatures = []
1052 for input_path in sig_files:
1053 infile = open(input_path, 'r')
1054 try:
1055 signatures += ParseSignatures(infile)
1056 finally:
1057 infile.close()
1059 def_file_path = os.path.join(
1060 out_dir, os.path.splitext(os.path.basename(module_name))[0] + '.def')
1061 outfile = open(def_file_path, 'w')
1063 try:
1064 WriteWindowsDefFile(module_name, signatures, outfile)
1065 finally:
1066 outfile.close()
1069 def CreatePosixStubsForSigFiles(sig_files, stub_name, out_dir,
1070 intermediate_dir, path_from_source,
1071 extra_stub_header, export_macro):
1072 """Create a posix stub library with a module for each signature file.
1074 Args:
1075 sig_files: Array of strings with the paths to each signature file.
1076 stub_name: String with the basename of the generated stub file.
1077 out_dir: String holding path to directory for the .h files.
1078 intermediate_dir: String holding path to directory for the .cc files.
1079 path_from_source: String with relative path of generated files from the
1080 project root.
1081 extra_stub_header: String with path to file of extra lines to insert
1082 into the generated header for the stub library.
1083 export_macro: A preprocessor macro used to annotate stub symbols with
1084 an EXPORT marking, to control visibility.
1086 header_base_name = stub_name + '.h'
1087 header_path = os.path.join(out_dir, header_base_name)
1088 impl_path = os.path.join(intermediate_dir, stub_name + '.cc')
1090 module_names = [ExtractModuleName(path) for path in sig_files]
1091 namespace = path_from_source.replace('/', '_').lower()
1092 header_guard = '%s_' % namespace.upper()
1093 header_include_path = os.path.join(path_from_source, header_base_name)
1095 # First create the implementation file.
1096 impl_file = open(impl_path, 'w')
1097 try:
1098 # Open the file, and create the preamble which consists of a file
1099 # header plus any necessary includes.
1100 PosixStubWriter.WriteImplementationPreamble(header_include_path,
1101 impl_file)
1102 if extra_stub_header is not None:
1103 extra_header_file = open(extra_stub_header, 'r')
1104 try:
1105 impl_file.write('\n')
1106 for line in extra_header_file:
1107 impl_file.write(line)
1108 impl_file.write('\n')
1109 finally:
1110 extra_header_file.close()
1112 # For each signature file, generate the stub population functions
1113 # for that file. Each file represents one module.
1114 for input_path in sig_files:
1115 name = ExtractModuleName(input_path)
1116 infile = open(input_path, 'r')
1117 try:
1118 signatures = ParseSignatures(infile)
1119 finally:
1120 infile.close()
1121 writer = PosixStubWriter(name, export_macro, signatures)
1122 writer.WriteImplementationContents(namespace, impl_file)
1124 # Lastly, output the umbrella function for the file.
1125 PosixStubWriter.WriteUmbrellaInitializer(module_names, namespace,
1126 impl_file)
1127 finally:
1128 impl_file.close()
1130 # Then create the associated header file.
1131 header_file = open(header_path, 'w')
1132 try:
1133 PosixStubWriter.WriteHeaderContents(module_names, namespace,
1134 header_guard, header_file)
1135 finally:
1136 header_file.close()
1139 def main():
1140 options, args = ParseOptions()
1141 out_dir, intermediate_dir = CreateOutputDirectories(options)
1143 if options.type == FILE_TYPE_WIN_X86:
1144 CreateWindowsLibForSigFiles(args, out_dir, intermediate_dir, 'X86',
1145 options.export_macro)
1146 elif options.type == FILE_TYPE_WIN_X64:
1147 CreateWindowsLibForSigFiles(args, out_dir, intermediate_dir, 'X64',
1148 options.export_macro)
1149 elif options.type == FILE_TYPE_POSIX_STUB:
1150 CreatePosixStubsForSigFiles(args, options.stubfile_name, out_dir,
1151 intermediate_dir, options.path_from_source,
1152 options.extra_stub_header, options.export_macro)
1153 elif options.type == FILE_TYPE_WIN_DEF:
1154 CreateWindowsDefForSigFiles(args, out_dir, options.module_name)
1157 if __name__ == '__main__':
1158 main()