
The methodology used to generate this CL is documented in https://crbug.com/1098010#c95. No-Try: true No-Presubmit: true Bug: 1098010 Change-Id: I3a8a7b150e7bd64690534727150646081df50439 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3900697 Reviewed-by: Mark Mentovai <mark@chromium.org> Auto-Submit: Avi Drissman <avi@chromium.org> Owners-Override: Avi Drissman <avi@chromium.org> Commit-Queue: Avi Drissman <avi@chromium.org> Cr-Commit-Position: refs/heads/main@{#1047644}
249 lines
6.8 KiB
Python
Executable File
249 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright 2012 The Chromium Authors
|
|
# 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>
|
|
|
|
class %(class_name)s {
|
|
public:
|
|
%(class_name)s();
|
|
~%(class_name)s();
|
|
|
|
bool Load(const std::string& library_name)
|
|
__attribute__((warn_unused_result));
|
|
|
|
bool loaded() const { return loaded_; }
|
|
|
|
%(member_decls)s
|
|
|
|
private:
|
|
void CleanUp(bool unload);
|
|
|
|
#if defined(%(unique_prefix)s_DLOPEN)
|
|
void* library_;
|
|
#endif
|
|
|
|
bool loaded_;
|
|
|
|
// Disallow copy constructor and assignment operator.
|
|
%(class_name)s(const %(class_name)s&);
|
|
void operator=(const %(class_name)s&);
|
|
};
|
|
|
|
#endif // %(unique_prefix)s
|
|
"""
|
|
|
|
|
|
HEADER_MEMBER_TEMPLATE = """ decltype(&::%(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"
|
|
|
|
#include <dlfcn.h>
|
|
|
|
// 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
|
|
|
|
%(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_)
|
|
return false;
|
|
|
|
#if defined(%(unique_prefix)s_DLOPEN)
|
|
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
|
|
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) {
|
|
dlclose(library_);
|
|
library_ = NULL;
|
|
}
|
|
#endif
|
|
loaded_ = false;
|
|
%(member_cleanup)s
|
|
}
|
|
"""
|
|
|
|
IMPL_MEMBER_INIT_TEMPLATE = """
|
|
#if defined(%(unique_prefix)s_DLOPEN)
|
|
%(function_name)s =
|
|
reinterpret_cast<decltype(this->%(function_name)s)>(
|
|
dlsym(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')
|
|
|
|
if os.path.dirname(options.output_cc) != os.path.dirname(options.output_h):
|
|
parser.error('--output-cc and --output-h must be in same directory')
|
|
|
|
# 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]', '_', os.path.basename(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.
|
|
source_tree_root = os.path.abspath(
|
|
os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
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': os.path.basename(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())
|