1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <iostream>
18 #include <sstream>
19
20 #include "Generator.h"
21 #include "Specification.h"
22 #include "Utilities.h"
23
24 using namespace std;
25
26 // Convert a file name into a string that can be used to guard the include file with #ifdef...
makeGuardString(const string & filename)27 static string makeGuardString(const string& filename) {
28 string s;
29 s.resize(15 + filename.size());
30 s = "RENDERSCRIPT_";
31 for (char c : filename) {
32 if (c == '.') {
33 s += '_';
34 } else {
35 s += toupper(c);
36 }
37 }
38 return s;
39 }
40
41 /* Write #ifdef's that ensure that the specified version is present. If we're at the final version,
42 * add a check on a flag that can be set for internal builds. This enables us to keep supporting
43 * old APIs in the runtime code.
44 */
writeVersionGuardStart(GeneratedFile * file,VersionInfo info,unsigned int finalVersion)45 static void writeVersionGuardStart(GeneratedFile* file, VersionInfo info, unsigned int finalVersion) {
46 if (info.intSize == 32) {
47 *file << "#ifndef __LP64__\n";
48 } else if (info.intSize == 64) {
49 *file << "#ifdef __LP64__\n";
50 }
51
52 ostringstream checkMaxVersion;
53 if (info.maxVersion > 0) {
54 checkMaxVersion << "(";
55 if (info.maxVersion == finalVersion) {
56 checkMaxVersion << "defined(RS_DECLARE_EXPIRED_APIS) || ";
57 }
58 checkMaxVersion << "RS_VERSION <= " << info.maxVersion << ")";
59 }
60
61 if (info.minVersion <= 1) {
62 // No minimum
63 if (info.maxVersion > 0) {
64 *file << "#if !defined(RS_VERSION) || " << checkMaxVersion.str() << "\n";
65 }
66 } else {
67 *file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion << ")";
68 if (info.maxVersion > 0) {
69 *file << " && " << checkMaxVersion.str();
70 }
71 *file << ")\n";
72 }
73 }
74
writeVersionGuardEnd(GeneratedFile * file,VersionInfo info)75 static void writeVersionGuardEnd(GeneratedFile* file, VersionInfo info) {
76 if (info.minVersion > 1 || info.maxVersion != 0) {
77 *file << "#endif\n";
78 }
79 if (info.intSize != 0) {
80 *file << "#endif\n";
81 }
82 }
83
writeComment(GeneratedFile * file,const string & name,const string & briefComment,const vector<string> & comment,bool addDeprecatedWarning,bool closeBlock)84 static void writeComment(GeneratedFile* file, const string& name, const string& briefComment,
85 const vector<string>& comment, bool addDeprecatedWarning,
86 bool closeBlock) {
87 if (briefComment.empty() && comment.size() == 0) {
88 return;
89 }
90 *file << "/*\n";
91 if (!briefComment.empty()) {
92 *file << " * " << name << ": " << briefComment << "\n";
93 *file << " *\n";
94 }
95 if (addDeprecatedWarning) {
96 *file << " * DEPRECATED. Do not use.\n";
97 *file << " *\n";
98 }
99 for (size_t ct = 0; ct < comment.size(); ct++) {
100 string s = stripHtml(comment[ct]);
101 s = stringReplace(s, "@", "");
102 if (!s.empty()) {
103 *file << " * " << s << "\n";
104 } else {
105 *file << " *\n";
106 }
107 }
108 if (closeBlock) {
109 *file << " */\n";
110 }
111 }
112
writeConstantComment(GeneratedFile * file,const Constant & constant)113 static void writeConstantComment(GeneratedFile* file, const Constant& constant) {
114 const string name = constant.getName();
115 writeComment(file, name, constant.getSummary(), constant.getDescription(),
116 constant.deprecated(), true);
117 }
118
writeConstantSpecification(GeneratedFile * file,const ConstantSpecification & spec)119 static void writeConstantSpecification(GeneratedFile* file, const ConstantSpecification& spec) {
120 const Constant* constant = spec.getConstant();
121 VersionInfo info = spec.getVersionInfo();
122 writeVersionGuardStart(file, info, constant->getFinalVersion());
123 *file << "#define " << constant->getName() << " " << spec.getValue() << "\n\n";
124 writeVersionGuardEnd(file, info);
125 }
126
writeTypeSpecification(GeneratedFile * file,const TypeSpecification & spec)127 static void writeTypeSpecification(GeneratedFile* file, const TypeSpecification& spec) {
128 const Type* type = spec.getType();
129 const string& typeName = type->getName();
130 const VersionInfo info = spec.getVersionInfo();
131 writeVersionGuardStart(file, info, type->getFinalVersion());
132
133 const string attribute =
134 makeAttributeTag(spec.getAttribute(), "", type->getDeprecatedApiLevel(),
135 type->getDeprecatedMessage());
136 *file << "typedef ";
137 switch (spec.getKind()) {
138 case SIMPLE:
139 *file << spec.getSimpleType() << attribute;
140 break;
141 case RS_OBJECT:
142 *file << "struct " << typeName << " _RS_OBJECT_DECL" << attribute;
143 break;
144 case ENUM: {
145 *file << "enum" << attribute << " ";
146 const string name = spec.getEnumName();
147 if (!name.empty()) {
148 *file << name << " ";
149 }
150 *file << "{\n";
151
152 const vector<string>& values = spec.getValues();
153 const vector<string>& valueComments = spec.getValueComments();
154 const size_t last = values.size() - 1;
155 for (size_t i = 0; i <= last; i++) {
156 *file << " " << values[i];
157 if (i != last) {
158 *file << ",";
159 }
160 if (valueComments.size() > i && !valueComments[i].empty()) {
161 *file << " // " << valueComments[i];
162 }
163 *file << "\n";
164 }
165 *file << "}";
166 break;
167 }
168 case STRUCT: {
169 *file << "struct" << attribute << " ";
170 const string name = spec.getStructName();
171 if (!name.empty()) {
172 *file << name << " ";
173 }
174 *file << "{\n";
175
176 const vector<string>& fields = spec.getFields();
177 const vector<string>& fieldComments = spec.getFieldComments();
178 for (size_t i = 0; i < fields.size(); i++) {
179 *file << " " << fields[i] << ";";
180 if (fieldComments.size() > i && !fieldComments[i].empty()) {
181 *file << " // " << fieldComments[i];
182 }
183 *file << "\n";
184 }
185 *file << "}";
186 break;
187 }
188 }
189 *file << " " << typeName << ";\n";
190
191 writeVersionGuardEnd(file, info);
192 *file << "\n";
193 }
194
writeTypeComment(GeneratedFile * file,const Type & type)195 static void writeTypeComment(GeneratedFile* file, const Type& type) {
196 const string name = type.getName();
197 writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
198 }
199
writeFunctionPermutation(GeneratedFile * file,const FunctionSpecification & spec,const FunctionPermutation & permutation)200 static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec,
201 const FunctionPermutation& permutation) {
202 Function* function = spec.getFunction();
203 writeVersionGuardStart(file, spec.getVersionInfo(), function->getFinalVersion());
204
205 // Write linkage info.
206 const auto inlineCodeLines = permutation.getInline();
207 if (inlineCodeLines.size() > 0) {
208 *file << "static inline ";
209 } else {
210 *file << "extern ";
211 }
212
213 // Write the return type.
214 auto ret = permutation.getReturn();
215 if (ret) {
216 *file << ret->rsType;
217 } else {
218 *file << "void";
219 }
220
221 *file << makeAttributeTag(spec.getAttribute(), spec.isOverloadable() ? "overloadable" : "",
222 function->getDeprecatedApiLevel(), function->getDeprecatedMessage());
223 *file << "\n";
224
225 // Write the function name.
226 *file << " " << permutation.getName() << "(";
227 const int offset = 4 + permutation.getName().size() + 1; // Size of above
228
229 // Write the arguments. We wrap on mulitple lines if a line gets too long.
230 int charsOnLine = offset;
231 bool hasGenerated = false;
232 for (auto p : permutation.getParams()) {
233 if (hasGenerated) {
234 *file << ",";
235 charsOnLine++;
236 }
237 ostringstream ps;
238 ps << p->rsType;
239 if (p->isOutParameter) {
240 ps << "*";
241 }
242 if (!p->specName.empty() && p->rsType != "...") {
243 ps << " " << p->specName;
244 }
245 const string s = ps.str();
246 if (charsOnLine + s.size() >= 100) {
247 *file << "\n" << string(offset, ' ');
248 charsOnLine = offset;
249 } else if (hasGenerated) {
250 *file << " ";
251 charsOnLine++;
252 }
253 *file << s;
254 charsOnLine += s.size();
255 hasGenerated = true;
256 }
257 // In C, if no parameters, we need to output void, e.g. fn(void).
258 if (!hasGenerated) {
259 *file << "void";
260 }
261 *file << ")";
262
263 // Write the inline code, if any.
264 if (inlineCodeLines.size() > 0) {
265 *file << " {\n";
266 for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) {
267 if (inlineCodeLines[ct].empty()) {
268 *file << "\n";
269 } else {
270 *file << " " << inlineCodeLines[ct] << "\n";
271 }
272 }
273 *file << "}\n";
274 } else {
275 *file << ";\n";
276 }
277
278 writeVersionGuardEnd(file, spec.getVersionInfo());
279 *file << "\n";
280 }
281
writeFunctionComment(GeneratedFile * file,const Function & function)282 static void writeFunctionComment(GeneratedFile* file, const Function& function) {
283 // Write the generic documentation.
284 writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
285 function.deprecated(), false);
286
287 // Comment the parameters.
288 if (function.someParametersAreDocumented()) {
289 *file << " *\n";
290 *file << " * Parameters:\n";
291 for (auto p : function.getParameters()) {
292 if (!p->documentation.empty()) {
293 *file << " * " << p->name << ": " << p->documentation << "\n";
294 }
295 }
296 }
297
298 // Comment the return type.
299 const string returnDoc = function.getReturnDocumentation();
300 if (!returnDoc.empty()) {
301 *file << " *\n";
302 *file << " * Returns: " << returnDoc << "\n";
303 }
304
305 *file << " */\n";
306 }
307
writeFunctionSpecification(GeneratedFile * file,const FunctionSpecification & spec)308 static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
309 // Write all the variants.
310 for (auto permutation : spec.getPermutations()) {
311 writeFunctionPermutation(file, spec, *permutation);
312 }
313 }
314
writeHeaderFile(const string & directory,const SpecFile & specFile)315 static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
316 const string headerFileName = specFile.getHeaderFileName();
317
318 // We generate one header file for each spec file.
319 GeneratedFile file;
320 if (!file.start(directory, headerFileName)) {
321 return false;
322 }
323
324 // Write the comments that start the file.
325 file.writeNotices();
326 writeComment(&file, headerFileName, specFile.getBriefDescription(),
327 specFile.getFullDescription(), false, true);
328 file << "\n";
329
330 // Write the ifndef that prevents the file from being included twice.
331 const string guard = makeGuardString(headerFileName);
332 file << "#ifndef " << guard << "\n";
333 file << "#define " << guard << "\n\n";
334
335 // Add lines that need to be put in "as is".
336 if (specFile.getVerbatimInclude().size() > 0) {
337 for (auto s : specFile.getVerbatimInclude()) {
338 file << s << "\n";
339 }
340 file << "\n";
341 }
342
343 /* Write the constants, types, and functions in the same order as
344 * encountered in the spec file.
345 */
346 set<Constant*> documentedConstants;
347 for (auto spec : specFile.getConstantSpecifications()) {
348 Constant* constant = spec->getConstant();
349 if (documentedConstants.find(constant) == documentedConstants.end()) {
350 documentedConstants.insert(constant);
351 writeConstantComment(&file, *constant);
352 }
353 writeConstantSpecification(&file, *spec);
354 }
355 set<Type*> documentedTypes;
356 for (auto spec : specFile.getTypeSpecifications()) {
357 Type* type = spec->getType();
358 if (documentedTypes.find(type) == documentedTypes.end()) {
359 documentedTypes.insert(type);
360 writeTypeComment(&file, *type);
361 }
362 writeTypeSpecification(&file, *spec);
363 }
364
365 set<Function*> documentedFunctions;
366 for (auto spec : specFile.getFunctionSpecifications()) {
367 // Do not include internal APIs in the header files.
368 if (spec->isInternal()) {
369 continue;
370 }
371 Function* function = spec->getFunction();
372 if (documentedFunctions.find(function) == documentedFunctions.end()) {
373 documentedFunctions.insert(function);
374 writeFunctionComment(&file, *function);
375 }
376 writeFunctionSpecification(&file, *spec);
377 }
378
379 file << "#endif // " << guard << "\n";
380 file.close();
381 return true;
382 }
383
generateHeaderFiles(const string & directory)384 bool generateHeaderFiles(const string& directory) {
385 bool success = true;
386 for (auto specFile : systemSpecification.getSpecFiles()) {
387 if (!writeHeaderFile(directory, *specFile)) {
388 success = false;
389 }
390 }
391 return success;
392 }
393