- add sources.
[platform/framework/web/crosswalk.git] / src / tools / generate_library_loader / generate_library_loader.py
1 #!/usr/bin/env python
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.
5
6 """
7 Creates a library loader (a header and implementation file),
8 which is a wrapper for dlopen or direct linking with given library.
9
10 The loader makes it possible to have the same client code for both cases,
11 and also makes it easier to write code using dlopen (and also provides
12 a standard way to do so, and limits the ugliness just to generated files).
13
14 For more info refer to http://crbug.com/162733 .
15 """
16
17
18 import optparse
19 import os.path
20 import re
21 import sys
22
23
24 HEADER_TEMPLATE = """// This is generated file. Do not modify directly.
25 // Path to the code generator: %(generator_path)s .
26
27 #ifndef %(unique_prefix)s
28 #define %(unique_prefix)s
29
30 %(wrapped_header_include)s
31
32 #include <string>
33
34 class %(class_name)s {
35  public:
36   %(class_name)s();
37   ~%(class_name)s();
38
39   bool Load(const std::string& library_name)
40       __attribute__((warn_unused_result));
41
42   bool loaded() const { return loaded_; }
43
44 %(member_decls)s
45
46  private:
47   void CleanUp(bool unload);
48
49 #if defined(%(unique_prefix)s_DLOPEN)
50   void* library_;
51 #endif
52
53   bool loaded_;
54
55   // Disallow copy constructor and assignment operator.
56   %(class_name)s(const %(class_name)s&);
57   void operator=(const %(class_name)s&);
58 };
59
60 #endif  // %(unique_prefix)s
61 """
62
63
64 HEADER_MEMBER_TEMPLATE = """  typeof(&::%(function_name)s) %(function_name)s;
65 """
66
67
68 IMPL_TEMPLATE = """// This is generated file. Do not modify directly.
69 // Path to the code generator: %(generator_path)s .
70
71 #include "%(generated_header_name)s"
72
73 #include <dlfcn.h>
74
75 // Put these sanity checks here so that they fire at most once
76 // (to avoid cluttering the build output).
77 #if !defined(%(unique_prefix)s_DLOPEN) && !defined(%(unique_prefix)s_DT_NEEDED)
78 #error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined
79 #endif
80 #if defined(%(unique_prefix)s_DLOPEN) && defined(%(unique_prefix)s_DT_NEEDED)
81 #error both %(unique_prefix)s_DLOPEN and %(unique_prefix)s_DT_NEEDED defined
82 #endif
83
84 %(class_name)s::%(class_name)s() : loaded_(false) {
85 }
86
87 %(class_name)s::~%(class_name)s() {
88   CleanUp(loaded_);
89 }
90
91 bool %(class_name)s::Load(const std::string& library_name) {
92   if (loaded_)
93     return false;
94
95 #if defined(%(unique_prefix)s_DLOPEN)
96   library_ = dlopen(library_name.c_str(), RTLD_LAZY);
97   if (!library_)
98     return false;
99 #endif
100
101 %(member_init)s
102
103   loaded_ = true;
104   return true;
105 }
106
107 void %(class_name)s::CleanUp(bool unload) {
108 #if defined(%(unique_prefix)s_DLOPEN)
109   if (unload) {
110     dlclose(library_);
111     library_ = NULL;
112   }
113 #endif
114   loaded_ = false;
115 %(member_cleanup)s
116 }
117 """
118
119 IMPL_MEMBER_INIT_TEMPLATE = """
120 #if defined(%(unique_prefix)s_DLOPEN)
121   %(function_name)s =
122       reinterpret_cast<typeof(this->%(function_name)s)>(
123           dlsym(library_, "%(function_name)s"));
124 #endif
125 #if defined(%(unique_prefix)s_DT_NEEDED)
126   %(function_name)s = &::%(function_name)s;
127 #endif
128   if (!%(function_name)s) {
129     CleanUp(true);
130     return false;
131   }
132 """
133
134 IMPL_MEMBER_CLEANUP_TEMPLATE = """  %(function_name)s = NULL;
135 """
136
137 def main():
138   parser = optparse.OptionParser()
139   parser.add_option('--name')
140   parser.add_option('--output-cc')
141   parser.add_option('--output-h')
142   parser.add_option('--header')
143
144   parser.add_option('--bundled-header')
145   parser.add_option('--use-extern-c', action='store_true', default=False)
146   parser.add_option('--link-directly', type=int, default=0)
147
148   options, args = parser.parse_args()
149
150   if not options.name:
151     parser.error('Missing --name parameter')
152   if not options.output_cc:
153     parser.error('Missing --output-cc parameter')
154   if not options.output_h:
155     parser.error('Missing --output-h parameter')
156   if not options.header:
157     parser.error('Missing --header paramater')
158   if not args:
159     parser.error('No function names specified')
160
161   # Make sure we are always dealing with paths relative to source tree root
162   # to avoid issues caused by different relative path roots.
163   source_tree_root = os.path.abspath(
164     os.path.join(os.path.dirname(__file__), '..', '..'))
165   options.output_cc = os.path.relpath(options.output_cc, source_tree_root)
166   options.output_h = os.path.relpath(options.output_h, source_tree_root)
167
168   # Create a unique prefix, e.g. for header guards.
169   # Stick a known string at the beginning to ensure this doesn't begin
170   # with an underscore, which is reserved for the C++ implementation.
171   unique_prefix = ('LIBRARY_LOADER_' +
172                    re.sub(r'[\W]', '_', options.output_h).upper())
173
174   member_decls = []
175   member_init = []
176   member_cleanup = []
177   for fn in args:
178     member_decls.append(HEADER_MEMBER_TEMPLATE % {
179       'function_name': fn,
180       'unique_prefix': unique_prefix
181     })
182     member_init.append(IMPL_MEMBER_INIT_TEMPLATE % {
183       'function_name': fn,
184       'unique_prefix': unique_prefix
185     })
186     member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % {
187       'function_name': fn,
188       'unique_prefix': unique_prefix
189     })
190
191   header = options.header
192   if options.link_directly == 0 and options.bundled_header:
193     header = options.bundled_header
194   wrapped_header_include = '#include %s\n' % header
195
196   # Some libraries (e.g. libpci) have headers that cannot be included
197   # without extern "C", otherwise they cause the link to fail.
198   # TODO(phajdan.jr): This is a workaround for broken headers. Remove it.
199   if options.use_extern_c:
200     wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include
201
202   # It seems cleaner just to have a single #define here and #ifdefs in bunch
203   # of places, rather than having a different set of templates, duplicating
204   # or complicating more code.
205   if options.link_directly == 0:
206     wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix
207   elif options.link_directly == 1:
208     wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix
209   else:
210     parser.error('Invalid value for --link-directly. Should be 0 or 1.')
211
212   # Make it easier for people to find the code generator just in case.
213   # Doing it this way is more maintainable, because it's going to work
214   # even if file gets moved without updating the contents.
215   generator_path = os.path.relpath(__file__, source_tree_root)
216
217   header_contents = HEADER_TEMPLATE % {
218     'generator_path': generator_path,
219     'unique_prefix': unique_prefix,
220     'wrapped_header_include': wrapped_header_include,
221     'class_name': options.name,
222     'member_decls': ''.join(member_decls),
223   }
224
225   impl_contents = IMPL_TEMPLATE % {
226     'generator_path': generator_path,
227     'unique_prefix': unique_prefix,
228     'generated_header_name': options.output_h,
229     'class_name': options.name,
230     'member_init': ''.join(member_init),
231     'member_cleanup': ''.join(member_cleanup),
232   }
233
234   header_file = open(os.path.join(source_tree_root, options.output_h), 'w')
235   try:
236     header_file.write(header_contents)
237   finally:
238     header_file.close()
239
240   impl_file = open(os.path.join(source_tree_root, options.output_cc), 'w')
241   try:
242     impl_file.write(impl_contents)
243   finally:
244     impl_file.close()
245
246   return 0
247
248 if __name__ == '__main__':
249   sys.exit(main())