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 # name: The name of the function.
89 # params: The parameters to the function.
90 # return_prefix: 'return ' if this function is not void. '' otherwise.
91 # arg_list: The arguments used to call the stub function.
92 STUB_FUNCTION_DEFINITION
= (
93 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
94 %(return_type)s %(name)s(%(params)s) {
95 %(return_prefix)s%(name)s_ptr(%(arg_list)s);
98 # Template for generating a variadic stub function definition with return
100 # Includes a forward declaration marking the symbol as weak.
101 # This template takes the following named parameters.
102 # return_type: The return type.
103 # name: The name of the function.
104 # params: The parameters to the function.
105 # arg_list: The arguments used to call the stub function without the
107 # last_named_arg: Name of the last named argument before the variadic
109 VARIADIC_STUB_FUNCTION_DEFINITION
= (
110 """extern %(return_type)s %(name)s(%(params)s) __attribute__((weak));
111 %(return_type)s %(name)s(%(params)s) {
113 va_start(args___, %(last_named_arg)s);
114 %(return_type)s ret___ = %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
119 # Template for generating a variadic stub function definition without
121 # Includes a forward declaration marking the symbol as weak.
122 # This template takes the following named parameters.
123 # name: The name of the function.
124 # params: The parameters to the function.
125 # arg_list: The arguments used to call the stub function without the
127 # last_named_arg: Name of the last named argument before the variadic
129 VOID_VARIADIC_STUB_FUNCTION_DEFINITION
= (
130 """extern void %(name)s(%(params)s) __attribute__((weak));
131 void %(name)s(%(params)s) {
133 va_start(args___, %(last_named_arg)s);
134 %(name)s_ptr(%(arg_list)s, va_arg(args___, void*));
138 # Template for the preamble for the stub header file with the header guards,
139 # standard set of includes, and namespace opener. This template takes the
140 # following named parameters:
141 # guard_name: The macro to use as the header guard.
142 # namespace: The namespace for the stub functions.
143 STUB_HEADER_PREAMBLE
= """// This is generated file. Do not modify directly.
145 #ifndef %(guard_name)s
146 #define %(guard_name)s
152 #include "base/logging.h"
154 namespace %(namespace)s {
157 # Template for the end of the stub header. This closes the namespace and the
158 # header guards. This template takes the following named parameters:
159 # guard_name: The macro to use as the header guard.
160 # namespace: The namespace for the stub functions.
161 STUB_HEADER_CLOSER
= """} // namespace %(namespace)s
163 #endif // %(guard_name)s
166 # The standard includes needed for the stub implementation file. Takes one
167 # string substition with the path to the associated stub header file.
168 IMPLEMENTATION_PREAMBLE
= """// This is generated file. Do not modify directly.
172 #include <stdlib.h> // For NULL.
173 #include <dlfcn.h> // For dysym, dlopen.
179 # The start and end templates for the enum definitions used by the Umbrella
181 UMBRELLA_ENUM_START
= """// Enum and typedef for umbrella initializer.
184 UMBRELLA_ENUM_END
= """ kNumStubModules
189 # Start and end of the extern "C" section for the implementation contents.
190 IMPLEMENTATION_CONTENTS_C_START
= """extern "C" {
193 IMPLEMENTATION_CONTENTS_C_END
= """
199 # Templates for the start and end of a namespace. Takes one parameter, the
201 NAMESPACE_START
= """namespace %s {
204 NAMESPACE_END
= """} // namespace %s
208 # Comment to include before the section declaring all the function pointers
209 # used by the stub functions.
210 FUNCTION_POINTER_SECTION_COMMENT
= (
211 """// Static pointers that will hold the location of the real function
212 // implementations after the module has been loaded.
215 # Template for the module initialization check function. This template
216 # takes two parameteres: the function name, and the conditional used to
217 # verify the module's initialization.
218 MODULE_INITIALIZATION_CHECK_FUNCTION
= (
219 """// Returns true if all stubs have been properly initialized.
230 # Template for the line that initialize the stub pointer. This template takes
231 # the following named parameters:
232 # name: The name of the function.
233 # return_type: The return type.
234 # params: The parameters to the function.
235 STUB_POINTER_INITIALIZER
= """ %(name)s_ptr =
236 reinterpret_cast<%(return_type)s (*)(%(parameters)s)>(
237 dlsym(module, "%(name)s"));
238 VLOG_IF(1, !%(name)s_ptr) << "Couldn't load %(name)s, dlerror() says:\\n"
242 # Template for module initializer function start and end. This template takes
243 # one parameter which is the initializer function name.
244 MODULE_INITIALIZE_START
= """// Initializes the module stubs.
245 void %s(void* module) {
247 MODULE_INITIALIZE_END
= """}
251 # Template for module uninitializer function start and end. This template
252 # takes one parameter which is the initializer function name.
253 MODULE_UNINITIALIZE_START
= (
254 """// Uninitialize the module stubs. Reset pointers to NULL.
257 MODULE_UNINITIALIZE_END
= """}
262 # Open namespace and add typedef for internal data structures used by the
263 # umbrella initializer.
264 UMBRELLA_INITIALIZER_START
= """namespace %s {
265 typedef std::map<StubModules, void*> StubHandleMap;
268 # Function close DSOs on error and clean up dangling references.
269 UMBRELLA_INITIALIZER_CLEANUP_FUNCTION
= (
270 """static void CloseLibraries(StubHandleMap* stub_handles) {
271 for (StubHandleMap::const_iterator it = stub_handles->begin();
272 it != stub_handles->end();
277 stub_handles->clear();
281 # Function to initialize each DSO for the given paths.
282 UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START
= (
283 """bool InitializeStubs(const StubPathMap& path_map) {
284 StubHandleMap opened_libraries;
285 for (int i = 0; i < kNumStubModules; ++i) {
286 StubModules cur_module = static_cast<StubModules>(i);
287 // If a module is missing, we fail.
288 StubPathMap::const_iterator it = path_map.find(cur_module);
289 if (it == path_map.end()) {
290 CloseLibraries(&opened_libraries);
294 // Otherwise, attempt to dlopen the library.
295 const std::vector<std::string>& paths = it->second;
296 bool module_opened = false;
297 for (std::vector<std::string>::const_iterator dso_path = paths.begin();
298 !module_opened && dso_path != paths.end();
300 void* handle = dlopen(dso_path->c_str(), RTLD_LAZY);
301 if (handle != NULL) {
302 module_opened = true;
303 opened_libraries[cur_module] = handle;
305 VLOG(1) << "dlopen(" << dso_path->c_str() << ") failed, "
306 << "dlerror() says:\\n" << dlerror();
310 if (!module_opened) {
311 CloseLibraries(&opened_libraries);
317 # Template to generate code to check if each module initializer correctly
318 # completed, and cleanup on failures. This template takes the following
320 # conditional: The conditional expression for successful initialization.
321 # uninitializers: The statements needed to uninitialize the modules.
322 UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP
= (
323 """ // Check that each module is initialized correctly.
324 // Close all previously opened libraries on failure.
325 if (%(conditional)s) {
327 CloseLibraries(&opened_libraries);
335 # Template for Initialize, Unininitialize, and IsInitialized functions for each
336 # module. This template takes the following named parameters:
337 # initialize: Name of the Initialize function.
338 # uninitialize: Name of the Uninitialize function.
339 # is_initialized: Name of the IsInitialized function.
340 MODULE_FUNCTION_PROTOTYPES
= """bool %(is_initialized)s();
341 void %(initialize)s(void* module);
342 void %(uninitialize)s();
346 # Template for umbrella initializer declaration and associated datatypes.
347 UMBRELLA_INITIALIZER_PROTOTYPE
= (
348 """typedef std::map<StubModules, std::vector<std::string> > StubPathMap;
350 // Umbrella initializer for all the modules in this stub file.
351 bool InitializeStubs(const StubPathMap& path_map);
355 def ExtractModuleName(infile_path
):
356 """Infers the module name from the input file path.
358 The input filename is supposed to be in the form "ModuleName.sigs".
359 This function splits the filename from the extention on that basename of
360 the path and returns that as the module name.
363 infile_path: String holding the path to the input file.
366 The module name as a string.
368 basename
= os
.path
.basename(infile_path
)
370 # This loop continously removes suffixes of the filename separated by a "."
373 new_basename
= os
.path
.splitext(basename
)[0]
374 if basename
== new_basename
:
377 basename
= new_basename
381 def ParseSignatures(infile
):
382 """Parses function signatures in the input file.
384 This function parses a file of signatures into a list of dictionaries that
385 represent the function signatures in the input file. Each dictionary has
387 return_type: A string with the return type.
388 name: A string with the name of the function.
389 params: A list of each function parameter declaration (type + name)
391 The format of the input file is one C-style function signature per line, no
392 trailing semicolon. Empty lines are allowed. An empty line is a line that
393 consists purely of whitespace. Lines that begin with a # are considered
394 comment lines and are ignored.
396 We assume that "int foo(void)" is the same as "int foo()", which is not
397 true in C where "int foo()" is equivalent to "int foo(...)". Our generated
398 code is C++, and we do not handle varargs, so this is a case that can be
402 infile: File object holding a text file of function signatures.
405 A list of dictionaries, where each dictionary represents one function
409 BadSignatureError: A line could not be parsed as a signature.
414 if line
and line
[0] != '#':
415 m
= SIGNATURE_REGEX
.match(line
)
417 raise BadSignatureError('Unparsable line: %s' % line
)
419 {'return_type': m
.group('return_type').strip(),
420 'name': m
.group('name').strip(),
421 'params': [arg
.strip() for arg
in m
.group('params').split(',')]})
425 def WriteWindowsDefFile(module_name
, signatures
, outfile
):
426 """Writes a windows def file to the given output file object.
428 The def file format is basically a list of function names. Generation is
429 simple. After outputting the LIBRARY and EXPORTS lines, print out each
430 function name, one to a line, preceeded by 2 spaces.
433 module_name: The name of the module we are writing a stub for.
434 signatures: The list of signature hashes, as produced by ParseSignatures,
436 outfile: File handle to populate with definitions.
438 outfile
.write('LIBRARY %s\n' % module_name
)
439 outfile
.write('EXPORTS\n')
441 for sig
in signatures
:
442 outfile
.write(' %s\n' % sig
['name'])
445 def QuietRun(args
, filter=None, write_to
=sys
.stdout
):
446 """Invoke |args| as command via subprocess.Popen, filtering lines starting
448 popen
= subprocess
.Popen(args
, stdout
=subprocess
.PIPE
)
449 out
, _
= popen
.communicate()
450 for line
in out
.splitlines():
451 if not filter or not line
.startswith(filter):
452 write_to
.write(line
+ '\n')
453 return popen
.returncode
456 def CreateWindowsLib(module_name
, signatures
, intermediate_dir
, outdir_path
,
458 """Creates a windows library file.
460 Calling this function will create a lib file in the outdir_path that exports
461 the signatures passed into the object. A temporary def file will be created
462 in the intermediate_dir.
465 module_name: The name of the module we are writing a stub for.
466 signatures: The list of signature hashes, as produced by ParseSignatures,
468 intermediate_dir: The directory where the generated .def files should go.
469 outdir_path: The directory where generated .lib files should go.
470 machine: String holding the machine type, 'X86' or 'X64'.
473 SubprocessError: If invoking the windows "lib" tool fails, this is raised
476 def_file_path
= os
.path
.join(intermediate_dir
,
477 module_name
+ '.def')
478 lib_file_path
= os
.path
.join(outdir_path
,
479 module_name
+ '.lib')
480 outfile
= open(def_file_path
, 'w')
482 WriteWindowsDefFile(module_name
, signatures
, outfile
)
486 # Invoke the "lib" program on Windows to create stub .lib files for the
487 # generated definitions. These .lib files can then be used during
488 # delayloading of the dynamic libraries.
489 ret
= QuietRun(['lib', '/nologo',
490 '/machine:' + machine
,
491 '/def:' + def_file_path
,
492 '/out:' + lib_file_path
],
493 filter=' Creating library')
495 raise SubprocessError(
496 'Failed creating %s for %s' % (lib_file_path
, def_file_path
),
500 class PosixStubWriter(object):
501 """Creates a file of stub functions for a library that is opened via dlopen.
503 Windows provides a function in their compiler known as delay loading, which
504 effectively generates a set of stub functions for a dynamic library that
505 delays loading of the dynamic library/resolution of the symbols until one of
506 the needed functions are accessed.
508 In posix, RTLD_LAZY does something similar with DSOs. This is the default
509 link mode for DSOs. However, even though the symbol is not resolved until
510 first usage, the DSO must be present at load time of the main binary.
512 To simulate the windows delay load procedure, we need to create a set of
513 stub functions that allow for correct linkage of the main binary, but
514 dispatch to the dynamically resolved symbol when the module is initialized.
516 This class takes a list of function signatures, and generates a set of stub
517 functions plus initialization code for them.
520 def __init__(self
, module_name
, signatures
):
521 """Initializes PosixStubWriter for this set of signatures and module_name.
524 module_name: The name of the module we are writing a stub for.
525 signatures: The list of signature hashes, as produced by ParseSignatures,
528 self
.signatures
= signatures
529 self
.module_name
= module_name
532 def CStyleIdentifier(cls
, identifier
):
533 """Generates a C style identifier.
535 The module_name has all invalid identifier characters removed (anything
536 that's not [_a-zA-Z0-9]) and is run through string.capwords to try
537 and approximate camel case.
540 identifier: The string with the module name to turn to C-style.
543 A string that can be used as part of a C identifier.
545 return string
.capwords(re
.sub(INVALID_C_IDENT_CHARS
, '', identifier
))
548 def EnumName(cls
, module_name
):
549 """Gets the enum name for the module.
551 Takes the module name and creates a suitable enum name. The module_name
552 is munged to be a valid C identifier then prefixed with the string
553 "kModule" to generate a Google style enum name.
556 module_name: The name of the module to generate an enum name for.
559 A string with the name of the enum value representing this module.
561 return 'kModule%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
564 def IsInitializedName(cls
, module_name
):
565 """Gets the name of function that checks initialization of this module.
567 The name is in the format IsModuleInitialized. Where "Module" is replaced
568 with the module name, munged to be a valid C identifier.
571 module_name: The name of the module to generate the function name for.
574 A string with the name of the initialization check function.
576 return 'Is%sInitialized' % PosixStubWriter
.CStyleIdentifier(module_name
)
579 def InitializeModuleName(cls
, module_name
):
580 """Gets the name of the function that initializes this module.
582 The name is in the format InitializeModule. Where "Module" is replaced
583 with the module name, munged to be a valid C identifier.
586 module_name: The name of the module to generate the function name for.
589 A string with the name of the initialization function.
591 return 'Initialize%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
594 def UninitializeModuleName(cls
, module_name
):
595 """Gets the name of the function that uninitializes this module.
597 The name is in the format UninitializeModule. Where "Module" is replaced
598 with the module name, munged to be a valid C identifier.
601 module_name: The name of the module to generate the function name for.
604 A string with the name of the uninitialization function.
606 return 'Uninitialize%s' % PosixStubWriter
.CStyleIdentifier(module_name
)
609 def StubFunctionPointer(cls
, signature
):
610 """Generates a function pointer declaration for the given signature.
613 signature: A signature hash, as produced by ParseSignatures,
614 representating the function signature.
617 A string with the declaration of the function pointer for the signature.
619 return 'static %s (*%s_ptr)(%s) = NULL;' % (signature
['return_type'],
621 ', '.join(signature
['params']))
624 def StubFunction(cls
, signature
):
625 """Generates a stub function definition for the given signature.
627 The function definitions are created with __attribute__((weak)) so that
628 they may be overridden by a real static link or mock versions to be used
632 signature: A signature hash, as produced by ParseSignatures,
633 representating the function signature.
636 A string with the stub function definition.
639 if signature
['return_type'] != 'void':
640 return_prefix
= 'return '
642 # Generate the argument list.
643 arguments
= [re
.split('[\*& ]', arg
)[-1].strip() for arg
in
645 arg_list
= ', '.join(arguments
)
646 if arg_list
== 'void':
649 if arg_list
!= '' and len(arguments
) > 1 and arguments
[-1] == '...':
650 # If the last argment is ... then this is a variadic function.
651 if return_prefix
!= '':
652 return VARIADIC_STUB_FUNCTION_DEFINITION
% {
653 'return_type': signature
['return_type'],
654 'name': signature
['name'],
655 'params': ', '.join(signature
['params']),
656 'arg_list': ', '.join(arguments
[0:-1]),
657 'last_named_arg': arguments
[-2]}
659 return VOID_VARIADIC_STUB_FUNCTION_DEFINITION
% {
660 'name': signature
['name'],
661 'params': ', '.join(signature
['params']),
662 'arg_list': ', '.join(arguments
[0:-1]),
663 'last_named_arg': arguments
[-2]}
665 # This is a regular function.
666 return STUB_FUNCTION_DEFINITION
% {
667 'return_type': signature
['return_type'],
668 'name': signature
['name'],
669 'params': ', '.join(signature
['params']),
670 'return_prefix': return_prefix
,
671 'arg_list': arg_list
}
674 def WriteImplementationPreamble(cls
, header_path
, outfile
):
675 """Write the necessary includes for the implementation file.
678 header_path: The path to the header file.
679 outfile: The file handle to populate.
681 outfile
.write(IMPLEMENTATION_PREAMBLE
% header_path
)
684 def WriteUmbrellaInitializer(cls
, module_names
, namespace
, outfile
):
685 """Writes a single function that will open + initialize each module.
687 This intializer will take in an stl map of that lists the correct
688 dlopen target for each module. The map type is
689 std::map<enum StubModules, vector<std::string>> which matches one module
690 to a list of paths to try in dlopen.
692 This function is an all-or-nothing function. If any module fails to load,
693 all other modules are dlclosed, and the function returns. Though it is
694 not enforced, this function should only be called once.
697 module_names: A list with the names of the modules in this stub file.
698 namespace: The namespace these functions should be in.
699 outfile: The file handle to populate with pointer definitions.
701 outfile
.write(UMBRELLA_INITIALIZER_START
% namespace
)
702 outfile
.write(UMBRELLA_INITIALIZER_CLEANUP_FUNCTION
)
704 # Create the initializaiton function that calls all module initializers,
705 # checks if they succeeded, and backs out module loads on an error.
706 outfile
.write(UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START
)
708 '\n // Initialize each module if we have not already failed.\n')
709 for module
in module_names
:
710 outfile
.write(' %s(opened_libraries[%s]);\n' %
711 (PosixStubWriter
.InitializeModuleName(module
),
712 PosixStubWriter
.EnumName(module
)))
715 # Output code to check the initialization status, clean up on error.
716 initializer_checks
= ['!%s()' % PosixStubWriter
.IsInitializedName(name
)
717 for name
in module_names
]
718 uninitializers
= ['%s()' % PosixStubWriter
.UninitializeModuleName(name
)
719 for name
in module_names
]
720 outfile
.write(UMBRELLA_INITIALIZER_CHECK_AND_CLEANUP
% {
721 'conditional': ' ||\n '.join(initializer_checks
),
722 'uninitializers': ';\n '.join(uninitializers
)})
723 outfile
.write('\n} // namespace %s\n' % namespace
)
726 def WriteHeaderContents(cls
, module_names
, namespace
, header_guard
, outfile
):
727 """Writes a header file for the stub file generated for module_names.
729 The header file exposes the following:
730 1) An enum, StubModules, listing with an entry for each enum.
731 2) A typedef for a StubPathMap allowing for specification of paths to
732 search for each module.
733 3) The IsInitialized/Initialize/Uninitialize functions for each module.
734 4) An umbrella initialize function for all modules.
737 module_names: A list with the names of each module in this stub file.
738 namespace: The namespace these functions should be in.
739 header_guard: The macro to use as our header guard.
740 outfile: The output handle to populate.
742 outfile
.write(STUB_HEADER_PREAMBLE
%
743 {'guard_name': header_guard
, 'namespace': namespace
})
745 # Generate the Initializer protoypes for each module.
746 outfile
.write('// Individual module initializer functions.\n')
747 for name
in module_names
:
748 outfile
.write(MODULE_FUNCTION_PROTOTYPES
% {
749 'is_initialized': PosixStubWriter
.IsInitializedName(name
),
750 'initialize': PosixStubWriter
.InitializeModuleName(name
),
751 'uninitialize': PosixStubWriter
.UninitializeModuleName(name
)})
753 # Generate the enum for umbrella initializer.
754 outfile
.write(UMBRELLA_ENUM_START
)
755 outfile
.write(' %s = 0,\n' % PosixStubWriter
.EnumName(module_names
[0]))
756 for name
in module_names
[1:]:
757 outfile
.write(' %s,\n' % PosixStubWriter
.EnumName(name
))
758 outfile
.write(UMBRELLA_ENUM_END
)
760 outfile
.write(UMBRELLA_INITIALIZER_PROTOTYPE
)
761 outfile
.write(STUB_HEADER_CLOSER
% {
762 'namespace': namespace
, 'guard_name':
765 def WriteImplementationContents(self
, namespace
, outfile
):
766 """Given a file handle, write out the stub definitions for this module.
769 namespace: The namespace these functions should be in.
770 outfile: The file handle to populate.
772 outfile
.write(IMPLEMENTATION_CONTENTS_C_START
)
773 self
.WriteFunctionPointers(outfile
)
774 self
.WriteStubFunctions(outfile
)
775 outfile
.write(IMPLEMENTATION_CONTENTS_C_END
)
777 outfile
.write(NAMESPACE_START
% namespace
)
778 self
.WriteModuleInitializeFunctions(outfile
)
779 outfile
.write(NAMESPACE_END
% namespace
)
781 def WriteFunctionPointers(self
, outfile
):
782 """Write the function pointer declarations needed by the stubs.
784 We need function pointers to hold the actual location of the function
785 implementation returned by dlsym. This function outputs a pointer
786 definition for each signature in the module.
788 Pointers will be named with the following pattern "FuntionName_ptr".
791 outfile: The file handle to populate with pointer definitions.
793 outfile
.write(FUNCTION_POINTER_SECTION_COMMENT
)
795 for sig
in self
.signatures
:
796 outfile
.write('%s\n' % PosixStubWriter
.StubFunctionPointer(sig
))
799 def WriteStubFunctions(self
, outfile
):
800 """Write the function stubs to handle dispatching to real implementations.
802 Functions that have a return type other than void will look as follows:
804 ReturnType FunctionName(A a) {
805 return FunctionName_ptr(a);
808 Functions with a return type of void will look as follows:
810 void FunctionName(A a) {
815 outfile: The file handle to populate.
817 outfile
.write('// Stubs that dispatch to the real implementations.\n')
818 for sig
in self
.signatures
:
819 outfile
.write('%s\n' % PosixStubWriter
.StubFunction(sig
))
821 def WriteModuleInitializeFunctions(self
, outfile
):
822 """Write functions to initialize/query initlialization of the module.
824 This creates 2 functions IsModuleInitialized and InitializeModule where
825 "Module" is replaced with the module name, first letter capitalized.
827 The InitializeModule function takes a handle that is retrieved from dlopen
828 and attempts to assign each function pointer above via dlsym.
830 The IsModuleInitialized returns true if none of the required functions
834 outfile: The file handle to populate.
836 ptr_names
= ['%s_ptr' % sig
['name'] for sig
in self
.signatures
]
838 # Construct the conditional expression to check the initialization of
839 # all the function pointers above. It should generate a conjuntion
840 # with each pointer on its own line, indented by six spaces to match
841 # the indentation level of MODULE_INITIALIZATION_CHECK_FUNCTION.
842 initialization_conditional
= ' &&\n '.join(ptr_names
)
844 outfile
.write(MODULE_INITIALIZATION_CHECK_FUNCTION
% (
845 PosixStubWriter
.IsInitializedName(self
.module_name
),
846 initialization_conditional
))
848 # Create function that initializes the module.
849 outfile
.write(MODULE_INITIALIZE_START
%
850 PosixStubWriter
.InitializeModuleName(self
.module_name
))
851 for sig
in self
.signatures
:
852 outfile
.write(STUB_POINTER_INITIALIZER
% {
854 'return_type': sig
['return_type'],
855 'parameters': ', '.join(sig
['params'])})
856 outfile
.write(MODULE_INITIALIZE_END
)
858 # Create function that uninitializes the module (sets all pointers to
860 outfile
.write(MODULE_UNINITIALIZE_START
%
861 PosixStubWriter
.UninitializeModuleName(self
.module_name
))
862 for sig
in self
.signatures
:
863 outfile
.write(' %s_ptr = NULL;\n' % sig
['name'])
864 outfile
.write(MODULE_UNINITIALIZE_END
)
867 def CreateOptionParser():
868 """Creates an OptionParser for the configuration options of script.
871 A OptionParser object.
873 parser
= optparse
.OptionParser(usage
='usage: %prog [options] input')
874 parser
.add_option('-o',
878 help='Output location.')
879 parser
.add_option('-i',
880 '--intermediate_dir',
881 dest
='intermediate_dir',
883 help=('Location of intermediate files. Ignored for %s type'
884 % FILE_TYPE_WIN_DEF
))
885 parser
.add_option('-t',
889 help=('Type of file. Valid types are "%s" or "%s" or "%s" '
891 (FILE_TYPE_POSIX_STUB
, FILE_TYPE_WIN_X86
,
892 FILE_TYPE_WIN_X64
, FILE_TYPE_WIN_DEF
)))
893 parser
.add_option('-s',
895 dest
='stubfile_name',
897 help=('Name of posix_stubs output file. Only valid with '
898 '%s type.' % FILE_TYPE_POSIX_STUB
))
899 parser
.add_option('-p',
900 '--path_from_source',
901 dest
='path_from_source',
903 help=('The relative path from the project root that the '
904 'generated file should consider itself part of (eg. '
905 'third_party/ffmpeg). This is used to generate the '
906 'header guard and namespace for our initializer '
907 'functions and does NOT affect the physical output '
908 'location of the file like -o does. Ignored for '
910 (FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
)))
911 parser
.add_option('-e',
912 '--extra_stub_header',
913 dest
='extra_stub_header',
915 help=('File to insert after the system includes in the '
916 'generated stub implemenation file. Ignored for '
918 (FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
)))
919 parser
.add_option('-m',
923 help=('Name of output DLL or LIB for DEF creation using '
924 '%s type.' % FILE_TYPE_WIN_DEF
))
930 """Parses the options and terminates program if they are not sane.
933 The pair (optparse.OptionValues, [string]), that is the output of
934 a successful call to parser.parse_args().
936 parser
= CreateOptionParser()
937 options
, args
= parser
.parse_args()
940 parser
.error('No inputs specified')
942 if options
.out_dir
is None:
943 parser
.error('Output location not specified')
945 if (options
.type not in
946 [FILE_TYPE_WIN_X86
, FILE_TYPE_WIN_X64
, FILE_TYPE_POSIX_STUB
,
948 parser
.error('Invalid output file type: %s' % options
.type)
950 if options
.type == FILE_TYPE_POSIX_STUB
:
951 if options
.stubfile_name
is None:
952 parser
.error('Output file name needed for %s' % FILE_TYPE_POSIX_STUB
)
953 if options
.path_from_source
is None:
954 parser
.error('Path from source needed for %s' % FILE_TYPE_POSIX_STUB
)
956 if options
.type == FILE_TYPE_WIN_DEF
:
957 if options
.module_name
is None:
958 parser
.error('Module name needed for %s' % FILE_TYPE_WIN_DEF
)
963 def EnsureDirExists(dir):
964 """Creates a directory. Does not use the more obvious 'if not exists: create'
965 to avoid race with other invocations of the same code, which will error out
966 on makedirs if another invocation has succeeded in creating the directory
967 since the existence check."""
971 if not os
.path
.isdir(dir):
975 def CreateOutputDirectories(options
):
976 """Creates the intermediate and final output directories.
978 Given the parsed options, create the intermediate and final output
979 directories if they do not exist. Returns the paths to both directories
983 options: An OptionParser.OptionValues object with the parsed options.
986 The pair (out_dir, intermediate_dir), both of which are strings.
988 out_dir
= os
.path
.normpath(options
.out_dir
)
989 intermediate_dir
= os
.path
.normpath(options
.intermediate_dir
)
990 if intermediate_dir
is None:
991 intermediate_dir
= out_dir
993 EnsureDirExists(out_dir
)
994 EnsureDirExists(intermediate_dir
)
996 return out_dir
, intermediate_dir
999 def CreateWindowsLibForSigFiles(sig_files
, out_dir
, intermediate_dir
, machine
):
1000 """For each signature file, create a windows lib.
1003 sig_files: Array of strings with the paths to each signature file.
1004 out_dir: String holding path to directory where the generated libs go.
1005 intermediate_dir: String holding path to directory generated intermdiate
1007 machine: String holding the machine type, 'X86' or 'X64'.
1009 for input_path
in sig_files
:
1010 infile
= open(input_path
, 'r')
1012 signatures
= ParseSignatures(infile
)
1013 module_name
= ExtractModuleName(os
.path
.basename(input_path
))
1014 CreateWindowsLib(module_name
, signatures
, intermediate_dir
, out_dir
,
1020 def CreateWindowsDefForSigFiles(sig_files
, out_dir
, module_name
):
1021 """For all signature files, create a single windows def file.
1024 sig_files: Array of strings with the paths to each signature file.
1025 out_dir: String holding path to directory where the generated def goes.
1026 module_name: Name of the output DLL or LIB which will link in the def file.
1029 for input_path
in sig_files
:
1030 infile
= open(input_path
, 'r')
1032 signatures
+= ParseSignatures(infile
)
1036 def_file_path
= os
.path
.join(
1037 out_dir
, os
.path
.splitext(os
.path
.basename(module_name
))[0] + '.def')
1038 outfile
= open(def_file_path
, 'w')
1041 WriteWindowsDefFile(module_name
, signatures
, outfile
)
1046 def CreatePosixStubsForSigFiles(sig_files
, stub_name
, out_dir
,
1047 intermediate_dir
, path_from_source
,
1049 """Create a posix stub library with a module for each signature file.
1052 sig_files: Array of strings with the paths to each signature file.
1053 stub_name: String with the basename of the generated stub file.
1054 out_dir: String holding path to directory for the .h files.
1055 intermediate_dir: String holding path to directory for the .cc files.
1056 path_from_source: String with relative path of generated files from the
1058 extra_stub_header: String with path to file of extra lines to insert
1059 into the generated header for the stub library.
1061 header_base_name
= stub_name
+ '.h'
1062 header_path
= os
.path
.join(out_dir
, header_base_name
)
1063 impl_path
= os
.path
.join(intermediate_dir
, stub_name
+ '.cc')
1065 module_names
= [ExtractModuleName(path
) for path
in sig_files
]
1066 namespace
= path_from_source
.replace('/', '_').lower()
1067 header_guard
= '%s_' % namespace
.upper()
1068 header_include_path
= os
.path
.join(path_from_source
, header_base_name
)
1070 # First create the implementation file.
1071 impl_file
= open(impl_path
, 'w')
1073 # Open the file, and create the preamble which consists of a file
1074 # header plus any necessary includes.
1075 PosixStubWriter
.WriteImplementationPreamble(header_include_path
,
1077 if extra_stub_header
is not None:
1078 extra_header_file
= open(extra_stub_header
, 'r')
1080 impl_file
.write('\n')
1081 for line
in extra_header_file
:
1082 impl_file
.write(line
)
1083 impl_file
.write('\n')
1085 extra_header_file
.close()
1087 # For each signature file, generate the stub population functions
1088 # for that file. Each file represents one module.
1089 for input_path
in sig_files
:
1090 name
= ExtractModuleName(input_path
)
1091 infile
= open(input_path
, 'r')
1093 signatures
= ParseSignatures(infile
)
1096 writer
= PosixStubWriter(name
, signatures
)
1097 writer
.WriteImplementationContents(namespace
, impl_file
)
1099 # Lastly, output the umbrella function for the file.
1100 PosixStubWriter
.WriteUmbrellaInitializer(module_names
, namespace
,
1105 # Then create the associated header file.
1106 header_file
= open(header_path
, 'w')
1108 PosixStubWriter
.WriteHeaderContents(module_names
, namespace
,
1109 header_guard
, header_file
)
1115 options
, args
= ParseOptions()
1116 out_dir
, intermediate_dir
= CreateOutputDirectories(options
)
1118 if options
.type == FILE_TYPE_WIN_X86
:
1119 CreateWindowsLibForSigFiles(args
, out_dir
, intermediate_dir
, 'X86')
1120 elif options
.type == FILE_TYPE_WIN_X64
:
1121 CreateWindowsLibForSigFiles(args
, out_dir
, intermediate_dir
, 'X64')
1122 elif options
.type == FILE_TYPE_POSIX_STUB
:
1123 CreatePosixStubsForSigFiles(args
, options
.stubfile_name
, out_dir
,
1124 intermediate_dir
, options
.path_from_source
,
1125 options
.extra_stub_header
)
1126 elif options
.type == FILE_TYPE_WIN_DEF
:
1127 CreateWindowsDefForSigFiles(args
, out_dir
, options
.module_name
)
1130 if __name__
== '__main__':