GenerateHeaderFiles.cpp revision 67923a9e829d89522bb5338a6d635d807a7ee59b
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
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.  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 */
45static void writeVersionGuardStart(GeneratedFile* file, VersionInfo info, 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
75static 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
84static 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
113static 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
119static 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
127static 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    switch (spec.getKind()) {
133        case SIMPLE:
134            *file << "typedef " << spec.getSimpleType() << " " << typeName << ";\n";
135            break;
136        case ENUM: {
137            *file << "typedef enum ";
138            const string name = spec.getEnumName();
139            if (!name.empty()) {
140                *file << name << " ";
141            }
142            *file << "{\n";
143
144            const vector<string>& values = spec.getValues();
145            const vector<string>& valueComments = spec.getValueComments();
146            const size_t last = values.size() - 1;
147            for (size_t i = 0; i <= last; i++) {
148                *file << "    " << values[i];
149                if (i != last) {
150                    *file << ",";
151                }
152                if (valueComments.size() > i && !valueComments[i].empty()) {
153                    *file << " // " << valueComments[i];
154                }
155                *file << "\n";
156            }
157            *file << "} " << typeName << ";\n";
158            break;
159        }
160        case STRUCT: {
161            *file << "typedef struct ";
162            const string name = spec.getStructName();
163            if (!name.empty()) {
164                *file << name << " ";
165            }
166            *file << "{\n";
167
168            const vector<string>& fields = spec.getFields();
169            const vector<string>& fieldComments = spec.getFieldComments();
170            for (size_t i = 0; i < fields.size(); i++) {
171                *file << "    " << fields[i] << ";";
172                if (fieldComments.size() > i && !fieldComments[i].empty()) {
173                    *file << " // " << fieldComments[i];
174                }
175                *file << "\n";
176            }
177            *file << "} ";
178            const string attrib = spec.getAttrib();
179            if (!attrib.empty()) {
180                *file << attrib << " ";
181            }
182            *file << typeName << ";\n";
183            break;
184        }
185    }
186    writeVersionGuardEnd(file, info);
187    *file << "\n";
188}
189
190static void writeTypeComment(GeneratedFile* file, const Type& type) {
191    const string name = type.getName();
192    writeComment(file, name, type.getSummary(), type.getDescription(), type.deprecated(), true);
193}
194
195static void writeFunctionPermutation(GeneratedFile* file, const FunctionSpecification& spec,
196                                     const FunctionPermutation& permutation) {
197    Function* function = spec.getFunction();
198    writeVersionGuardStart(file, spec.getVersionInfo(), function->getFinalVersion());
199
200    // Write linkage info.
201    const auto inlineCodeLines = permutation.getInline();
202    if (inlineCodeLines.size() > 0) {
203        *file << "static inline ";
204    } else {
205        *file << "extern ";
206    }
207
208    // Write the return type.
209    auto ret = permutation.getReturn();
210    if (ret) {
211        *file << ret->rsType;
212    } else {
213        *file << "void";
214    }
215
216    // Write the attribute.
217    *file << " __attribute__((";
218    const string attrib = spec.getAttribute();
219    if (attrib.empty()) {
220        *file << "overloadable";
221    } else if (attrib[0] == '=') {
222        /* If starts with an equal, we don't automatically add overloadable.
223         * This is because of the error we made defining rsUnpackColor8888().
224         */
225        *file << attrib.substr(1);
226    } else {
227        *file << attrib << ", overloadable";
228    }
229    *file << "))\n";
230
231    // Write the function name.
232    *file << "    " << permutation.getName() << "(";
233    const int offset = 4 + permutation.getName().size() + 1;  // Size of above
234
235    // Write the arguments.  We wrap on mulitple lines if a line gets too long.
236    int charsOnLine = offset;
237    bool hasGenerated = false;
238    for (auto p : permutation.getParams()) {
239        if (hasGenerated) {
240            *file << ",";
241            charsOnLine++;
242        }
243        ostringstream ps;
244        ps << p->rsType;
245        if (p->isOutParameter) {
246            ps << "*";
247        }
248        if (!p->specName.empty()) {
249            ps << " " << p->specName;
250        }
251        const string s = ps.str();
252        if (charsOnLine + s.size() >= 100) {
253            *file << "\n" << string(offset, ' ');
254            charsOnLine = offset;
255        } else if (hasGenerated) {
256            *file << " ";
257            charsOnLine++;
258        }
259        *file << s;
260        charsOnLine += s.size();
261        hasGenerated = true;
262    }
263    // In C, if no parameters, we need to output void, e.g. fn(void).
264    if (!hasGenerated) {
265        *file << "void";
266    }
267    *file << ")";
268
269    // Write the inline code, if any.
270    if (inlineCodeLines.size() > 0) {
271        *file << " {\n";
272        for (size_t ct = 0; ct < inlineCodeLines.size(); ct++) {
273            if (inlineCodeLines[ct].empty()) {
274                *file << "\n";
275            } else {
276                *file << "    " << inlineCodeLines[ct] << "\n";
277            }
278        }
279        *file << "}\n";
280    } else {
281        *file << ";\n";
282    }
283
284    writeVersionGuardEnd(file, spec.getVersionInfo());
285    *file << "\n";
286}
287
288static void writeFunctionComment(GeneratedFile* file, const Function& function) {
289    // Write the generic documentation.
290    writeComment(file, function.getName(), function.getSummary(), function.getDescription(),
291                 function.deprecated(), false);
292
293    // Comment the parameters.
294    if (function.someParametersAreDocumented()) {
295        *file << " *\n";
296        *file << " * Parameters:\n";
297        for (auto p : function.getParameters()) {
298            if (!p->documentation.empty()) {
299                *file << " *   " << p->name << ": " << p->documentation << "\n";
300            }
301        }
302    }
303
304    // Comment the return type.
305    const string returnDoc = function.getReturnDocumentation();
306    if (!returnDoc.empty()) {
307        *file << " *\n";
308        *file << " * Returns: " << returnDoc << "\n";
309    }
310
311    *file << " */\n";
312}
313
314static void writeFunctionSpecification(GeneratedFile* file, const FunctionSpecification& spec) {
315    // Write all the variants.
316    for (auto permutation : spec.getPermutations()) {
317        writeFunctionPermutation(file, spec, *permutation);
318    }
319}
320
321static bool writeHeaderFile(const string& directory, const SpecFile& specFile) {
322    const string headerFileName = specFile.getHeaderFileName();
323
324    // We generate one header file for each spec file.
325    GeneratedFile file;
326    if (!file.start(directory, headerFileName)) {
327        return false;
328    }
329
330    // Write the comments that start the file.
331    file.writeNotices();
332    writeComment(&file, headerFileName, specFile.getBriefDescription(),
333                 specFile.getFullDescription(), false, true);
334    file << "\n";
335
336    // Write the ifndef that prevents the file from being included twice.
337    const string guard = makeGuardString(headerFileName);
338    file << "#ifndef " << guard << "\n";
339    file << "#define " << guard << "\n\n";
340
341    // Add lines that need to be put in "as is".
342    if (specFile.getVerbatimInclude().size() > 0) {
343        for (auto s : specFile.getVerbatimInclude()) {
344            file << s << "\n";
345        }
346        file << "\n";
347    }
348
349    /* Write the constants, types, and functions in the same order as
350     * encountered in the spec file.
351     */
352    set<Constant*> documentedConstants;
353    for (auto spec : specFile.getConstantSpecifications()) {
354        Constant* constant = spec->getConstant();
355        if (documentedConstants.find(constant) == documentedConstants.end()) {
356            documentedConstants.insert(constant);
357            writeConstantComment(&file, *constant);
358        }
359        writeConstantSpecification(&file, *spec);
360    }
361    set<Type*> documentedTypes;
362    for (auto spec : specFile.getTypeSpecifications()) {
363        Type* type = spec->getType();
364        if (documentedTypes.find(type) == documentedTypes.end()) {
365            documentedTypes.insert(type);
366            writeTypeComment(&file, *type);
367        }
368        writeTypeSpecification(&file, *spec);
369    }
370
371    set<Function*> documentedFunctions;
372    for (auto spec : specFile.getFunctionSpecifications()) {
373        Function* function = spec->getFunction();
374        if (documentedFunctions.find(function) == documentedFunctions.end()) {
375            documentedFunctions.insert(function);
376            writeFunctionComment(&file, *function);
377        }
378        writeFunctionSpecification(&file, *spec);
379    }
380
381    file << "#endif // " << guard << "\n";
382    file.close();
383    return true;
384}
385
386bool generateHeaderFiles(const string& directory) {
387    bool success = true;
388    for (auto specFile : systemSpecification.getSpecFiles()) {
389        if (!writeHeaderFile(directory, *specFile)) {
390            success = false;
391        }
392    }
393    return success;
394}
395