Index: tools/generate_library_loader/generate_library_loader.py |
diff --git a/tools/generate_library_loader/generate_library_loader.py b/tools/generate_library_loader/generate_library_loader.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..edf0706a76a8d56301ba07f7f67f11781edf24a1 |
--- /dev/null |
+++ b/tools/generate_library_loader/generate_library_loader.py |
@@ -0,0 +1,248 @@ |
+#!/usr/bin/env python |
+# Copyright (c) 2012 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 a library loader (a header and implementation file), |
+which is a wrapper for dlopen or direct linking with given library. |
+ |
+The loader makes it possible to have the same client code for both cases, |
+and also makes it easier to write code using dlopen (and also provides |
+a standard way to do so, and limits the ugliness just to generated files). |
+ |
+For more info refer to http://crbug.com/162733 . |
+""" |
+ |
+ |
+import optparse |
+import os.path |
+import re |
+import sys |
+ |
+ |
+HEADER_TEMPLATE = """// This is generated file. Do not modify directly. |
+// Path to the code generator: %(generator_path)s . |
+ |
+#ifndef %(unique_prefix)s |
+#define %(unique_prefix)s |
+ |
+%(wrapped_header_include)s |
+ |
+#include <string> |
+ |
+#include "base/basictypes.h" |
+#include "base/compiler_specific.h" |
+#if defined(%(unique_prefix)s_DLOPEN) |
+#include "base/native_library.h" |
+#endif |
+ |
+class %(class_name)s { |
+ public: |
+ %(class_name)s(); |
+ ~%(class_name)s(); |
+ |
+ bool Load(const std::string& library_name) WARN_UNUSED_RESULT; |
+ |
+%(member_decls)s |
+ |
+ private: |
+ void CleanUp(bool unload); |
+ |
+#if defined(%(unique_prefix)s_DLOPEN) |
+ base::NativeLibrary library_; |
+#endif |
+ |
+ bool loaded_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(%(class_name)s); |
+}; |
+ |
+#endif // %(unique_prefix)s |
+""" |
+ |
+ |
+HEADER_MEMBER_TEMPLATE = """ typeof(&::%(function_name)s) %(function_name)s; |
+""" |
+ |
+ |
+IMPL_TEMPLATE = """// This is generated file. Do not modify directly. |
+// Path to the code generator: %(generator_path)s . |
+ |
+#include "%(generated_header_name)s" |
+ |
+// Put these sanity checks here so that they fire at most once |
+// (to avoid cluttering the build output). |
+#if !defined(%(unique_prefix)s_DLOPEN) && !defined(%(unique_prefix)s_DT_NEEDED) |
+#error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined |
+#endif |
+#if defined(%(unique_prefix)s_DLOPEN) && defined(%(unique_prefix)s_DT_NEEDED) |
+#error both %(unique_prefix)s_DLOPEN and %(unique_prefix)s_DT_NEEDED defined |
+#endif |
+ |
+#include "base/file_path.h" |
+#include "base/logging.h" |
+ |
+%(class_name)s::%(class_name)s() : loaded_(false) { |
+} |
+ |
+%(class_name)s::~%(class_name)s() { |
+ CleanUp(loaded_); |
+} |
+ |
+bool %(class_name)s::Load(const std::string& library_name) { |
+ if (loaded_) { |
+ NOTREACHED(); |
+ return false; |
+ } |
+ |
+#if defined(%(unique_prefix)s_DLOPEN) |
+ library_ = base::LoadNativeLibrary(FilePath(library_name), NULL); |
+ if (!library_) |
+ return false; |
+#endif |
+ |
+%(member_init)s |
+ |
+ loaded_ = true; |
+ return true; |
+} |
+ |
+void %(class_name)s::CleanUp(bool unload) { |
+#if defined(%(unique_prefix)s_DLOPEN) |
+ if (unload) { |
+ base::UnloadNativeLibrary(library_); |
+ library_ = NULL; |
+ } |
+#endif |
+ loaded_ = false; |
+%(member_cleanup)s |
+} |
+""" |
+ |
+IMPL_MEMBER_INIT_TEMPLATE = """ |
+#if defined(%(unique_prefix)s_DLOPEN) |
+ %(function_name)s = |
+ reinterpret_cast<typeof(this->%(function_name)s)>( |
+ base::GetFunctionPointerFromNativeLibrary( |
+ library_, "%(function_name)s")); |
+#endif |
+#if defined(%(unique_prefix)s_DT_NEEDED) |
+ %(function_name)s = &::%(function_name)s; |
+#endif |
+ if (!%(function_name)s) { |
+ CleanUp(true); |
+ return false; |
+ } |
+""" |
+ |
+IMPL_MEMBER_CLEANUP_TEMPLATE = """ %(function_name)s = NULL; |
+""" |
+ |
+def main(): |
+ parser = optparse.OptionParser() |
+ parser.add_option('--name') |
+ parser.add_option('--output-cc') |
+ parser.add_option('--output-h') |
+ parser.add_option('--header') |
+ |
+ parser.add_option('--use-extern-c', action='store_true', default=False) |
+ parser.add_option('--link-directly', type=int, default=0) |
+ |
+ options, args = parser.parse_args() |
+ |
+ if not options.name: |
+ parser.error('Missing --name parameter') |
+ if not options.output_cc: |
+ parser.error('Missing --output-cc parameter') |
+ if not options.output_h: |
+ parser.error('Missing --output-h parameter') |
+ if not options.header: |
+ parser.error('Missing --header paramater') |
+ if not args: |
+ parser.error('No function names specified') |
+ |
+ # Make sure we are always dealing with an absolute path |
+ # to avoid issues caused by different relative path roots. |
+ options.output_cc = os.path.abspath(options.output_cc) |
+ options.output_h = os.path.abspath(options.output_h) |
+ |
+ # Create a unique prefix, e.g. for header guards. |
+ # Stick a known string at the beginning to ensure this doesn't begin |
+ # with an underscore, which is reserved for the C++ implementation. |
+ unique_prefix = ('LIBRARY_LOADER_' + |
+ re.sub(r'[\W]', '_', options.output_h).upper()) |
+ |
+ member_decls = [] |
+ member_init = [] |
+ member_cleanup = [] |
+ for fn in args: |
+ member_decls.append(HEADER_MEMBER_TEMPLATE % { |
+ 'function_name': fn, |
+ 'unique_prefix': unique_prefix |
+ }) |
+ member_init.append(IMPL_MEMBER_INIT_TEMPLATE % { |
+ 'function_name': fn, |
+ 'unique_prefix': unique_prefix |
+ }) |
+ member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % { |
+ 'function_name': fn, |
+ 'unique_prefix': unique_prefix |
+ }) |
+ |
+ wrapped_header_include = '#include %s' % options.header |
+ |
+ # Some libraries (e.g. libpci) have headers that cannot be included |
+ # without extern "C", otherwise they cause the link to fail. |
+ # TODO(phajdan.jr): This is a workaround for broken headers. Remove it. |
+ if options.use_extern_c: |
+ wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include |
+ |
+ # It seems cleaner just to have a single #define here and #ifdefs in bunch |
+ # of places, rather than having a different set of templates, duplicating |
+ # or complicating more code. |
+ if options.link_directly == 0: |
+ wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix |
+ elif options.link_directly == 1: |
+ wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix |
+ else: |
+ parser.error('Invalid value for --link-directly. Should be 0 or 1.') |
+ |
+ # Make it easier for people to find the code generator just in case. |
+ # Doing it this way is more maintainable, because it's going to work |
+ # even if file gets moved without updating the contents. |
+ generator_path = os.path.abspath(__file__) |
+ |
+ header_contents = HEADER_TEMPLATE % { |
+ 'generator_path': generator_path, |
+ 'unique_prefix': unique_prefix, |
+ 'wrapped_header_include': wrapped_header_include, |
+ 'class_name': options.name, |
+ 'member_decls': ''.join(member_decls), |
+ } |
+ |
+ impl_contents = IMPL_TEMPLATE % { |
+ 'generator_path': generator_path, |
+ 'unique_prefix': unique_prefix, |
+ 'generated_header_name': options.output_h, |
+ 'class_name': options.name, |
+ 'member_init': ''.join(member_init), |
+ 'member_cleanup': ''.join(member_cleanup), |
+ } |
+ |
+ header_file = open(options.output_h, 'w') |
+ try: |
+ header_file.write(header_contents) |
+ finally: |
+ header_file.close() |
+ |
+ impl_file = open(options.output_cc, 'w') |
+ try: |
+ impl_file.write(impl_contents) |
+ finally: |
+ impl_file.close() |
+ |
+ return 0 |
+ |
+if __name__ == '__main__': |
+ sys.exit(main()) |