blob: db94b404ecfe10cb50d4bae91c6eb5858145ccd1 [file] [log] [blame]
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -07001/*
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
24using namespace std;
25
26// Convert a file name into a string that can be used to guard the include file with #ifdef...
27static 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
42static void writeVersionGuardStart(GeneratedFile* file, VersionInfo info) {
43 if (info.intSize == 32) {
44 *file << "#ifndef __LP64__\n";
45 } else if (info.intSize == 64) {
46 *file << "#ifdef __LP64__\n";
47 }
48
49 if (info.minVersion <= 1) {
50 // No minimum
51 if (info.maxVersion > 0) {
52 *file << "#if !defined(RS_VERSION) || (RS_VERSION <= " << info.maxVersion << ")\n";
53 }
54 } else {
55 if (info.maxVersion == 0) {
56 // No maximum
57 *file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion << "))\n";
58 } else {
59 *file << "#if (defined(RS_VERSION) && (RS_VERSION >= " << info.minVersion
60 << ") && (RS_VERSION <= " << info.maxVersion << "))\n";
61 }
62 }
63}
64
65static void writeVersionGuardEnd(GeneratedFile* file, VersionInfo info) {
66 if (info.minVersion > 1 || info.maxVersion != 0) {
67 *file << "#endif\n";
68 }
69 if (info.intSize != 0) {
70 *file << "#endif\n";
71 }
72}
73
74static void writeComment(GeneratedFile* file, const string& name, const string& briefComment,
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -070075 const vector<string>& comment, bool addDeprecatedWarning,
76 bool closeBlock) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -070077 if (briefComment.empty() && comment.size() == 0) {
78 return;
79 }
80 *file << "/*\n";
81 if (!briefComment.empty()) {
82 *file << " * " << name << ": " << briefComment << "\n";
83 *file << " *\n";
84 }
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -070085 if (addDeprecatedWarning) {
86 *file << " * DEPRECATED. Do not use.\n";
87 *file << " *\n";
88 }
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -070089 for (size_t ct = 0; ct < comment.size(); ct++) {
90 string s = stripHtml(comment[ct]);
91 s = stringReplace(s, "@", "");
92 if (!s.empty()) {
93 *file << " * " << s << "\n";
94 } else {
95 *file << " *\n";
96 }
97 }
98 if (closeBlock) {
99 *file << " */\n";
100 }
101}
102
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700103static void writeConstantComment(GeneratedFile* file, const Constant& constant) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700104 const string name = constant.getName();
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700105 writeComment(file, name, constant.getSummary(), constant.getDescription(),
106 constant.deprecated(), true);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700107}
108
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700109static void writeConstantSpecification(GeneratedFile* file, const ConstantSpecification& spec) {
110 VersionInfo info = spec.getVersionInfo();
111 writeVersionGuardStart(file, info);
112 *file << "#define " << spec.getConstant()->getName() << " " << spec.getValue() << "\n\n";
113 writeVersionGuardEnd(file, info);
114}
115
116static void writeTypeSpecification(GeneratedFile* file, const TypeSpecification& spec) {
117 const string& typeName = spec.getType()->getName();
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700118 const VersionInfo info = spec.getVersionInfo();
119 writeVersionGuardStart(file, info);
120 switch (spec.getKind()) {
121 case SIMPLE:
122 *file << "typedef " << spec.getSimpleType() << " " << typeName << ";\n";
123 break;
124 case ENUM: {
125 *file << "typedef enum ";
126 const string name = spec.getEnumName();
127 if (!name.empty()) {
128 *file << name << " ";
129 }
130 *file << "{\n";
131
132 const vector<string>& values = spec.getValues();
133 const vector<string>& valueComments = spec.getValueComments();
134 const size_t last = values.size() - 1;
135 for (size_t i = 0; i <= last; i++) {
136 *file << " " << values[i];
137 if (i != last) {
138 *file << ",";
139 }
140 if (valueComments.size() > i && !valueComments[i].empty()) {
141 *file << " // " << valueComments[i];
142 }
143 *file << "\n";
144 }
145 *file << "} " << typeName << ";\n";
146 break;
147 }
148 case STRUCT: {
149 *file << "typedef struct ";
150 const string name = spec.getStructName();
151 if (!name.empty()) {
152 *file << name << " ";
153 }
154 *file << "{\n";
155
156 const vector<string>& fields = spec.getFields();
157 const vector<string>& fieldComments = spec.getFieldComments();
158 for (size_t i = 0; i < fields.size(); i++) {
159 *file << " " << fields[i] << ";";
160 if (fieldComments.size() > i && !fieldComments[i].empty()) {
161 *file << " // " << fieldComments[i];
162 }
163 *file << "\n";
164 }
165 *file << "} ";
166 const string attrib = spec.getAttrib();
167 if (!attrib.empty()) {
168 *file << attrib << " ";
169 }
170 *file << typeName << ";\n";
171 break;
172 }
173 }
174 writeVersionGuardEnd(file, info);
175 *file << "\n";
176}
177
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700178static void writeTypeComment(GeneratedFile* file, const Type& type) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700179 const string name = type.getName();
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700180 writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700181}
182
183static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec,
184 const FunctionPermutation& permutation) {
185 writeVersionGuardStart(file, spec.getVersionInfo());
186
187 // Write linkage info.
188 const auto inlineCodeLines = permutation.getInline();
189 if (inlineCodeLines.size() > 0) {
190 *file << "static inline ";
191 } else {
192 *file << "extern ";
193 }
194
195 // Write the return type.
196 auto ret = permutation.getReturn();
197 if (ret) {
198 *file << ret->rsType;
199 } else {
200 *file << "void";
201 }
202
203 // Write the attribute.
204 *file << " __attribute__((";
205 const string attrib = spec.getAttribute();
206 if (attrib.empty()) {
207 *file << "overloadable";
208 } else if (attrib[0] == '=') {
209 /* If starts with an equal, we don't automatically add overloadable.
210 * This is because of the error we made defining rsUnpackColor8888().
211 */
212 *file << attrib.substr(1);
213 } else {
214 *file << attrib << ", overloadable";
215 }
216 *file << "))\n";
217
218 // Write the function name.
219 *file << " " << permutation.getName() << "(";
220 const int offset = 4 + permutation.getName().size() + 1; // Size of above
221
222 // Write the arguments. We wrap on mulitple lines if a line gets too long.
223 int charsOnLine = offset;
224 bool hasGenerated = false;
225 for (auto p : permutation.getParams()) {
226 if (hasGenerated) {
227 *file << ",";
228 charsOnLine++;
229 }
230 ostringstream ps;
231 ps << p->rsType;
232 if (p->isOutParameter) {
233 ps << "*";
234 }
235 if (!p->specName.empty()) {
236 ps << " " << p->specName;
237 }
238 const string s = ps.str();
239 if (charsOnLine + s.size() >= 100) {
240 *file << "\n" << string(offset, ' ');
241 charsOnLine = offset;
242 } else if (hasGenerated) {
243 *file << " ";
244 charsOnLine++;
245 }
246 *file << s;
247 charsOnLine += s.size();
248 hasGenerated = true;
249 }
250 // In C, if no parameters, we need to output void, e.g. fn(void).
251 if (!hasGenerated) {
252 *file << "void";
253 }
254 *file << ")";
255
256 // Write the inline code, if any.
257 if (inlineCodeLines.size() > 0) {
258 *file << " {\n";
259 for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) {
260 if (inlineCodeLines[ct].empty()) {
261 *file << "\n";
262 } else {
263 *file << " " << inlineCodeLines[ct] << "\n";
264 }
265 }
266 *file << "}\n";
267 } else {
268 *file << ";\n";
269 }
270
271 writeVersionGuardEnd(file, spec.getVersionInfo());
272 *file << "\n";
273}
274
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700275static void writeFunctionComment(GeneratedFile* file, const Function& function) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700276 // Write the generic documentation.
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700277 writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
278 function.deprecated(), false);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700279
280 // Comment the parameters.
281 if (function.someParametersAreDocumented()) {
282 *file << " *\n";
283 *file << " * Parameters:\n";
284 for (auto p : function.getParameters()) {
285 if (!p->documentation.empty()) {
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700286 *file << " * " << p->name << ": " << p->documentation << "\n";
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700287 }
288 }
289 }
290
291 // Comment the return type.
292 const string returnDoc = function.getReturnDocumentation();
293 if (!returnDoc.empty()) {
294 *file << " *\n";
295 *file << " * Returns: " << returnDoc << "\n";
296 }
297
298 *file << " */\n";
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700299}
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700300
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700301static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700302 // Write all the variants.
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700303 for (auto permutation : spec.getPermutations()) {
304 writeFunctionPermutation(file, spec, *permutation);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700305 }
306}
307
Jean-Luc Brouillet62e09932015-03-22 11:14:07 -0700308static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700309 const string headerFileName = specFile.getHeaderFileName();
310
311 // We generate one header file for each spec file.
312 GeneratedFile file;
Jean-Luc Brouillet62e09932015-03-22 11:14:07 -0700313 if (!file.start(directory, headerFileName)) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700314 return false;
315 }
316
317 // Write the comments that start the file.
318 file.writeNotices();
319 writeComment(&file, headerFileName, specFile.getBriefDescription(),
Jean-Luc Brouillet4a730042015-04-02 16:15:25 -0700320 specFile.getFullDescription(), false, true);
321 file << "\n";
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700322
323 // Write the ifndef that prevents the file from being included twice.
324 const string guard = makeGuardString(headerFileName);
325 file << "#ifndef " << guard << "\n";
326 file << "#define " << guard << "\n\n";
327
328 // Add lines that need to be put in "as is".
329 if (specFile.getVerbatimInclude().size() > 0) {
330 for (auto s : specFile.getVerbatimInclude()) {
331 file << s << "\n";
332 }
333 file << "\n";
334 }
335
336 /* Write the constants, types, and functions in the same order as
337 * encountered in the spec file.
338 */
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700339 set<Constant*> documentedConstants;
340 for (auto spec : specFile.getConstantSpecifications()) {
341 Constant* constant = spec->getConstant();
342 if (documentedConstants.find(constant) == documentedConstants.end()) {
343 documentedConstants.insert(constant);
344 writeConstantComment(&file, *constant);
345 }
346 writeConstantSpecification(&file, *spec);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700347 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700348 set<Type*> documentedTypes;
349 for (auto spec : specFile.getTypeSpecifications()) {
350 Type* type = spec->getType();
351 if (documentedTypes.find(type) == documentedTypes.end()) {
352 documentedTypes.insert(type);
353 writeTypeComment(&file, *type);
354 }
355 writeTypeSpecification(&file, *spec);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700356 }
Jean-Luc Brouillet7c078542015-03-23 16:16:08 -0700357
358 set<Function*> documentedFunctions;
359 for (auto spec : specFile.getFunctionSpecifications()) {
360 Function* function = spec->getFunction();
361 if (documentedFunctions.find(function) == documentedFunctions.end()) {
362 documentedFunctions.insert(function);
363 writeFunctionComment(&file, *function);
364 }
365 writeFunctionSpecification(&file, *spec);
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700366 }
367
368 file << "#endif // " << guard << "\n";
369 file.close();
370 return true;
371}
372
Jean-Luc Brouillet62e09932015-03-22 11:14:07 -0700373bool GenerateHeaderFiles(const string& directory) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700374 bool success = true;
375 for (auto specFile : systemSpecification.getSpecFiles()) {
Jean-Luc Brouillet62e09932015-03-22 11:14:07 -0700376 if (!writeHeaderFile(directory, *specFile)) {
Jean-Luc Brouilletc5184e22015-03-13 13:51:24 -0700377 success = false;
378 }
379 }
380 return success;
381}