257 lines
7.2 KiB
Python
257 lines
7.2 KiB
Python
|
#!/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;
|
||
|
|
||
|
bool loaded() const { return loaded_; }
|
||
|
|
||
|
%(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/files/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(base::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('--bundled-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 paths relative to source tree root
|
||
|
# to avoid issues caused by different relative path roots.
|
||
|
source_tree_root = os.path.abspath(
|
||
|
os.path.join(os.path.dirname(__file__), '..', '..'))
|
||
|
options.output_cc = os.path.relpath(options.output_cc, source_tree_root)
|
||
|
options.output_h = os.path.relpath(options.output_h, source_tree_root)
|
||
|
|
||
|
# 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
|
||
|
})
|
||
|
|
||
|
header = options.header
|
||
|
if options.link_directly == 0 and options.bundled_header:
|
||
|
header = options.bundled_header
|
||
|
wrapped_header_include = '#include %s\n' % 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.relpath(__file__, source_tree_root)
|
||
|
|
||
|
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(os.path.join(source_tree_root, options.output_h), 'w')
|
||
|
try:
|
||
|
header_file.write(header_contents)
|
||
|
finally:
|
||
|
header_file.close()
|
||
|
|
||
|
impl_file = open(os.path.join(source_tree_root, options.output_cc), 'w')
|
||
|
try:
|
||
|
impl_file.write(impl_contents)
|
||
|
finally:
|
||
|
impl_file.close()
|
||
|
|
||
|
return 0
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
sys.exit(main())
|