summaryrefslogtreecommitdiffstats
path: root/third_party/ffmpeg
diff options
context:
space:
mode:
authorajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-03 21:12:01 +0000
committerajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-03 21:12:01 +0000
commitf5b50b81cdf4a883c1eba35394b7b2191179d246 (patch)
tree2ab8ee914b6d655b4b7d3900d642e75266040234 /third_party/ffmpeg
parent9cee84a06a0694547bf977f81918d318aecd2d9c (diff)
downloadchromium_src-f5b50b81cdf4a883c1eba35394b7b2191179d246.zip
chromium_src-f5b50b81cdf4a883c1eba35394b7b2191179d246.tar.gz
chromium_src-f5b50b81cdf4a883c1eba35394b7b2191179d246.tar.bz2
Python script for auto-generating stub/def files for a list of signatures.
This python script takes a list of C-style signatures and generates either a windows def file, or a C++ stub file that allows for binding of functions via dlopen/dlsym. This is used to support delay loading in windows and simulate it on posix systems. Review URL: http://codereview.chromium.org/113803 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17534 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/ffmpeg')
-rwxr-xr-xthird_party/ffmpeg/generate_stubs.py833
-rwxr-xr-xthird_party/ffmpeg/generate_stubs_unittest.py311
2 files changed, 1144 insertions, 0 deletions
diff --git a/third_party/ffmpeg/generate_stubs.py b/third_party/ffmpeg/generate_stubs.py
new file mode 100755
index 0000000..1288697
--- /dev/null
+++ b/third_party/ffmpeg/generate_stubs.py
@@ -0,0 +1,833 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Creates windows and posix stub files for a given set of signatures.
+
+For libraries that need to be loaded outside of the standard executable startup
+path mechanism, stub files need to be generated for the wanted functions. In
+windows, this is done via "def" files and the delay load mechanism. On a posix
+system, a set of stub functions need to be generated that dispatch to functions
+found via dlsym.
+
+This script takes a set of files, where each file is a list of C-style
+signatures (one signature per line). The output is either a windows def file,
+or a header + implementation file of stubs suitable for use in a posix system.
+"""
+
+__author__ = 'ajwong@chromium.org (Albert J. Wong)'
+
+import optparse
+import os
+import re
+import string
+import subprocess
+
+
+class Error(Exception):
+ pass
+
+
+class BadSignatureError(Error):
+ pass
+
+
+# Regular expression used to parse signatures in the input files. The regex
+# is built around identifying the "identifier" for the function name. We
+# consider the identifier to be the string that follows these constraints:
+#
+# 1) Starts with [_a-ZA-Z] (C++ spec 2.10).
+# 2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10).
+# 3) Preceeds an opening parenthesis by 0 or more whitespace chars.
+#
+# From that, all preceeding characters are considered the return value.
+# Trailing characters should have a substring matching the form (.*). That
+# is considered the arguments.
+SIGNATURE_REGEX = re.compile('(.+?)([_a-zA-Z][_a-zA-Z0-9]+)\s*\((.*?)\)')
+
+# Used for generating C++ identifiers.
+INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]')
+
+# Constans defning the supported file types options.
+FILE_TYPE_WIN = 'windows_lib'
+FILE_TYPE_POSIX_STUB = 'posix_stubs'
+
+
+def RemoveTrailingSlashes(path):
+ """Removes trailing forward slashes from the path.
+
+ We expect unix style path separators (/). Gyp passes unix style
+ separators even in windows so this is okay.
+
+ Args:
+ path: The path to strip trailing slashes from.
+
+ Returns:
+ The path with trailing slashes removed.
+ """
+ # Special case a single trailing slash since '/' != '' in a path.
+ if path == '/':
+ return path
+
+ return path.rstrip('/')
+
+
+def ExtractModuleName(infile_path):
+ """Infers the module name from the input file path.
+
+ The input filename is supposed to be in the form "ModuleName.sigs".
+ This function splits the filename from the extention on that basename of
+ the path and returns that as the module name.
+
+ Args:
+ infile_path: String holding the path to the input file.
+
+ Returns:
+ The module name as a string.
+ """
+ # File name format is ModuleName.sig so split at the period.
+ basename = os.path.basename(infile_path)
+ return os.path.splitext(basename)[0]
+
+
+def ParseSignatures(infile):
+ """Parses function signatures in the input file.
+
+ This function parses a file of signatures into a list of dictionaries that
+ represent the function signatures in the input file. Each dictionary has
+ the following keys:
+ return_type: A string with the return type.
+ name: A string with the name of the function.
+ params: A list of each function parameter declaration (type + name)
+
+ The format of the input file is one C-style function signature per line, no
+ trailing semicolon. Empty lines are allowed. An empty line is a line that
+ consists purely of whitespace. Lines that begin with a # are considered
+ comment lines and are ignored.
+
+ We assume that "int foo(void)" is the same as "int foo()", which is not
+ true in C where "int foo()" is equivalent to "int foo(...)". Our generated
+ code is C++, and we do not handle varargs, so this is a case that can be
+ ignored for now.
+
+ Args:
+ infile: File object holding a text file of function signatures.
+
+ Returns:
+ A list of dictionaries, where each dictionary represents one function
+ signature.
+
+ Raises:
+ BadSignatureError: A line could not be parsed as a signature.
+ """
+ signatures = []
+ for line in infile:
+ line = line.strip()
+ if line and line[0] != '#':
+ m = SIGNATURE_REGEX.match(line)
+ if m is None:
+ raise BadSignatureError('Unparsable line: %s' % line)
+ signatures.append(
+ {'return_type': m.group(1).strip(),
+ 'name': m.group(2).strip(),
+ 'params': [arg.strip() for arg in m.group(3).split(',')]})
+ return signatures
+
+
+class WindowsLibCreator(object):
+ """Creates an import library for the functions provided.
+
+ In windows, we created a def file listing the library entry points, then
+ generate a stub .lib based on that def file. This class enapsulates this
+ logic.
+ """
+
+ def __init__(self, module_name, signatures, outdir_path):
+ """Initializes the WindowsLibCreator for creating a library stub.
+
+ Args:
+ module_name: The name of the module we are writing a stub for.
+ signatures: The list of signatures to create stubs for.
+ outdir_path: The directory that generated files should go into.
+ """
+ self.module_name = module_name
+ self.signatures = signatures
+ self.outdir_path = outdir_path
+
+ def DefFilePath(self):
+ """Generates the path of the def file for the given module_name.
+
+ Returns:
+ A string with the path to the def file.
+ """
+ # Output file name is in the form "module_name.def".
+ return '%s/%s.def' % (self.outdir_path, self.module_name)
+
+ def LibFilePath(self):
+ """Generates the path of the lib file for the given module_name.
+
+ Returns:
+ A string with the path to the lib file.
+ """
+ # Output file name is in the form "module_name.lib".
+ return '%s/%s.lib' % (self.outdir_path, self.module_name)
+
+ def WriteDefFile(self):
+ """Creates a windows library defintion file.
+
+ The def file format is basically a list of function names. Generation is
+ simple. After outputting the LIBRARY and EXPORTS lines, print out each
+ function name, one to a line, preceeded by 2 spaces.
+
+ Calling this function will create a def file in the outdir_path with the
+ name "module_name.def".
+ """
+ # Open the def file for writing.
+ try:
+ outfile = open(self.DefFilePath(), 'w')
+ self._WriteDefFile(outfile)
+ finally:
+ if outfile is not None:
+ outfile.close()
+
+ def _WriteDefFile(self, outfile):
+ """Implementation of WriteDefFile for testing.
+
+ This implementation allows injection of the outfile object for testing.
+
+ Args:
+ outfile: File to populate with definitions.
+ """
+ # Write file header.
+ outfile.write('LIBRARY %s\n' % self.module_name)
+ outfile.write('EXPORTS\n')
+
+ # Output an entry for each signature.
+ for sig in self.signatures:
+ outfile.write(' ')
+ outfile.write(sig['name'])
+ outfile.write('\n')
+
+ def CreateLib(self):
+ """Creates a windows library file.
+
+ Calling this function will create a lib file in the outdir_path that exports
+ the signatures passed into the object.
+ """
+ self.WriteDefFile()
+ subprocess.call(['lib', '/nologo', '/machine:X86',
+ '/def:' + self.DefFilePath(),
+ '/out:' + self.LibFilePath()])
+
+
+class PosixStubWriter(object):
+ """Creates a file of stub functions for a library that is opened via dloepn.
+
+ Windows provides a function in their compiler known as delay loading, which
+ effectively generates a set of stub functions for a dynamic library that
+ delays loading of the dynamic library/resolution of the symbols until one of
+ the needed functions are accessed.
+
+ In posix, RTLD_LAZY does something similar with DSOs. This is the default
+ link mode for DSOs. However, even though the symbol is not resolved until
+ first usage, the DSO must be present at load time of the main binary.
+
+ To simulate the windows delay load procedure, we need to create a set of
+ stub functions that allow for correct linkage of the main binary, but
+ dispatch to the dynamically resolved symbol when the module is initialized.
+
+ This class takes a list of function signatures, and generates a set of stub
+ functions plus initialization code for them.
+ """
+
+ def __init__(self, module_name, signatures):
+ """Initializes PosixStubWriter for this set of signatures and module_name.
+
+ Args:
+ module_name: The name of the module we are writing a stub for.
+ signatures: The list of signatures to create stubs for.
+ """
+ self.signatures = signatures
+ self.module_name = module_name
+
+ @classmethod
+ def CStyleIdentifier(cls, identifier):
+ """Generates a C style identifier.
+
+ The module_name has all invalid identifier characters removed (anything
+ that's not [_a-zA-Z0-9]) and is run through string.capwords to try
+ and approximate camel case.
+
+ Args:
+ identifier: The string with the module name to turn to C-style.
+
+ Returns:
+ A string that can be used as part of a C identifier.
+ """
+ return string.capwords(re.sub(INVALID_C_IDENT_CHARS, '', identifier))
+
+ @classmethod
+ def EnumName(cls, module_name):
+ """Gets the enum name for the module.
+
+ Takes the module name and creates a suitable enum name. The module_name
+ is munged to be a valid C identifier then prefixed with the string
+ "kModule" to generate a Google style enum name.
+
+ Args:
+ module_name: The name of the module to generate an enum name for.
+
+ Returns:
+ A string with the name of the enum value representing this module.
+ """
+ return 'kModule%s' % PosixStubWriter.CStyleIdentifier(module_name)
+
+ @classmethod
+ def IsInitializedName(cls, module_name):
+ """Gets the name of function that checks initialization of this module.
+
+ The name is in the format IsModuleInitialized. Where "Module" is replaced
+ with the module name, munged to be a valid C identifier.
+
+ Args:
+ module_name: The name of the module to generate the function name for.
+
+ Returns:
+ A string with the name of the initialization check function.
+ """
+ return 'Is%sInitialized' % PosixStubWriter.CStyleIdentifier(module_name)
+
+ @classmethod
+ def InitializeModuleName(cls, module_name):
+ """Gets the name of the function that initializes this module.
+
+ The name is in the format InitializeModule. Where "Module" is replaced
+ with the module name, munged to be a valid C identifier.
+
+ Args:
+ module_name: The name of the module to generate the function name for.
+
+ Returns:
+ A string with the name of the initialization function.
+ """
+ return 'Initialize%s' % PosixStubWriter.CStyleIdentifier(module_name)
+
+ @classmethod
+ def UninitializeModuleName(cls, module_name):
+ """Gets the name of the function that uninitializes this module.
+
+ The name is in the format UninitializeModule. Where "Module" is replaced
+ with the module name, munged to be a valid C identifier.
+
+ Args:
+ module_name: The name of the module to generate the function name for.
+
+ Returns:
+ A string with the name of the uninitialization function.
+ """
+ return 'Uninitialize%s' % PosixStubWriter.CStyleIdentifier(module_name)
+
+ @classmethod
+ def HeaderFilePath(cls, stubfile_name, outdir_path):
+ """Generate the path to the header file for the stub code.
+
+ Args:
+ stubfile_name: A string with name of the output file (minus extension).
+ outdir_path: A string with the path of the directory to create the files
+ in.
+
+ Returns:
+ A string with the path to the header file.
+ """
+ return '%s/%s.h' % (outdir_path, stubfile_name)
+
+ @classmethod
+ def ImplementationFilePath(cls, stubfile_name, outdir_path):
+ """Generate the path to the implementation file for the stub code.
+
+ Args:
+ stubfile_name: A string with name of the output file (minus extension).
+ outdir_path: A string with the path of the directory to create the files
+ in.
+
+ Returns:
+ A string with the path to the implementation file.
+ """
+ return '%s/%s.cc' % (outdir_path, stubfile_name)
+
+ @classmethod
+ def StubFunctionPointer(cls, signature):
+ """Generates a function pointer declaration for the given signature.
+
+ Args:
+ signature: The hash representing the function signature.
+
+ Returns:
+ A string with the declaration of the function pointer for the signature.
+ """
+ return 'static %s (*%s_ptr)(%s) = NULL;' % (signature['return_type'],
+ signature['name'],
+ ', '.join(signature['params']))
+
+ @classmethod
+ def StubFunction(cls, signature):
+ """Generates a stub function definition for the given signature.
+
+ Args:
+ signature: The hash representing the function signature.
+
+ Returns:
+ A string with the stub function definition.
+ """
+ # Generate the return statement prefix if this is not a void function.
+ return_prefix = ''
+ if signature['return_type'] != 'void':
+ return_prefix = 'return '
+
+ # Generate the argument list.
+ arguments = [re.split('[\*& ]', arg)[-1].strip() for arg in
+ signature['params']]
+ arg_list = ', '.join(arguments)
+ if arg_list == 'void':
+ arg_list = ''
+
+ return """%(return_type)s %(name)s(%(params)s) {
+ %(return_prefix)s%(name)s_ptr(%(arg_list)s);
+}""" % {'return_type': signature['return_type'],
+ 'name': signature['name'],
+ 'params': ', '.join(signature['params']),
+ 'return_prefix': return_prefix,
+ 'arg_list': arg_list}
+
+ @classmethod
+ def WriteImplementationPreamble(cls, header_path, outfile):
+ """Write the necessary includes for the implementation file.
+
+ Args:
+ header_path: The path to the header file.
+ outfile: The file handle to populate.
+ """
+ # Write file header.
+ outfile.write("""// This is generated file. Do not modify directly.
+
+#include "%s"
+
+#include <stdlib.h> // For NULL.
+#include <dlfcn.h> // For dysym, dlopen.
+
+#include <map>
+#include <vector>
+""" % header_path)
+
+ @classmethod
+ def WriteUmbrellaInitializer(cls, module_names, namespace, outfile):
+ """Writes a single function that will open + inialize each module.
+
+ This intializer will take in an stl map of that lists the correct
+ dlopen target for each module. The map type is
+ std::map<enum StubModules, vector<std::string>> which matches one module
+ to a list of paths to try in dlopen.
+
+ This function is an all-or-nothing function. If any module fails to load,
+ all other modules are dlclosed, and the function returns. Though it is
+ not enforced, this function should only be called once.
+
+ Args:
+ module_names: A list with the names of the modules in this stub file.
+ namespace: The namespace these functions should be in.
+ outfile: The file handle to populate with pointer definitions.
+ """
+ # Open namespace and add typedef for internal data structure.
+ outfile.write("""namespace %s {
+typedef std::map<StubModules, void*> StubHandleMap;
+""" % namespace)
+
+ # Write a static cleanup function.
+ outfile.write("""static void CloseLibraries(StubHandleMap* stub_handles) {
+ for (StubHandleMap::const_iterator it = stub_handles->begin();
+ it != stub_handles->end();
+ ++it) {
+ dlclose(it->second);
+ }
+
+ stub_handles->clear();
+}
+""")
+
+ # Create the stub initialize function.
+ outfile.write("""bool InitializeStubs(const StubPathMap& path_map) {
+ StubHandleMap opened_libraries;
+ for (int i = 0; i < kNumStubModules; ++i) {
+ StubModules cur_module = static_cast<StubModules>(i);
+ // If a module is missing, we fail.
+ StubPathMap::const_iterator it = path_map.find(cur_module);
+ if (it == path_map.end()) {
+ CloseLibraries(&opened_libraries);
+ return false;
+ }
+
+ // Otherwise, attempt to dlopen the library.
+ const std::vector<std::string>& paths = it->second;
+ bool module_opened = false;
+ for (std::vector<std::string>::const_iterator dso_path = paths.begin();
+ !module_opened && dso_path != paths.end();
+ ++dso_path) {
+ void* handle = dlopen(dso_path->c_str(), RTLD_LAZY);
+ if (handle != NULL) {
+ module_opened = true;
+ opened_libraries[cur_module] = handle;
+ }
+ }
+
+ if (!module_opened) {
+ CloseLibraries(&opened_libraries);
+ return false;
+ }
+ }
+
+ // Initialize each module if we have not already failed.
+""")
+
+ # Call each module initializer.
+ for module in module_names:
+ outfile.write(' %s(opened_libraries[%s]);\n' %
+ (PosixStubWriter.InitializeModuleName(module),
+ PosixStubWriter.EnumName(module)))
+ outfile.write('\n')
+
+ # Check the initialization status, clean up on error.
+ initializer_checks = ['!%s()' % PosixStubWriter.IsInitializedName(name)
+ for name in module_names]
+ uninitializers = ['%s()' % PosixStubWriter.UninitializeModuleName(name)
+ for name in module_names]
+ outfile.write(""" // Check that each module is initialized correctly.
+ // Close all previously opened libraries on failure.
+ if (%(conditional)s) {
+ %(uninitializers)s;
+ CloseLibraries(&opened_libraries);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace %(namespace)s
+""" % {'conditional': ' ||\n '.join(initializer_checks),
+ 'uninitializers': ';\n '.join(uninitializers),
+ 'namespace': namespace})
+
+ @classmethod
+ def WriteHeaderContents(cls, module_names, namespace, header_guard, outfile):
+ """Writes a header file for the stub file generated for module_names.
+
+ The header file exposes the following:
+ 1) An enum, StubModules, listing with an entry for each enum.
+ 2) A typedef for a StubPathMap allowing for specification of paths to
+ search for each module.
+ 3) The IsInitialized/Initialize/Uninitialize functions for each module.
+ 4) An umbrella initialize function for all modules.
+
+ Args:
+ module_names: A list with the names of each module in this stub file.
+ namespace: The namespace these functions should be in.
+ header_guard: The macro to use as our header guard.
+ outfile: The output file to populate.
+ """
+ outfile.write("""// This is generated file. Do not modify directly.
+
+#ifndef %(guard_name)s
+#define %(guard_name)s
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace %(namespace)s {
+""" % {'guard_name': header_guard, 'namespace': namespace})
+
+ # Generate the Initializer protoypes for each module.
+ outfile.write('// Individual module initializer functions.\n')
+ for name in module_names:
+ outfile.write("""bool %s();
+void %s(void* module);
+void %s();
+
+""" % (PosixStubWriter.IsInitializedName(name),
+ PosixStubWriter.InitializeModuleName(name),
+ PosixStubWriter.UninitializeModuleName(name)))
+
+ # Generate the enum for umbrella initializer.
+ outfile.write('// Enum and typedef for umbrella initializer.\n')
+ outfile.write('enum StubModules {\n')
+ outfile.write(' %s = 0,\n' % PosixStubWriter.EnumName(module_names[0]))
+ for name in module_names[1:]:
+ outfile.write(' %s,\n' % PosixStubWriter.EnumName(name))
+ outfile.write(' kNumStubModules\n')
+ outfile.write('};\n')
+ outfile.write('\n')
+
+ # Generate typedef and prototype for umbrella initializer and close the
+ # header file.
+ outfile.write(
+"""typedef std::map<StubModules, std::vector<std::string> > StubPathMap;
+
+// Umbrella initializer for all the modules in this stub file.
+bool InitializeStubs(const StubPathMap& path_map);
+} // namespace %s
+
+#endif // %s
+""" % (namespace, header_guard))
+
+ def WriteImplementationContents(self, namespace, outfile):
+ """Given a file handle, write out the stub definitions for this module.
+
+ Args:
+ namespace: The namespace these functions should be in.
+ outfile: The file handle to populate.
+ """
+ outfile.write('extern "C" {\n')
+ self.WriteFunctionPointers(outfile)
+ self.WriteStubFunctions(outfile)
+ outfile.write('} // extern "C"\n')
+ outfile.write('\n')
+
+ outfile.write('namespace %s {\n' % namespace)
+ outfile.write('\n')
+ self.WriteModuleInitializeFunctions(outfile)
+ outfile.write('} // namespace %s\n' % namespace)
+
+ def WriteFunctionPointers(self, outfile):
+ """Write the function pointer declarations needed by the stubs.
+
+ We need function pointers to hold the actual location of the function
+ implementation returned by dlsym. This function outputs a pointer
+ definition for each signature in the module.
+
+ Pointers will be named with the following pattern "FuntionName_ptr".
+
+ Args:
+ outfile: The file handle to populate with pointer definitions.
+ """
+ outfile.write(
+"""// Static pointers that will hold the location of the real function
+// implementations after the module has been loaded.
+""")
+ for sig in self.signatures:
+ outfile.write("%s\n" % PosixStubWriter.StubFunctionPointer(sig))
+ outfile.write("\n");
+
+ def WriteStubFunctions(self, outfile):
+ """Write the function stubs to handle dispatching to real implementations.
+
+ Functions that have a return type other than void will look as follows:
+
+ ReturnType FunctionName(A a) {
+ return FunctionName_ptr(a);
+ }
+
+ Functions with a return type of void will look as follows:
+
+ void FunctionName(A a) {
+ FunctionName_ptr(a);
+ }
+
+ Args:
+ outfile: The file handle to populate.
+ """
+ outfile.write('// Stubs that dispatch to the real implementations.\n')
+ for sig in self.signatures:
+ outfile.write("%s\n" % PosixStubWriter.StubFunction(sig));
+
+ def WriteModuleInitializeFunctions(self, outfile):
+ """Write functions to initialize/query initlialization of the module.
+
+ This creates 2 functions IsModuleInitialized and InitializeModule where
+ "Module" is replaced with the module name, first letter capitalized.
+
+ The InitializeModule function takes a handle that is retrieved from dlopen
+ and attempts to assign each function pointer above via dlsym.
+
+ The IsModuleInitialized returns true if none of the required functions
+ pointers are NULL.
+
+ Args:
+ outfile: The file handle to populate.
+ """
+ ptr_names = ['%s_ptr' % sig['name'] for sig in self.signatures]
+
+ # Construct the function that tests that all the function pointers above
+ # have been properly populated.
+ outfile.write(
+"""// Returns true if all stubs have been properly initialized.
+bool %s() {
+ if (%s) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+""" % (PosixStubWriter.IsInitializedName(self.module_name),
+ ' &&\n '.join(ptr_names)))
+
+ # Create function that initializes the module.
+ outfile.write('// Initializes the module stubs.\n')
+ outfile.write('void %s(void* module) {\n' %
+ PosixStubWriter.InitializeModuleName(self.module_name))
+ for sig in self.signatures:
+ outfile.write(""" %(name)s_ptr =
+ reinterpret_cast<%(return_type)s (*)(%(parameters)s)>(
+ dlsym(module, "%(name)s"));
+""" % {'name': sig['name'],
+ 'return_type': sig['return_type'],
+ 'parameters': ', '.join(sig['params'])})
+ outfile.write('}\n')
+ outfile.write('\n')
+
+ # Create function that uninitializes the module (sets all pointers to
+ # NULL).
+ outfile.write('// Uninitialize the module stubs. Reset to NULL.\n')
+ outfile.write('void %s() {\n' %
+ PosixStubWriter.UninitializeModuleName(self.module_name))
+ for sig in self.signatures:
+ outfile.write(' %s_ptr = NULL;\n' % sig['name'])
+ outfile.write('}\n')
+ outfile.write('\n')
+
+
+def main():
+ parser = optparse.OptionParser(usage='usage: %prog [options] input')
+ parser.add_option('-o',
+ '--output',
+ dest='out_dir',
+ default=None,
+ help='Output location.')
+ parser.add_option('-t',
+ '--type',
+ dest='type',
+ default=None,
+ help=('Type of file. Either "%s" or "%s"' %
+ (FILE_TYPE_POSIX_STUB, FILE_TYPE_WIN)))
+ parser.add_option('-s',
+ '--stubfile_name',
+ dest='stubfile_name',
+ default=None,
+ help=('Name of posix_stubs output file. Ignored for '
+ '%s type.' % FILE_TYPE_WIN))
+ parser.add_option('-p',
+ '--path_from_source',
+ dest='path_from_source',
+ default=None,
+ help=('The relative path from the project root that the '
+ 'generated file should consider itself part of (eg. '
+ 'third_party/ffmpeg). This is used to generate the '
+ 'header guard and namespace for our initializer '
+ 'functions and does NOT affect the physical output '
+ 'location of the file like -o does. Ignored for '
+ ' %s type.' % FILE_TYPE_WIN))
+ parser.add_option('-e',
+ '--extra_stub_header',
+ dest='extra_stub_header',
+ default=None,
+ help=('File to insert after the system includes in the '
+ 'generated stub implemenation file. Ignored for '
+ '%s type.' % FILE_TYPE_WIN))
+ (options, args) = parser.parse_args()
+
+ if options.out_dir is None:
+ parser.error('Output location not specified')
+ if args:
+ parser.error('No inputs specified')
+
+ if options.type not in [FILE_TYPE_WIN, FILE_TYPE_POSIX_STUB]:
+ parser.error('Invalid output file type')
+
+ if options.type == FILE_TYPE_POSIX_STUB:
+ if options.stubfile_name is None:
+ parser.error('Output file name need for %s' % FILE_TYPE_POSIX_STUB)
+ if options.path_from_source is None:
+ parser.error('Path from source needed for %s' % FILE_TYPE_POSIX_STUB)
+
+ # Make sure output directory exists.
+ out_dir = RemoveTrailingSlashes(options.out_dir)
+ if not os.path.exists(out_dir):
+ os.makedirs(out_dir)
+
+ if options.type == FILE_TYPE_WIN:
+ for input_path in args:
+ try:
+ infile = open(input_path, 'r')
+ signatures = ParseSignatures(infile)
+ module_name = ExtractModuleName(os.path.basename(input_path))
+ WindowsLibCreator(module_name, signatures, out_dir).CreateLib()
+ finally:
+ if infile is not None:
+ infile.close()
+ elif options.type == FILE_TYPE_POSIX_STUB:
+ # Find the paths to the output files.
+ header_path = PosixStubWriter.HeaderFilePath(options.stubfile_name,
+ out_dir)
+ impl_path = PosixStubWriter.ImplementationFilePath(options.stubfile_name,
+ out_dir)
+
+ # Generate some convenience variables for bits of data needed below.
+ module_names = [ExtractModuleName(path) for path in args]
+ namespace = options.path_from_source.replace('/', '_').lower()
+ header_guard = '%s__' % namespace.upper()
+ header_include_path = '%s/%s' % (options.path_from_source,
+ os.path.basename(header_path))
+
+ # First create the implementation file.
+ try:
+ # Open the file, and create the preamble which consists of a file
+ # header plus any necessary includes.
+ impl_file = open(impl_path, 'w')
+ PosixStubWriter.WriteImplementationPreamble(header_include_path,
+ impl_file)
+ if options.extra_stub_header is not None:
+ try:
+ extra_header_file = open(options.extra_stub_header, 'r')
+ for line in extra_header_file:
+ impl_file.write(line)
+ impl_file.write('\n')
+ finally:
+ if extra_header_file is not None:
+ extra_header_file.close()
+
+ # For each signature file, generate the stub population functions
+ # for that file. Each file represents one module.
+ for input_path in args:
+ name = ExtractModuleName(input_path)
+ try:
+ infile = open(input_path, 'r')
+ signatures = ParseSignatures(infile)
+ finally:
+ if infile is not None:
+ infile.close()
+ writer = PosixStubWriter(name, signatures)
+ writer.WriteImplementationContents(namespace, impl_file)
+
+ # Lastly, output the umbrella function for the file.
+ PosixStubWriter.WriteUmbrellaInitializer(module_names, namespace,
+ impl_file)
+ finally:
+ if impl_file is not None:
+ impl_file.close()
+
+ # Then create the associated header file.
+ try:
+ header_file = open(header_path, 'w')
+ PosixStubWriter.WriteHeaderContents(module_names, namespace,
+ header_guard, header_file)
+ finally:
+ if header_file is not None:
+ header_file.close()
+
+ else:
+ raise Error('Should not reach here')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/third_party/ffmpeg/generate_stubs_unittest.py b/third_party/ffmpeg/generate_stubs_unittest.py
new file mode 100755
index 0000000..ccaa35b
--- /dev/null
+++ b/third_party/ffmpeg/generate_stubs_unittest.py
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Unittest for the generate_stubs.py.
+
+Since generate_stubs.py is a code generator, it is hard to do a very good
+test. Instead of creating a golden-file test, which might be flakey, this
+test elects instead to verify that various components "exist" within the
+generated file as a sanity check. In particular, there is a simple hit
+test to make sure that umbrella functions, etc., do try and include every
+function they are responsible for invoking. Missing an invocation is quite
+easily missed.
+
+There is no attempt to verify ordering of different components, or whether
+or not those components are going to parse incorrectly because of prior
+errors or positioning. Most of that should be caught really fast anyways
+during any attempt to use a badly behaving script.
+"""
+
+import re
+import StringIO
+import unittest
+import generate_stubs as gs
+
+
+def _MakeSignature(return_type, name, params):
+ return {'return_type': return_type,
+ 'name': name,
+ 'params': params}
+
+
+SIMPLE_SIGNATURES = [
+ ('int foo(int a)', _MakeSignature('int', 'foo', ['int a'])),
+ ('int bar(int a, double b)', _MakeSignature('int', 'bar',
+ ['int a', 'double b'])),
+ ('int baz(void)', _MakeSignature('int', 'baz', ['void'])),
+ ('void quux(void)', _MakeSignature('void', 'quux', ['void'])),
+ ('void waldo(void);', _MakeSignature('void', 'waldo', ['void'])),
+ ('int corge(void);', _MakeSignature('int', 'corge', ['void'])),
+ ]
+
+TRICKY_SIGNATURES = [
+ ('const struct name *foo(int a, struct Test* b); ',
+ _MakeSignature('const struct name *',
+ 'foo',
+ ['int a', 'struct Test* b'])),
+ ('const struct name &foo(int a, struct Test* b);',
+ _MakeSignature('const struct name &',
+ 'foo',
+ ['int a', 'struct Test* b'])),
+ ('const struct name &_foo(int a, struct Test* b);',
+ _MakeSignature('const struct name &',
+ '_foo',
+ ['int a', 'struct Test* b'])),
+ ('struct name const * const _foo(int a, struct Test* b) '
+ '__attribute__((inline));',
+ _MakeSignature('struct name const * const',
+ '_foo',
+ ['int a', 'struct Test* b']))
+ ]
+
+INVALID_SIGNATURES = ['I am bad', 'Seriously bad(', ';;;']
+
+
+class GenerateStubModuleFunctionsUnittest(unittest.TestCase):
+ def testRemoveTrailingSlashes(self):
+ # Check simple cases.
+ self.assertEqual('/a/path', gs.RemoveTrailingSlashes('/a/path/'))
+ self.assertEqual('/a/path', gs.RemoveTrailingSlashes('/a/path///'))
+ self.assertEqual('/a/path', gs.RemoveTrailingSlashes('/a/path'))
+
+ # Should not remove the last slash.
+ self.assertEqual('/', gs.RemoveTrailingSlashes('/'))
+
+ def testExtractModuleName(self):
+ self.assertEqual('somefile-2', gs.ExtractModuleName('somefile-2.ext'))
+
+ def testParseSignatures_EmptyFile(self):
+ # Empty file just generates empty signatures.
+ infile = StringIO.StringIO()
+ signatures = gs.ParseSignatures(infile)
+ self.assertEqual(0, len(signatures))
+
+ def testParseSignatures_SimpleSignatures(self):
+ file_contents = '\n'.join([x[0] for x in SIMPLE_SIGNATURES])
+ infile = StringIO.StringIO(file_contents)
+ signatures = gs.ParseSignatures(infile)
+ self.assertEqual(len(SIMPLE_SIGNATURES), len(signatures))
+
+ # We assume signatures are in order.
+ for i in xrange(len(SIMPLE_SIGNATURES)):
+ self.assertEqual(SIMPLE_SIGNATURES[i][1], signatures[i],
+ msg='Expected %s\nActual %s\nFor %s' %
+ (SIMPLE_SIGNATURES[i][1],
+ signatures[i],
+ SIMPLE_SIGNATURES[i][0]))
+
+ def testParseSignatures_TrickySignatures(self):
+ file_contents = '\n'.join([x[0] for x in TRICKY_SIGNATURES])
+ infile = StringIO.StringIO(file_contents)
+ signatures = gs.ParseSignatures(infile)
+ self.assertEqual(len(TRICKY_SIGNATURES), len(signatures))
+
+ # We assume signatures are in order.
+ for i in xrange(len(TRICKY_SIGNATURES)):
+ self.assertEqual(TRICKY_SIGNATURES[i][1], signatures[i],
+ msg='Expected %s\nActual %s\nFor %s' %
+ (TRICKY_SIGNATURES[i][1],
+ signatures[i],
+ TRICKY_SIGNATURES[i][0]))
+
+ def testParseSignatures_InvalidSignatures(self):
+ for i in INVALID_SIGNATURES:
+ infile = StringIO.StringIO(i)
+ self.assertRaises(gs.BadSignatureError, gs.ParseSignatures, infile)
+
+ def testParseSignatures_CommentsIgnored(self):
+ my_sigs = []
+ my_sigs.append('# a comment')
+ my_sigs.append(SIMPLE_SIGNATURES[0][0])
+ my_sigs.append('# another comment')
+ my_sigs.append(SIMPLE_SIGNATURES[0][0])
+ my_sigs.append('# a third comment')
+ my_sigs.append(SIMPLE_SIGNATURES[0][0])
+
+ file_contents = '\n'.join(my_sigs)
+ infile = StringIO.StringIO(file_contents)
+ signatures = gs.ParseSignatures(infile)
+ self.assertEqual(3, len(signatures))
+
+
+class WindowsLibCreatorUnittest(unittest.TestCase):
+ def setUp(self):
+ self.module_name = 'my_module-1'
+ self.signatures = [sig[1] for sig in SIMPLE_SIGNATURES]
+ self.out_dir = 'out_dir'
+ self.creator = gs.WindowsLibCreator(self.module_name,
+ self.signatures,
+ self.out_dir)
+
+ def testDefFilePath(self):
+ self.assertEqual('out_dir/my_module-1.def', self.creator.DefFilePath())
+
+ def testLibFilePath(self):
+ self.assertEqual('out_dir/my_module-1.lib', self.creator.LibFilePath())
+
+ def testWriteDefFile(self):
+ outfile = StringIO.StringIO()
+ self.creator._WriteDefFile(outfile)
+ contents = outfile.getvalue()
+
+ # Check that the file header is correct.
+ self.assertTrue(contents.startswith("""LIBRARY %s
+EXPORTS
+""" % self.module_name))
+
+ # Check that the signatures were exported.
+ for sig in self.signatures:
+ pattern = '\n %s\n' % sig['name']
+ self.assertTrue(re.search(pattern, contents),
+ msg='Expected match of "%s" in %s' % (pattern, contents))
+
+
+class PosixStubWriterUnittest(unittest.TestCase):
+ def setUp(self):
+ self.module_name = 'my_module-1'
+ self.signatures = [sig[1] for sig in SIMPLE_SIGNATURES]
+ self.out_dir = 'out_dir'
+ self.writer = gs.PosixStubWriter(self.module_name, self.signatures)
+
+ def testEnumName(self):
+ self.assertEqual('kModuleMy_module1',
+ gs.PosixStubWriter.EnumName(self.module_name))
+
+ def testIsInitializedName(self):
+ self.assertEqual('IsMy_module1Initialized',
+ gs.PosixStubWriter.IsInitializedName(self.module_name))
+
+ def testInitializeModuleName(self):
+ self.assertEqual(
+ 'InitializeMy_module1',
+ gs.PosixStubWriter.InitializeModuleName(self.module_name))
+
+ def testUninitializeModuleName(self):
+ self.assertEqual(
+ 'UninitializeMy_module1',
+ gs.PosixStubWriter.UninitializeModuleName(self.module_name))
+
+ def testHeaderFilePath(self):
+ self.assertEqual('outdir/mystubs-1.h',
+ gs.PosixStubWriter.HeaderFilePath('mystubs-1', 'outdir'))
+
+ def testImplementationFilePath(self):
+ self.assertEqual(
+ 'outdir/mystubs-1.cc',
+ gs.PosixStubWriter.ImplementationFilePath('mystubs-1', 'outdir'))
+
+ def testStubFunctionPointer(self):
+ self.assertEqual(
+ 'static int (*foo_ptr)(int a) = NULL;',
+ gs.PosixStubWriter.StubFunctionPointer(SIMPLE_SIGNATURES[0][1]))
+
+ def testStubFunction(self):
+ # Test for a signature with a return value and a parameter.
+ self.assertEqual("""int foo(int a) {
+ return foo_ptr(a);
+}""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[0][1]))
+
+ # Test for a signature with a void return value and no parameters.
+ self.assertEqual("""void waldo(void) {
+ waldo_ptr();
+}""", gs.PosixStubWriter.StubFunction(SIMPLE_SIGNATURES[4][1]))
+
+ def testWriteImplemenationContents(self):
+ outfile = StringIO.StringIO()
+ self.writer.WriteImplementationContents('my_namespace', outfile)
+ contents = outfile.getvalue()
+
+ # Verify namespace exists somewhere.
+ self.assertTrue(contents.find('namespace my_namespace {') != -1)
+
+ # Verify that each signature has an _ptr and a function call in the file.
+ # Check that the signatures were exported.
+ for sig in self.signatures:
+ decl = gs.PosixStubWriter.StubFunctionPointer(sig)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+
+ # Verify that each signature has an stub function generated for it.
+ for sig in self.signatures:
+ decl = gs.PosixStubWriter.StubFunction(sig)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+
+ # Find module initializer functions. Make sure all 3 exist.
+ decl = gs.PosixStubWriter.InitializeModuleName(self.module_name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+ decl = gs.PosixStubWriter.UninitializeModuleName(self.module_name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+ decl = gs.PosixStubWriter.IsInitializedName(self.module_name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+
+ def testWriteHeaderContents(self):
+ # Data for header generation.
+ module_names = ['oneModule', 'twoModule']
+
+ # Make the header.
+ outfile = StringIO.StringIO()
+ self.writer.WriteHeaderContents(module_names, 'my_namespace', 'GUARD_',
+ outfile)
+ contents = outfile.getvalue()
+
+ # Check for namespace and header guard.
+ self.assertTrue(contents.find('namespace my_namespace {') != -1)
+ self.assertTrue(contents.find('#ifndef GUARD_') != -1)
+
+ # Check for umbrella initializer.
+ self.assertTrue(contents.find('InitializeStubs(') != -1)
+
+ # Check per-module declarations.
+ for name in module_names:
+ # Check for enums.
+ decl = gs.PosixStubWriter.EnumName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+
+ # Check for module initializer functions.
+ decl = gs.PosixStubWriter.IsInitializedName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+ decl = gs.PosixStubWriter.InitializeModuleName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+ decl = gs.PosixStubWriter.UninitializeModuleName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+
+ def testWriteUmbrellaInitializer(self):
+ # Data for header generation.
+ module_names = ['oneModule', 'twoModule']
+
+ # Make the header.
+ outfile = StringIO.StringIO()
+ self.writer.WriteUmbrellaInitializer(module_names, 'my_namespace', outfile)
+ contents = outfile.getvalue()
+
+ # Check for umbrella initializer declaration.
+ self.assertTrue(contents.find('bool InitializeStubs(') != -1)
+
+ # If the umbrella initializer is correctly written, each module will have
+ # its initializer called, checked, and uninitialized on failure. Sanity
+ # check that here.
+ for name in module_names:
+ # Check for module initializer functions.
+ decl = gs.PosixStubWriter.IsInitializedName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+ decl = gs.PosixStubWriter.InitializeModuleName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+ decl = gs.PosixStubWriter.UninitializeModuleName(name)
+ self.assertTrue(contents.find(decl) != -1,
+ msg='Expected "%s" in %s' % (decl, contents))
+
+if __name__ == '__main__':
+ unittest.main()