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
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
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
32 __author__
= 'ajwong@chromium.org (Albert J. Wong)'
42 class Error(Exception):
46 class BadSignatureError(Error
):
50 class SubprocessError(Error
):
51 def __init__(self
, message
, error_code
):
53 self
.message
= message
54 self
.error_code
= error_code
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
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);
99 # Template for generating a variadic stub function definition with return
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
109 # last_named_arg: Name of the last named argument before the variadic
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) {
115 va_start(args___, %(last_named_arg)s);
116 %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
121 # Template for generating a variadic stub function definition without
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
130 # last_named_arg: Name of the last named argument before the variadic
132 VOID_VARIADIC_STUB_FUNCTION_DEFINITION
= (
133 """extern void %(name)s(%(params)s) __attribute__((weak));
134 void %(export)s %(name)s(%(params)s) {
136 va_start(args___, %(last_named_arg)s);
137 %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
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
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.
176 #include <stdlib.h> // For NULL.
177 #include <dlfcn.h> // For dysym, dlopen.
183 # The start and end templates for the enum definitions used by the Umbrella
185 UMBRELLA_ENUM_START
= """// Enum and typedef for umbrella initializer.
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
= """
203 # Templates for the start and end of a namespace. Takes one parameter, the
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.
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.
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"
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.
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();
281 stub_handles->clear();
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);
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();
304 void* handle = dlopen(dso_path->c_str(), RTLD_LAZY);
305 if (handle != NULL) {
306 module_opened = true;
307 opened_libraries[cur_module] = handle;
309 VLOG(1) << "dlopen(" << dso_path->c_str() << ") failed, "
310 << "dlerror() says:\\n" << dlerror();
314 if (!module_opened) {
315 CloseLibraries(&opened_libraries);
321 # Template to generate code to check if each module initializer correctly
322 # completed, and cleanup on failures. This template takes the following
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) {
331 CloseLibraries(&opened_libraries);
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);
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.
367 infile_path: String holding the path to the input file.
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 "."
377 new_basename
= os
.path
.splitext(basename
)[0]
378 if basename
== new_basename
:
381 basename
= new_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
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
406 infile: File object holding a text file of function signatures.
409 A list of dictionaries, where each dictionary represents one function
413 BadSignatureError: A line could not be parsed as a signature.
418 if line
and line
[0] != '#' and line
[0:2] != '//':
419 m
= SIGNATURE_REGEX
.match(line
)
421 raise BadSignatureError('Unparsable line: %s' % line
)
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(',')]})
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.
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,
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
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
,
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.
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,
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'.
477 SubprocessError: If invoking the windows "lib" tool fails, this is raised
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')
486 WriteWindowsDefFile(module_name
, signatures
, outfile
)
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')
499 raise SubprocessError(
500 'Failed creating %s for %s' % (lib_file_path
, def_file_path
),
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.
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,
534 self
.signatures
= signatures
535 self
.module_name
= module_name
536 self
.export_macro
= export_macro
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.
547 identifier: The string with the module name to turn to C-style.
550 A string that can be used as part of a C identifier.
552 return string
.capwords(re
.sub(INVALID_C_IDENT_CHARS
, '', identifier
))
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.
563 module_name: The name of the module to generate an enum name for.
566 A string with the name of the enum value representing this module.
568 return 'kModule%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
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.
578 module_name: The name of the module to generate the function name for.
581 A string with the name of the initialization check function.
583 return 'Is%sInitialized' % PosixStubWriter
.CStyleIdentifier(module_name
)
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.
593 module_name: The name of the module to generate the function name for.
596 A string with the name of the initialization function.
598 return 'Initialize%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
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.
608 module_name: The name of the module to generate the function name for.
611 A string with the name of the uninitialization function.
613 return 'Uninitialize%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
616 def StubFunctionPointer(cls
, signature
):
617 """Generates a function pointer declaration for the given signature.
620 signature: A signature hash, as produced by ParseSignatures,
621 representating the function signature.
624 A string with the declaration of the function pointer for the signature.
626 return 'static %s (*%s_ptr)(%s) = NULL;' % (signature
['return_type'],
628 ', '.join(signature
['params']))
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
639 signature: A signature hash, as produced by ParseSignatures,
640 representating the function signature.
643 A string with the stub function definition.
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
652 arg_list
= ', '.join(arguments
)
653 if arg_list
== 'void':
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', '')}
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', '')}
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', '')}
684 def WriteImplementationPreamble(cls
, header_path
, outfile
):
685 """Write the necessary includes for the implementation file.
688 header_path: The path to the header file.
689 outfile: The file handle to populate.
691 outfile
.write(IMPLEMENTATION_PREAMBLE
% header_path
)
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.
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
)
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
)))
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
)
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.
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':
775 def WriteImplementationContents(self
, namespace
, outfile
):
776 """Given a file handle, write out the stub definitions for this module.
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".
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
))
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) {
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
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
% {
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
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.
882 A OptionParser object.
884 parser
= optparse
.OptionParser(usage
='usage: %prog [options] input')
885 parser
.add_option('-o',
889 help='Output location.')
890 parser
.add_option('-i',
891 '--intermediate_dir',
892 dest
='intermediate_dir',
894 help=('Location of intermediate files. Ignored for %s type'
895 % FILE_TYPE_WIN_DEF
))
896 parser
.add_option('-t',
900 help=('Type of file. Valid types are "%s" or "%s" 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',
906 dest
='stubfile_name',
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',
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 '
921 (FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
)))
922 parser
.add_option('-e',
923 '--extra_stub_header',
924 dest
='extra_stub_header',
926 help=('File to insert after the system includes in the '
927 'generated stub implemenation file. Ignored for '
929 (FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
)))
930 parser
.add_option('-m',
934 help=('Name of output DLL or LIB for DEF creation using '
935 '%s type.' % FILE_TYPE_WIN_DEF
))
936 parser
.add_option('-x',
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.'))
948 """Parses the options and terminates program if they are not sane.
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()
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
,
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
)
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."""
989 if not os
.path
.isdir(dir):
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
1001 options: An OptionParser.OptionValues object with the parsed options.
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
,
1019 """For each signature file, create a windows lib.
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
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')
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
,
1043 def CreateWindowsDefForSigFiles(sig_files
, out_dir
, module_name
):
1044 """For all signature files, create a single windows def file.
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.
1052 for input_path
in sig_files
:
1053 infile
= open(input_path
, 'r')
1055 signatures
+= ParseSignatures(infile
)
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')
1064 WriteWindowsDefFile(module_name
, signatures
, outfile
)
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.
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
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')
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
,
1102 if extra_stub_header
is not None:
1103 extra_header_file
= open(extra_stub_header
, 'r')
1105 impl_file
.write('\n')
1106 for line
in extra_header_file
:
1107 impl_file
.write(line
)
1108 impl_file
.write('\n')
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')
1118 signatures
= ParseSignatures(infile
)
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
,
1130 # Then create the associated header file.
1131 header_file
= open(header_path
, 'w')
1133 PosixStubWriter
.WriteHeaderContents(module_names
, namespace
,
1134 header_guard
, header_file
)
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__':