OLD | NEW |
---|---|
(Empty) | |
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 #include "base/basictypes.h" | |
35 #include "base/compiler_specific.h" | |
36 #ifdef %(unique_prefix)s_DLOPEN | |
37 #include "base/native_library.h" | |
38 #endif | |
39 | |
40 class %(class_name)s { | |
41 public: | |
42 %(class_name)s(); | |
43 ~%(class_name)s(); | |
44 | |
45 bool Load(const std::string& library_name) WARN_UNUSED_RESULT; | |
46 | |
47 %(member_decls)s | |
48 | |
49 private: | |
50 void CleanUp(bool unload); | |
Mark Mentovai
2012/11/27 23:09:11
Nothing ever calls CleanUp(false), so get rid of t
Paweł Hajdan Jr.
2012/11/28 01:05:52
In theory when loaded_ is false that would happen.
| |
51 | |
52 #ifdef %(unique_prefix)s_DLOPEN | |
53 base::NativeLibrary library_; | |
54 #endif | |
55 | |
56 bool loaded_; | |
Mark Mentovai
2012/11/27 23:09:11
In the DLOPEN variant, you can get rid of loaded_
Paweł Hajdan Jr.
2012/11/28 01:05:52
Yeah, I think it'd just further complicate the cod
| |
57 | |
58 DISALLOW_COPY_AND_ASSIGN(%(class_name)s); | |
59 }; | |
60 | |
61 #endif // %(unique_prefix)s | |
62 """ | |
63 | |
64 | |
65 HEADER_MEMBER_TEMPLATE = """ typeof(&::%(function_name)s) %(function_name)s; | |
66 """ | |
67 | |
68 | |
69 IMPL_TEMPLATE = """// This is generated file. Do not modify directly. | |
70 // Path to the code generator: %(generator_path)s . | |
71 | |
72 #include "%(generated_header_name)s" | |
73 | |
74 #include "base/file_path.h" | |
75 #include "base/logging.h" | |
76 | |
77 %(class_name)s::%(class_name)s() : loaded_(false) { | |
78 } | |
79 | |
80 %(class_name)s::~%(class_name)s() { | |
81 CleanUp(loaded_); | |
Mark Mentovai
2012/11/27 23:09:11
I’d just leave the destructor at calling base::Unl
Paweł Hajdan Jr.
2012/11/28 01:05:52
That's guarded by loaded_. I think you've asked fo
Mark Mentovai
2012/11/28 04:29:12
Paweł Hajdan Jr. wrote:
| |
82 } | |
83 | |
84 bool %(class_name)s::Load(const std::string& library_name) { | |
85 if (loaded_) { | |
86 NOTREACHED(); | |
87 return false; | |
88 } | |
89 | |
90 #ifdef %(unique_prefix)s_DLOPEN | |
91 library_ = base::LoadNativeLibrary(FilePath(library_name), NULL); | |
92 if (!library_) | |
93 return false; | |
94 #endif | |
95 | |
96 %(member_init)s | |
97 | |
98 loaded_ = true; | |
99 return true; | |
100 } | |
101 | |
102 void %(class_name)s::CleanUp(bool unload) { | |
103 #if defined(%(unique_prefix)s_DLOPEN) | |
104 if (unload) { | |
105 base::UnloadNativeLibrary(library_); | |
106 library_ = NULL; | |
107 } | |
108 #endif | |
109 loaded_ = false; | |
110 %(member_cleanup)s | |
111 } | |
112 """ | |
113 | |
114 IMPL_MEMBER_INIT_TEMPLATE = """ | |
115 #if defined(%(unique_prefix)s_DLOPEN) | |
116 this->%(function_name)s = | |
Mark Mentovai
2012/11/27 23:09:11
What’s all this->, then? We don’t normally write t
Paweł Hajdan Jr.
2012/11/28 01:05:52
Done.
| |
117 reinterpret_cast<typeof(this->%(function_name)s)>( | |
118 base::GetFunctionPointerFromNativeLibrary( | |
119 library_, "%(function_name)s")); | |
120 #elif defined(%(unique_prefix)s_DT_NEEDED) | |
Mark Mentovai
2012/11/27 23:09:11
Good name choice. It’s nice to have a name for thi
| |
121 this->%(function_name)s = &::%(function_name)s; | |
122 #else | |
123 #error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined | |
Mark Mentovai
2012/11/27 23:09:11
I don’t think you need one #error for each functio
Paweł Hajdan Jr.
2012/11/28 01:05:52
Done.
| |
124 #endif | |
125 if (!this->%(function_name)s) { | |
126 CleanUp(true); | |
127 return false; | |
128 } | |
129 """ | |
130 | |
131 IMPL_MEMBER_CLEANUP_TEMPLATE = """ this->%(function_name)s = NULL; | |
132 """ | |
133 | |
134 def main(): | |
135 parser = optparse.OptionParser() | |
136 parser.add_option('--name') | |
137 parser.add_option('--output-cc') | |
138 parser.add_option('--output-h') | |
139 parser.add_option('--header') | |
140 | |
141 parser.add_option('--use-extern-c', action='store_true', default=False) | |
142 parser.add_option('--link-directly', type=int, default=0) | |
143 | |
144 options, args = parser.parse_args() | |
145 | |
146 if not options.name: | |
147 parser.error('Missing --name parameter') | |
148 if not options.output_cc: | |
149 parser.error('Missing --output-cc parameter') | |
150 if not options.output_h: | |
151 parser.error('Missing --output-h parameter') | |
152 if not options.header: | |
153 parser.error('Missing --header paramater') | |
154 if not args: | |
155 parser.error('No function names specified') | |
156 | |
157 # Make sure we are always dealing with an absolute path | |
158 # to avoid issues caused by different relative path roots. | |
159 options.output_cc = os.path.abspath(options.output_cc) | |
160 options.output_h = os.path.abspath(options.output_h) | |
161 | |
162 # Create a unique prefix, e.g. for header guards. | |
163 # Stick a known string at the beginning to ensure this doesn't begin | |
164 # with an underscore, which is reserved for the C++ implementation. | |
165 unique_prefix = ('LIBRARY_LOADER_' + | |
166 re.sub(r'[\W]', '_', options.output_h).upper()) | |
167 | |
168 member_decls = [] | |
169 member_init = [] | |
170 member_cleanup = [] | |
171 for fn in args: | |
172 member_decls += HEADER_MEMBER_TEMPLATE % { | |
173 'function_name': fn, | |
174 'unique_prefix': unique_prefix | |
175 } | |
176 member_init += IMPL_MEMBER_INIT_TEMPLATE % { | |
177 'function_name': fn, | |
178 'unique_prefix': unique_prefix | |
179 } | |
180 member_cleanup += IMPL_MEMBER_CLEANUP_TEMPLATE % { | |
181 'function_name': fn, | |
182 'unique_prefix': unique_prefix | |
183 } | |
184 | |
185 wrapped_header_include = '#include %s' % options.header | |
186 | |
187 # Some libraries (e.g. libpci) have headers that cannot be included | |
188 # without extern "C", otherwise they cause the link to fail. | |
189 # TODO(phajdan.jr): This is a workaround for broken headers. Remove it. | |
190 if options.use_extern_c: | |
191 wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include | |
192 | |
193 # It seems cleaner just to have a single #define here and #ifdefs in bunch | |
194 # of places, rather than having a different set of templates, duplicating | |
195 # or complicating more code. | |
196 if options.link_directly == 0: | |
197 wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix | |
198 elif options.link_directly == 1: | |
199 wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix | |
200 else: | |
201 parser.error('Invalid value for --link-directly. Should be 0 or 1.') | |
202 | |
203 # Make it easier for people to find the code generator just in case. | |
204 # Doing it this way is more maintainable, because it's going to work | |
205 # even if file gets moved without updating the contents. | |
206 generator_path = os.path.abspath(__file__) | |
207 | |
208 header_contents = HEADER_TEMPLATE % { | |
209 'generator_path': generator_path, | |
210 'unique_prefix': unique_prefix, | |
211 'wrapped_header_include': wrapped_header_include, | |
212 'class_name': options.name, | |
213 'member_decls': ''.join(member_decls), | |
214 } | |
215 | |
216 impl_contents = IMPL_TEMPLATE % { | |
217 'generator_path': generator_path, | |
218 'unique_prefix': unique_prefix, | |
219 'generated_header_name': options.output_h, | |
220 'class_name': options.name, | |
221 'member_init': ''.join(member_init), | |
222 'member_cleanup': ''.join(member_cleanup), | |
223 } | |
224 | |
225 header_file = open(options.output_h, 'w') | |
226 try: | |
227 header_file.write(header_contents) | |
228 finally: | |
229 header_file.close() | |
230 | |
231 impl_file = open(options.output_cc, 'w') | |
232 try: | |
233 impl_file.write(impl_contents) | |
234 finally: | |
235 impl_file.close() | |
236 | |
237 return 0 | |
238 | |
239 if __name__ == '__main__': | |
240 sys.exit(main()) | |
OLD | NEW |